diff --git a/backend/core/pom.xml b/backend/core/pom.xml index efa0217cb..6f0c0f994 100644 --- a/backend/core/pom.xml +++ b/backend/core/pom.xml @@ -46,7 +46,7 @@ gr.cite oidc-authz - 2.1.0 + 2.2.0 org.opencdmp diff --git a/backend/core/src/main/java/org/opencdmp/event/EventBroker.java b/backend/core/src/main/java/org/opencdmp/event/EventBroker.java index defc5be38..fe4ad8ecf 100644 --- a/backend/core/src/main/java/org/opencdmp/event/EventBroker.java +++ b/backend/core/src/main/java/org/opencdmp/event/EventBroker.java @@ -53,6 +53,10 @@ public class EventBroker { this.applicationEventPublisher.publishEvent(event); } + public void emit(PlanStatusTouchedEvent event) { + this.applicationEventPublisher.publishEvent(event); + } + public void emit(TagTouchedEvent event) { this.applicationEventPublisher.publishEvent(event); } diff --git a/backend/core/src/main/java/org/opencdmp/event/PlanStatusTouchedEvent.java b/backend/core/src/main/java/org/opencdmp/event/PlanStatusTouchedEvent.java new file mode 100644 index 000000000..0822dc2a9 --- /dev/null +++ b/backend/core/src/main/java/org/opencdmp/event/PlanStatusTouchedEvent.java @@ -0,0 +1,24 @@ +package org.opencdmp.event; + +import java.util.UUID; + +public class PlanStatusTouchedEvent { + + public PlanStatusTouchedEvent() { + } + + public PlanStatusTouchedEvent(UUID id) { + this.id = id; + } + + private UUID id; + + public UUID getId() { + return id; + } + + public void setId(UUID id) { + this.id = id; + } + +} diff --git a/backend/core/src/main/java/org/opencdmp/event/PlanTouchedEvent.java b/backend/core/src/main/java/org/opencdmp/event/PlanTouchedEvent.java index 13f362c3a..edc4feab7 100644 --- a/backend/core/src/main/java/org/opencdmp/event/PlanTouchedEvent.java +++ b/backend/core/src/main/java/org/opencdmp/event/PlanTouchedEvent.java @@ -22,3 +22,4 @@ public class PlanTouchedEvent { } } + diff --git a/backend/core/src/main/java/org/opencdmp/query/PlanStatusQuery.java b/backend/core/src/main/java/org/opencdmp/query/PlanStatusQuery.java index 503757bfa..add261cc3 100644 --- a/backend/core/src/main/java/org/opencdmp/query/PlanStatusQuery.java +++ b/backend/core/src/main/java/org/opencdmp/query/PlanStatusQuery.java @@ -193,6 +193,8 @@ public class PlanStatusQuery extends QueryBase { return PlanStatusEntity._internalStatus; else if (item.prefix(PlanStatus._definition)) return PlanStatusEntity._definition; + else if (item.match(PlanStatus._definition)) + return PlanStatusEntity._definition; else if (item.match(PlanStatus._createdAt)) return PlanStatusEntity._createdAt; else if (item.match(PlanStatus._updatedAt)) diff --git a/backend/core/src/main/java/org/opencdmp/service/planstatus/PlanStatusService.java b/backend/core/src/main/java/org/opencdmp/service/planstatus/PlanStatusService.java index a2df92905..6af2ea77c 100644 --- a/backend/core/src/main/java/org/opencdmp/service/planstatus/PlanStatusService.java +++ b/backend/core/src/main/java/org/opencdmp/service/planstatus/PlanStatusService.java @@ -1,5 +1,6 @@ package org.opencdmp.service.planstatus; +import gr.cite.commons.web.authz.configuration.Permission; import gr.cite.tools.exception.MyApplicationException; import gr.cite.tools.exception.MyForbiddenException; import gr.cite.tools.exception.MyNotFoundException; @@ -10,6 +11,7 @@ import org.opencdmp.model.persist.planstatus.PlanStatusPersist; import org.opencdmp.model.planstatus.PlanStatus; import javax.management.InvalidApplicationException; +import java.util.HashMap; import java.util.List; import java.util.UUID; diff --git a/backend/core/src/main/java/org/opencdmp/service/planstatus/PlanStatusServiceImpl.java b/backend/core/src/main/java/org/opencdmp/service/planstatus/PlanStatusServiceImpl.java index d9bdb9e5f..378ed2d8f 100644 --- a/backend/core/src/main/java/org/opencdmp/service/planstatus/PlanStatusServiceImpl.java +++ b/backend/core/src/main/java/org/opencdmp/service/planstatus/PlanStatusServiceImpl.java @@ -1,5 +1,6 @@ package org.opencdmp.service.planstatus; +import gr.cite.commons.web.authz.configuration.PermissionPolicyContext; import gr.cite.commons.web.authz.service.AuthorizationService; import gr.cite.tools.data.builder.BuilderFactory; import gr.cite.tools.data.deleter.DeleterFactory; @@ -29,6 +30,8 @@ import org.opencdmp.data.PlanStatusEntity; import org.opencdmp.data.TenantEntityManager; import org.opencdmp.errorcode.ErrorThesaurusProperties; import org.opencdmp.event.EventBroker; +import org.opencdmp.event.PlanStatusTouchedEvent; +import org.opencdmp.event.PlanTouchedEvent; import org.opencdmp.model.builder.planstatus.PlanStatusBuilder; import org.opencdmp.model.deleter.PlanStatusDeleter; import org.opencdmp.model.persist.planstatus.PlanStatusDefinitionAuthorizationItemPersist; @@ -36,6 +39,7 @@ import org.opencdmp.model.persist.planstatus.PlanStatusDefinitionAuthorizationPe import org.opencdmp.model.persist.planstatus.PlanStatusDefinitionPersist; import org.opencdmp.model.persist.planstatus.PlanStatusPersist; import org.opencdmp.model.planstatus.PlanStatus; +import org.opencdmp.model.planstatus.PlanStatusDefinitionAuthorization; import org.opencdmp.query.PlanStatusQuery; import org.opencdmp.service.planworkflow.PlanWorkflowService; import org.slf4j.LoggerFactory; @@ -45,9 +49,7 @@ import org.springframework.stereotype.Service; import javax.management.InvalidApplicationException; import java.time.Instant; -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; +import java.util.*; import java.util.stream.Collectors; @Service @@ -66,7 +68,7 @@ public class PlanStatusServiceImpl implements PlanStatusService { private final ErrorThesaurusProperties errors; private final QueryFactory queryFactory; private final PlanWorkflowService planWorkflowService; - + private final EventBroker eventBroker; public PlanStatusServiceImpl(BuilderFactory builderFactory, DeleterFactory deleterFactory, AuthorizationService authorizationService, ConventionService conventionService, XmlHandlingService xmlHandlingService, TenantEntityManager entityManager, MessageSource messageSource, ErrorThesaurusProperties errors, EventBroker eventBroker, QueryFactory queryFactory, PlanWorkflowService planWorkflowService) { this.builderFactory = builderFactory; this.deleterFactory = deleterFactory; @@ -79,6 +81,7 @@ public class PlanStatusServiceImpl implements PlanStatusService { this.errors = errors; this.queryFactory = queryFactory; this.planWorkflowService = planWorkflowService; + this.eventBroker = eventBroker; } @Override @@ -115,6 +118,8 @@ public class PlanStatusServiceImpl implements PlanStatusService { this.entityManager.flush(); + this.eventBroker.emit(new PlanStatusTouchedEvent(data.getId())); + return this.builderFactory.builder(PlanStatusBuilder.class).build(BaseFieldSet.build(fields, PlanStatus._id), data); } diff --git a/backend/web/pom.xml b/backend/web/pom.xml index c5ec3c171..29727e9ef 100644 --- a/backend/web/pom.xml +++ b/backend/web/pom.xml @@ -128,6 +128,11 @@ exceptions-web 2.2.0 + + gr.cite + oidc-authz + 2.2.0 + diff --git a/backend/web/src/main/java/org/opencdmp/configurations/OpencdmpPermissionPolicyContextImpl.java b/backend/web/src/main/java/org/opencdmp/configurations/OpencdmpPermissionPolicyContextImpl.java new file mode 100644 index 000000000..82f23052f --- /dev/null +++ b/backend/web/src/main/java/org/opencdmp/configurations/OpencdmpPermissionPolicyContextImpl.java @@ -0,0 +1,62 @@ +package org.opencdmp.configurations; + +import gr.cite.commons.web.authz.configuration.AuthorizationConfiguration; +import gr.cite.commons.web.authz.configuration.Permission; +import gr.cite.commons.web.authz.configuration.PermissionPolicyContextImpl; +import gr.cite.tools.data.query.QueryFactory; +import gr.cite.tools.fieldset.BaseFieldSet; +import gr.cite.tools.logging.LoggerService; +import org.opencdmp.commons.XmlHandlingService; +import org.opencdmp.commons.enums.IsActive; +import org.opencdmp.commons.types.planstatus.PlanStatusDefinitionEntity; +import org.opencdmp.data.PlanStatusEntity; +import org.opencdmp.event.PlanStatusTouchedEvent; +import org.opencdmp.model.planstatus.PlanStatus; +import org.opencdmp.model.planstatus.PlanStatusDefinitionAuthorization; +import org.opencdmp.query.PlanStatusQuery; +import org.slf4j.LoggerFactory; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Service; + +import java.util.*; + +@Service +public class OpencdmpPermissionPolicyContextImpl extends PermissionPolicyContextImpl { + private final QueryFactory queryFactory; + private final XmlHandlingService xmlHandlingService; + private static final LoggerService logger = new LoggerService(LoggerFactory.getLogger(OpencdmpPermissionPolicyContextImpl.class)); + + public OpencdmpPermissionPolicyContextImpl(AuthorizationConfiguration authorizationConfiguration, QueryFactory queryFactory, XmlHandlingService xmlHandlingService) { + super(authorizationConfiguration); + this.queryFactory = queryFactory; + this.xmlHandlingService = xmlHandlingService; + } + + @EventListener + public void handleTenantTouchedEvent(PlanStatusTouchedEvent event) { + this.refresh(true); + } + + @Override + public void refresh(boolean force) { + if (!force && this.policies != null) return; + this.policies = this.authorizationConfiguration.getRawPolicies(); + this.extendedClaims = this.authorizationConfiguration.getRawExtendedClaims(); + this.policies.putAll(this.buildPlanStatusPolicies()); + logger.info("Authorization policies found: {}", this.policies.size()); + this.reload(); + } + + private HashMap buildPlanStatusPolicies(){ + HashMap policies = new HashMap<>(); + List entities = this.queryFactory.query(PlanStatusQuery.class).isActives(IsActive.Active).collectAs(new BaseFieldSet().ensure(PlanStatus._id).ensure(PlanStatus._definition)); + for (PlanStatusEntity entity : entities) { + PlanStatusDefinitionEntity definition = this.xmlHandlingService.fromXmlSafe(PlanStatusDefinitionEntity.class, entity.getDefinition()); + if (definition != null && definition.getAuthorization() != null && definition.getAuthorization().getEdit() != null) policies.put(this.getPlanStatusCanEditStatusPermission(entity.getId()), new gr.cite.commons.web.authz.configuration.Permission(new HashSet<>(definition.getAuthorization().getEdit().getRoles()), new ArrayList<>(), new HashSet<>(), definition.getAuthorization().getEdit().getAllowAnonymous(), definition.getAuthorization().getEdit().getAllowAuthenticated())); + } + return policies; + } + private String getPlanStatusCanEditStatusPermission(UUID id){ + return "PlanStatus" + "_" + id + "_" + PlanStatusDefinitionAuthorization._edit; + } +} diff --git a/backend/web/src/main/java/org/opencdmp/configurations/SecurityConfiguration.java b/backend/web/src/main/java/org/opencdmp/configurations/SecurityConfiguration.java index 065766c5f..460a1cab3 100644 --- a/backend/web/src/main/java/org/opencdmp/configurations/SecurityConfiguration.java +++ b/backend/web/src/main/java/org/opencdmp/configurations/SecurityConfiguration.java @@ -3,7 +3,6 @@ package org.opencdmp.configurations; import org.opencdmp.authorization.*; import gr.cite.commons.web.authz.handler.AuthorizationHandler; -import gr.cite.commons.web.authz.handler.PermissionClientAuthorizationHandler; import gr.cite.commons.web.authz.policy.AuthorizationRequirement; import gr.cite.commons.web.authz.policy.AuthorizationRequirementMapper; import gr.cite.commons.web.authz.policy.AuthorizationResource; @@ -26,9 +25,7 @@ import org.springframework.security.web.authentication.preauth.AbstractPreAuthen import jakarta.servlet.Filter; import jakarta.servlet.http.HttpServletRequest; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; +import java.util.*; @Configuration @EnableWebSecurity @@ -42,10 +39,10 @@ public class SecurityConfiguration { @Autowired public SecurityConfiguration(WebSecurityProperties webSecurityProperties, - @Qualifier("tokenAuthenticationResolver") AuthenticationManagerResolver authenticationManagerResolver, - @Qualifier("apiKeyFilter") Filter apiKeyFilter, - @Qualifier("ownedAuthorizationHandler") OwnedAuthorizationHandler ownedAuthorizationHandler, - @Qualifier("affiliatedAuthorizationHandler") AffiliatedAuthorizationHandler affiliatedAuthorizationHandler) { + @Qualifier("tokenAuthenticationResolver") AuthenticationManagerResolver authenticationManagerResolver, + @Qualifier("apiKeyFilter") Filter apiKeyFilter, + @Qualifier("ownedAuthorizationHandler") OwnedAuthorizationHandler ownedAuthorizationHandler, + @Qualifier("affiliatedAuthorizationHandler") AffiliatedAuthorizationHandler affiliatedAuthorizationHandler) { this.webSecurityProperties = webSecurityProperties; this.authenticationManagerResolver = authenticationManagerResolver; this.apiKeyFilter = apiKeyFilter; @@ -107,6 +104,11 @@ public class SecurityConfiguration { }; } +// @Bean() +// public PermissionPolicyContext permissionPolicyContext(){ +// return new PermissionPolicyContextImpl(configuration); +// } + @Bean AuthorizationRequirementMapper authorizationRequirementMapper() { return new AuthorizationRequirementMapper() { @@ -147,4 +149,5 @@ public class SecurityConfiguration { } return endpoint; } -} \ No newline at end of file +} + diff --git a/backend/web/src/main/java/org/opencdmp/models/AccountBuilder.java b/backend/web/src/main/java/org/opencdmp/models/AccountBuilder.java index 6a58e7e5b..bfc1b2562 100644 --- a/backend/web/src/main/java/org/opencdmp/models/AccountBuilder.java +++ b/backend/web/src/main/java/org/opencdmp/models/AccountBuilder.java @@ -1,7 +1,7 @@ package org.opencdmp.models; -import gr.cite.commons.web.authz.configuration.AuthorizationConfiguration; import gr.cite.commons.web.authz.configuration.Permission; +import gr.cite.commons.web.authz.configuration.PermissionPolicyContext; import gr.cite.commons.web.oidc.principal.CurrentPrincipalResolver; import gr.cite.commons.web.oidc.principal.MyPrincipal; import gr.cite.commons.web.oidc.principal.extractor.ClaimExtractor; @@ -33,7 +33,7 @@ public class AccountBuilder { private final ClaimExtractor claimExtractor; private final Set excludeMoreClaim; private final CurrentPrincipalResolver currentPrincipalResolver; - private final AuthorizationConfiguration authorizationConfiguration; + private final PermissionPolicyContext permissionPolicyContext; private final AuthorizationContentResolver authorizationContentResolver; private final JsonHandlingService jsonHandlingService; private final UserScope userScope; @@ -42,10 +42,10 @@ public class AccountBuilder { private final QueryFactory queryFactory; private final BuilderFactory builderFactory; - public AccountBuilder(ClaimExtractor claimExtractor, CurrentPrincipalResolver currentPrincipalResolver, AuthorizationConfiguration authorizationConfiguration, AuthorizationContentResolver authorizationContentResolver, JsonHandlingService jsonHandlingService, UserScope userScope, TenantEntityManager entityManager, TenantScope tenantScope, QueryFactory queryFactory, BuilderFactory builderFactory) { + public AccountBuilder(ClaimExtractor claimExtractor, CurrentPrincipalResolver currentPrincipalResolver, PermissionPolicyContext permissionPolicyContext, AuthorizationContentResolver authorizationContentResolver, JsonHandlingService jsonHandlingService, UserScope userScope, TenantEntityManager entityManager, TenantScope tenantScope, QueryFactory queryFactory, BuilderFactory builderFactory) { this.claimExtractor = claimExtractor; this.currentPrincipalResolver = currentPrincipalResolver; - this.authorizationConfiguration = authorizationConfiguration; + this.permissionPolicyContext = permissionPolicyContext; this.authorizationContentResolver = authorizationContentResolver; this.jsonHandlingService = jsonHandlingService; this.userScope = userScope; @@ -107,8 +107,8 @@ public class AccountBuilder { } if (fields.hasField(Account._permissions)) { List roles = this.claimExtractor.roles(this.currentPrincipalResolver.currentPrincipal()); - Set permissions = this.authorizationConfiguration.permissionsOfRoles(roles); - for (Map.Entry permissionEntry : this.authorizationConfiguration.getRawPolicies().entrySet()){ + Set permissions = this.permissionPolicyContext.permissionsOfRoles(roles); + for (Map.Entry permissionEntry : this.permissionPolicyContext.getRawPolicies().entrySet()){ if (permissionEntry.getValue().getAllowAuthenticated()){ permissions.add(permissionEntry.getKey()); }