From 2d10b2b5c63afe7ae3784498b3d73d39b5961bf5 Mon Sep 17 00:00:00 2001 From: Ali Oezyetimoglu Date: Mon, 21 Aug 2023 20:48:52 +0200 Subject: [PATCH] added password policy and caught exception --- build.gradle.kts | 1 - .../service/TenantManagementService.java | 39 +++++++++++++++---- .../knecon/fforesight/tests/TenantsTest.java | 20 +++++++++- .../fforesight/utils/TestTenantService.java | 38 +++++++++++++++--- 4 files changed, 82 insertions(+), 16 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 07c114e..1e244ab 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -88,7 +88,6 @@ dependencies { implementation("com.google.guava:guava:31.1-jre") implementation("org.liquibase:liquibase-core:4.17.2") implementation("org.keycloak:keycloak-admin-client:21.0.1") - implementation("org.keycloak:keycloak-model-jpa:21.0.1") implementation("org.springframework.boot:spring-boot-starter-amqp") implementation("org.springframework.boot:spring-boot-starter-validation") implementation("org.springframework.retry:spring-retry") diff --git a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java index d016aaa..b903ef9 100644 --- a/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java +++ b/src/main/java/com/knecon/fforesight/tenantusermanagement/service/TenantManagementService.java @@ -10,11 +10,12 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; import javax.sql.DataSource; -import org.keycloak.policy.PasswordPolicyNotMetException; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.RealmRepresentation; @@ -194,7 +195,6 @@ public class TenantManagementService implements TenantProvider { throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Failed to create KC realm"); } keycloak.getAdminClient().realms(); - System.out.println(keycloak.getAdminClient().realm("test-tenant-fforesight").toRepresentation()); generalConfigurationService.initGeneralConfiguration(tenantRequest.getTenantId()); keyCloakRoleManagerService.updateRoles(tenantRequest.getTenantId()); @@ -300,12 +300,7 @@ public class TenantManagementService implements TenantProvider { realm.setUsers(users.stream().map(this::toUserRepresentation).collect(Collectors.toList())); - try { - realm.setPasswordPolicy("digits(1) and length(12) and lowerCase(1) and notEmail and notUsername and specialChars(1) and upperCase(1)"); - } catch (PasswordPolicyNotMetException e) { - throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage(), e); - } - + realm.setPasswordPolicy("digits(1) and length(12) and lowerCase(1) and notEmail and notUsername and specialChars(1) and upperCase(1)"); keycloak.getAdminClient().realms().create(realm); } @@ -325,6 +320,7 @@ public class TenantManagementService implements TenantProvider { var credentialRepresentation = new CredentialRepresentation(); credentialRepresentation.setType("password"); + validatePasswordWithPolicy(redUser.getPassword(), redUser.getUsername(), redUser.getEmail()); credentialRepresentation.setValue(redUser.getPassword()); var user = new UserRepresentation(); @@ -351,6 +347,33 @@ public class TenantManagementService implements TenantProvider { } + private void validatePasswordWithPolicy(String password, String username, String email) { + if (password.contains(username) || password.contains(email) || !checkStringContainsUpperCaseAndLowerCaseAndDigitsAndSpecialCharsAndHasLength(password)) { + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Password does not match password policy."); + } + } + + + private boolean checkStringContainsUpperCaseAndLowerCaseAndDigitsAndSpecialCharsAndHasLength(String str) { + + if (str.length() >= 12) { + Pattern lowerCaseLetter = Pattern.compile("[a-z]"); + Pattern upperCaseLetter = Pattern.compile("[A-Z]"); + Pattern digit = Pattern.compile("[0-9]"); + Pattern special = Pattern.compile("[^a-zA-Z0-9 ]"); + + Matcher hasLowerCaseLetter = lowerCaseLetter.matcher(str); + Matcher hasUpperCaseLetter = upperCaseLetter.matcher(str); + Matcher hasDigit = digit.matcher(str); + Matcher hasSpecialChar = special.matcher(str); + + return hasLowerCaseLetter.find() && hasUpperCaseLetter.find() && hasDigit.find() && hasSpecialChar.find(); + } + + return false; + } + + @Override @Transactional public void updateDetails(String tenantId, com.knecon.fforesight.tenantcommons.model.UpdateDetailsRequest request) { diff --git a/src/test/java/com/knecon/fforesight/tests/TenantsTest.java b/src/test/java/com/knecon/fforesight/tests/TenantsTest.java index 3fe7071..8b6a67c 100644 --- a/src/test/java/com/knecon/fforesight/tests/TenantsTest.java +++ b/src/test/java/com/knecon/fforesight/tests/TenantsTest.java @@ -1,11 +1,15 @@ package com.knecon.fforesight.tests; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertThrows; +import java.util.List; import java.util.Map; import java.util.Set; +import javax.ws.rs.BadRequestException; + import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -18,6 +22,7 @@ import com.knecon.fforesight.tenantcommons.model.DatabaseConnection; import com.knecon.fforesight.tenantcommons.model.S3StorageConnection; import com.knecon.fforesight.tenantcommons.model.SearchConnection; import com.knecon.fforesight.tenantusermanagement.model.TenantRequest; +import com.knecon.fforesight.tenantusermanagement.model.TenantUser; import com.knecon.fforesight.utils.TestTenantService; import feign.FeignException; @@ -43,8 +48,6 @@ public class TenantsTest extends AbstractTenantUserManagementIntegrationTest { testTenantService.createTestTenantIfNotExists("new_tenant", minioPort); - Thread.sleep(100000); - TenantContext.setTenantId("new_tenant"); var deploymentKey = tenantsClient.getDeploymentKey("new_tenant"); @@ -226,4 +229,17 @@ public class TenantsTest extends AbstractTenantUserManagementIntegrationTest { TenantContext.clear(); } + + @Test + public void testPasswordPolicy() { + testTenantService.createTestTenantIfNotExists("new_tenant", minioPort); + + TenantContext.setTenantId("new_tenant"); + + assertThat(tenantsClient.getTenant("new_tenant")).isNotNull(); + + assertThatThrownBy(() -> testTenantService.createTestTenantIfNotExists("new_tenant_2", minioPort, "passwordDoesNotMatchPolicy")).isInstanceOf(FeignException.class); + + TenantContext.clear(); + } } diff --git a/src/test/java/com/knecon/fforesight/utils/TestTenantService.java b/src/test/java/com/knecon/fforesight/utils/TestTenantService.java index c1f2009..ef1b956 100644 --- a/src/test/java/com/knecon/fforesight/utils/TestTenantService.java +++ b/src/test/java/com/knecon/fforesight/utils/TestTenantService.java @@ -29,6 +29,7 @@ public class TestTenantService { private final TokenService tokenService; + public void createTestTenantIfNotExists(String testTenantId, int actualPort) { try { @@ -42,7 +43,9 @@ public class TestTenantService { } + public void createTestTenantWithoutStorageIfNotExist(String testTenantId) { + try { var tenantExists = internalTenantsResource.getTenant(testTenantId); assertThat(tenantExists.getGuid()).isNotBlank(); @@ -53,14 +56,36 @@ public class TestTenantService { } } + + public void createTestTenantIfNotExists(String testTenantId, int actualPort, String userPassword) { + + try { + var tenantExists = internalTenantsResource.getTenant(testTenantId); + assertThat(tenantExists.getGuid()).isNotBlank(); + + } catch (Exception e) { + // not found + createUser(testTenantId, actualPort, true, userPassword); + } + + } + + private void createUser(String testTenantId, int actualPort, boolean withStorage) { + + createUser(testTenantId, actualPort, withStorage, "secret1234!OH"); + } + + + private void createUser(String testTenantId, int actualPort, boolean withStorage, String userPassword) { + // not found TenantRequest tenantRequest; var tenantRequestBuilder = TenantRequest.builder() .tenantId(testTenantId) .displayName(testTenantId) .guid(UUID.randomUUID().toString()) - .defaultUsers(List.of(TenantUser.builder().roles(Set.of("SUPER_USER")).username("test@fforesight.com").password("secret1234!OH").email("test@fforesight.com").build())) + .defaultUsers(List.of(TenantUser.builder().roles(Set.of("SUPER_USER")).username("test@fforesight.com").password(userPassword).email("test@fforesight.com").build())) .databaseConnection(DatabaseConnection.builder() .driver("postgresql") .host(SpringPostgreSQLTestContainer.getInstance().getHost()) @@ -73,9 +98,12 @@ public class TestTenantService { .searchConnection(SearchConnection.builder().hosts(Set.of("localhost")).port(9200).scheme("http").numberOfShards("1").numberOfReplicas("5").build()); if (withStorage) { - tenantRequest = tenantRequestBuilder - .s3StorageConnection(S3StorageConnection.builder().key("minioadmin").secret("minioadmin").bucketName("redaction").endpoint("http://localhost:" + actualPort).build()) - .build(); + tenantRequest = tenantRequestBuilder.s3StorageConnection(S3StorageConnection.builder() + .key("minioadmin") + .secret("minioadmin") + .bucketName("redaction") + .endpoint("http://localhost:" + actualPort) + .build()).build(); } else { tenantRequest = tenantRequestBuilder.build(); } @@ -84,7 +112,7 @@ public class TestTenantService { assertThat(response.getGuid()).isNotBlank(); TenantContext.setTenantId(testTenantId); - tokenService.setUser("test@fforesight.com", "secret1234!OH"); + tokenService.setUser("test@fforesight.com", userPassword); var tenant = tenantsClient.getTenant(testTenantId); assertThat(tenant.getGuid()).isNotBlank();