diff --git a/Jenkinsfile b/Jenkinsfile index 6b07a1a4b13..b912296d579 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -102,9 +102,14 @@ pipeline { sh 'npmrc default' sh ''' - $MVN_COMMAND clean verify -U -Pvitam -pl '!cots/vitamui-nginx,!cots/vitamui-mongod,!cots/vitamui-logstash,!cots/vitamui-mongo-express' -DskipTests -DskipAllFrontend + $MVN_COMMAND clean verify -U -Pvitam -pl '!cots/vitamui-nginx,!cots/vitamui-mongod,!cots/vitamui-logstash,!cots/vitamui-mongo-express' ''' } + post { + always { + junit '**/target/surefire-reports/*.xml' + } + } } stage('Generate front ressources') { diff --git a/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/cas/service/CasInternalService.java b/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/cas/service/CasInternalService.java index f464a57a1b0..34c03535247 100644 --- a/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/cas/service/CasInternalService.java +++ b/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/cas/service/CasInternalService.java @@ -269,7 +269,7 @@ public void updateNbFailedAttempsPlusLastConnectionAndStatus(final User user, fi } public User findUserByEmailAndCustomerId(final String email, String customerId) { - final User user = userRepository.findByEmailAndCustomerId(email, customerId); + final User user = userRepository.findByEmailIgnoreCaseAndCustomerId(email, customerId); if (user == null) { throw new NotFoundException(USER_NOT_FOUND_MESSAGE + email); } else if (UserTypeEnum.NOMINATIVE != user.getType()) { diff --git a/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/user/dao/UserRepository.java b/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/user/dao/UserRepository.java index 9d569d06d8e..4bc52f19800 100644 --- a/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/user/dao/UserRepository.java +++ b/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/user/dao/UserRepository.java @@ -48,24 +48,24 @@ /** * MongoDB repository for the users. - * - * */ public interface UserRepository extends VitamUIRepository { Optional findByIdAndCustomerId(String id, String customerId); User findByEmailIgnoreCaseAndCustomerId(String email, String customerId); + List findAllByEmailIgnoreCase(String email); - boolean existsByEmailAndCustomerId(String email, String customerId); + boolean existsByEmailIgnoreCaseAndCustomerId(String email, String customerId); long countByGroupId(String profileGroupId); long countByGroupIdIn(List groupIds); - Page findByCustomerIdAndSubrogeableAndTypeAndStatus(String customerId, boolean subrogeable, UserTypeEnum type, UserStatusEnum status, - Pageable pageable); + Page findByCustomerIdAndSubrogeableAndTypeAndStatus(String customerId, boolean subrogeable, UserTypeEnum type, + UserStatusEnum status, + Pageable pageable); List findByCustomerId(String customerId); diff --git a/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/user/service/UserEmailInternalService.java b/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/user/service/UserEmailInternalService.java index bd0a52afdee..ea90ea666d2 100644 --- a/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/user/service/UserEmailInternalService.java +++ b/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/user/service/UserEmailInternalService.java @@ -89,20 +89,16 @@ public void sendCreationEmail(final UserDto userDto) { if (userDto != null && userDto.getStatus() == UserStatusEnum.ENABLED && userDto.getType() == UserTypeEnum.NOMINATIVE) { final List providers = - internalIdentityProviderService.getAll(Optional.empty(), Optional.empty()); - if (identityProviderHelper.identifierMatchProviderPattern(providers, userDto.getEmail(), - userDto.getCustomerId())) { + internalIdentityProviderService.getAll(Optional.empty(), Optional.empty()); + if (identityProviderHelper.identifierMatchProviderPattern(providers, userDto.getEmail(), + userDto.getCustomerId())) { LOGGER.debug("Sending mail after creating user: {}", userDto.getEmail()); final UserInfoDto userInfoDto = userInfoInternalService.getOne(userDto.getUserInfoId()); - restClientFactory.getRestTemplate() - .getForEntity(restClientFactory.getBaseUrl() + casResetPasswordUrl, Boolean.class, - userDto.getEmail(), userDto.getFirstname(), userDto.getLastname(), - LanguageDto.valueOf(userInfoDto.getLanguage()).getLanguage(), userDto.getCustomerId()); + restClientFactory.getRestTemplate() + .getForEntity(restClientFactory.getBaseUrl() + casResetPasswordUrl, Boolean.class, + userDto.getEmail(), userDto.getFirstname(), userDto.getLastname(), + LanguageDto.valueOf(userInfoDto.getLanguage()).getLanguage(), userDto.getCustomerId()); } - } catch (final Exception e) { - LOGGER.error("User creation: failed to send mail after creation. \n{}", e); - } - } } } diff --git a/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/user/service/UserInternalService.java b/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/user/service/UserInternalService.java index e1fda3709e9..49cf2908a79 100644 --- a/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/user/service/UserInternalService.java +++ b/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/user/service/UserInternalService.java @@ -36,9 +36,6 @@ */ package fr.gouv.vitamui.iam.internal.server.user.service; -import static fr.gouv.vitamui.commons.api.CommonConstants.*; -import static fr.gouv.vitamui.commons.logbook.common.EventType.*; - import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -51,11 +48,13 @@ import fr.gouv.vitamui.commons.api.converter.Converter; import fr.gouv.vitamui.commons.api.domain.ApplicationDto; import fr.gouv.vitamui.commons.api.domain.GroupDto; +import fr.gouv.vitamui.commons.api.domain.IdDto; import fr.gouv.vitamui.commons.api.domain.ProfileDto; import fr.gouv.vitamui.commons.api.domain.ServicesData; import fr.gouv.vitamui.commons.api.domain.TenantDto; import fr.gouv.vitamui.commons.api.domain.TenantInformationDto; import fr.gouv.vitamui.commons.api.domain.UserDto; +import fr.gouv.vitamui.commons.api.domain.UserInfoDto; import fr.gouv.vitamui.commons.api.enums.UserStatusEnum; import fr.gouv.vitamui.commons.api.enums.UserTypeEnum; import fr.gouv.vitamui.commons.api.exception.ApplicationServerException; @@ -101,14 +100,6 @@ import fr.gouv.vitamui.iam.internal.server.user.domain.AlertAnalytics; import fr.gouv.vitamui.iam.internal.server.user.domain.User; import fr.gouv.vitamui.iam.security.service.InternalSecurityService; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.time.OffsetDateTime; -import java.util.*; -import java.util.Map.Entry; -import java.util.regex.Pattern; -import java.util.stream.Collectors; -import java.util.stream.Stream; import lombok.Getter; import lombok.Setter; import org.apache.commons.collections4.MapUtils; @@ -128,6 +119,8 @@ import org.springframework.transaction.support.DefaultTransactionDefinition; import org.springframework.util.Assert; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.Arrays; @@ -142,10 +135,19 @@ import java.util.Set; import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.util.stream.Stream; import static fr.gouv.vitamui.commons.api.CommonConstants.APPLICATION_ID; import static fr.gouv.vitamui.commons.api.CommonConstants.GPDR_DEFAULT_VALUE; import static fr.gouv.vitamui.commons.api.CommonConstants.USER_ID_ATTRIBUTE; +import static fr.gouv.vitamui.commons.logbook.common.EventType.EXT_VITAMUI_BLOCK_USER; +import static fr.gouv.vitamui.commons.logbook.common.EventType.EXT_VITAMUI_CREATE_USER; +import static fr.gouv.vitamui.commons.logbook.common.EventType.EXT_VITAMUI_CREATE_USER_INFO; +import static fr.gouv.vitamui.commons.logbook.common.EventType.EXT_VITAMUI_PASSWORD_CHANGE; +import static fr.gouv.vitamui.commons.logbook.common.EventType.EXT_VITAMUI_PASSWORD_INIT; +import static fr.gouv.vitamui.commons.logbook.common.EventType.EXT_VITAMUI_PASSWORD_REVOCATION; +import static fr.gouv.vitamui.commons.logbook.common.EventType.EXT_VITAMUI_UPDATE_USER; +import static fr.gouv.vitamui.commons.logbook.common.EventType.EXT_VITAMUI_UPDATE_USER_INFO; /** * The service to read, create, update and delete the users. @@ -164,7 +166,7 @@ public class UserInternalService extends VitamUICrudService { EXT_VITAMUI_PASSWORD_CHANGE, EXT_VITAMUI_CREATE_USER_INFO, EXT_VITAMUI_UPDATE_USER_INFO - ); + ); private UserRepository userRepository; @@ -224,7 +226,8 @@ public UserInternalService(final SequenceGeneratorService sequenceGeneratorServi final MongoTransactionManager mongoTransactionManager, final LogbookService logbookService, final AddressService addressService, final ApplicationInternalService applicationInternalService, final PasswordConfiguration passwordConfiguration, - final UserExportService userExportService, final UserInfoInternalService userInfoInternalService, final ConnectionHistoryService connectionHistoryService) { + final UserExportService userExportService, final UserInfoInternalService userInfoInternalService, + final ConnectionHistoryService connectionHistoryService) { super(sequenceGeneratorService); this.userRepository = userRepository; this.groupInternalService = groupInternalService; @@ -266,7 +269,7 @@ public UserDto findUserByEmailAndCustomerId(final String email, final String cus } public List findUsersByEmail(final String email) { - List users = getRepository().findAllByEmail(email); + List users = getRepository().findAllByEmailIgnoreCase(email); return users.stream() .map(this::convertFromEntityToDto) .collect(Collectors.toList()); @@ -318,7 +321,8 @@ public Resource exportUsers(final Optional criteria) { final List userEvents = mapToEvents(userOperations, userInfoOperations); final List filteredUserEvents = filterUserEvents(userEvents, userIds); - userExportService.createXlsxFile(usersDto, filteredUserEvents, buildUsersInfoLangMap(userInfoIds), buildUsersGroupNamesMap(userGroupIds), xlsOutputStream); + userExportService.createXlsxFile(usersDto, filteredUserEvents, buildUsersInfoLangMap(userInfoIds), + buildUsersGroupNamesMap(userGroupIds), xlsOutputStream); return new ByteArrayResource(xlsOutputStream.toByteArray()); } catch (final IOException exception) { @@ -328,7 +332,8 @@ public Resource exportUsers(final Optional criteria) { private List filterUserEvents(final List userEvents, final List userIds) { return userEvents.stream() - .filter(logbookEventDto -> USER_OPERATIONS_EVENT_TYPES.contains(EventType.valueOf(logbookEventDto.getEvType()))) + .filter( + logbookEventDto -> USER_OPERATIONS_EVENT_TYPES.contains(EventType.valueOf(logbookEventDto.getEvType()))) .filter(logbookEventDto -> userIds.contains(logbookEventDto.getObId())) .collect(Collectors.toList()); } @@ -337,36 +342,45 @@ private List getIdentifiers(List usersDto) { return usersDto.stream().map(UserDto::getIdentifier).collect(Collectors.toList()); } - private List mapToEvents(LogbookOperationsResponseDto userOperations, LogbookOperationsResponseDto userInfoOperations) { - final Stream mergedOperations = Stream.concat(userOperations.getResults().stream(), userInfoOperations.getResults().stream()); + private List mapToEvents(LogbookOperationsResponseDto userOperations, + LogbookOperationsResponseDto userInfoOperations) { + final Stream mergedOperations = + Stream.concat(userOperations.getResults().stream(), userInfoOperations.getResults().stream()); return mergedOperations .map(operation -> { - operation.getEvents().forEach(logbookEventDto -> logbookEventDto.setEvIdAppSession(operation.getEvIdAppSession())); + operation.getEvents() + .forEach(logbookEventDto -> logbookEventDto.setEvIdAppSession(operation.getEvIdAppSession())); return operation.getEvents(); }) .flatMap(Collection::stream).collect(Collectors.toList()); } private LogbookOperationsResponseDto getUserOperations(List userIdentifiers) { - VitamContext vitamContext = internalSecurityService.buildVitamContext(internalSecurityService.getTenantIdentifier()); + VitamContext vitamContext = + internalSecurityService.buildVitamContext(internalSecurityService.getTenantIdentifier()); ObjectNode usersQuery = logbookService.buildQuery(userIdentifiers, MongoDbCollections.USERS); try { - RequestResponse usersLogbookOperations = logbookService.selectOperations(usersQuery, vitamContext); - return VitamRestUtils.responseMapping(usersLogbookOperations.toJsonNode(), LogbookOperationsResponseDto.class); + RequestResponse usersLogbookOperations = + logbookService.selectOperations(usersQuery, vitamContext); + return VitamRestUtils.responseMapping(usersLogbookOperations.toJsonNode(), + LogbookOperationsResponseDto.class); } catch (VitamClientException exception) { throw new InternalServerException("An error occurred while fetching user operations", exception); } } private LogbookOperationsResponseDto getUserInfoOperations(List userIdentifiers) { - VitamContext vitamContext = internalSecurityService.buildVitamContext(internalSecurityService.getTenantIdentifier()); + VitamContext vitamContext = + internalSecurityService.buildVitamContext(internalSecurityService.getTenantIdentifier()); ObjectNode userInfoQuery = logbookService.buildQuery(userIdentifiers, MongoDbCollections.USER_INFOS); try { - RequestResponse userInfoLogbookOperations = logbookService.selectOperations(userInfoQuery, vitamContext); - return VitamRestUtils.responseMapping(userInfoLogbookOperations.toJsonNode(), LogbookOperationsResponseDto.class); + RequestResponse userInfoLogbookOperations = + logbookService.selectOperations(userInfoQuery, vitamContext); + return VitamRestUtils.responseMapping(userInfoLogbookOperations.toJsonNode(), + LogbookOperationsResponseDto.class); } catch (VitamClientException exception) { throw new InternalServerException("An error occurred while fetching user operations", exception); } @@ -518,7 +532,8 @@ public UserDto update(final UserDto dto) { return updatedUser; } - public void saveCurrentPasswordInOldPasswords(final User user, final String newPassword, final Integer maxOldPassword) { + public void saveCurrentPasswordInOldPasswords(final User user, final String newPassword, + final Integer maxOldPassword) { if (StringUtils.isNotBlank(newPassword)) { List oldPasswords = user.getOldPasswords(); @@ -823,7 +838,8 @@ private void checkEmail(final String email, final String customerId, final Strin Assert.notNull(email, "email : " + email + " format is not allowed"); Assert.isTrue(Pattern.matches(IamUtils.EMAIL_VALID_REGEXP, email), "email : " + email + " format is not allowed"); - Assert.isNull(getRepository().findByEmailIgnoreCaseAndCustomerId(email, customerId), message + ": mail already exists"); + Assert.isNull(getRepository().findByEmailIgnoreCaseAndCustomerId(email, customerId), + message + ": mail already exists"); if (email.matches(ADMIN_EMAIL_PATTERN + ".*")) { final Query query = new Query(); query.addCriteria(Criteria.where("email").regex("^" + ADMIN_EMAIL_PATTERN)); @@ -1113,7 +1129,7 @@ public UserDto patchAnalytics(final Map partialDto) { patchLastTenantIdentifier(user, CastUtils.toInteger(value)); break; case "alerts": - finalObjectMapper objectMapper = new ObjectMapper(); + final ObjectMapper objectMapper = new ObjectMapper(); final List alertAnalytics = objectMapper.convertValue(CastUtils.toList(value), new TypeReference<>() { }); @@ -1172,11 +1188,13 @@ private void checkApplicationAccessPermission(final String applicationId) { } private Map buildUsersInfoLangMap(final List userInfoIds) { - return userInfoInternalService.getMany(userInfoIds).stream().collect(Collectors.toMap(IdDto::getId, UserInfoDto::getLanguage)); + return userInfoInternalService.getMany(userInfoIds).stream() + .collect(Collectors.toMap(IdDto::getId, UserInfoDto::getLanguage)); } private Map buildUsersGroupNamesMap(final List userGroupIds) { - return groupInternalService.getMany(userGroupIds).stream().collect(Collectors.toMap(IdDto::getId, GroupDto::getName)); + return groupInternalService.getMany(userGroupIds).stream() + .collect(Collectors.toMap(IdDto::getId, GroupDto::getName)); } @Override @@ -1184,7 +1202,7 @@ protected Document groupFields(final Optional criteriaJsonString, final return super.groupFields(criteriaJsonString, fields); } - public List findByCustomerId(String customerId){ + public List findByCustomerId(String customerId) { return this.userRepository.findByCustomerId(customerId); } } diff --git a/api/api-iam/iam-internal/src/main/resources/application-dev.yml b/api/api-iam/iam-internal/src/main/resources/application-dev.yml index 9ac05ea1c46..c4684642c8f 100644 --- a/api/api-iam/iam-internal/src/main/resources/application-dev.yml +++ b/api/api-iam/iam-internal/src/main/resources/application-dev.yml @@ -121,20 +121,6 @@ provisioning-client: key-password: changeme type: JKS hostname-verification: false - - idp-identifier: keycloak-idp - uri: https://localhost:8090/users - client: - secure: true - ssl-configuration: - keystore: - key-path: src/main/resources/dev/keystore_iam-internal.jks - key-password: changeme - type: JKS - truststore: - key-path: src/main/resources/dev/truststore_server.jks - key-password: changeme - type: JKS - hostname-verification: false address: max-street-length: 250 diff --git a/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/cas/service/CasServiceIntegrationTest.java b/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/cas/service/CasServiceIntegrationTest.java index 8fb1cfa96d6..08c9b83967b 100644 --- a/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/cas/service/CasServiceIntegrationTest.java +++ b/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/cas/service/CasServiceIntegrationTest.java @@ -165,7 +165,8 @@ public void testLogoutSubrogation() { subro.setSurrogateCustomerId(surrogateCustomerId); Mockito.when(subrogationRepository.findBySuperUserAndSuperUserCustomerIdAndSurrogateAndSurrogateCustomerId( superUser, superUserCustomerId, surrogate, surrogateCustomerId)).thenReturn(Optional.of(subro)); - Mockito.when(userRepository.findByEmailIgnoreCaseAndCustomerId(surrogate, surrogateCustomerId)).thenReturn(new User()); + Mockito.when(userRepository.findByEmailIgnoreCaseAndCustomerId(surrogate, surrogateCustomerId)) + .thenReturn(new User()); Mockito.when(userRepository.findByEmailIgnoreCaseAndCustomerId(superUser, superUserCustomerId)) .thenReturn(new User()); casService.deleteSubrogationBySuperUserAndSurrogate(superUser, superUserCustomerId, @@ -225,9 +226,16 @@ private Subrogation getUsersByEmail(final UserDto user) { Mockito.when(internalUserService.findUsersByEmail(email)) .thenReturn(List.of(user)); Mockito.when(internalUserService.loadGroupAndProfiles(ArgumentMatchers.any())).thenReturn(authUser); - Mockito.when(subrogationRepository.findOneBySurrogate(ArgumentMatchers.anyString())).thenReturn(subro); - Mockito.when(userRepository.findByEmail(ArgumentMatchers.anyString())).thenReturn(new User()); - casService.getUserByEmail(email, Optional.of(CommonConstants.AUTH_TOKEN_PARAMETER + "," + CommonConstants.SURROGATION_PARAMETER)); + Mockito.when(subrogationRepository.findOneBySurrogateAndSurrogateCustomerId(email, customerId)) + .thenReturn(subro); + Mockito.when(userRepository.findAllByEmailIgnoreCase(email)) + .thenReturn(List.of(new User())); + Mockito.when(userRepository.findByEmailIgnoreCaseAndCustomerId(email, customerId)) + .thenReturn(new User()); + Mockito.when(userRepository.findByEmailIgnoreCaseAndCustomerId("superuser@vitamui.com", "customer_system")) + .thenReturn(new User()); + casService.getUsersByEmail(email, + CommonConstants.AUTH_TOKEN_PARAMETER + "," + CommonConstants.SURROGATION_PARAMETER); return subro; } diff --git a/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/rest/UserControllerTest.java b/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/rest/UserControllerTest.java index 0ca7b38a7cb..26d261683b5 100644 --- a/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/rest/UserControllerTest.java +++ b/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/rest/UserControllerTest.java @@ -160,7 +160,8 @@ public void testCreationFailsAsCustomerDoesNotExist() throws Exception { userController.create(userDto); fail("should fail"); } catch (final IllegalArgumentException e) { - assertEquals("Unable to create user user@supermail.fr (unknownCustomerId): customer does not exist", e.getMessage()); + assertEquals("Unable to create user user@supermail.fr (unknownCustomerId): customer does not exist", + e.getMessage()); } } @@ -211,7 +212,8 @@ public void testCreationFailsAsIdenfierIsNotNull() userController.create(userDto); fail("should fail"); } catch (final IllegalArgumentException e) { - assertEquals("Unable to create user user@supermail.fr (customerId): identifier must be null", e.getMessage()); + assertEquals("Unable to create user user@supermail.fr (customerId): identifier must be null", + e.getMessage()); } } @@ -224,7 +226,8 @@ public void testCreationFailsAsEmailAlreadyExistsForSameCustomer() throws Invali userDto.setIdentifier(null); prepareServices(); - when(userRepository.findByEmailIgnoreCaseAndCustomerId(IamServerUtilsTest.USER_MAIL, IamServerUtilsTest.CUSTOMER_ID)) + when(userRepository.findByEmailIgnoreCaseAndCustomerId(IamServerUtilsTest.USER_MAIL, + IamServerUtilsTest.CUSTOMER_ID)) .thenReturn(buildUser()); try { @@ -245,9 +248,11 @@ public void testCreationDoesNotFailAsEmailAlreadyExistsForOtherCustomer() throws User someOtherUser = new User(); prepareServices(); - when(userRepository.findByEmailAndCustomerId(any(), Mockito.eq(IamServerUtilsTest.CUSTOMER_ID))).thenReturn( + when(userRepository.findByEmailIgnoreCaseAndCustomerId(any(), + Mockito.eq(IamServerUtilsTest.CUSTOMER_ID))).thenReturn( null); - when(userRepository.findByEmailAndCustomerId(any(), Mockito.eq(SOME_CUSTOMER_ID))).thenReturn(someOtherUser); + when(userRepository.findByEmailIgnoreCaseAndCustomerId(any(), Mockito.eq(SOME_CUSTOMER_ID))).thenReturn( + someOtherUser); userController.create(userDto); } @@ -282,7 +287,8 @@ public void testCreationFailsAsLevelIsNotValid() userController.create(userDto); fail("should fail"); } catch (final IllegalArgumentException e) { - assertEquals("Unable to create user user@supermail.fr (customerId): identifier must be null", e.getMessage()); + assertEquals("Unable to create user user@supermail.fr (customerId): identifier must be null", + e.getMessage()); } } @@ -366,9 +372,11 @@ public void testUpdateSuccessAsTheEmailAlreadyExistsOnlyForOtherCustomer() prepareServices(); - when(userRepository.findByEmailAndCustomerId(any(), Mockito.eq(IamServerUtilsTest.CUSTOMER_ID))).thenReturn( + when(userRepository.findByEmailIgnoreCaseAndCustomerId(any(), + Mockito.eq(IamServerUtilsTest.CUSTOMER_ID))).thenReturn( null); - when(userRepository.findByEmailAndCustomerId(any(), Mockito.eq(SOME_CUSTOMER_ID))).thenReturn(someOtherUser); + when(userRepository.findByEmailIgnoreCaseAndCustomerId(any(), Mockito.eq(SOME_CUSTOMER_ID))).thenReturn( + someOtherUser); userController.update(userDto.getId(), userDto); diff --git a/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/subrogation/service/InternalSubrogationServiceTest.java b/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/subrogation/service/InternalSubrogationServiceTest.java index fe73b919a34..4a514bfe995 100644 --- a/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/subrogation/service/InternalSubrogationServiceTest.java +++ b/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/subrogation/service/InternalSubrogationServiceTest.java @@ -112,8 +112,10 @@ public void testCreateSubrogation() { Mockito.when(internalSecurityService.getUser()).thenReturn( IamDtoBuilder.buildAuthUserDto("id", USER1_EMAIL, USER1_CUSTOMER_ID)); Mockito.when(subrogationRepository.save(ArgumentMatchers.any())).thenReturn(subro); - Mockito.when(userRepository.findByIgnoreCaseEmailAndCustomerId(USER2_EMAIL, USER2_CUSTOMER_ID)).thenReturn(user2); - Mockito.when(userRepository.findByEmailIgnoreCaseAndCustomerId(USER1_EMAIL, USER1_CUSTOMER_ID)).thenReturn(user1); + Mockito.when(userRepository.findByEmailIgnoreCaseAndCustomerId(USER2_EMAIL, USER2_CUSTOMER_ID)) + .thenReturn(user2); + Mockito.when(userRepository.findByEmailIgnoreCaseAndCustomerId(USER1_EMAIL, USER1_CUSTOMER_ID)) + .thenReturn(user1); Mockito.when(customerRepository.findById(ArgumentMatchers.any())).thenReturn(Optional.of(customer)); final SubrogationDto subroToCreate = new SubrogationDto(); @@ -145,7 +147,8 @@ public void testCreateSubrogationForAnotherUser() { VitamUIUtils.copyProperties(extUser2, user2); VitamUIUtils.copyProperties(extUser3, user3); final Subrogation subro = buildSubrogation(); - Mockito.when(userRepository.findByEmailIgnoreCaseAndCustomerId(USER2_EMAIL, USER2_CUSTOMER_ID)).thenReturn(user2); + Mockito.when(userRepository.findByEmailIgnoreCaseAndCustomerId(USER2_EMAIL, USER2_CUSTOMER_ID)) + .thenReturn(user2); Mockito.when(customerRepository.findById(ArgumentMatchers.any())).thenReturn(Optional.of(customer)); final SubrogationDto subroToCreate = new SubrogationDto(); diff --git a/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/user/dao/UserRepositoryTest.java b/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/user/dao/UserRepositoryTest.java index 1093c5d2a5b..2e0522571b7 100644 --- a/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/user/dao/UserRepositoryTest.java +++ b/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/user/dao/UserRepositoryTest.java @@ -32,11 +32,10 @@ /** * Tests for {@link UserRepository} - * */ @RunWith(SpringRunner.class) -@Import({ TestMongoConfig.class }) +@Import({TestMongoConfig.class}) @EnableMongoRepositories(basePackageClasses = UserRepository.class, repositoryBaseClass = VitamUIRepositoryImpl.class) public class UserRepositoryTest { @@ -85,7 +84,7 @@ public void testAllFindByEmail() { repository.save(user1bis); repository.save(user2); - final List user = repository.findAllByEmail(email1); + final List user = repository.findAllByEmailIgnoreCase(email1); assertNotNull(user); assertThat(user).hasSize(2); assertThat(user).extracting(User::getEmail, User::getCustomerId) @@ -119,12 +118,14 @@ public void testCheckExistUser() { final User probe = new User(); probe.setEmail(email); Example example = Example.of(probe, - ExampleMatcher.matching().withIgnoreNullValues().withIgnorePaths("otp", "subrogeable", "canLogin", "nbFailedAttempts")); + ExampleMatcher.matching().withIgnoreNullValues() + .withIgnorePaths("otp", "subrogeable", "canLogin", "nbFailedAttempts")); boolean exist = repository.exists(example); assertThat(exist).isTrue(); probe.setEmail(StringUtils.EMPTY); - example = Example.of(probe, ExampleMatcher.matching().withIgnoreNullValues().withIgnorePaths("otp", "subrogeable", "canLogin", "nbFailedAttempts")); + example = Example.of(probe, ExampleMatcher.matching().withIgnoreNullValues() + .withIgnorePaths("otp", "subrogeable", "canLogin", "nbFailedAttempts")); exist = repository.exists(example); assertThat(exist).isFalse(); } @@ -139,24 +140,30 @@ public void testFindByCustomerIdAndSubrogeable() { userVitamUI.setType(UserTypeEnum.NOMINATIVE); repository.save(userVitamUI); - Page users = repository.findByCustomerIdAndSubrogeableAndTypeAndStatus(CUSTOMER_ID, true, UserTypeEnum.NOMINATIVE, UserStatusEnum.ENABLED, + Page users = + repository.findByCustomerIdAndSubrogeableAndTypeAndStatus(CUSTOMER_ID, true, UserTypeEnum.NOMINATIVE, + UserStatusEnum.ENABLED, PageRequest.of(0, 20)); assertThat(users).isNotEmpty(); assertThat(users.getContent().size()).isEqualTo(1); - users = repository.findByCustomerIdAndSubrogeableAndTypeAndStatus(CUSTOMER_ID, true, UserTypeEnum.GENERIC, UserStatusEnum.ENABLED, - PageRequest.of(0, 20)); + users = repository.findByCustomerIdAndSubrogeableAndTypeAndStatus(CUSTOMER_ID, true, UserTypeEnum.GENERIC, + UserStatusEnum.ENABLED, + PageRequest.of(0, 20)); assertThat(users).isEmpty(); - users = repository.findByCustomerIdAndSubrogeableAndTypeAndStatus(CUSTOMER_ID, true, UserTypeEnum.NOMINATIVE, UserStatusEnum.DISABLED, - PageRequest.of(0, 20)); + users = repository.findByCustomerIdAndSubrogeableAndTypeAndStatus(CUSTOMER_ID, true, UserTypeEnum.NOMINATIVE, + UserStatusEnum.DISABLED, + PageRequest.of(0, 20)); assertThat(users).isEmpty(); - users = repository.findByCustomerIdAndSubrogeableAndTypeAndStatus(CUSTOMER_ID, false, UserTypeEnum.NOMINATIVE, UserStatusEnum.ENABLED, - PageRequest.of(0, 20)); + users = repository.findByCustomerIdAndSubrogeableAndTypeAndStatus(CUSTOMER_ID, false, UserTypeEnum.NOMINATIVE, + UserStatusEnum.ENABLED, + PageRequest.of(0, 20)); assertThat(users).isEmpty(); - users = repository.findByCustomerIdAndSubrogeableAndTypeAndStatus("toto", true, UserTypeEnum.NOMINATIVE, UserStatusEnum.ENABLED, PageRequest.of(0, 20)); + users = repository.findByCustomerIdAndSubrogeableAndTypeAndStatus("toto", true, UserTypeEnum.NOMINATIVE, + UserStatusEnum.ENABLED, PageRequest.of(0, 20)); assertThat(users).isEmpty(); } @@ -177,7 +184,8 @@ public void testGetPaginatedValues() { Query query = Query.query(Criteria.where("customerId").is(CUSTOMER_ID).and("type").is(UserTypeEnum.GENERIC)); - PaginatedValuesDto users = repository.getPaginatedValues(0, 20, Optional.of(query), Optional.empty(), Optional.empty()); + PaginatedValuesDto users = + repository.getPaginatedValues(0, 20, Optional.of(query), Optional.empty(), Optional.empty()); assertThat(users.getValues()).isNotEmpty(); assertThat(users.getValues().size()).isEqualTo(1); diff --git a/cas/cas-server/src/main/config/cas-server-application-dev.yml b/cas/cas-server/src/main/config/cas-server-application-dev.yml index cd4ed307bf5..1d5983da554 100644 --- a/cas/cas-server/src/main/config/cas-server-application-dev.yml +++ b/cas/cas-server/src/main/config/cas-server-application-dev.yml @@ -206,7 +206,7 @@ logging: org.springframework.amqp: 'OFF' org.springframework.context.annotation: 'OFF' org.springframework.boot.devtools: 'OFF' - org.apereo.inspektr.audit.support: 'OFF' + org.apereo.inspektr.audit.support: 'INFO' # Cas CORS (necessary for mobile app) cas.http-web-request.cors.enabled: true diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/authentication/UserPrincipalResolver.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/authentication/UserPrincipalResolver.java index aed227292e2..2b12af99592 100644 --- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/authentication/UserPrincipalResolver.java +++ b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/authentication/UserPrincipalResolver.java @@ -51,16 +51,6 @@ import fr.gouv.vitamui.iam.common.dto.IdentityProviderDto; import fr.gouv.vitamui.iam.common.utils.IdentityProviderHelper; import fr.gouv.vitamui.iam.external.client.CasExternalRestClient; -import java.security.cert.CertificateParsingException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.regex.Pattern; import lombok.RequiredArgsConstructor; import lombok.val; import org.apache.commons.lang.StringUtils; @@ -102,7 +92,6 @@ import static fr.gouv.vitamui.commons.api.CommonConstants.AUTHTOKEN_ATTRIBUTE; import static fr.gouv.vitamui.commons.api.CommonConstants.AUTH_TOKEN_PARAMETER; import static fr.gouv.vitamui.commons.api.CommonConstants.BASIC_CUSTOMER_ATTRIBUTE; -import static fr.gouv.vitamui.commons.api.CommonConstants.CENTER_CODE; import static fr.gouv.vitamui.commons.api.CommonConstants.CENTER_CODES; import static fr.gouv.vitamui.commons.api.CommonConstants.CUSTOMER_IDENTIFIER_ATTRIBUTE; import static fr.gouv.vitamui.commons.api.CommonConstants.CUSTOMER_ID_ATTRIBUTE; @@ -225,11 +214,13 @@ public Principal resolve(final Credential credential, final Optional .collect(Collectors.toList()); if (certProviders.isEmpty()) { - LOGGER.warn("Cert authentication failed - No valid certificate identity provider found for: {}", userDomain); + LOGGER.warn("Cert authentication failed - No valid certificate identity provider found for: {}", + userDomain); return NullPrincipal.getInstance(); } if (certProviders.size() > 1) { - LOGGER.warn("Cert authentication failed - Too many certificate identity providers found for: {}", userDomain); + LOGGER.warn("Cert authentication failed - Too many certificate identity providers found for: {}", + userDomain); return NullPrincipal.getInstance(); } diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/config/WebflowConfig.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/config/WebflowConfig.java index 037025081ec..0a5434863cd 100644 --- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/config/WebflowConfig.java +++ b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/config/WebflowConfig.java @@ -43,14 +43,13 @@ import fr.gouv.vitamui.cas.util.Utils; import fr.gouv.vitamui.cas.web.CustomOidcRevocationEndpointController; import fr.gouv.vitamui.cas.webflow.actions.CheckMfaTokenAction; -import fr.gouv.vitamui.cas.webflow.actions.CustomDelegatedAuthenticationClientLogoutAction; import fr.gouv.vitamui.cas.webflow.actions.CustomDelegatedClientAuthenticationAction; import fr.gouv.vitamui.cas.webflow.actions.CustomSendTokenAction; +import fr.gouv.vitamui.cas.webflow.actions.CustomerSelectedAction; import fr.gouv.vitamui.cas.webflow.actions.DispatcherAction; import fr.gouv.vitamui.cas.webflow.actions.GeneralTerminateSessionAction; import fr.gouv.vitamui.cas.webflow.actions.I18NSendPasswordResetInstructionsAction; import fr.gouv.vitamui.cas.webflow.actions.ListCustomersAction; -import fr.gouv.vitamui.cas.webflow.actions.CustomerSelectedAction; import fr.gouv.vitamui.cas.webflow.actions.TriggerChangePasswordAction; import fr.gouv.vitamui.cas.webflow.configurer.CustomCasSimpleMultifactorWebflowConfigurer; import fr.gouv.vitamui.cas.webflow.configurer.CustomLoginWebflowConfigurer; @@ -83,8 +82,6 @@ import org.apereo.cas.ticket.factory.DefaultTransientSessionTicketFactory; import org.apereo.cas.ticket.registry.TicketRegistry; import org.apereo.cas.ticket.registry.TicketRegistrySupport; -import org.apereo.cas.util.spring.beans.BeanCondition; -import org.apereo.cas.util.spring.beans.BeanSupplier; import org.apereo.cas.web.cookie.CasCookieBuilder; import org.apereo.cas.web.flow.CasWebflowConfigurer; import org.apereo.cas.web.flow.CasWebflowConstants; @@ -98,7 +95,6 @@ import org.apereo.cas.web.flow.resolver.CasWebflowEventResolver; import org.apereo.cas.web.flow.resolver.impl.CasWebflowEventResolutionConfigurationContext; import org.apereo.cas.web.flow.util.MultifactorAuthenticationWebflowUtils; -import org.pac4j.core.client.Clients; import org.pac4j.core.context.session.SessionStore; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; diff --git a/cas/cas-server/src/main/resources/static/js/password-checks.js b/cas/cas-server/src/main/resources/static/js/password-checks.js index f5a76bc2693..872a9f204f7 100644 --- a/cas/cas-server/src/main/resources/static/js/password-checks.js +++ b/cas/cas-server/src/main/resources/static/js/password-checks.js @@ -37,6 +37,7 @@ function checkValidity(password) { var policyPatternRegex = new RegExp(policyPattern); var valid = policyPatternRegex.test(password); onConditionAddClassValid(valid, "#passwords-strongness"); + checkInvalidCharacter(password); } function checkPasswordsEquality(password, confirmPassword) { @@ -49,3 +50,20 @@ function checkRules() { checkValidity(password); checkPasswordsEquality(password, confirmPassword); } + + +function checkInvalidCharacter(password) { + const invalidCharacterElement = document.getElementById( + "invalid-character-error" + ); + if (invalidCharacterElement) { + const isValid = /^[\s\da-zA-Z!"#$%&£'()*+,-./:;<=>?@\]\[^_`{|}~]*$/.test( + password + ); + if (isValid) { + invalidCharacterElement.style.display = "none"; + } else { + invalidCharacterElement.style.display = "block"; + } + } +} diff --git a/cas/cas-server/src/main/resources/templates/fragments/pwdupdateform.html b/cas/cas-server/src/main/resources/templates/fragments/pwdupdateform.html index 2a4cfbd0192..c6c27e91b64 100644 --- a/cas/cas-server/src/main/resources/templates/fragments/pwdupdateform.html +++ b/cas/cas-server/src/main/resources/templates/fragments/pwdupdateform.html @@ -54,6 +54,9 @@ Confirmer le nouveau mot de passe * +