keycloak-d4science-spi-parent/avatar-realm-resource/src/main/java/org/gcube/keycloak/avatar/storage/s3/MinioAvatarStorageProvider....

139 lines
4.8 KiB
Java

package org.gcube.keycloak.avatar.storage.s3;
import java.io.InputStream;
import org.gcube.keycloak.avatar.storage.AvatarStorageProvider;
import org.jboss.logging.Logger;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import io.minio.GetObjectArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.RemoveObjectArgs;
import io.minio.errors.ErrorResponseException;
public class MinioAvatarStorageProvider implements AvatarStorageProvider {
private static final Logger logger = Logger.getLogger(MinioAvatarStorageProvider.class);
private static final String AVATAR_FOLDER = "avatar";
private static final int CHUNK_SIZE = 5242880;
private final Configuration configuration;
public MinioAvatarStorageProvider(Configuration configuration) {
this.configuration = configuration;
}
@Override
public void saveAvatarImage(RealmModel realmModel, UserModel userModel, InputStream input) {
logger.debugf("Saving avatar to S3 for user: %s", userModel.getUsername());
execute(minioClient -> {
String avatarFileName = getAvatarFilePath(realmModel, userModel);
logger.tracef("Saving avatar file object as: %s", avatarFileName);
return minioClient
.putObject(PutObjectArgs.builder().bucket(configuration.rootBucket).object(avatarFileName)
.stream(input, -1, CHUNK_SIZE).build());
});
}
@Override
public InputStream loadAvatarImage(RealmModel realmModel, UserModel userModel) {
logger.debugf("Loading avatar from S3 for user: %s", userModel.getUsername());
return execute(new Executor<InputStream>() {
public InputStream execute(MinioClient minioClient) throws Exception {
try {
String avatarFileName = getAvatarFilePath(realmModel, userModel);
logger.tracef("Getting avatar file object: %s", avatarFileName);
return minioClient
.getObject(
GetObjectArgs.builder().bucket(configuration.rootBucket).object(avatarFileName)
.build());
} catch (ErrorResponseException e) {
if (e.response().code() == 404) {
logger.debugf("Avatar file not found for user '%s' in realm '%s'", userModel.getUsername(),
realmModel.getName());
return null;
} else {
throw e;
}
}
};
});
}
@Override
public void deleteAvatarImage(RealmModel realmModel, UserModel userModel) {
logger.debugf("Deleting avatar from S3 for user: %s", userModel.getUsername());
execute(minioClient -> {
String avatarFileName = getAvatarFilePath(realmModel, userModel);
logger.tracef("Deleting avatar file object: %s", avatarFileName);
minioClient.removeObject(RemoveObjectArgs.builder().bucket(configuration.rootBucket)
.object(avatarFileName).build());
return true;
});
}
@Override
public void close() {
// NOOP
}
public String getAvatarFilePath(RealmModel realmModel, UserModel userModel) {
return AVATAR_FOLDER + "/" + realmModel.getName() + "/" + userModel.getUsername();
}
public <T> T execute(Executor<T> executor) {
try {
MinioClient minioClient = MinioClient.builder()
.endpoint(configuration.getServerUrl())
.credentials(configuration.getAccessKey(), configuration.getSecretKey())
.build();
return executor.execute(minioClient);
} catch (Exception e) {
throw new RuntimeException("Executing operation on S3 persistnce", e);
}
}
public static class Configuration {
private final String serverUrl;
private final String accessKey;
private final String secretKey;
private final String rootBucket;
public Configuration(String serverUrl, String accessKey, String secretKey, String rootBucket) {
this.serverUrl = serverUrl;
this.accessKey = accessKey;
this.secretKey = secretKey;
this.rootBucket = rootBucket;
}
public String getServerUrl() {
return serverUrl;
}
public String getAccessKey() {
return accessKey;
}
public String getSecretKey() {
return secretKey;
}
public String getRootBucket() {
return rootBucket;
}
}
public interface Executor<T> {
T execute(MinioClient minioClient) throws Exception;
}
}