argos/dmp-backend/core/src/main/java/eu/eudat/service/tenant/TenantServiceImpl.java

231 lines
12 KiB
Java

package eu.eudat.service.tenant;
import eu.eudat.authorization.AuthorizationFlags;
import eu.eudat.authorization.AuthorizationProperties;
import eu.eudat.authorization.Permission;
import eu.eudat.commons.XmlHandlingService;
import eu.eudat.commons.enums.IsActive;
import eu.eudat.commons.scope.tenant.TenantScope;
import eu.eudat.convention.ConventionService;
import eu.eudat.data.TenantEntity;
import eu.eudat.data.TenantEntityManager;
import eu.eudat.data.UserCredentialEntity;
import eu.eudat.data.UserRoleEntity;
import eu.eudat.errorcode.ErrorThesaurusProperties;
import eu.eudat.integrationevent.outbox.tenantremoval.TenantRemovalIntegrationEvent;
import eu.eudat.integrationevent.outbox.tenantremoval.TenantRemovalIntegrationEventHandler;
import eu.eudat.integrationevent.outbox.tenanttouched.TenantTouchedIntegrationEvent;
import eu.eudat.integrationevent.outbox.tenanttouched.TenantTouchedIntegrationEventHandler;
import eu.eudat.model.Tenant;
import eu.eudat.model.builder.TenantBuilder;
import eu.eudat.model.deleter.TenantDeleter;
import eu.eudat.model.persist.TenantPersist;
import eu.eudat.query.UserCredentialQuery;
import eu.eudat.query.UserRoleQuery;
import eu.eudat.service.encryption.EncryptionService;
import eu.eudat.service.keycloak.KeycloakService;
import gr.cite.commons.web.authz.service.AuthorizationService;
import gr.cite.tools.data.builder.BuilderFactory;
import gr.cite.tools.data.deleter.DeleterFactory;
import gr.cite.tools.data.query.QueryFactory;
import gr.cite.tools.exception.MyApplicationException;
import gr.cite.tools.exception.MyForbiddenException;
import gr.cite.tools.exception.MyNotFoundException;
import gr.cite.tools.exception.MyValidationException;
import gr.cite.tools.fieldset.BaseFieldSet;
import gr.cite.tools.fieldset.FieldSet;
import gr.cite.tools.logging.LoggerService;
import gr.cite.tools.logging.MapLogEntry;
import org.jetbrains.annotations.NotNull;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.management.InvalidApplicationException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@Service
public class TenantServiceImpl implements TenantService {
private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(TenantServiceImpl.class));
private final TenantEntityManager entityManager;
private final AuthorizationService authorizationService;
private final DeleterFactory deleterFactory;
private final BuilderFactory builderFactory;
private final ConventionService conventionService;
private final MessageSource messageSource;
private final XmlHandlingService xmlHandlingService;
private final ErrorThesaurusProperties errors;
private final EncryptionService encryptionService;
private final TenantProperties properties;
private final TenantTouchedIntegrationEventHandler tenantTouchedIntegrationEventHandler;
private final TenantRemovalIntegrationEventHandler tenantRemovalIntegrationEventHandler;
private final KeycloakService keycloakService;
private final AuthorizationProperties authorizationProperties;
private final TenantScope tenantScope;
private final QueryFactory queryFactory;
@Autowired
public TenantServiceImpl(
TenantEntityManager entityManager,
AuthorizationService authorizationService,
DeleterFactory deleterFactory,
BuilderFactory builderFactory,
ConventionService conventionService,
MessageSource messageSource,
XmlHandlingService xmlHandlingService,
ErrorThesaurusProperties errors,
EncryptionService encryptionService, TenantProperties properties, TenantTouchedIntegrationEventHandler tenantTouchedIntegrationEventHandler, TenantRemovalIntegrationEventHandler tenantRemovalIntegrationEventHandler, KeycloakService keycloakService, AuthorizationProperties authorizationProperties, TenantScope tenantScope, QueryFactory queryFactory) {
this.entityManager = entityManager;
this.authorizationService = authorizationService;
this.deleterFactory = deleterFactory;
this.builderFactory = builderFactory;
this.conventionService = conventionService;
this.messageSource = messageSource;
this.xmlHandlingService = xmlHandlingService;
this.errors = errors;
this.encryptionService = encryptionService;
this.properties = properties;
this.tenantTouchedIntegrationEventHandler = tenantTouchedIntegrationEventHandler;
this.tenantRemovalIntegrationEventHandler = tenantRemovalIntegrationEventHandler;
this.keycloakService = keycloakService;
this.authorizationProperties = authorizationProperties;
this.tenantScope = tenantScope;
this.queryFactory = queryFactory;
}
@Override
public Tenant persist(TenantPersist model, FieldSet fields) throws MyForbiddenException, MyValidationException, MyApplicationException, MyNotFoundException, InvalidApplicationException, InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
logger.debug(new MapLogEntry("persisting data").And("model", model).And("fields", fields));
this.authorizationService.authorizeForce(Permission.EditTenant);
Boolean isUpdate = this.conventionService.isValidGuid(model.getId());
TenantEntity data;
if (isUpdate) {
data = this.entityManager.find(TenantEntity.class, model.getId());
if (data == null)
throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Tenant.class.getSimpleName()}, LocaleContextHolder.getLocale()));
if (!this.conventionService.hashValue(data.getUpdatedAt()).equals(model.getHash()))
throw new MyValidationException(this.errors.getHashConflict().getCode(), this.errors.getHashConflict().getMessage());
} else {
data = new TenantEntity();
data.setId(UUID.randomUUID());
data.setIsActive(IsActive.Active);
data.setCreatedAt(Instant.now());
}
data.setCode(model.getCode());
data.setName(model.getName());
data.setDescription(model.getDescription());
data.setUpdatedAt(Instant.now());
if (isUpdate) this.entityManager.merge(data);
else this.entityManager.persist(data);
this.entityManager.flush();
if (!isUpdate) {
this.keycloakService.createTenantGroups(data.getCode());
this.autoAssignGlobalAdminsToNewTenant(data);
}
TenantTouchedIntegrationEvent tenantTouchedIntegrationEvent = new TenantTouchedIntegrationEvent();
tenantTouchedIntegrationEvent.setId(data.getId());
tenantTouchedIntegrationEvent.setCode(data.getCode());
this.tenantTouchedIntegrationEventHandler.handle(tenantTouchedIntegrationEvent);
return this.builderFactory.builder(TenantBuilder.class).authorize(AuthorizationFlags.OwnerOrDmpAssociatedOrPermission).build(BaseFieldSet.build(fields, Tenant._id), data);
}
private void autoAssignGlobalAdminsToNewTenant(TenantEntity tenant){
if (!this.authorizationProperties.getAutoAssignGlobalAdminToNewTenants()) return;
List<UserRoleEntity> existingItems;
List<UserCredentialEntity> userCredentialEntities;
try {
this.tenantScope.setTempTenant(this.entityManager.getEntityManager(), null, this.tenantScope.getDefaultTenantCode());
existingItems = this.queryFactory.query(UserRoleQuery.class).tenantIsSet(false).roles(this.authorizationProperties.getGlobalAdminRole()).collect();
userCredentialEntities = this.queryFactory.query(UserCredentialQuery.class).userIds(existingItems.stream().map(UserRoleEntity::getUserId).distinct().toList()).collect();
} finally {
this.tenantScope.removeTempTenant(this.entityManager.getEntityManager());
}
try {
this.tenantScope.setTempTenant(this.entityManager.getEntityManager(), tenant.getId(), tenant.getCode());
for (UUID userId : existingItems.stream().map(UserRoleEntity::getUserId).distinct().toList()) {
UserCredentialEntity userCredential = userCredentialEntities.stream().filter(x-> !this.conventionService.isNullOrEmpty(x.getExternalId()) && x.getUserId().equals(userId)).findFirst().orElse(null);
if (userCredential == null) continue;
UserRoleEntity item = new UserRoleEntity();
item.setId(UUID.randomUUID());
item.setUserId(userId);
item.setRole(this.authorizationProperties.getGlobalAdminRole());
item.setCreatedAt(Instant.now());
this.entityManager.persist(item);
this.keycloakService.addUserToGlobalRoleGroup(userCredential.getExternalId(), this.authorizationProperties.getGlobalAdminRole());
}
} finally {
this.tenantScope.removeTempTenant(this.entityManager.getEntityManager());
}
this.entityManager.flush();
}
@Override
public Tenant decryptTenant(Tenant model) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException, InvalidApplicationException {
// if (model.getConfig() != null && model.getConfig().getDeposit() != null && model.getConfig().getDeposit().getSources() != null) {
// for (TenantSource source : model.getConfig().getDeposit().getSources().stream().toList()) {
// source.setClientSecret(this.encryptionService.decryptAES(source.getClientSecret(), properties.getConfigEncryptionAesKey(), properties.getConfigEncryptionAesIv()));
// }
// }
// if (model.getConfig() != null && model.getConfig().getFileTransformers() != null && model.getConfig().getFileTransformers().getSources() != null) {
// for (TenantSource source : model.getConfig().getFileTransformers().getSources().stream().toList()) {
// source.setClientSecret(this.encryptionService.decryptAES(source.getClientSecret(), properties.getConfigEncryptionAesKey(), properties.getConfigEncryptionAesIv()));
// }
// }
TenantEntity data = this.entityManager.find(TenantEntity.class, model.getId());
if (data == null) throw new MyNotFoundException(messageSource.getMessage("General_ItemNotFound", new Object[]{model.getId(), Tenant.class.getSimpleName()}, LocaleContextHolder.getLocale()));
TenantTouchedIntegrationEvent tenantTouchedIntegrationEvent = new TenantTouchedIntegrationEvent();
tenantTouchedIntegrationEvent.setId(data.getId());
tenantTouchedIntegrationEvent.setCode(data.getCode());
this.tenantTouchedIntegrationEventHandler.handle(tenantTouchedIntegrationEvent);
return model;
}
@Override
public void deleteAndSave(UUID id) throws MyForbiddenException, InvalidApplicationException {
logger.debug("deleting : {}", id);
this.authorizationService.authorizeForce(Permission.DeleteTenant);
this.deleterFactory.deleter(TenantDeleter.class).deleteAndSaveByIds(List.of(id));
TenantRemovalIntegrationEvent tenantRemovalIntegrationEvent = new TenantRemovalIntegrationEvent();
tenantRemovalIntegrationEvent.setId(id);
this.tenantRemovalIntegrationEventHandler.handle(tenantRemovalIntegrationEvent);
}
}