2023-10-09 17:20:47 +02:00
|
|
|
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) {
|
2023-10-10 15:41:48 +02:00
|
|
|
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());
|
|
|
|
});
|
2023-10-09 17:20:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public InputStream loadAvatarImage(RealmModel realmModel, UserModel userModel) {
|
2023-10-10 15:41:48 +02:00
|
|
|
logger.debugf("Loading avatar from S3 for user: %s", userModel.getUsername());
|
2023-10-09 17:20:47 +02:00
|
|
|
return execute(new Executor<InputStream>() {
|
|
|
|
public InputStream execute(MinioClient minioClient) throws Exception {
|
|
|
|
try {
|
2023-10-10 15:41:48 +02:00
|
|
|
String avatarFileName = getAvatarFilePath(realmModel, userModel);
|
|
|
|
logger.tracef("Getting avatar file object: %s", avatarFileName);
|
2023-10-09 17:20:47 +02:00
|
|
|
return minioClient
|
2023-10-10 15:41:48 +02:00
|
|
|
.getObject(
|
|
|
|
GetObjectArgs.builder().bucket(configuration.rootBucket).object(avatarFileName)
|
|
|
|
.build());
|
2023-10-09 17:20:47 +02:00
|
|
|
} catch (ErrorResponseException e) {
|
|
|
|
if (e.response().code() == 404) {
|
2023-10-10 15:41:48 +02:00
|
|
|
logger.debugf("Avatar file not found for user '%s' in realm '%s'", userModel.getUsername(),
|
|
|
|
realmModel.getName());
|
|
|
|
|
2023-10-09 17:20:47 +02:00
|
|
|
return null;
|
|
|
|
} else {
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void deleteAvatarImage(RealmModel realmModel, UserModel userModel) {
|
2023-10-10 15:41:48 +02:00
|
|
|
logger.debugf("Deleting avatar from S3 for user: %s", userModel.getUsername());
|
2023-10-09 17:20:47 +02:00
|
|
|
execute(minioClient -> {
|
2023-10-10 15:41:48 +02:00
|
|
|
String avatarFileName = getAvatarFilePath(realmModel, userModel);
|
|
|
|
logger.tracef("Deleting avatar file object: %s", avatarFileName);
|
2023-10-09 17:20:47 +02:00
|
|
|
minioClient.removeObject(RemoveObjectArgs.builder().bucket(configuration.rootBucket)
|
2023-10-10 15:41:48 +02:00
|
|
|
.object(avatarFileName).build());
|
|
|
|
|
2023-10-09 17:20:47 +02:00
|
|
|
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;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|