diff --git a/build.gradle.kts b/build.gradle.kts index da1b094..eb9cd4e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -61,7 +61,7 @@ tasks.named("bootBuildImage") { dependencies { - implementation("com.knecon.fforesight:keycloak-commons:0.12.0") + implementation("com.knecon.fforesight:keycloak-commons:0.14.0") implementation("com.knecon.fforesight:swagger-commons:0.5.0") implementation("org.postgresql:postgresql:42.5.4") implementation("com.google.guava:guava:31.1-jre") diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/properties/TenantUserManagementProperties.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/properties/TenantUserManagementProperties.java index 30c2dea..28f981e 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/properties/TenantUserManagementProperties.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/properties/TenantUserManagementProperties.java @@ -14,6 +14,7 @@ import lombok.Data; public class TenantUserManagementProperties { private String serverUrl; + private String publicServerUrl; private String realm; private String applicationClientId; private String clientId; diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java index a7ca005..99ed2cb 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java @@ -28,6 +28,7 @@ import org.springframework.jdbc.core.StatementCallback; import org.springframework.jdbc.datasource.SingleConnectionDataSource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.ObjectUtils; import org.springframework.web.server.ResponseStatusException; import com.knecon.fforesight.tenantcommons.EncryptionDecryptionService; @@ -75,20 +76,22 @@ public class TenantManagementService implements TenantProvider { @Value("${fforesight.tenant-exchange.name}") private String tenantExchangeName; - @Value("${fforesight.tenant-use-management.server-url:}") - private String authServerUrl; - @SneakyThrows public void createTenant(TenantRequest tenantRequest) { + log.info("Requested to create tenant for: {}", tenantRequest.getTenantId()); + try { if (tenantRepository.findById(tenantRequest.getTenantId()).isEmpty()) { + log.info("Creating tenant: {}", tenantRequest.getTenantId()); var jdbcUrl = JDBCUtils.buildJdbcUrlWithSchema(tenantRequest.getDatabaseConnection()); validateJdbcUrl(jdbcUrl); createSchema(tenantRequest); + log.info("Created schema for tenant: {}", tenantRequest.getTenantId()); + TenantEntity tenantEntity = TenantEntity.builder() .tenantId(tenantRequest.getTenantId()) .displayName(tenantRequest.getDisplayName()) @@ -132,6 +135,7 @@ public class TenantManagementService implements TenantProvider { } createRealm(tenantRequest.getTenantId(), tenantRequest.getDefaultUsers()); + log.info("Created realm for tenant: {}", tenantRequest.getTenantId()); var waitTime = 0; boolean realmReady; @@ -153,19 +157,25 @@ public class TenantManagementService implements TenantProvider { generalConfigurationService.initGeneralConfiguration(tenantRequest.getTenantId()); keyCloakRoleManagerService.updateRoles(tenantRequest.getTenantId()); + log.info("Updated roles for tenant: {}", tenantRequest.getTenantId()); + tenantRepository.save(tenantEntity); + log.info("Persisted tenant: {}", tenantRequest.getTenantId()); + TenantContext.setTenantId(tenantEntity.getTenantId()); rabbitTemplate.convertAndSend(tenantExchangeName, "tenant.created", new TenantCreatedEvent(tenantEntity.getTenantId())); TenantContext.clear(); + log.info("Dispatched message for tenant: {}", tenantRequest.getTenantId()); + } else { throw new ResponseStatusException(HttpStatus.CONFLICT, "Tenant exists"); } } catch (Exception e) { log.error("Tenant creation failed, rolling back", e); tenantRepository.deleteById(tenantRequest.getTenantId()); - throw new ResponseStatusException(HttpStatus.BAD_REQUEST,"Tenant creation failed", e); + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Tenant creation failed", e); } } @@ -217,9 +227,11 @@ public class TenantManagementService implements TenantProvider { realm.setAccessTokenLifespan(tenantUserManagementProperties.getTenantAccessTokenLifeSpan()); realm.setSsoSessionIdleTimeout(tenantUserManagementProperties.getSsoSessionIdleTimeout()); - Map attributes = new HashMap<>(); - attributes.put(FRONTEND_URL_PROPERY, authServerUrl); - realm.setAttributes(attributes); + if (!ObjectUtils.isEmpty(tenantUserManagementProperties.getPublicServerUrl())) { + Map attributes = new HashMap<>(); + attributes.put(FRONTEND_URL_PROPERY, tenantUserManagementProperties.getPublicServerUrl()); + realm.setAttributes(attributes); + } var applicationClient = new ClientRepresentation(); applicationClient.setEnabled(true); diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/UserService.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/UserService.java index 65ba5cc..1a7fc86 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/UserService.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/UserService.java @@ -155,7 +155,12 @@ public class UserService { var newRolesRank = newRoles.stream().map(r -> roleMapping.getRole(r).getRank()).toList(); var maxNewRolesRank = newRolesRank.stream().max(Integer::compare).orElse(-1); - var untouchableRoles = userRoles.stream().filter(roleMapping::isValidRole).map(roleMapping::getRole).filter(r -> r.getRank() > maxRank).map(KCRole::getName).collect(Collectors.toSet()); + var untouchableRoles = userRoles.stream() + .filter(roleMapping::isValidRole) + .map(roleMapping::getRole) + .filter(r -> r.getRank() > maxRank) + .map(KCRole::getName) + .collect(Collectors.toSet()); if (maxNewRolesRank > maxRank) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Cannot assign this role to that user. Insufficient rights"); @@ -165,7 +170,7 @@ public class UserService { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Cannot modify some roles for this user. Insufficient rights"); } - if (userId.equals(KeycloakSecurity.getUserId()) && maxRank.equals(roleMapping.getMaxRank()) && !maxNewRolesRank.equals(maxRank) ) { + if (userId.equals(KeycloakSecurity.getUserId()) && maxRank.equals(roleMapping.getMaxRank()) && !maxNewRolesRank.equals(maxRank)) { throw new ResponseStatusException(HttpStatus.CONFLICT, "Cannot remove highest ranking role from self."); } @@ -361,10 +366,10 @@ public class UserService { } var currentUserResource = getUserResource(KeycloakSecurity.getUserId()); - var currentRoles = currentUserResource.roles().realmLevel().listEffective().stream().map(RoleRepresentation::getName).toList(); + var currentRoles = getRoles(currentUserResource.toRepresentation().getId()); var userResource = getUserResource(userId); - var userRoles = userResource.roles().realmLevel().listEffective().stream().map(RoleRepresentation::getName).toList(); + var userRoles = getRoles(userId); var roleMapping = tenantUserManagementProperties.getKcRoleMapping(); var maxRank = currentRoles.stream().map(r -> roleMapping.getRole(r).getRank()).max(Integer::compare).orElse(-1); @@ -374,7 +379,7 @@ public class UserService { throw new ResponseStatusException(HttpStatus.FORBIDDEN, "It is not allowed to delete a user with higher ranking roles"); } - var userToBeRemoved = getUserByUsername(userId); + var userToBeRemoved = getUserByUsername(userResource.toRepresentation().getUsername()); userResource.remove(); this.rabbitTemplate.convertAndSend(userExchangeName, "user.deleted", (new UserRemovedEvent(userToBeRemoved, KeycloakSecurity.getUserId()))); diff --git a/src/main/resources/application-redaction.yaml b/src/main/resources/application-redaction.yaml index 082fe05..b133f31 100644 --- a/src/main/resources/application-redaction.yaml +++ b/src/main/resources/application-redaction.yaml @@ -6,9 +6,9 @@ fforesight: tenant-access-token-life-span: 300 realm: master default-theme: 'redaction' - valid-redirect-uris: ['/redaction-gateway-v1/*','/tenant-user-management/*','http://localhost:4200/*'] + valid-redirect-uris: [ '/redaction-gateway-v1/*','/tenant-user-management/*','http://localhost:4200/*','/ui/*' ] kc-role-mapping: - unmappedPermissions: ["red-unarchive-dossier", "red-update-license", "red-get-rss","fforesight-create-tenant"] + unmappedPermissions: [ "red-unarchive-dossier", "red-update-license", "red-get-rss","fforesight-create-tenant" ] compositeRoles: - name: RED_MANAGER composites: @@ -22,34 +22,34 @@ fforesight: - name: RED_USER set-by-default: true rank: 100 - permissions: ["red-add-comment", "red-read-license", "red-read-app-configuration", "red-read-dossier-status", "red-add-dossier-dictionary-entry", "red-add-redaction", "red-add-update-dossier-dictionary-type", - "red-delete-comment", "red-delete-dossier-dictionary-entry", "red-delete-dossier-dictionary-type", "red-delete-file", "red-delete-manual-redaction", "red-download-annotated-file", - "red-download-original-file", "red-download-redacted-file", "red-download-redaction-preview-file", "red-download-report-template", "red-exclude-include-file", - "red-exclude-include-pages", "red-get-report-templates", "fforesight-manage-user-preferences", "red-manage-viewed-pages", "red-process-download", "red-process-manual-redaction-request", - "red-read-colors", "red-read-dictionary-types", "red-read-digital-signature", "red-read-dossier", "red-read-dossier-attributes", "red-read-dossier-attributes-config", - "red-read-dossier-templates", "red-read-download-status", "red-read-file-attributes-config", "red-read-file-status", "fforesight-read-general-configuration", "red-read-legal-basis", - "red-read-manual-redactions", "red-read-notification", "red-read-redaction-log", "red-read-rules", "fforesight-read-users", "red-read-versions", "red-read-watermark", "red-reanalyze-dossier", - "red-reanalyze-file", "red-request-redaction", "red-rotate-page", "red-search", "red-search-audit-log", "red-set-reviewer", "red-set-status-approved", "red-set-status-under-approval", - "fforesight-update-my-profile", "red-update-notification", "red-upload-file", "red-write-file-attributes", "red-process-texthighlights", "red-get-highlights", "red-convert-highlights", "red-delete-highlights", "red-delete-imported-redactions"] + permissions: [ "red-add-comment", "red-read-license", "red-read-app-configuration", "red-read-dossier-status", "red-add-dossier-dictionary-entry", "red-add-redaction", "red-add-update-dossier-dictionary-type", + "red-delete-comment", "red-delete-dossier-dictionary-entry", "red-delete-dossier-dictionary-type", "red-delete-file", "red-delete-manual-redaction", "red-download-annotated-file", + "red-download-original-file", "red-download-redacted-file", "red-download-redaction-preview-file", "red-download-report-template", "red-exclude-include-file", + "red-exclude-include-pages", "red-get-report-templates", "fforesight-manage-user-preferences", "red-manage-viewed-pages", "red-process-download", "red-process-manual-redaction-request", + "red-read-colors", "red-read-dictionary-types", "red-read-digital-signature", "red-read-dossier", "red-read-dossier-attributes", "red-read-dossier-attributes-config", + "red-read-dossier-templates", "red-read-download-status", "red-read-file-attributes-config", "red-read-file-status", "fforesight-read-general-configuration", "red-read-legal-basis", + "red-read-manual-redactions", "red-read-notification", "red-read-redaction-log", "red-read-rules", "fforesight-read-users", "red-read-versions", "red-read-watermark", "red-reanalyze-dossier", + "red-reanalyze-file", "red-request-redaction", "red-rotate-page", "red-search", "red-search-audit-log", "red-set-reviewer", "red-set-status-approved", "red-set-status-under-approval", + "fforesight-update-my-profile", "red-update-notification", "red-upload-file", "red-write-file-attributes", "red-process-texthighlights", "red-get-highlights", "red-convert-highlights", "red-delete-highlights", "red-delete-imported-redactions" ] - name: RED_ADMIN set-by-default: false rank: 800 - permissions: ["red-add-dictionary-entry", "red-add-update-dictionary-type", "red-write-dossier-status", "red-read-dossier-status", "red-delete-dictionary-entry", "red-delete-dictionary-type", - "red-delete-report-template", "red-download-report-template", "red-get-report-templates", "fforesight-manage-user-preferences", "red-ead-colors", "red-read-dictionary-types", - "red-read-digital-signature", "red-read-dossier-attributes", "red-read-dossier-attributes-config", "red-read-dossier-templates", "red-read-file-attributes-config", - "red-read-legal-basis", "red-read-license-report", "red-read-notification", "red-read-rules", "fforesight-read-smtp-configuration", "red-read-versions", "red-read-watermark", - "red-reindex", "red-search-audit-log", "red-update-notification", "red-upload-report-template", "red-write-colors", "red-write-digital-signature", "red-write-dossier-attributes-config", - "red-write-dossier-templates", "red-write-file-attributes-config", "fforesight-write-general-configuration", "red-write-legal-basis", "red-write-rules", "fforesight-write-smtp-configuration", - "red-write-watermark", "red-write-app-configuration", "red-manage-acl-permissions", "fforesight-create-tenant", "fforesight-get-tenants", "fforesight-deployment-info"] + permissions: [ "red-add-dictionary-entry", "red-add-update-dictionary-type", "red-write-dossier-status", "red-read-dossier-status", "red-delete-dictionary-entry", "red-delete-dictionary-type", + "red-delete-report-template", "red-download-report-template", "red-get-report-templates", "fforesight-manage-user-preferences", "red-ead-colors", "red-read-dictionary-types", + "red-read-digital-signature", "red-read-dossier-attributes", "red-read-dossier-attributes-config", "red-read-dossier-templates", "red-read-file-attributes-config", + "red-read-legal-basis", "red-read-license-report", "red-read-notification", "red-read-rules", "fforesight-read-smtp-configuration", "red-read-versions", "red-read-watermark", + "red-reindex", "red-search-audit-log", "red-update-notification", "red-upload-report-template", "red-write-colors", "red-write-digital-signature", "red-write-dossier-attributes-config", + "red-write-dossier-templates", "red-write-file-attributes-config", "fforesight-write-general-configuration", "red-write-legal-basis", "red-write-rules", "fforesight-write-smtp-configuration", + "red-write-watermark", "red-write-app-configuration", "red-manage-acl-permissions", "fforesight-create-tenant", "fforesight-get-tenants", "fforesight-deployment-info" ] - name: RED_MANAGER set-by-default: false rank: 200 - permissions: ["red-add-update-dossier", "red-archived-dossier", "red-delete-dossier", "red-write-dossier-attributes"] + permissions: [ "red-add-update-dossier", "red-archived-dossier", "red-delete-dossier", "red-write-dossier-attributes" ] - name: RED_USER_ADMIN set-by-default: false rank: 400 - permissions: ["fforesight-manage-user-preferences", "fforesight-read-all-users", "red-read-dossier", "red-read-app-configuration", "fforesight-read-general-configuration", - "red-read-notification", "fforesight-read-users", "fforesight-update-my-profile", "red-update-notification", "fforesight-write-users", "red-read-license"] + permissions: [ "fforesight-manage-user-preferences", "fforesight-read-all-users", "red-read-dossier", "red-read-app-configuration", "fforesight-read-general-configuration", + "red-read-notification", "fforesight-read-users", "fforesight-update-my-profile", "red-update-notification", "fforesight-write-users", "red-read-license" ] springdoc: default-tenant: 'redaction'