diff --git a/src/main/java/coop/libriciel/ipcore/business/auth/DelegationBusinessService.java b/src/main/java/coop/libriciel/ipcore/business/auth/DelegationBusinessService.java new file mode 100644 index 0000000000000000000000000000000000000000..7e4e02aaaba2d39ea8a3c4246961e8a7fca2730a --- /dev/null +++ b/src/main/java/coop/libriciel/ipcore/business/auth/DelegationBusinessService.java @@ -0,0 +1,98 @@ +/* + * iparapheur Core + * Copyright (C) 2018-2025 Libriciel-SCOP + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package coop.libriciel.ipcore.business.auth; + +import coop.libriciel.ipcore.model.auth.Desk; +import coop.libriciel.ipcore.model.database.Subtype; +import coop.libriciel.ipcore.model.database.Tenant; +import coop.libriciel.ipcore.model.database.Type; +import coop.libriciel.ipcore.model.permission.DelegationDto; +import coop.libriciel.ipcore.services.auth.AuthServiceInterface; +import coop.libriciel.ipcore.services.database.SubtypeRepository; +import coop.libriciel.ipcore.services.database.TypeRepository; +import coop.libriciel.ipcore.utils.LocalizedStatusException; +import lombok.extern.log4j.Log4j2; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; +import org.springframework.stereotype.Service; + +import java.util.*; + +import static org.springframework.http.HttpStatus.*; + + +@Log4j2 +@Service +public class DelegationBusinessService { + + + // <editor-fold desc="Beans"> + + + private final AuthServiceInterface authService; + private final SubtypeRepository subtypeRepository; + private final TypeRepository typeRepository; + + + public DelegationBusinessService(AuthServiceInterface authService, + SubtypeRepository subtypeRepository, + TypeRepository typeRepository) { + this.authService = authService; + this.subtypeRepository = subtypeRepository; + this.typeRepository = typeRepository; + } + + + // </editor-fold desc="Beans"> + + + public void checkDelegationIntegrity(@NotNull Tenant tenant, @NotNull Desk desk, DelegationDto delegationDto) { + + if (StringUtils.equals(desk.getId(), delegationDto.getSubstituteDeskId())) { + throw new LocalizedStatusException(NOT_ACCEPTABLE, "message.redundant_delegation_target"); + } + + log.debug("tenant:{}, desk:{}", tenant.getId(), delegationDto.getSubstituteDeskId()); + + Optional.ofNullable(delegationDto.getSubstituteDeskId()) + .map(id -> authService.findDeskByIdNoException(tenant.getId(), id)) + .map(Desk::getId) + .orElseThrow(() -> new LocalizedStatusException(NOT_FOUND, "message.unknown_desk_id")); + + if (StringUtils.isEmpty(delegationDto.getTypeId()) && StringUtils.isNotEmpty(delegationDto.getSubtypeId())) { + log.error("The type is mandatory on a subtype-restricted delegation"); + throw new LocalizedStatusException(NOT_FOUND, "messages.unknown_subtype_id"); + } + + if (StringUtils.isNotEmpty(delegationDto.getTypeId()) && StringUtils.isEmpty(delegationDto.getSubtypeId())) { + typeRepository.findByIdAndTenant_Id(delegationDto.getTypeId(), tenant.getId()) + .map(Type::getId) + .orElseThrow(() -> new LocalizedStatusException(NOT_FOUND, "messages.unknown_type_id")); + } + + if (StringUtils.isNoneEmpty(delegationDto.getTypeId(), delegationDto.getSubtypeId())) { + subtypeRepository.findByIdAndParentType_IdAndTenant_Id(delegationDto.getSubtypeId(), delegationDto.getTypeId(), tenant.getId()) + .map(Subtype::getId) + .orElseThrow(() -> new LocalizedStatusException(NOT_FOUND, "messages.unknown_subtype_id")); + } + } + + +} diff --git a/src/main/java/coop/libriciel/ipcore/business/auth/DeskBusinessService.java b/src/main/java/coop/libriciel/ipcore/business/auth/DeskBusinessService.java index 46245f04545325d67c98cb83d43cf8f8d64b564d..5f5470cdb97deebff225e6ad5396c7e8a18b9098 100644 --- a/src/main/java/coop/libriciel/ipcore/business/auth/DeskBusinessService.java +++ b/src/main/java/coop/libriciel/ipcore/business/auth/DeskBusinessService.java @@ -146,19 +146,7 @@ public class DeskBusinessService { // Fetch real models - Set<String> deskIds = Stream - .concat(delegationDtoList.stream().map(DelegationDto::getDelegatingDeskId), - delegationDtoList.stream().map(DelegationDto::getSubstituteDeskId)) - .collect(toSet()); - - // TODO: this, with both delegation and delegating desks - // updateInnerValues( - // delegationDtoList.stream().collect(toMap( - // delegationDto -> () -> singletonList(delegationDto.getDelegatingDeskId()), - // delegationDto -> desks -> delegationDto.setDelegatingDesk(desks.stream().findFirst().orElse(null)) - // )) - // ); - + Set<String> deskIds = delegationDtoList.stream().map(DelegationDto::getSubstituteDeskId).collect(toSet()); Map<String, String> deskNames = authService.getDeskNames(deskIds); Set<String> typeIds = delegationDtoList.stream().map(DelegationDto::getTypeId).collect(toSet()); @@ -181,9 +169,6 @@ public class DeskBusinessService { delegationDtoList.forEach(delegationDto -> { - String delegatingDeskId = delegationDto.getDelegatingDeskId(); - delegationDto.setDelegatingDesk(new DeskRepresentation(delegatingDeskId, deskNames.get(delegatingDeskId))); - String substituteDeskId = delegationDto.getSubstituteDeskId(); delegationDto.setSubstituteDesk(new DeskRepresentation(substituteDeskId, deskNames.get(substituteDeskId))); diff --git a/src/main/java/coop/libriciel/ipcore/business/typology/TypologyBusinessService.java b/src/main/java/coop/libriciel/ipcore/business/typology/TypologyBusinessService.java index 9da20b093d2ae57e308e67995d6495ddbc26daa4..5bf4d696f550b9efb054d91bb54824e1ad2b6cef 100644 --- a/src/main/java/coop/libriciel/ipcore/business/typology/TypologyBusinessService.java +++ b/src/main/java/coop/libriciel/ipcore/business/typology/TypologyBusinessService.java @@ -232,14 +232,18 @@ public class TypologyBusinessService { tenantId, delegationList.stream() .collect(toMap( - delegationDto -> delegationDto::getTypeId, + delegationDto -> () -> Optional.ofNullable(delegationDto.getType()) + .map(TypeRepresentation::getId) + .orElse(null), delegationDto -> type -> Optional.ofNullable(type) .map(t -> returnRedactedResponse(modelMapper, t, TypeRepresentation::new)) .ifPresent(delegationDto::setType) )), delegationList.stream() .collect(toMap( - delegationDto -> delegationDto::getSubtypeId, + delegationDto -> () -> Optional.ofNullable(delegationDto.getSubtype()) + .map(SubtypeRepresentation::getId) + .orElse(null), delegationDto -> subtype -> Optional.ofNullable(subtype) .map(st -> returnRedactedResponse(modelMapper, st, SubtypeRepresentation::new)) .ifPresent(delegationDto::setSubtype) diff --git a/src/main/java/coop/libriciel/ipcore/configuration/ModelMapperConfigurer.java b/src/main/java/coop/libriciel/ipcore/configuration/ModelMapperConfigurer.java index c80c2ef2ac7b70f0dc09c8bda7aaed080b788578..af4ed1384533d25f84728283ab68e0e61f43fc5b 100644 --- a/src/main/java/coop/libriciel/ipcore/configuration/ModelMapperConfigurer.java +++ b/src/main/java/coop/libriciel/ipcore/configuration/ModelMapperConfigurer.java @@ -21,16 +21,13 @@ package coop.libriciel.ipcore.configuration; import coop.libriciel.ipcore.model.auth.Desk; import coop.libriciel.ipcore.model.auth.DeskRepresentation; -import coop.libriciel.ipcore.model.auth.User; import coop.libriciel.ipcore.model.auth.requests.DeskDto; import coop.libriciel.ipcore.model.auth.requests.UserRepresentation; import coop.libriciel.ipcore.model.crypto.CertificateInformations; import coop.libriciel.ipcore.model.crypto.SealCertificate; import coop.libriciel.ipcore.model.crypto.SealCertificateDto; import coop.libriciel.ipcore.model.database.*; -import coop.libriciel.ipcore.model.database.requests.SubtypeDto; -import coop.libriciel.ipcore.model.database.requests.SubtypeLayerDto; -import coop.libriciel.ipcore.model.database.requests.SubtypeMetadataDto; +import coop.libriciel.ipcore.model.database.requests.*; import coop.libriciel.ipcore.model.database.userPreferences.*; import coop.libriciel.ipcore.model.pdfstamp.Layer; import coop.libriciel.ipcore.model.permission.Delegation; @@ -135,23 +132,45 @@ public class ModelMapperConfigurer { .map(TypologyEntity::getId) .orElse(null); + public static final Converter<TypeRepresentation, String> TYPE_REPRESENTATION_TO_ID_CONVERTER = + context -> Optional.ofNullable(context.getSource()) + .map(TypeRepresentation::getId) + .orElse(null); + public static final Converter<String, Type> ID_TO_TYPE_CONVERTER = context -> Optional.ofNullable(context.getSource()) .filter(StringUtils::isNotEmpty) .map(i -> Type.builder().id(i).build()) .orElse(null); + public static final Converter<String, TypeRepresentation> ID_TO_TYPE_REPRESENTATION_CONVERTER = + context -> Optional.ofNullable(context.getSource()) + .filter(StringUtils::isNotEmpty) + .map(i -> new TypeRepresentation(i, null, null)) + .orElse(null); + public static final Converter<Subtype, String> SUBTYPE_TO_ID_CONVERTER = context -> Optional.ofNullable(context.getSource()) .map(TypologyEntity::getId) .orElse(null); + public static final Converter<SubtypeRepresentation, String> SUBTYPE_REPRESENTATION_TO_ID_CONVERTER = + context -> Optional.ofNullable(context.getSource()) + .map(SubtypeRepresentation::getId) + .orElse(null); + public static final Converter<String, Subtype> ID_TO_SUBTYPE_CONVERTER = context -> Optional.ofNullable(context.getSource()) .filter(StringUtils::isNotEmpty) .map(i -> Subtype.builder().id(i).build()) .orElse(null); + public static final Converter<String, SubtypeRepresentation> ID_TO_SUBTYPE_REPRESENTATION_CONVERTER = + context -> Optional.ofNullable(context.getSource()) + .filter(StringUtils::isNotEmpty) + .map(i -> new SubtypeRepresentation(i, null)) + .orElse(null); + private static final Converter<Layer, String> LAYER_TO_ID_CONVERTER = context -> Optional.ofNullable(context.getSource()) .map(Layer::getId) @@ -174,22 +193,11 @@ public class ModelMapperConfigurer { .map(i -> Metadata.builder().id(i).build()) .orElse(null); - public static final Converter<User, String> USER_TO_ID_CONVERTER = - context -> Optional.ofNullable(context.getSource()) - .map(User::getId) - .orElse(null); - public static final Converter<UserRepresentation, String> USER_REP_TO_ID_CONVERTER = context -> Optional.ofNullable(context.getSource()) .map(UserRepresentation::getId) .orElse(null); - public static final Converter<String, User> ID_TO_USER_CONVERTER = - context -> Optional.ofNullable(context.getSource()) - .filter(StringUtils::isNotEmpty) - .map(User::new) - .orElse(null); - public static final Converter<String, UserRepresentation> ID_TO_USER_REP_CONVERTER = context -> Optional.ofNullable(context.getSource()) .filter(StringUtils::isNotEmpty) @@ -443,24 +451,20 @@ public class ModelMapperConfigurer { private void setupDelegationDto(@NotNull ModelMapper modelMapper) { modelMapper.typeMap(Delegation.class, DelegationDto.class) - .addMappings(m -> m.using(DESK_REPRESENTATION_TO_ID_CONVERTER) - .map(Delegation::getDelegatingDesk, DelegationDto::setDelegatingDeskId)) - .addMappings(m -> m.using(DESK_REPRESENTATION_TO_ID_CONVERTER) - .map(Delegation::getSubstituteDesk, DelegationDto::setSubstituteDeskId)) - .addMappings(m -> m.using(TYPE_TO_ID_CONVERTER) - .map(Delegation::getType, DelegationDto::setTypeId)) - .addMappings(m -> m.using(SUBTYPE_TO_ID_CONVERTER) - .map(Delegation::getSubtype, DelegationDto::setSubtypeId)); + .addMappings(m -> m.using(ID_TO_DESK_REPRESENTATION_CONVERTER) + .map(Delegation::getSubstituteDeskId, DelegationDto::setSubstituteDesk)) + .addMappings(m -> m.using(ID_TO_TYPE_REPRESENTATION_CONVERTER) + .map(Delegation::getTypeId, DelegationDto::setType)) + .addMappings(m -> m.using(ID_TO_SUBTYPE_REPRESENTATION_CONVERTER) + .map(Delegation::getSubtypeId, DelegationDto::setSubtype)); modelMapper.emptyTypeMap(DelegationDto.class, Delegation.class) - .addMappings(m -> m.using(ID_TO_DESK_REPRESENTATION_CONVERTER) - .map(DelegationDto::getDelegatingDeskId, Delegation::setDelegatingDesk)) - .addMappings(m -> m.using(ID_TO_DESK_REPRESENTATION_CONVERTER) - .map(DelegationDto::getSubstituteDeskId, Delegation::setSubstituteDesk)) - .addMappings(m -> m.using(ID_TO_TYPE_CONVERTER) - .map(DelegationDto::getTypeId, Delegation::setType)) - .addMappings(m -> m.using(ID_TO_SUBTYPE_CONVERTER) - .map(DelegationDto::getSubtypeId, Delegation::setSubtype)) + .addMappings(m -> m.using(DESK_REPRESENTATION_TO_ID_CONVERTER) + .map(DelegationDto::getSubstituteDesk, Delegation::setSubstituteDeskId)) + .addMappings(m -> m.using(TYPE_REPRESENTATION_TO_ID_CONVERTER) + .map(DelegationDto::getType, Delegation::setTypeId)) + .addMappings(m -> m.using(SUBTYPE_REPRESENTATION_TO_ID_CONVERTER) + .map(DelegationDto::getSubtype, Delegation::setSubtypeId)) .implicitMappings(); } diff --git a/src/main/java/coop/libriciel/ipcore/controller/DeskController.java b/src/main/java/coop/libriciel/ipcore/controller/DeskController.java index ae7ea9d961e38235e4243f919755d96020e07d49..3a837ff1d2b7fdc43faa6109eb5bf0890c52388a 100644 --- a/src/main/java/coop/libriciel/ipcore/controller/DeskController.java +++ b/src/main/java/coop/libriciel/ipcore/controller/DeskController.java @@ -19,6 +19,7 @@ package coop.libriciel.ipcore.controller; +import coop.libriciel.ipcore.business.auth.DelegationBusinessService; import coop.libriciel.ipcore.business.auth.DeskBusinessService; import coop.libriciel.ipcore.model.auth.Desk; import coop.libriciel.ipcore.model.auth.DeskRepresentation; @@ -104,6 +105,7 @@ public class DeskController { private final AuthServiceInterface authService; + private final DelegationBusinessService delegationBusinessService; private final DeskBusinessService deskBusinessService; private final MetadataRepository metadataRepository; private final ModelMapper modelMapper; @@ -114,6 +116,7 @@ public class DeskController { @Autowired public DeskController(AuthServiceInterface authService, + DelegationBusinessService delegationBusinessService, DeskBusinessService deskBusinessService, MetadataRepository metadataRepository, ModelMapper modelMapper, @@ -121,6 +124,7 @@ public class DeskController { StatsServiceInterface statsService, WorkflowServiceInterface workflowService) { this.authService = authService; + this.delegationBusinessService = delegationBusinessService; this.deskBusinessService = deskBusinessService; this.metadataRepository = metadataRepository; this.modelMapper = modelMapper; @@ -370,17 +374,19 @@ public class DeskController { @TenantResolved Tenant tenant, @Parameter(description = DeskRepresentation.API_DOC_ID_VALUE) @PathVariable(name = DeskRepresentation.API_PATH) String deskId, + @DeskResolved Desk desk, @Parameter(description = DelegationDto.API_DOC, required = true) @RequestBody DelegationDto delegationDto) { log.info("createDelegation delegating:{} substitute:{}", deskId, delegationDto.getSubstituteDeskId()); log.info(" type:{} subtype:{}", delegationDto.getTypeId(), delegationDto.getSubtypeId()); + // Integrity check + // TODO: Check if the current user is the desk owner or a delegationManager + delegationBusinessService.checkDelegationIntegrity(tenant, desk, delegationDto); - if (StringUtils.equals(deskId, delegationDto.getSubstituteDeskId())) { - throw new LocalizedStatusException(NOT_ACCEPTABLE, "message.redundant_delegation_target"); - } + // Actual register permissionService.addDelegation( tenant.getId(), @@ -397,6 +403,54 @@ public class DeskController { } + @PutMapping(API_INTERNAL + "/admin/tenant/{tenantId}/desk/{deskId}/delegation/{delegationId}") + @Operation(summary = "Update a delegation from target Desk") + @PreAuthorize(PREAUTHORIZE_TENANT_MEMBER_OR_MORE) + @ApiResponses(value = { + @ApiResponse(responseCode = CODE_200), + @ApiResponse(responseCode = CODE_401, content = @Content(schema = @Schema(implementation = ErrorResponse.class))), + @ApiResponse(responseCode = CODE_403, content = @Content(schema = @Schema(implementation = ErrorResponse.class))), + @ApiResponse(responseCode = CODE_404, content = @Content(schema = @Schema(implementation = ErrorResponse.class))), + }) + public DelegationDto updateDelegation(@Parameter(description = Tenant.API_DOC_ID_VALUE) + @PathVariable(name = Tenant.API_PATH) String tenantId, + @TenantResolved Tenant tenant, + @Parameter(description = DeskRepresentation.API_DOC_ID_VALUE) + @PathVariable(name = DeskRepresentation.API_PATH) String deskId, + @DeskResolved Desk desk, + @Parameter(description = DelegationDto.API_DOC_ID_VALUE) + @PathVariable(name = DelegationDto.API_PATH) String delegationId, + @DelegationResolved Delegation existingDelegation, + @Parameter(description = DelegationDto.API_DOC, required = true) + @RequestBody DelegationDto updatedDelegation) { + + log.info("updateDelegationAsAdmin - delegating desk : {} ", deskId); + log.trace("updateDelegationAsAdmin - existingDelegation:{} delegationUpdate:{}", existingDelegation, updatedDelegation); + + // Integrity check + + // TODO: Check if the current user is the desk owner or a delegationManager + delegationBusinessService.checkDelegationIntegrity(tenant, desk, updatedDelegation); + + // Actual update + + permissionService.deleteDelegation(tenant.getId(), existingDelegation); + + permissionService.addDelegation( + tenant.getId(), + updatedDelegation.getSubstituteDeskId(), + deskId, + updatedDelegation.getTypeId(), + updatedDelegation.getSubtypeId(), + updatedDelegation.getStart(), + updatedDelegation.getEnd() + ); + + statsService.registerAdminAction(tenant, DESK, UPDATE, deskId); + return updatedDelegation; + } + + @DeleteMapping(API_INTERNAL + "/tenant/{tenantId}/desk/{deskId}/delegation/{delegationId}") @Operation(summary = "Remove an active or planned delegation from target Desk") @PreAuthorize(PREAUTHORIZE_TENANT_MEMBER_OR_MORE) @@ -420,17 +474,7 @@ public class DeskController { // TODO: Check if the current user is the desk owner or a delegationManager - permissionService.deleteDelegation( - delegationId, - tenant.getId(), - delegation.getSubstituteDesk().getId(), - deskId, - Optional.ofNullable(delegation.getType()).map(Type::getId).orElse(null), - Optional.ofNullable(delegation.getSubtype()).map(Subtype::getId).orElse(null), - delegation.getStart(), - delegation.getEnd() - ); - + permissionService.deleteDelegation(tenant.getId(), delegation); statsService.registerAdminAction(tenant, DESK, UPDATE, deskId); } @@ -460,7 +504,6 @@ public class DeskController { .map(delegation -> modelMapper.map(delegation, DelegationDto.class)) .collect(toMutableList()); - delegationsDtoList.forEach(dto -> dto.setDelegatingDeskId(deskId)); deskBusinessService.updateInnerDeskValues(tenantId, delegationsDtoList); // TODO: Check if the current user is the desk owner or a delegationManager diff --git a/src/main/java/coop/libriciel/ipcore/controller/admin/AdminDeskController.java b/src/main/java/coop/libriciel/ipcore/controller/admin/AdminDeskController.java index 48429976f3f7b970829113ba84ab06bfda1afe45..bc80d147d4621d296b95b197b1a6f022f9188b4d 100644 --- a/src/main/java/coop/libriciel/ipcore/controller/admin/AdminDeskController.java +++ b/src/main/java/coop/libriciel/ipcore/controller/admin/AdminDeskController.java @@ -19,6 +19,7 @@ package coop.libriciel.ipcore.controller.admin; +import coop.libriciel.ipcore.business.auth.DelegationBusinessService; import coop.libriciel.ipcore.business.auth.DeskBusinessService; import coop.libriciel.ipcore.business.auth.UserBusinessService; import coop.libriciel.ipcore.business.database.MetadataBusinessService; @@ -27,13 +28,10 @@ import coop.libriciel.ipcore.model.auth.Desk; import coop.libriciel.ipcore.model.auth.DeskRepresentation; import coop.libriciel.ipcore.model.auth.HierarchisedDeskRepresentation; import coop.libriciel.ipcore.model.auth.requests.DeskDto; -import coop.libriciel.ipcore.model.database.Subtype; import coop.libriciel.ipcore.model.database.Tenant; -import coop.libriciel.ipcore.model.database.Type; import coop.libriciel.ipcore.model.permission.Delegation; import coop.libriciel.ipcore.model.permission.DelegationDto; import coop.libriciel.ipcore.model.permission.DelegationSortBy; -import coop.libriciel.ipcore.model.permission.DelegationUpdateDto; import coop.libriciel.ipcore.model.workflow.Folder; import coop.libriciel.ipcore.model.workflow.WorkflowDefinition; import coop.libriciel.ipcore.services.auth.AuthServiceInterface; @@ -70,7 +68,6 @@ import org.springframework.web.bind.annotation.*; import java.util.Comparator; import java.util.List; -import java.util.Optional; import static coop.libriciel.ipcore.IpCoreApplication.*; import static coop.libriciel.ipcore.business.database.MetadataBusinessService.METADATA_REPRESENTATION_NAME_COMPARATOR; @@ -100,6 +97,7 @@ public class AdminDeskController { private final AuthServiceInterface authService; + private final DelegationBusinessService delegationBusinessService; private final DeskBusinessService deskBusinessService; private final MetadataBusinessService metadataBusinessService; private final ModelMapper modelMapper; @@ -112,6 +110,7 @@ public class AdminDeskController { @Autowired public AdminDeskController(AuthServiceInterface authService, + DelegationBusinessService delegationBusinessService, DeskBusinessService deskBusinessService, MetadataBusinessService metadataBusinessService, ModelMapper modelMapper, @@ -121,6 +120,7 @@ public class AdminDeskController { UserBusinessService userBusinessService, WorkflowServiceInterface workflowService) { this.authService = authService; + this.delegationBusinessService = delegationBusinessService; this.deskBusinessService = deskBusinessService; this.metadataBusinessService = metadataBusinessService; this.modelMapper = modelMapper; @@ -354,7 +354,7 @@ public class AdminDeskController { if (!workflowsUsingDeskId.isEmpty()) { Long workflowsNb = workflowsUsingDeskId.getTotalElements(); - log.error("deleteDesk error: the desk is used by " + workflowsNb + " workflow(s)"); + log.error("deleteDesk error: the desk is used by {} workflow(s)", workflowsNb); throw new LocalizedStatusException(NOT_ACCEPTABLE, "message.cannot_delete_a_desk_used_by_workflows", workflowsNb, @@ -487,25 +487,33 @@ public class AdminDeskController { @PathVariable(name = Tenant.API_PATH) String tenantId, @TenantResolved Tenant tenant, @Parameter(description = "Delegating desk Id") - @PathVariable String deskId, + @PathVariable(name = DeskRepresentation.API_PATH) String deskId, + @DeskResolved Desk desk, @Parameter(description = DelegationDto.API_DOC, required = true) @RequestBody DelegationDto delegationDto) { log.info("createDelegationAsAdmin delegating:{} substitute:{}", deskId, delegationDto.getSubstituteDeskId()); log.info(" type:{} subtype:{}", delegationDto.getTypeId(), delegationDto.getSubtypeId()); + // Integrity check + if (!permissionService.currentUserHasAdminRightOnDesk(tenant.getId(), deskId)) { log.debug("User is not super admin, tenant admin nor functional admin for this desk, abort"); throw new LocalizedStatusException(FORBIDDEN, "message.you_can_t_administrate_this_desk"); } - if (StringUtils.equals(deskId, delegationDto.getSubstituteDeskId())) { - throw new LocalizedStatusException(NOT_ACCEPTABLE, "message.redundant_delegation_target"); - } + delegationBusinessService.checkDelegationIntegrity(tenant, desk, delegationDto); + + // Actual save permissionService.addDelegation( - tenant.getId(), delegationDto.getSubstituteDeskId(), deskId, - delegationDto.getTypeId(), delegationDto.getSubtypeId(), delegationDto.getStart(), delegationDto.getEnd() + tenant.getId(), + delegationDto.getSubstituteDeskId(), + deskId, + delegationDto.getTypeId(), + delegationDto.getSubtypeId(), + delegationDto.getStart(), + delegationDto.getEnd() ); statsService.registerAdminAction(tenant, DESK, UPDATE, deskId); @@ -526,7 +534,8 @@ public class AdminDeskController { @PathVariable(name = Tenant.API_PATH) String tenantId, @TenantResolved Tenant tenant, @Parameter(description = "Delegating desk Id") - @PathVariable String deskId, + @PathVariable(name = DeskRepresentation.API_PATH) String deskId, + @DeskResolved Desk desk, @Parameter(description = DelegationDto.API_DOC_ID_VALUE) @PathVariable String delegationId, @DelegationResolved Delegation delegation) { @@ -541,7 +550,6 @@ public class AdminDeskController { // Fetch missing values DelegationDto delegationDto = modelMapper.map(delegation, DelegationDto.class); - delegationDto.setDelegatingDeskId(deskId); deskBusinessService.updateInnerDeskValues(tenantId, singletonList(delegationDto)); typologyBusinessService.updateInnerValues(tenantId, singletonList(delegationDto)); @@ -550,6 +558,58 @@ public class AdminDeskController { } + @PutMapping(API_PROVISIONING_V1 + "/admin/tenant/{tenantId}/desk/{deskId}/delegation/{delegationId}") + @Operation(summary = "Update a delegation from target Desk") + @PreAuthorize(PREAUTHORIZE_TENANT_FUNCTIONAL_ADMIN_OR_MORE) + @ApiResponses(value = { + @ApiResponse(responseCode = CODE_200), + @ApiResponse(responseCode = CODE_401, content = @Content(schema = @Schema(implementation = ErrorResponse.class))), + @ApiResponse(responseCode = CODE_403, content = @Content(schema = @Schema(implementation = ErrorResponse.class))), + @ApiResponse(responseCode = CODE_404, content = @Content(schema = @Schema(implementation = ErrorResponse.class))), + }) + public DelegationDto updateDelegationAsAdmin(@Parameter(description = Tenant.API_DOC_ID_VALUE) + @PathVariable(name = Tenant.API_PATH) String tenantId, + @TenantResolved Tenant tenant, + @Parameter(description = DeskRepresentation.API_DOC_ID_VALUE) + @PathVariable(name = DeskRepresentation.API_PATH) String deskId, + @DeskResolved Desk desk, + @Parameter(description = DelegationDto.API_DOC_ID_VALUE) + @PathVariable(name = DelegationDto.API_PATH) String delegationId, + @DelegationResolved Delegation existingDelegation, + @Parameter(description = DelegationDto.API_DOC, required = true) + @RequestBody DelegationDto updatedDelegation) { + + log.info("updateDelegationAsAdmin - delegating desk : {} ", deskId); + log.trace("updateDelegationAsAdmin - existingDelegation:{} delegationUpdate:{}", existingDelegation, updatedDelegation); + + // Integrity check + + if (!permissionService.currentUserHasAdminRightOnDesk(tenant.getId(), deskId)) { + log.error("User is not super admin, tenant admin nor functional admin for this desk, abort"); + throw new LocalizedStatusException(FORBIDDEN, "message.you_can_t_administrate_this_desk"); + } + + delegationBusinessService.checkDelegationIntegrity(tenant, desk, updatedDelegation); + + // Actual update + + permissionService.deleteDelegation(tenant.getId(), existingDelegation); + + permissionService.addDelegation( + tenant.getId(), + updatedDelegation.getSubstituteDeskId(), + deskId, + updatedDelegation.getTypeId(), + updatedDelegation.getSubtypeId(), + updatedDelegation.getStart(), + updatedDelegation.getEnd() + ); + + statsService.registerAdminAction(tenant, DESK, UPDATE, deskId); + return updatedDelegation; + } + + @DeleteMapping(API_PROVISIONING_V1 + "/admin/tenant/{tenantId}/desk/{deskId}/delegation/{delegationId}") @Operation(summary = "Remove an active or planned delegation from target Desk") @PreAuthorize(PREAUTHORIZE_TENANT_FUNCTIONAL_ADMIN_OR_MORE) @@ -576,26 +636,13 @@ public class AdminDeskController { throw new LocalizedStatusException(FORBIDDEN, "message.you_can_t_administrate_this_desk"); } - permissionService.deleteDelegation( - delegationId, - tenant.getId(), - delegation.getSubstituteDesk().getId(), - deskId, - Optional.ofNullable(delegation.getType()).map(Type::getId).orElse(null), - Optional.ofNullable(delegation.getSubtype()).map(Subtype::getId).orElse(null), - delegation.getStart(), - delegation.getEnd() - ); - + permissionService.deleteDelegation(tenant.getId(), delegation); statsService.registerAdminAction(tenant, DESK, UPDATE, deskId); } @GetMapping(API_PROVISIONING_V1 + "/admin/tenant/{tenantId}/desk/{deskId}/delegation") - @Operation( - summary = "List delegations (active and planned) for given delegating desk", - description = DelegationSortBy.Constants.API_DOC_SORT_BY_VALUES - ) + @Operation(summary = "List delegations (active and planned) for given delegating desk", description = DelegationSortBy.Constants.API_DOC_SORT_BY_VALUES) @PreAuthorize(PREAUTHORIZE_TENANT_FUNCTIONAL_ADMIN_OR_MORE) @ApiResponses(value = { @ApiResponse(responseCode = CODE_200), @@ -622,11 +669,10 @@ public class AdminDeskController { List<DelegationDto> delegationsDtoList = permissionService.getDelegations(tenant.getId(), desk.getId()).stream() .map(delegation -> modelMapper.map(delegation, DelegationDto.class)) - .collect(toMutableList());; + .collect(toMutableList()); // Fetch missing values - delegationsDtoList.forEach(dto -> dto.setDelegatingDeskId(desk.getId())); deskBusinessService.updateInnerDeskValues(tenant.getId(), delegationsDtoList); typologyBusinessService.updateInnerValues(tenant.getId(), delegationsDtoList); diff --git a/src/main/java/coop/libriciel/ipcore/model/permission/Delegation.java b/src/main/java/coop/libriciel/ipcore/model/permission/Delegation.java index d34631c8608d65f7863c643c66b4dc76aa738369..0661fe84192d582b8a0e067a658ae4e71e772798 100644 --- a/src/main/java/coop/libriciel/ipcore/model/permission/Delegation.java +++ b/src/main/java/coop/libriciel/ipcore/model/permission/Delegation.java @@ -19,9 +19,6 @@ package coop.libriciel.ipcore.model.permission; -import coop.libriciel.ipcore.model.auth.DeskRepresentation; -import coop.libriciel.ipcore.model.database.Subtype; -import coop.libriciel.ipcore.model.database.Type; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -42,11 +39,11 @@ public class Delegation { private @Id String id; - private @NotNull DeskRepresentation substituteDesk; - private @NotNull DeskRepresentation delegatingDesk; + private @NotNull String substituteDeskId; + private @NotNull String delegatingDeskId; - private @Nullable Type type; - private @Nullable Subtype subtype; + private @Nullable String typeId; + private @Nullable String subtypeId; private @Nullable Date start; private @Nullable Date end; diff --git a/src/main/java/coop/libriciel/ipcore/model/permission/DelegationDto.java b/src/main/java/coop/libriciel/ipcore/model/permission/DelegationDto.java index 87131901933dc8b80310e4b98ed004f13671573d..1c01f776ade9a691f4d01d9aeef890b5f4961d4d 100644 --- a/src/main/java/coop/libriciel/ipcore/model/permission/DelegationDto.java +++ b/src/main/java/coop/libriciel/ipcore/model/permission/DelegationDto.java @@ -58,14 +58,6 @@ public class DelegationDto { @Schema(accessMode = AccessMode.READ_ONLY) private DeskRepresentation substituteDesk; - @JsonProperty(access = Access.WRITE_ONLY) - @Schema(accessMode = AccessMode.WRITE_ONLY) - private String delegatingDeskId; - - @JsonProperty(access = Access.READ_ONLY) - @Schema(accessMode = AccessMode.READ_ONLY) - private DeskRepresentation delegatingDesk; - @JsonProperty(access = Access.WRITE_ONLY) @Schema(accessMode = AccessMode.WRITE_ONLY, nullable = true) private String typeId; diff --git a/src/main/java/coop/libriciel/ipcore/model/permission/DelegationSortBy.java b/src/main/java/coop/libriciel/ipcore/model/permission/DelegationSortBy.java index e6f989a8552c6d8522b2f0efa89caca79736025d..ea87ffdbccdbd8f08b89c4d868cf210ae385710f 100644 --- a/src/main/java/coop/libriciel/ipcore/model/permission/DelegationSortBy.java +++ b/src/main/java/coop/libriciel/ipcore/model/permission/DelegationSortBy.java @@ -60,7 +60,7 @@ public enum DelegationSortBy { public static Comparator<DelegationDto> generateDelegationDtoComparator(@NotNull DelegationSortBy sortBy, boolean asc) { return switch (sortBy) { case SUBSTITUTE_DESK -> comparing( - (Function<DelegationDto, String>) dto -> Optional.ofNullable(dto.getDelegatingDesk()).map(DeskRepresentation::getName).orElse(null), + (Function<DelegationDto, String>) dto -> Optional.ofNullable(dto.getSubstituteDesk()).map(DeskRepresentation::getName).orElse(null), asc ? nullsLast(naturalOrder()) : nullsFirst(reverseOrder()) ); case START -> comparing(DelegationDto::getStart, asc ? nullsLast(naturalOrder()) : nullsFirst(reverseOrder())); diff --git a/src/main/java/coop/libriciel/ipcore/model/permission/DelegationUpdateDto.java b/src/main/java/coop/libriciel/ipcore/model/permission/DelegationUpdateDto.java deleted file mode 100644 index 485e40f7db863870add985f091da4ddc350a6aef..0000000000000000000000000000000000000000 --- a/src/main/java/coop/libriciel/ipcore/model/permission/DelegationUpdateDto.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * iparapheur Core - * Copyright (C) 2018-2025 Libriciel-SCOP - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - * - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package coop.libriciel.ipcore.model.permission; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - - -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class DelegationUpdateDto { - - DelegationDto previousDelegationData; - DelegationDto updatedDelegationData; -} diff --git a/src/main/java/coop/libriciel/ipcore/services/auth/KeycloakService.java b/src/main/java/coop/libriciel/ipcore/services/auth/KeycloakService.java index 97bf94019f29a302953bddb6c3fa78359fb8c1d3..f940ea2a8c559105c5ad375b3ad28357a754d805 100644 --- a/src/main/java/coop/libriciel/ipcore/services/auth/KeycloakService.java +++ b/src/main/java/coop/libriciel/ipcore/services/auth/KeycloakService.java @@ -1430,9 +1430,7 @@ public class KeycloakService implements AuthServiceInterface { RoleRepresentation role; try { - role = realmResource - .rolesById() - .getRole(id); + role = realmResource.rolesById().getRole(id); } catch (Exception e) { return null; } diff --git a/src/main/java/coop/libriciel/ipcore/services/permission/KeycloakResourceService.java b/src/main/java/coop/libriciel/ipcore/services/permission/KeycloakResourceService.java index bc9c959330c3e388a4e88a31beae8518ab510ea2..c72b90556f92d0e890887da783958249b6bb8411 100644 --- a/src/main/java/coop/libriciel/ipcore/services/permission/KeycloakResourceService.java +++ b/src/main/java/coop/libriciel/ipcore/services/permission/KeycloakResourceService.java @@ -1776,56 +1776,55 @@ public class KeycloakResourceService implements PermissionServiceInterface { .filter(p -> StringUtils.equals(p.getId(), id)) .map(this::toDelegationList) .flatMap(Collection::stream) + .peek(delegation -> delegation.setDelegatingDeskId(deskId)) .findFirst() .orElse(null); } @Override - public void deleteDelegation(@NotNull String delegationId, - @NotNull String tenantId, - @Nullable String substituteDeskId, - @NotNull String delegatingDeskId, - @Nullable String typeId, - @Nullable String subtypeId, - @Nullable Date beginDate, - @Nullable Date endDate) { - - log.debug("deleteDelegation id:{}", delegationId); - - ScopePermissionResource permissionResource = scopePermissionsClient.findById(delegationId); + public void deleteDelegation(@NotNull String tenantId, + @NotNull Delegation delegation) { + + log.debug("deleteDelegation id:{}", delegation.getId()); + + ScopePermissionResource permissionResource = scopePermissionsClient.findById(delegation.getId()); ScopePermissionRepresentation permissionRepresentation = permissionResource.toRepresentation(); Set<String> associatedPoliciesNames = permissionResource.associatedPolicies().stream() .map(AbstractPolicyRepresentation::getName) .collect(toSet()); - if (substituteDeskId == null) { + if (delegation.getSubstituteDeskId() == null) { // probably a delegation to a deleted desk, we just double-check that only target calendar policies remain if (associatedPoliciesNames.stream().map(DELEGATION_CALENDAR_POLICY_INTERNAL_NAME_PATTERN::matcher).allMatch(Matcher::matches)) { - associatedPoliciesNames.forEach(aggregateTimePolicyInternalName -> { - deleteSpecificTimePolicyAndOwnerFromAggregate(aggregateTimePolicyInternalName, beginDate, endDate, "NULL", associatedPoliciesNames); - }); + associatedPoliciesNames.forEach(aggregateTimePolicyInternalName -> deleteSpecificTimePolicyAndOwnerFromAggregate( + aggregateTimePolicyInternalName, + delegation.getStart(), + delegation.getEnd(), + "NULL", + associatedPoliciesNames + )); } } else { - boolean isTimedDelegation = ObjectUtils.anyNotNull(beginDate, endDate); + boolean isTimedDelegation = ObjectUtils.anyNotNull(delegation.getStart(), delegation.getEnd()); - Map<String, String> substituteOwnerPolicySubstitutions = Map.of(TENANT_PLACEHOLDER, tenantId, DESK_PLACEHOLDER, substituteDeskId); + Map<String, String> substituteOwnerPolicySubstitutions = Map.of(TENANT_PLACEHOLDER, tenantId, DESK_PLACEHOLDER, delegation.getSubstituteDeskId()); String substituteOwnerPolicyName = StringSubstitutor.replace(POLICY_DESK_OWNER_INTERNAL_NAME, substituteOwnerPolicySubstitutions); if (isTimedDelegation) { Map<String, String> substitutions = Map.of( TENANT_PLACEHOLDER, tenantId, - DESK_PLACEHOLDER, delegatingDeskId, - TARGET_DESK_PLACEHOLDER, substituteDeskId, - TARGET_TYPE_PLACEHOLDER, firstNonNull(typeId, EMPTY), - TARGET_SUBTYPE_PLACEHOLDER, firstNonNull(subtypeId, EMPTY) + DESK_PLACEHOLDER, delegation.getDelegatingDeskId(), + TARGET_DESK_PLACEHOLDER, delegation.getSubstituteDeskId(), + TARGET_TYPE_PLACEHOLDER, firstNonNull(delegation.getTypeId(), EMPTY), + TARGET_SUBTYPE_PLACEHOLDER, firstNonNull(delegation.getSubtypeId(), EMPTY) ); String aggregateTimePolicyInternalNameTemplate; - if (subtypeId != null) { + if (delegation.getSubtypeId() != null) { aggregateTimePolicyInternalNameTemplate = POLICY_DESK_TARGET_SUBTYPE_CALENDAR_INTERNAL_NAME; - } else if (typeId != null) { + } else if (delegation.getTypeId() != null) { aggregateTimePolicyInternalNameTemplate = POLICY_DESK_TARGET_TYPE_CALENDAR_INTERNAL_NAME; } else { aggregateTimePolicyInternalNameTemplate = POLICY_DESK_TARGET_CALENDAR_INTERNAL_NAME; @@ -1838,8 +1837,8 @@ public class KeycloakResourceService implements PermissionServiceInterface { } deleteSpecificTimePolicyAndOwnerFromAggregate(aggregateTimePolicyInternalName, - beginDate, - endDate, + delegation.getStart(), + delegation.getEnd(), substituteOwnerPolicyName, associatedPoliciesNames); } else { @@ -2011,9 +2010,9 @@ public class KeycloakResourceService implements PermissionServiceInterface { return singletonList( Delegation.builder() .id(policyRepresentation.getId()) - .substituteDesk(new DeskRepresentation(substituteDeskId)) - .type(new Type(typeScope.getRight())) - .subtype(new Subtype(subtypeScope.getRight())) + .substituteDeskId(substituteDeskId) + .typeId(typeScope.getRight()) + .subtypeId(subtypeScope.getRight()) .build() ); } else { @@ -2021,11 +2020,11 @@ public class KeycloakResourceService implements PermissionServiceInterface { .stream() .map(i -> Delegation.builder() .id(policyRepresentation.getId()) - .substituteDesk(new DeskRepresentation(substituteDeskId)) + .substituteDeskId(substituteDeskId) .start(i.getLeft()) .end(i.getRight()) - .type(Optional.ofNullable(typeScope.getRight()).map(Type::new).orElse(null)) - .subtype(Optional.ofNullable(subtypeScope.getRight()).map(Subtype::new).orElse(null)) + .typeId(typeScope.getRight()) + .subtypeId(subtypeScope.getRight()) .build()) .toList(); } diff --git a/src/main/java/coop/libriciel/ipcore/services/permission/NonePermissionService.java b/src/main/java/coop/libriciel/ipcore/services/permission/NonePermissionService.java index 31c3d8bd3990dbd943660e973d5f9e92fb6fdf14..167f39cd1623fa2c66cc7ea0b8d61126b5735cf5 100644 --- a/src/main/java/coop/libriciel/ipcore/services/permission/NonePermissionService.java +++ b/src/main/java/coop/libriciel/ipcore/services/permission/NonePermissionService.java @@ -211,14 +211,8 @@ public class NonePermissionService implements PermissionServiceInterface { @Override - public void deleteDelegation(@NotNull String delegationId, - @NotNull String tenantId, - @NotNull String substituteDeskId, - @NotNull String delegatingDeskId, - @Nullable String typeId, - @Nullable String subtypeId, - @Nullable Date beginDate, - @Nullable Date endDate) { + public void deleteDelegation(@NotNull String tenantId, + @NotNull Delegation delegation) { throw new NoPermissionServiceError(); } diff --git a/src/main/java/coop/libriciel/ipcore/services/permission/PermissionServiceInterface.java b/src/main/java/coop/libriciel/ipcore/services/permission/PermissionServiceInterface.java index 455b52b0cb999dc48d3f3871656660eb74fc365a..fa94dee30ede95e7f16c7f3d56bb23a1347d5808 100644 --- a/src/main/java/coop/libriciel/ipcore/services/permission/PermissionServiceInterface.java +++ b/src/main/java/coop/libriciel/ipcore/services/permission/PermissionServiceInterface.java @@ -134,28 +134,35 @@ public interface PermissionServiceInterface { @Nullable String typeId, @Nullable String subtypeId); + boolean currentUserHasViewingRightOnSomeDeskIn(@NotNull String tenantId, @NotNull Set<String> deskIds, @Nullable String typeId, @Nullable String subtypeId); + boolean currentUserHasArchivingRightOnSomeDeskIn(@NotNull String tenantId, @NotNull Set<String> deskIds, @Nullable String typeId, @Nullable String subtypeId); + boolean currentUserHasChainingRightOnSomeDeskIn(@NotNull String tenantId, @NotNull Set<String> deskIds, @Nullable String typeId, @Nullable String subtypeId); + boolean currentUserHasFolderCreationRightOnSomeDeskIn(@NotNull String tenantId, @NotNull Set<String> deskIds); + boolean currentUserHasAdminRightOnDesk(@NotNull String tenantId, @NotNull String deskIds); + boolean currentUserHasAdminRightsOnTenant(@NotNull String tenantId); + boolean currentUserHasFolderActionRightOnSomeDeskIn(@NotNull String tenantId, @NotNull Set<String> deskIds, @Nullable String typeId, @@ -206,8 +213,10 @@ public interface PermissionServiceInterface { @NotNull Set<String> getAllDesksHavingDelegationFrom(@NotNull String tenantId, @NotNull Set<String> deskIds); + @NotNull List<DeskRepresentation> getAllDesksHavingDelegationTo(@NotNull Set<String> deskIds); + void createSubtypeResource(@NotNull String tenantId, @NotNull String subtypeId); @@ -224,14 +233,8 @@ public interface PermissionServiceInterface { @Nullable Delegation getDelegation(@NotNull String deskId, @NotNull String id); - void deleteDelegation(@NotNull String delegationId, - @NotNull String tenantId, - @NotNull String substituteDeskId, - @NotNull String delegatingDeskId, - @Nullable String typeId, - @Nullable String subtypeId, - @Nullable Date beginDate, - @Nullable Date endDate); + void deleteDelegation(@NotNull String tenantId, + @NotNull Delegation delegation); @NotNull List<Delegation> getDelegations(@NotNull String tenantId, @NotNull String delegatingDeskId); diff --git a/src/main/java/coop/libriciel/ipcore/services/resolvers/DeskResolver.java b/src/main/java/coop/libriciel/ipcore/services/resolvers/DeskResolver.java index 6ba7d9bc7aabbf3ec0c4cde38202ff1e0daa8740..8f4c6a022320398e775579f2a5e7e02c2b220744 100644 --- a/src/main/java/coop/libriciel/ipcore/services/resolvers/DeskResolver.java +++ b/src/main/java/coop/libriciel/ipcore/services/resolvers/DeskResolver.java @@ -102,7 +102,7 @@ public class DeskResolver implements HandlerMethodArgumentResolver { throw new LocalizedStatusException(NOT_FOUND, "message.unknown_desk_id"); } - return Optional.ofNullable(authService.findDeskById(tenantId, deskId)) + return Optional.ofNullable(authService.findDeskByIdNoException(tenantId, deskId)) .orElseThrow(() -> new LocalizedStatusException(NOT_FOUND, "message.unknown_desk_id")); } diff --git a/src/test/java/coop/libriciel/ipcore/controller/admin/AdminDeskControllerTest.java b/src/test/java/coop/libriciel/ipcore/controller/admin/AdminDeskControllerTest.java index 8fd94aa7200010404218e063c19318e5ce55df8c..f711d7a277a371303c843b98b962555e8a7861aa 100644 --- a/src/test/java/coop/libriciel/ipcore/controller/admin/AdminDeskControllerTest.java +++ b/src/test/java/coop/libriciel/ipcore/controller/admin/AdminDeskControllerTest.java @@ -19,6 +19,7 @@ package coop.libriciel.ipcore.controller.admin; +import coop.libriciel.ipcore.business.auth.DelegationBusinessService; import coop.libriciel.ipcore.business.auth.DeskBusinessService; import coop.libriciel.ipcore.business.auth.UserBusinessService; import coop.libriciel.ipcore.business.database.MetadataBusinessService; @@ -76,6 +77,7 @@ class AdminDeskControllerTest { adminDeskController = new AdminDeskController( authServiceMock, + mock(DelegationBusinessService.class), mock(DeskBusinessService.class), mock(MetadataBusinessService.class), mock(ModelMapper.class), diff --git a/src/test/java/coop/libriciel/ipcore/model/permission/DelegationDtoTest.java b/src/test/java/coop/libriciel/ipcore/model/permission/DelegationDtoTest.java index 9a69096e164869d152332ff87a2e362f385aa7b3..d3a64bb7336ef6ef08dec227e9cc03802d191c4c 100644 --- a/src/test/java/coop/libriciel/ipcore/model/permission/DelegationDtoTest.java +++ b/src/test/java/coop/libriciel/ipcore/model/permission/DelegationDtoTest.java @@ -20,8 +20,8 @@ package coop.libriciel.ipcore.model.permission; import coop.libriciel.ipcore.model.auth.DeskRepresentation; -import coop.libriciel.ipcore.model.database.Subtype; -import coop.libriciel.ipcore.model.database.Type; +import coop.libriciel.ipcore.model.database.requests.SubtypeRepresentation; +import coop.libriciel.ipcore.model.database.requests.TypeRepresentation; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.modelmapper.ModelMapper; @@ -47,11 +47,9 @@ class DelegationDtoTest { private void assertDeepEquals(Delegation entity, DelegationDto dto) { assertEquals(entity.getId(), dto.getId()); - assertEquals(Optional.ofNullable(entity.getSubstituteDesk()).map(DeskRepresentation::getId).orElse(null), dto.getSubstituteDeskId()); - assertEquals(Optional.ofNullable(entity.getDelegatingDesk()).map(DeskRepresentation::getId).orElse(null), dto.getDelegatingDeskId()); - - assertEquals(Optional.ofNullable(entity.getType()).map(Type::getId).orElse(null), dto.getTypeId()); - assertEquals(Optional.ofNullable(entity.getSubtype()).map(Subtype::getId).orElse(null), dto.getSubtypeId()); + assertEquals(Optional.ofNullable(dto.getSubstituteDesk()).map(DeskRepresentation::getId).orElse(null), entity.getSubstituteDeskId()); + assertEquals(Optional.ofNullable(dto.getType()).map(TypeRepresentation::getId).orElse(null), entity.getTypeId()); + assertEquals(Optional.ofNullable(dto.getSubtype()).map(SubtypeRepresentation::getId).orElse(null), entity.getSubtypeId()); assertEquals(entity.getStart(), dto.getStart()); assertEquals(entity.getEnd(), dto.getEnd()); @@ -67,10 +65,10 @@ class DelegationDtoTest { Delegation entity = new Delegation(); entity.setId(UUID.randomUUID().toString()); - entity.setSubstituteDesk(new DeskRepresentation(UUID.randomUUID().toString(), null)); - entity.setDelegatingDesk(new DeskRepresentation(UUID.randomUUID().toString(), null)); - entity.setType(new Type(UUID.randomUUID().toString())); - entity.setSubtype(new Subtype(UUID.randomUUID().toString())); + entity.setSubstituteDeskId(UUID.randomUUID().toString()); + entity.setDelegatingDeskId(UUID.randomUUID().toString()); + entity.setTypeId(UUID.randomUUID().toString()); + entity.setSubtypeId(UUID.randomUUID().toString()); entity.setStart(new Date(new Random().nextLong())); entity.setEnd(new Date(new Random().nextLong())); @@ -104,7 +102,6 @@ class DelegationDtoTest { DelegationDto dto = new DelegationDto(); dto.setId(UUID.randomUUID().toString()); dto.setSubstituteDeskId(UUID.randomUUID().toString()); - dto.setDelegatingDeskId(UUID.randomUUID().toString()); dto.setTypeId(UUID.randomUUID().toString()); dto.setSubtypeId(UUID.randomUUID().toString()); dto.setStart(new Date(new Random().nextLong())); diff --git a/src/test/java/coop/libriciel/ipcore/services/permission/DummyPermissionService.java b/src/test/java/coop/libriciel/ipcore/services/permission/DummyPermissionService.java index b09608667d0951c4de3d81621f036c413e3d9da7..d696bc56ea10f9299d9478d980104ca6519a0209 100644 --- a/src/test/java/coop/libriciel/ipcore/services/permission/DummyPermissionService.java +++ b/src/test/java/coop/libriciel/ipcore/services/permission/DummyPermissionService.java @@ -189,14 +189,8 @@ public class DummyPermissionService implements PermissionServiceInterface { @Override - public void deleteDelegation(@NotNull String delegationId, - @NotNull String tenantId, - @NotNull String substituteDeskId, - @NotNull String delegatingDeskId, - @Nullable String typeId, - @Nullable String subtypeId, - @Nullable Date beginDate, - @Nullable Date endDate) {} + public void deleteDelegation(@NotNull String tenantId, + @NotNull Delegation delegation) {} @Override diff --git a/src/test/resources/ip-core-openapi-provisioning.json b/src/test/resources/ip-core-openapi-provisioning.json index a63f51a8bf621e8fdc4e2736402e72bdf915b9c4..eaf0d0d3bb9bf704a13969d6200caad8fbe3d928 100644 --- a/src/test/resources/ip-core-openapi-provisioning.json +++ b/src/test/resources/ip-core-openapi-provisioning.json @@ -35,13 +35,6 @@ "DelegationDto": { "description": "target substitute desk, optional type/subtype; and schedule containing Dates and actions to perform.", "properties": { - "delegatingDesk": { - "$ref": "#/components/schemas/DeskRepresentation" - }, - "delegatingDeskId": { - "type": "string", - "writeOnly": true - }, "end": { "format": "date-time", "nullable": true, @@ -3184,6 +3177,94 @@ "tags": [ "admin-desk" ] + }, + "put": { + "operationId": "updateDelegationAsAdmin", + "parameters": [ + { + "description": "Tenant id", + "in": "path", + "name": "tenantId", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "Desk id", + "in": "path", + "name": "deskId", + "required": true, + "schema": { + "type": "string" + } + }, + { + "description": "Delegation Id", + "in": "path", + "name": "delegationId", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DelegationDto" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DelegationDto" + } + } + }, + "description": "OK" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Unauthorized" + }, + "403": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Forbidden" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + }, + "description": "Not Found" + } + }, + "summary": "Update a delegation from target Desk", + "tags": [ + "admin-desk" + ] } }, "/api/provisioning/v1/admin/tenant/{tenantId}/folder/{folderId}": {