diff --git a/dbrepo-metadata-service/pom.xml b/dbrepo-metadata-service/pom.xml index 5b1d35d5eb1738b9c8b5a543d1385ad31ed2e39d..6311add8b60aefd47f3777317c119da138d64771 100644 --- a/dbrepo-metadata-service/pom.xml +++ b/dbrepo-metadata-service/pom.xml @@ -65,6 +65,7 @@ <springdoc-openapi.version>2.1.0</springdoc-openapi.version> <testcontainers.version>1.18.3</testcontainers.version> <opensearch-testcontainer.version>2.0.0</opensearch-testcontainer.version> + <keycloak-testcontainer.version>2.5.0</keycloak-testcontainer.version> <opensearch-client.version>1.1.0</opensearch-client.version> <opensearch-rest-client.version>2.8.0</opensearch-rest-client.version> <jackson.version>2.15.2</jackson.version> @@ -289,6 +290,12 @@ <version>${testcontainers.version}</version> <scope>test</scope> </dependency> + <dependency> + <groupId>com.github.dasniko</groupId> + <artifactId>testcontainers-keycloak</artifactId> + <version>${keycloak-testcontainer.version}</version> + <scope>test</scope> + </dependency> <dependency> <groupId>org.opensearch</groupId> <artifactId>opensearch-testcontainers</artifactId> diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/config/KeycloakContainerConfig.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/config/KeycloakContainerConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..2a8e08d4a0beb469da50a3997a643f96ba279524 --- /dev/null +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/config/KeycloakContainerConfig.java @@ -0,0 +1,71 @@ +package at.tuwien.config; + +import at.tuwien.test.BaseTest; +import dasniko.testcontainers.keycloak.KeycloakContainer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.testcontainers.containers.MariaDBContainer; +import org.testcontainers.images.PullPolicy; + +/** + * This class configures the MariaDB container for the integration tests. + */ +@Configuration +public class KeycloakContainerConfig { + + public static CustomKeycloakContainer getContainer() { + return CustomKeycloakContainer.getInstance(); + } + + @Bean + public CustomKeycloakContainer keycloakContainer() { + return getContainer(); + } + + /** + * This class represents the customized MariaDB container. It is a singleton to avoid the recreation of containers + * which can be very time-consuming. + */ + public static class CustomKeycloakContainer extends KeycloakContainer { + + private static CustomKeycloakContainer instance; + + private boolean started = false; + + public static synchronized CustomKeycloakContainer getInstance() { + if(instance == null) { + instance = new CustomKeycloakContainer("quay.io/keycloak/keycloak:21.0"); + instance.withImagePullPolicy(PullPolicy.alwaysPull()); + instance.addFixedExposedPort(BaseTest.CONTAINER_1_PORT, BaseTest.IMAGE_1_PORT); + instance.withAdminUsername("fda"); + instance.withAdminPassword("fda"); + instance.addFixedExposedPort(8080, 8080); + instance.withRealmImportFile("./dbrepo-realm.json"); + } + return instance; + } + + private CustomKeycloakContainer(String dockerImageName) { + super(dockerImageName); + } + + @Override + protected void configure() { + super.configure(); + this.addEnv("KC_HOSTNAME_STRICT_HTTPS", "false"); + } + + @Override + public synchronized void start() { + if(!started) { + super.start(); + started = true; + } + } + + @Override + public void stop() { + // do nothing, JVM handles shut down + } + } +} diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/DatabaseEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/DatabaseEndpointUnitTest.java index fbff73859ad900c4696cd2cd9376626ab0b0a7ee..d9ae17223957d0014fb074fac4c20784bb53d4dc 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/DatabaseEndpointUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/DatabaseEndpointUnitTest.java @@ -13,6 +13,7 @@ import at.tuwien.exception.*; import at.tuwien.gateway.KeycloakGateway; import at.tuwien.repository.mdb.DatabaseAccessRepository; import at.tuwien.repository.mdb.IdentifierRepository; +import at.tuwien.repository.mdb.UserRepository; import at.tuwien.repository.sdb.DatabaseIdxRepository; import at.tuwien.service.AccessService; import at.tuwien.service.ContainerService; @@ -34,6 +35,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; import java.security.Principal; import java.util.List; +import java.util.Optional; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; @@ -73,6 +75,9 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { @MockBean private IdentifierRepository identifierRepository; + @MockBean + private UserRepository userRepository; + @Autowired private DatabaseEndpoint databaseEndpoint; @@ -86,7 +91,7 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { .build(); /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { create_generic(DATABASE_1_ID, null, request, null, null); }); } @@ -101,7 +106,7 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { .build(); /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { create_generic(DATABASE_3_ID, null, request, USER_4, USER_4_PRINCIPAL); }); } @@ -140,6 +145,8 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { .thenReturn(DATABASE_1_USER_1_WRITE_ALL_ACCESS); when(keycloakGateway.findByUsername(USER_1_USERNAME)) .thenReturn(USER_1_KEYCLOAK_DTO); + when(userRepository.findByUsername(USER_1_USERNAME)) + .thenReturn(Optional.of(USER_1)); /* test */ create_generic(DATABASE_1_ID, null, request, USER_1, USER_1_PRINCIPAL); @@ -186,7 +193,7 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { .build(); /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { visibility_generic(DATABASE_1_ID, DATABASE_1, DATABASE_1_DTO, request, null); }); } @@ -202,6 +209,8 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { /* mock */ when(keycloakGateway.findByUsername(USER_1_USERNAME)) .thenReturn(USER_1_KEYCLOAK_DTO); + when(userRepository.findByUsername(USER_1_USERNAME)) + .thenReturn(Optional.of(USER_1)); /* test */ visibility_generic(DATABASE_1_ID, DATABASE_1, DATABASE_1_DTO, request, USER_1_PRINCIPAL); @@ -215,7 +224,7 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { .build(); /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { visibility_generic(DATABASE_1_ID, DATABASE_1, DATABASE_1_DTO, request, USER_4_PRINCIPAL); }); } @@ -227,8 +236,12 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { .isPublic(true) .build(); + /* mock */ + when(userRepository.findByUsername(USER_2_USERNAME)) + .thenReturn(Optional.of(USER_2)); + /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(NotAllowedException.class, () -> { visibility_generic(DATABASE_1_ID, DATABASE_1, DATABASE_1_DTO, request, USER_2_PRINCIPAL); }); } @@ -241,7 +254,7 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { .build(); /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { databaseEndpoint.transfer(DATABASE_3_ID, request, USER_4_PRINCIPAL); }); } @@ -256,9 +269,11 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { /* mock */ when(databaseService.findById(DATABASE_1_ID)) .thenReturn(DATABASE_1); + when(userRepository.findByUsername(USER_2_USERNAME)) + .thenReturn(Optional.of(USER_2)); /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(NotAllowedException.class, () -> { databaseEndpoint.transfer(DATABASE_1_ID, request, USER_2_PRINCIPAL); }); } @@ -276,6 +291,8 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { .thenReturn(DATABASE_1); when(keycloakGateway.findByUsername(USER_1_USERNAME)) .thenReturn(USER_1_KEYCLOAK_DTO); + when(userRepository.findByUsername(USER_1_USERNAME)) + .thenReturn(Optional.of(USER_1)); /* test */ databaseEndpoint.transfer(DATABASE_1_ID, request, USER_1_PRINCIPAL); @@ -362,7 +379,7 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { public void delete_anonymous_fails() { /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { delete_generic(DATABASE_1_ID, DATABASE_1, null); }); } @@ -372,7 +389,7 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { public void delete_noRole_fails() { /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { delete_generic(DATABASE_1_ID, DATABASE_1, USER_1_PRINCIPAL); }); } @@ -436,7 +453,7 @@ public class DatabaseEndpointUnitTest extends BaseUnitTest { public void visibility_generic(Long databaseId, Database database, DatabaseDto dto, DatabaseModifyVisibilityDto data, Principal principal) throws NotAllowedException, - DatabaseNotFoundException, UserNotFoundException, KeycloakRemoteException, AccessDeniedException { + DatabaseNotFoundException, UserNotFoundException { /* mock */ if (database != null) { diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/PersistenceEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/PersistenceEndpointUnitTest.java index 5ec031a117ac67762ce86735593d226591ad42b5..0f01a366fb69dd83d9512afe2336bdb6b0126262 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/PersistenceEndpointUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/PersistenceEndpointUnitTest.java @@ -555,7 +555,7 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { public void update_anonymous_fails() { /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { generic_update(IDENTIFIER_3_ID, IDENTIFIER_3, IDENTIFIER_3_DTO_UPDATE_REQUEST, null, null, null); }); } @@ -565,7 +565,7 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { public void update_noRole_fails() { /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { generic_update(IDENTIFIER_3_ID, IDENTIFIER_3, IDENTIFIER_3_DTO_UPDATE_REQUEST, USER_4_USERNAME, USER_4, USER_4_PRINCIPAL); }); } @@ -600,7 +600,7 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { public void delete_anonymous_fails() { /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { this.generic_delete(IDENTIFIER_1_ID, IDENTIFIER_1); }); } @@ -610,7 +610,7 @@ public class PersistenceEndpointUnitTest extends BaseUnitTest { public void delete_noRole_fails() { /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { this.generic_delete(IDENTIFIER_1_ID, IDENTIFIER_1); }); } diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableDataEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableDataEndpointUnitTest.java index 28d17f856881a7d7b55c338b930eb50e195c50bc..801aec38422b9e10d082a799736e742b0d760894 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableDataEndpointUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableDataEndpointUnitTest.java @@ -65,7 +65,7 @@ public class TableDataEndpointUnitTest extends BaseUnitTest { public void import_publicAnonymous_fails() { /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { generic_import(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, null, null, null); }); } @@ -75,7 +75,7 @@ public class TableDataEndpointUnitTest extends BaseUnitTest { public void import_publicNoRoleRead_fails() { /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { generic_import(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, USER_2_ID, DATABASE_1_USER_1_READ_ACCESS, USER_2_PRINCIPAL); }); @@ -86,7 +86,7 @@ public class TableDataEndpointUnitTest extends BaseUnitTest { public void import_publicNoRoleWriteOwn_fails() { /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { generic_import(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, USER_2_ID, DATABASE_1_USER_1_WRITE_OWN_ACCESS, USER_2_PRINCIPAL); }); @@ -97,7 +97,7 @@ public class TableDataEndpointUnitTest extends BaseUnitTest { public void import_privateAnonymous_fails() { /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { generic_import(DATABASE_2_ID, DATABASE_2, TABLE_1_ID, TABLE_1, null, null, null); }); } @@ -107,7 +107,7 @@ public class TableDataEndpointUnitTest extends BaseUnitTest { public void import_privateNoRoleRead_fails() { /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { generic_import(DATABASE_2_ID, DATABASE_2, TABLE_1_ID, TABLE_1, USER_2_ID, DATABASE_2_USER_1_READ_ACCESS, USER_2_PRINCIPAL); }); @@ -118,7 +118,7 @@ public class TableDataEndpointUnitTest extends BaseUnitTest { public void import_privateNoRoleWriteOwn_fails() { /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { generic_import(DATABASE_2_ID, DATABASE_2, TABLE_1_ID, TABLE_1, USER_2_ID, DATABASE_2_USER_1_WRITE_OWN_ACCESS, USER_2_PRINCIPAL); }); @@ -129,7 +129,7 @@ public class TableDataEndpointUnitTest extends BaseUnitTest { public void import_publicAnonymous_succeeds() { /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { generic_import(DATABASE_3_ID, DATABASE_3, TABLE_8_ID, TABLE_8, null, null, null); }); } @@ -138,7 +138,7 @@ public class TableDataEndpointUnitTest extends BaseUnitTest { @WithMockUser(username = USER_1_USERNAME, authorities = {"insert-table-data"}) public void import_publicWriteAll_succeeds() throws UserNotFoundException, TableNotFoundException, NotAllowedException, TableMalformedException, DatabaseConnectionException, QueryMalformedException, DatabaseNotFoundException, - ImageNotSupportedException, at.tuwien.exception.AccessDeniedException { + ImageNotSupportedException, AccessDeniedException { /* test */ generic_import(DATABASE_3_ID, DATABASE_3, TABLE_8_ID, TABLE_8, USER_1_ID, @@ -149,7 +149,7 @@ public class TableDataEndpointUnitTest extends BaseUnitTest { @WithMockUser(username = USER_1_USERNAME, authorities = {"insert-table-data"}) public void import_privateWriteAll_succeeds() throws UserNotFoundException, TableNotFoundException, NotAllowedException, TableMalformedException, DatabaseConnectionException, QueryMalformedException, DatabaseNotFoundException, - ImageNotSupportedException, at.tuwien.exception.AccessDeniedException { + ImageNotSupportedException, AccessDeniedException { /* test */ generic_import(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, USER_1_ID, @@ -161,7 +161,7 @@ public class TableDataEndpointUnitTest extends BaseUnitTest { public void insert_publicAnonymous_fails() { /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { generic_insert(DATABASE_1_ID, TABLE_1_ID, DATABASE_1, TABLE_1, USER_2_ID, null, TABLE_1_CSV_DTO, null); }); @@ -172,7 +172,7 @@ public class TableDataEndpointUnitTest extends BaseUnitTest { public void insert_publicNoRoleRead_fails() { /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { generic_insert(DATABASE_1_ID, TABLE_1_ID, DATABASE_1, TABLE_1, USER_2_ID, DATABASE_1_USER_1_READ_ACCESS, TABLE_1_CSV_DTO, USER_2_PRINCIPAL); }); @@ -183,7 +183,7 @@ public class TableDataEndpointUnitTest extends BaseUnitTest { public void insert_publicNoRoleWriteOwn_fails() { /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { generic_insert(DATABASE_1_ID, TABLE_1_ID, DATABASE_1, TABLE_1, USER_2_ID, DATABASE_1_USER_1_WRITE_OWN_ACCESS, TABLE_1_CSV_DTO, USER_2_PRINCIPAL); }); @@ -194,7 +194,7 @@ public class TableDataEndpointUnitTest extends BaseUnitTest { public void insert_privateAnonymous_fails() { /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { generic_insert(DATABASE_2_ID, TABLE_1_ID, DATABASE_2, TABLE_1, USER_2_ID, null, TABLE_1_CSV_DTO, null); }); @@ -205,7 +205,7 @@ public class TableDataEndpointUnitTest extends BaseUnitTest { public void insert_privateNoRoleRead_fails() { /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { generic_insert(DATABASE_2_ID, TABLE_1_ID, DATABASE_2, TABLE_1, USER_2_ID, DATABASE_2_USER_1_READ_ACCESS, TABLE_1_CSV_DTO, USER_2_PRINCIPAL); }); @@ -216,7 +216,7 @@ public class TableDataEndpointUnitTest extends BaseUnitTest { public void insert_privateNoRoleWriteOwn_fails() { /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { generic_insert(DATABASE_2_ID, TABLE_1_ID, DATABASE_2, TABLE_1, USER_2_ID, DATABASE_2_USER_1_WRITE_OWN_ACCESS, TABLE_1_CSV_DTO, USER_2_PRINCIPAL); }); @@ -226,7 +226,7 @@ public class TableDataEndpointUnitTest extends BaseUnitTest { @WithMockUser(username = USER_1_USERNAME, authorities = {"insert-table-data"}) public void insert_publicWriteAll_succeeds() throws UserNotFoundException, TableNotFoundException, NotAllowedException, TableMalformedException, DatabaseConnectionException, DatabaseNotFoundException, - ImageNotSupportedException, ContainerNotFoundException, at.tuwien.exception.AccessDeniedException { + ImageNotSupportedException, ContainerNotFoundException, AccessDeniedException { /* test */ generic_insert(DATABASE_3_ID, TABLE_8_ID, DATABASE_3, TABLE_8, USER_1_ID, @@ -237,7 +237,7 @@ public class TableDataEndpointUnitTest extends BaseUnitTest { @WithMockUser(username = USER_1_USERNAME, authorities = {"insert-table-data"}) public void insert_privateWriteAll_succeeds() throws UserNotFoundException, TableNotFoundException, NotAllowedException, TableMalformedException, DatabaseConnectionException, DatabaseNotFoundException, - ImageNotSupportedException, ContainerNotFoundException, at.tuwien.exception.AccessDeniedException { + ImageNotSupportedException, ContainerNotFoundException, AccessDeniedException { /* test */ generic_insert(DATABASE_1_ID, TABLE_1_ID, DATABASE_1, TABLE_1, USER_1_ID, DATABASE_1_USER_1_WRITE_ALL_ACCESS, TABLE_1_CSV_DTO, USER_1_PRINCIPAL); @@ -247,7 +247,7 @@ public class TableDataEndpointUnitTest extends BaseUnitTest { @WithMockUser(username = USER_1_USERNAME, authorities = {"insert-table-data"}) public void insert_privateDataNull_fails() throws UserNotFoundException, TableNotFoundException, NotAllowedException, TableMalformedException, DatabaseConnectionException, DatabaseNotFoundException, - ImageNotSupportedException, ContainerNotFoundException, at.tuwien.exception.AccessDeniedException { + ImageNotSupportedException, ContainerNotFoundException, AccessDeniedException { /* test */ generic_insert(DATABASE_1_ID, TABLE_1_ID, DATABASE_1, TABLE_1, USER_1_ID, DATABASE_1_USER_1_WRITE_ALL_ACCESS, null, USER_1_PRINCIPAL); @@ -344,22 +344,22 @@ public class TableDataEndpointUnitTest extends BaseUnitTest { TABLE_8, null, null, null, null, null, null, null, null), Arguments.arguments("public read", DATABASE_3_ID, TABLE_8_ID, DATABASE_3, TABLE_8, - USER_1_USERNAME, + USER_1_ID, DATABASE_3_USER_1_READ_ACCESS, USER_1_PRINCIPAL, null, null, null, null, null), Arguments.arguments("public write-own", DATABASE_3_ID, TABLE_8_ID, DATABASE_3, - TABLE_8, USER_1_USERNAME, + TABLE_8, USER_1_ID, DATABASE_3_USER_1_WRITE_OWN_ACCESS, USER_1_PRINCIPAL, null, null, null, null, null), Arguments.arguments("public write-all", DATABASE_3_ID, TABLE_8_ID, DATABASE_3, - TABLE_8, USER_1_USERNAME, + TABLE_8, USER_1_ID, DATABASE_3_USER_1_WRITE_ALL_ACCESS, USER_1_PRINCIPAL, null, null, null, null, null), Arguments.arguments("private read", DATABASE_1_ID, TABLE_1_ID, DATABASE_1, TABLE_1, - USER_1_USERNAME, + USER_1_ID, DATABASE_1_USER_1_READ_ACCESS, USER_1_PRINCIPAL, null, null, null, null, null), Arguments.arguments("private write-own", DATABASE_1_ID, TABLE_1_ID, DATABASE_1, - TABLE_1, USER_1_USERNAME, + TABLE_1, USER_1_ID, DATABASE_1_USER_1_WRITE_OWN_ACCESS, USER_1_PRINCIPAL, null, null, null, null, null), Arguments.arguments("private write-all", DATABASE_1_ID, TABLE_1_ID, DATABASE_1, - TABLE_1, USER_1_USERNAME, + TABLE_1, USER_1_ID, DATABASE_1_USER_1_WRITE_ALL_ACCESS, USER_1_PRINCIPAL, null, null, null, null, null) ); } @@ -372,7 +372,7 @@ public class TableDataEndpointUnitTest extends BaseUnitTest { SortType sortDirection, String sortColumn) throws UserNotFoundException, TableNotFoundException, SortException, TableMalformedException, NotAllowedException, DatabaseConnectionException, QueryMalformedException, DatabaseNotFoundException, ImageNotSupportedException, - PaginationException, at.tuwien.exception.AccessDeniedException { + PaginationException, AccessDeniedException { /* test */ generic_getAll(databaseId, tableId, database, table, userId, access, principal, timestamp, @@ -411,7 +411,7 @@ public class TableDataEndpointUnitTest extends BaseUnitTest { UUID userId, DatabaseAccess access, Principal principal, Instant timestamp) throws UserNotFoundException, TableNotFoundException, QueryStoreException, TableMalformedException, NotAllowedException, DatabaseConnectionException, QueryMalformedException, DatabaseNotFoundException, - ImageNotSupportedException, at.tuwien.exception.AccessDeniedException { + ImageNotSupportedException, AccessDeniedException { /* test */ generic_getCount(databaseId, tableId, database, table, userId, access, principal, timestamp); @@ -426,7 +426,7 @@ public class TableDataEndpointUnitTest extends BaseUnitTest { DatabaseAccess access, Principal principal) throws DatabaseNotFoundException, TableNotFoundException, NotAllowedException, UserNotFoundException, TableMalformedException, DatabaseConnectionException, QueryMalformedException, ImageNotSupportedException, - at.tuwien.exception.AccessDeniedException { + AccessDeniedException { final ImportDto request = ImportDto.builder().location("test:csv/csv_01.csv").build(); /* mock */ @@ -444,7 +444,7 @@ public class TableDataEndpointUnitTest extends BaseUnitTest { DatabaseAccess access, TableCsvDto data, Principal principal) throws DatabaseNotFoundException, TableNotFoundException, NotAllowedException, UserNotFoundException, TableMalformedException, DatabaseConnectionException, ImageNotSupportedException, - ContainerNotFoundException, at.tuwien.exception.AccessDeniedException { + ContainerNotFoundException, AccessDeniedException { /* mock */ when(databaseService.find(databaseId)).thenReturn(database); @@ -462,7 +462,7 @@ public class TableDataEndpointUnitTest extends BaseUnitTest { SortType sortDirection, String sortColumn) throws UserNotFoundException, TableMalformedException, NotAllowedException, PaginationException, TableNotFoundException, SortException, DatabaseConnectionException, QueryMalformedException, DatabaseNotFoundException, ImageNotSupportedException, - at.tuwien.exception.AccessDeniedException { + AccessDeniedException { /* mock */ when(databaseService.find(databaseId)).thenReturn(database); @@ -484,7 +484,7 @@ public class TableDataEndpointUnitTest extends BaseUnitTest { DatabaseAccess access, Principal principal, Instant timestamp) throws UserNotFoundException, TableMalformedException, NotAllowedException, TableNotFoundException, QueryStoreException, DatabaseConnectionException, QueryMalformedException, DatabaseNotFoundException, - ImageNotSupportedException, at.tuwien.exception.AccessDeniedException { + ImageNotSupportedException, AccessDeniedException { /* mock */ when(databaseService.find(databaseId)).thenReturn(database); diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/ViewEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/ViewEndpointUnitTest.java index 9f1d45eed84a134be84f77b58d62694803149e42..d74bc927205c692b940a238d4dfb9b8b2210b5bc 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/ViewEndpointUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/ViewEndpointUnitTest.java @@ -95,7 +95,7 @@ public class ViewEndpointUnitTest extends BaseUnitTest { public void create_publicAnonymous_succeeds() { /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { create_generic(DATABASE_3_ID, DATABASE_3, null, null, null); }); } @@ -125,7 +125,7 @@ public class ViewEndpointUnitTest extends BaseUnitTest { public void create_publicNoRole_fails() { /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { create_generic(DATABASE_3_ID, DATABASE_3, USER_2_ID, USER_2_PRINCIPAL, null); }); } @@ -171,7 +171,7 @@ public class ViewEndpointUnitTest extends BaseUnitTest { public void delete_publicAnonymous_fails() { /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { delete_generic(DATABASE_3_ID, VIEW_1_ID, DATABASE_3, null, null, null); }); } @@ -191,7 +191,7 @@ public class ViewEndpointUnitTest extends BaseUnitTest { public void delete_publicNoRole_fails() { /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { delete_generic(DATABASE_3_ID, VIEW_1_ID, DATABASE_3, USER_2_ID, USER_2_PRINCIPAL, DATABASE_2_USER_1_READ_ACCESS); }); } @@ -291,7 +291,7 @@ public class ViewEndpointUnitTest extends BaseUnitTest { public void create_privateAnonymous_succeeds() { /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { create_generic(DATABASE_1_ID, DATABASE_1, null, null, null); }); } @@ -321,7 +321,7 @@ public class ViewEndpointUnitTest extends BaseUnitTest { public void create_privateNoRole_fails() { /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { create_generic(DATABASE_1_ID, DATABASE_1, USER_2_ID, USER_2_PRINCIPAL, null); }); } @@ -367,7 +367,7 @@ public class ViewEndpointUnitTest extends BaseUnitTest { public void delete_privateAnonymous_fails() { /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { delete_generic(DATABASE_1_ID, VIEW_1_ID, DATABASE_1, null, null, null); }); } @@ -387,7 +387,7 @@ public class ViewEndpointUnitTest extends BaseUnitTest { public void delete_privateNoRole_fails() { /* test */ - assertThrows(AccessDeniedException.class, () -> { + assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { delete_generic(DATABASE_1_ID, VIEW_1_ID, DATABASE_1, USER_1_ID, USER_2_PRINCIPAL, DATABASE_2_USER_1_READ_ACCESS); }); } diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/AccessServiceUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/AccessServiceUnitTest.java index 98f12a337b649ef0c3cb26b85078c5ace028376f..52cb521ff7c95b67e832469f05c2a16507ccdc13 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/AccessServiceUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/AccessServiceUnitTest.java @@ -11,6 +11,7 @@ import at.tuwien.exception.AccessDeniedException; import at.tuwien.exception.NotAllowedException; import at.tuwien.repository.mdb.DatabaseAccessRepository; import at.tuwien.repository.mdb.DatabaseRepository; +import at.tuwien.repository.mdb.UserRepository; import lombok.extern.log4j.Log4j2; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -39,6 +40,9 @@ public class AccessServiceUnitTest extends BaseUnitTest { @MockBean private DatabaseAccessRepository databaseAccessRepository; + @MockBean + private UserRepository userRepository; + @Autowired private AccessService accessService; @@ -100,6 +104,8 @@ public class AccessServiceUnitTest extends BaseUnitTest { /* mock */ when(databaseRepository.findById(DATABASE_1_ID)) .thenReturn(Optional.of(DATABASE_1)); + when(userRepository.findById(USER_1_ID)) + .thenReturn(Optional.of(USER_1)); /* test */ assertThrows(NotAllowedException.class, () -> { diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceComponentTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceComponentTest.java index dd6cf5370826ead3fe585033e413a9e60f2eb592..47318a596f24170ebb8b1aa5176b8ea6f8059567 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceComponentTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceComponentTest.java @@ -9,6 +9,7 @@ import at.tuwien.config.MariaDbConfig; import at.tuwien.entities.database.Database; import at.tuwien.repository.mdb.ContainerRepository; import at.tuwien.repository.mdb.DatabaseRepository; +import at.tuwien.repository.mdb.UserRepository; import at.tuwien.repository.sdb.DatabaseIdxRepository; import at.tuwien.service.impl.MariaDbServiceImpl; import lombok.extern.log4j.Log4j2; @@ -48,6 +49,9 @@ public class DatabaseServiceComponentTest extends BaseUnitTest { @MockBean private DatabaseIdxRepository databaseIdxRepository; + @MockBean + private UserRepository userRepository; + @Autowired private MariaDbServiceImpl databaseService; @@ -61,11 +65,13 @@ public class DatabaseServiceComponentTest extends BaseUnitTest { } @Test - public void create_elasticSearch_succeeds() throws Exception { + public void create_openSearch_succeeds() throws Exception { /* mock */ when(databaseIdxRepository.save(any(DatabaseDto.class))) .thenReturn(DATABASE_3_DTO); + when(userRepository.findByUsername(USER_1_USERNAME)) + .thenReturn(Optional.of(USER_1)); /* test */ generic_create(DATABASE_3_CREATE, DATABASE_3); diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/QueryServiceIntegrationTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/QueryServiceIntegrationTest.java index c08785afeddff8e3768e06c7e7c03981da29ef52..58223493e3e697563418430416b4e38bfee702eb 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/QueryServiceIntegrationTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/QueryServiceIntegrationTest.java @@ -20,6 +20,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.testcontainers.containers.MariaDBContainer; @@ -34,12 +35,10 @@ import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.temporal.ChronoUnit; -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; +import java.util.*; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.when; @Log4j2 @Testcontainers @@ -51,6 +50,9 @@ import static org.junit.jupiter.api.Assertions.*; @MockOpensearch public class QueryServiceIntegrationTest extends BaseUnitTest { + @MockBean + private UserRepository userRepository; + @Autowired private DatabaseRepository databaseRepository; @@ -295,6 +297,10 @@ public class QueryServiceIntegrationTest extends BaseUnitTest { .statement("SELECT n.`firstname`, n.`lastname`, n.`birth`, n.`reminder`, z.`animal_name`, z.`legs` FROM `likes` l JOIN `names` n ON l.`name_id` = n.`id` JOIN `mock_view` z ON z.`id` = l.`zoo_id`") .build(); + /* mock */ + when(userRepository.findByUsername(USER_1_USERNAME)) + .thenReturn(Optional.of(USER_1)); + /* test */ Thread.sleep(1000) /* wait for test container some more */; final QueryResultDto response = queryService.execute(DATABASE_2_ID, request, USER_1_PRINCIPAL, 0L, 100L, null, null); @@ -332,6 +338,10 @@ public class QueryServiceIntegrationTest extends BaseUnitTest { .statement("SELECT `location`, `lng` FROM `weather_location` WHERE `lat` IS NULL") .build(); + /* mock */ + when(userRepository.findByUsername(USER_1_USERNAME)) + .thenReturn(Optional.of(USER_1)); + /* test */ Thread.sleep(1000) /* wait for test container some more */; final QueryResultDto response = queryService.execute(DATABASE_1_ID, request, USER_1_PRINCIPAL, @@ -352,6 +362,10 @@ public class QueryServiceIntegrationTest extends BaseUnitTest { .statement("SELECT `location` FROM `weather_location` WHERE `lat` IS NULL") .build(); + /* mock */ + when(userRepository.findByUsername(USER_1_USERNAME)) + .thenReturn(Optional.of(USER_1)); + /* test */ Thread.sleep(1000) /* wait for test container some more */; final QueryResultDto response = queryService.execute(DATABASE_1_ID, request, USER_1_PRINCIPAL, @@ -373,6 +387,10 @@ public class QueryServiceIntegrationTest extends BaseUnitTest { .statement("SELECT `lat`, `lng` FROM `weather_location` WHERE `lat` IS NULL") .build(); + /* mock */ + when(userRepository.findByUsername(USER_1_USERNAME)) + .thenReturn(Optional.of(USER_1)); + /* test */ Thread.sleep(1000) /* wait for test container some more */; final QueryResultDto response = queryService.execute(DATABASE_1_ID, request, USER_1_PRINCIPAL, @@ -389,6 +407,10 @@ public class QueryServiceIntegrationTest extends BaseUnitTest { .statement("SELECT aus.location as a, loc.location from weather_aus aus, weather_location loc") .build(); + /* mock */ + when(userRepository.findByUsername(USER_1_USERNAME)) + .thenReturn(Optional.of(USER_1)); + /* test */ Thread.sleep(1000) /* wait for test container some more */; final QueryResultDto response = queryService.execute(DATABASE_1_ID, request, USER_1_PRINCIPAL, 0L, 100L, null, null); @@ -426,6 +448,8 @@ public class QueryServiceIntegrationTest extends BaseUnitTest { /* mock */ Thread.sleep(1000) /* wait for test container some more */; + when(userRepository.findByUsername(USER_1_USERNAME)) + .thenReturn(Optional.of(USER_1)); /* test */ final QueryResultDto response = queryService.execute(DATABASE_1_ID, request, USER_1_PRINCIPAL, diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServiceIntegrationTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServiceIntegrationTest.java index cd8763f03d29e05883982006e60044f2b80c5149..fd561cadd89ea07dfbe74aae84adf900047fc96b 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServiceIntegrationTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServiceIntegrationTest.java @@ -7,23 +7,33 @@ import at.tuwien.api.auth.SignupRequestDto; import at.tuwien.api.user.*; import at.tuwien.entities.user.User; import at.tuwien.exception.*; +import at.tuwien.repository.mdb.UserRepository; +import dasniko.testcontainers.keycloak.KeycloakContainer; import lombok.extern.log4j.Log4j2; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.annotation.Transactional; +import org.testcontainers.containers.MariaDBContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; import java.util.List; +import java.util.Optional; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.when; @Log4j2 +@Testcontainers @EnableAutoConfiguration(exclude = RabbitAutoConfiguration.class) @DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) @SpringBootTest @@ -32,9 +42,21 @@ import static org.junit.jupiter.api.Assertions.*; @MockOpensearch public class UserServiceIntegrationTest extends BaseUnitTest { + @Autowired + private UserRepository userRepository; + @Autowired private UserService userService; + @Container + @Autowired + private KeycloakContainer keycloakContainer; + + @BeforeEach + public void beforeEach() { + userRepository.save(USER_1); + } + @Test public void findByUsername_succeeds() throws UserNotFoundException { @@ -84,7 +106,7 @@ public class UserServiceIntegrationTest extends BaseUnitTest { .build(); /* test */ - assertThrows(DataIntegrityViolationException.class, () -> { + assertThrows(UserAlreadyExistsException.class, () -> { userService.create(request); }); } @@ -98,7 +120,7 @@ public class UserServiceIntegrationTest extends BaseUnitTest { .build(); /* test */ - assertThrows(DataIntegrityViolationException.class, () -> { + assertThrows(UserAlreadyExistsException.class, () -> { userService.create(request); }); } @@ -138,13 +160,21 @@ public class UserServiceIntegrationTest extends BaseUnitTest { } @Test - public void updatePassword_succeeds() throws KeycloakRemoteException, AccessDeniedException { + public void updatePassword_succeeds() throws KeycloakRemoteException, AccessDeniedException, UserNotFoundException, + UserAlreadyExistsException { final UserPasswordDto request = UserPasswordDto.builder() - .password(USER_1_PASSWORD) + .password(USER_3_PASSWORD) .build(); + /* mock */ + final User user = userService.create(SignupRequestDto.builder() + .username(USER_3_USERNAME) + .password(USER_3_PASSWORD) + .email(USER_3_EMAIL) + .build()); + /* test */ - userService.updatePassword(USER_1_ID, request); + userService.updatePassword(user.getId(), request); } @Test diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServiceUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServiceUnitTest.java index 515795e3c068105c58d7f655d5ceb234fc456d07..3282718db00f35428046a9b1ff09f54b670482c2 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServiceUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServiceUnitTest.java @@ -8,6 +8,7 @@ import at.tuwien.api.user.UserDto; import at.tuwien.entities.user.User; import at.tuwien.exception.*; import at.tuwien.gateway.KeycloakGateway; +import at.tuwien.repository.mdb.UserRepository; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; @@ -16,6 +17,7 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.util.List; +import java.util.Optional; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -30,12 +32,19 @@ public class UserServiceUnitTest extends BaseUnitTest { @MockBean private KeycloakGateway keycloakGateway; + @MockBean + private UserRepository userRepository; + @Autowired private UserService userService; @Test public void findByUsername_succeeds() throws UserNotFoundException { + /* mock */ + when(userRepository.findByUsername(USER_1_USERNAME)) + .thenReturn(Optional.of(USER_1)); + /* test */ final User response = userService.findByUsername(USER_1_USERNAME); assertEquals(USER_1_ID, response.getId()); @@ -45,6 +54,10 @@ public class UserServiceUnitTest extends BaseUnitTest { @Test public void find_succeeds() throws UserNotFoundException { + /* mock */ + when(userRepository.findById(USER_1_ID)) + .thenReturn(Optional.of(USER_1)); + /* test */ final User response = userService.find(USER_1_ID); assertEquals(USER_1_ID, response.getId()); @@ -54,9 +67,13 @@ public class UserServiceUnitTest extends BaseUnitTest { @Test public void findAll_succeeds() throws UserNotFoundException { + /* mock */ + when(userRepository.findAll()) + .thenReturn(List.of(USER_1, USER_2)); + /* test */ final List<User> response = userService.findAll(); - assertEquals(1, response.size()); + assertEquals(2, response.size()); } @Test @@ -64,6 +81,10 @@ public class UserServiceUnitTest extends BaseUnitTest { UserAlreadyExistsException { /* mock */ + when(userRepository.findById(USER_1_ID)) + .thenReturn(Optional.of(USER_1)); + when(userRepository.save(any(User.class))) + .thenReturn(USER_1); doNothing() .when(keycloakGateway) .createUser(USER_1_KEYCLOAK_SIGNUP_REQUEST); @@ -79,6 +100,12 @@ public class UserServiceUnitTest extends BaseUnitTest { @Test public void modify_succeeds() throws UserNotFoundException { + /* mock */ + when(userRepository.findById(USER_1_ID)) + .thenReturn(Optional.of(USER_1)); + when(userRepository.save(any(User.class))) + .thenReturn(USER_1); + /* test */ final User response = userService.modify(USER_1_ID, USER_1_UPDATE_DTO); assertEquals(USER_1_ID, response.getId()); @@ -88,8 +115,12 @@ public class UserServiceUnitTest extends BaseUnitTest { @Test public void modify_notExists_succeeds() { + /* mock */ + when(userRepository.findById(USER_1_ID)) + .thenReturn(Optional.empty()); + /* test */ - assertThrows(KeycloakRemoteException.class, () -> { + assertThrows(UserNotFoundException.class, () -> { userService.modify(USER_1_ID, USER_1_UPDATE_DTO); }); } @@ -97,6 +128,12 @@ public class UserServiceUnitTest extends BaseUnitTest { @Test public void toggleTheme_succeeds() throws UserNotFoundException { + /* mock */ + when(userRepository.findById(USER_1_ID)) + .thenReturn(Optional.of(USER_1)); + when(userRepository.save(any(User.class))) + .thenReturn(USER_1); + /* test */ final User response = userService.toggleTheme(USER_1_ID, USER_1_THEME_SET_DTO); assertEquals(USER_1_ID, response.getId()); @@ -105,7 +142,7 @@ public class UserServiceUnitTest extends BaseUnitTest { } @Test - public void updatePassword_succeeds() throws KeycloakRemoteException, AccessDeniedException { + public void updatePassword_succeeds() throws KeycloakRemoteException, AccessDeniedException, UserNotFoundException { /* mock */ doNothing() diff --git a/dbrepo-metadata-service/rest-service/src/test/resources/application.properties b/dbrepo-metadata-service/rest-service/src/test/resources/application.properties index 72324c813e9a123babce15fdf742f814373ad06e..025a23176df6e0b655b927abf4a45fb9611870e7 100644 --- a/dbrepo-metadata-service/rest-service/src/test/resources/application.properties +++ b/dbrepo-metadata-service/rest-service/src/test/resources/application.properties @@ -33,5 +33,8 @@ fda.datacite.prefix: 10.12345 fda.datacite.username: test-user fda.datacite.password: test-password +# keycloak +fda.keycloak.endpoint: http://localhost:8080/ + # consumers fda.consumers=2 \ No newline at end of file diff --git a/dbrepo-metadata-service/rest-service/src/test/resources/dbrepo-realm.json b/dbrepo-metadata-service/rest-service/src/test/resources/dbrepo-realm.json new file mode 100644 index 0000000000000000000000000000000000000000..639d73ea15fdba309347312286e71a7f0706268e --- /dev/null +++ b/dbrepo-metadata-service/rest-service/src/test/resources/dbrepo-realm.json @@ -0,0 +1,1525 @@ +{ + "id": "4ef53018-8322-40ed-a44a-a7c5292a07be", + "realm": "dbrepo", + "displayName": "", + "displayNameHtml": "", + "notBefore": 0, + "defaultSignatureAlgorithm": "RS256", + "revokeRefreshToken": false, + "refreshTokenMaxReuse": 0, + "accessTokenLifespan": 300, + "accessTokenLifespanForImplicitFlow": 900, + "ssoSessionIdleTimeout": 1800, + "ssoSessionMaxLifespan": 36000, + "ssoSessionIdleTimeoutRememberMe": 0, + "ssoSessionMaxLifespanRememberMe": 0, + "offlineSessionIdleTimeout": 2592000, + "offlineSessionMaxLifespanEnabled": false, + "offlineSessionMaxLifespan": 5184000, + "clientSessionIdleTimeout": 0, + "clientSessionMaxLifespan": 0, + "clientOfflineSessionIdleTimeout": 0, + "clientOfflineSessionMaxLifespan": 0, + "accessCodeLifespan": 60, + "accessCodeLifespanUserAction": 300, + "accessCodeLifespanLogin": 1800, + "actionTokenGeneratedByAdminLifespan": 43200, + "actionTokenGeneratedByUserLifespan": 300, + "oauth2DeviceCodeLifespan": 600, + "oauth2DevicePollingInterval": 5, + "enabled": true, + "sslRequired": "none", + "registrationAllowed": false, + "registrationEmailAsUsername": false, + "rememberMe": false, + "verifyEmail": false, + "loginWithEmailAllowed": true, + "duplicateEmailsAllowed": false, + "resetPasswordAllowed": false, + "editUsernameAllowed": false, + "bruteForceProtected": false, + "permanentLockout": false, + "maxFailureWaitSeconds": 900, + "minimumQuickLoginWaitSeconds": 60, + "waitIncrementSeconds": 60, + "quickLoginCheckMilliSeconds": 1000, + "maxDeltaTimeSeconds": 43200, + "failureFactor": 30, + "defaultRole": { + "id": "7a02acbe-09aa-4af7-8e79-fbb0cf5fd0d6", + "name": "default-roles-dbrepo", + "description": "${role_default-roles}", + "composite": true, + "clientRole": false, + "containerId": "4ef53018-8322-40ed-a44a-a7c5292a07be" + }, + "requiredCredentials": [ + "password" + ], + "otpPolicyType": "totp", + "otpPolicyAlgorithm": "HmacSHA1", + "otpPolicyInitialCounter": 0, + "otpPolicyDigits": 6, + "otpPolicyLookAheadWindow": 1, + "otpPolicyPeriod": 30, + "otpPolicyCodeReusable": false, + "otpSupportedApplications": [ + "totpAppGoogleName", + "totpAppMicrosoftAuthenticatorName", + "totpAppFreeOTPName" + ], + "webAuthnPolicyRpEntityName": "keycloak", + "webAuthnPolicySignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyRpId": "", + "webAuthnPolicyAttestationConveyancePreference": "not specified", + "webAuthnPolicyAuthenticatorAttachment": "not specified", + "webAuthnPolicyRequireResidentKey": "not specified", + "webAuthnPolicyUserVerificationRequirement": "not specified", + "webAuthnPolicyCreateTimeout": 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyAcceptableAaguids": [], + "webAuthnPolicyPasswordlessRpEntityName": "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyPasswordlessRpId": "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified", + "webAuthnPolicyPasswordlessCreateTimeout": 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyPasswordlessAcceptableAaguids": [], + "scopeMappings": [ + { + "clientScope": "offline_access", + "roles": [ + "offline_access" + ] + } + ], + "clientScopes": [ + { + "id": "4410371f-2840-458a-a496-375bd500f5fc", + "name": "email", + "description": "OpenID Connect built-in scope: email", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${emailScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "7001651a-7ded-4a0f-9cb7-816b058af730", + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + }, + { + "id": "25e4ad82-8b9b-463d-8b35-02fae44ec64c", + "name": "email verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "emailVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email_verified", + "jsonType.label": "boolean" + } + } + ] + }, + { + "id": "ebe0a886-5df1-48f3-9f00-a45d686b0b02", + "name": "address", + "description": "OpenID Connect built-in scope: address", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${addressScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "866be669-9799-45ad-abdd-23f5d1c82293", + "name": "address", + "protocol": "openid-connect", + "protocolMapper": "oidc-address-mapper", + "consentRequired": false, + "config": { + "user.attribute.formatted": "formatted", + "user.attribute.country": "country", + "user.attribute.postal_code": "postal_code", + "userinfo.token.claim": "true", + "user.attribute.street": "street", + "id.token.claim": "true", + "user.attribute.region": "region", + "access.token.claim": "true", + "user.attribute.locality": "locality" + } + } + ] + }, + { + "id": "be0f3000-0106-4edd-a8f5-0618e3a3f7fe", + "name": "microprofile-jwt", + "description": "Microprofile - JWT built-in scope", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "093f222d-cee9-495a-81b7-ddae4b730ab2", + "name": "groups", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "multivalued": "true", + "user.attribute": "foo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "groups", + "jsonType.label": "String" + } + }, + { + "id": "2e21b9f5-c428-4a5b-9dd8-8a80bc6d9795", + "name": "upn", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "upn", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "2a95813e-053d-4c20-9f2b-f7e1ad1798f5", + "name": "role_list", + "description": "SAML role list", + "protocol": "saml", + "attributes": { + "consent.screen.text": "${samlRoleListScopeConsentText}", + "display.on.consent.screen": "true" + }, + "protocolMappers": [ + { + "id": "9c8a5628-af2a-4ba4-9cad-a8ae58c8526a", + "name": "role list", + "protocol": "saml", + "protocolMapper": "saml-role-list-mapper", + "consentRequired": false, + "config": { + "single": "false", + "attribute.nameformat": "Basic", + "attribute.name": "Role" + } + } + ] + }, + { + "id": "c6375bbe-9820-4781-8828-99020210f5bd", + "name": "roles", + "description": "OpenID Connect scope for add user roles to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "true", + "consent.screen.text": "${rolesScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "a1db2464-4e12-4696-914d-d252263e4470", + "name": "audience resolve", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-resolve-mapper", + "consentRequired": false, + "config": {} + }, + { + "id": "c5513105-d577-4ba0-bff8-f96e9272f6fd", + "name": "client roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-client-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "resource_access.${client_id}.roles", + "jsonType.label": "String", + "multivalued": "true" + } + }, + { + "id": "66901106-8ea3-4712-8bdf-bc67da4a9d2d", + "name": "realm roles", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "user.attribute": "foo", + "access.token.claim": "true", + "claim.name": "realm_access.roles", + "jsonType.label": "String", + "multivalued": "true" + } + } + ] + }, + { + "id": "f0ea8ba1-794d-4b89-9ad3-b7f390c5241e", + "name": "phone", + "description": "OpenID Connect built-in scope: phone", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${phoneScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "b3789148-8427-4af8-bc03-4dd3fae4b312", + "name": "phone number", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumber", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number", + "jsonType.label": "String" + } + }, + { + "id": "552ac5e8-6e04-47d5-8ddf-89230ca31b43", + "name": "phone number verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumberVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number_verified", + "jsonType.label": "boolean" + } + } + ] + }, + { + "id": "8ff9a2f3-582c-4cda-8ea8-d08850f8a394", + "name": "web-origins", + "description": "OpenID Connect scope for add allowed web origins to the access token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false", + "consent.screen.text": "" + }, + "protocolMappers": [ + { + "id": "52af8bfd-44b1-4b69-bfd5-bc92cf1629aa", + "name": "allowed web origins", + "protocol": "openid-connect", + "protocolMapper": "oidc-allowed-origins-mapper", + "consentRequired": false, + "config": {} + } + ] + }, + { + "id": "b3ebaf28-e467-4c70-b993-d7b8459dfb5a", + "name": "profile", + "description": "OpenID Connect built-in scope: profile", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "true", + "display.on.consent.screen": "true", + "consent.screen.text": "${profileScopeConsentText}" + }, + "protocolMappers": [ + { + "id": "1025302a-104e-42e8-acf8-83579c21bfbb", + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "id": "70a9942e-85c1-4f15-9605-6408e0087b74", + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "id": "1e36c899-c472-427e-a024-c12a64d6084c", + "name": "website", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "website", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "website", + "jsonType.label": "String" + } + }, + { + "id": "b5e54114-79cb-488d-9e60-6a804ca3b22a", + "name": "profile", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "profile", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "profile", + "jsonType.label": "String" + } + }, + { + "id": "3e9406b9-a0ae-48d2-98b9-66ab7360b2a2", + "name": "gender", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "gender", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "gender", + "jsonType.label": "String" + } + }, + { + "id": "aec55dcc-59e7-4ea7-9eb5-55954d983fd0", + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + }, + { + "id": "11fd80da-8760-4fa6-9d1b-32da9df170bd", + "name": "picture", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "picture", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "picture", + "jsonType.label": "String" + } + }, + { + "id": "90f93a66-27d0-4890-846b-79d99e02ad76", + "name": "updated at", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "updatedAt", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "updated_at", + "jsonType.label": "long" + } + }, + { + "id": "6c8aa1f6-3aa2-4e48-9f9c-44cffe71e7d2", + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "id": "a861da8e-87e7-4b5d-92fa-4ca61671c77f", + "name": "birthdate", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "birthdate", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "birthdate", + "jsonType.label": "String" + } + }, + { + "id": "ae301563-61a7-413b-87a0-036e1af942bb", + "name": "middle name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "middleName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "middle_name", + "jsonType.label": "String" + } + }, + { + "id": "c39e0763-48a9-4f69-84f4-4e334365f99b", + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "id": "a54ae213-5854-4f77-8646-5cc7767fb914", + "name": "nickname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "nickname", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "nickname", + "jsonType.label": "String" + } + }, + { + "id": "41824638-4a2f-4259-90fe-431a32371256", + "name": "zoneinfo", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "zoneinfo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "zoneinfo", + "jsonType.label": "String" + } + } + ] + }, + { + "id": "4c7c18fe-2aff-4df3-aeff-4dca9de93a79", + "name": "offline_access", + "description": "OpenID Connect built-in scope: offline_access", + "protocol": "openid-connect", + "attributes": { + "consent.screen.text": "${offlineAccessScopeConsentText}", + "display.on.consent.screen": "true" + } + }, + { + "id": "30b249b2-c788-47ef-a8c7-daa0e3713b7c", + "name": "acr", + "description": "OpenID Connect scope for add acr (authentication context class reference) to the token", + "protocol": "openid-connect", + "attributes": { + "include.in.token.scope": "false", + "display.on.consent.screen": "false" + }, + "protocolMappers": [ + { + "id": "50cf075c-fdc9-43f4-ab85-1e32cafbbb26", + "name": "acr loa level", + "protocol": "openid-connect", + "protocolMapper": "oidc-acr-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true" + } + } + ] + } + ], + "defaultDefaultClientScopes": [ + "role_list", + "profile", + "email", + "roles", + "web-origins", + "acr" + ], + "defaultOptionalClientScopes": [ + "offline_access", + "address", + "phone", + "microprofile-jwt" + ], + "browserSecurityHeaders": { + "contentSecurityPolicyReportOnly": "", + "xContentTypeOptions": "nosniff", + "xRobotsTag": "none", + "xFrameOptions": "SAMEORIGIN", + "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "xXSSProtection": "1; mode=block", + "strictTransportSecurity": "max-age=31536000; includeSubDomains" + }, + "smtpServer": {}, + "eventsEnabled": false, + "eventsListeners": [ + "jboss-logging" + ], + "enabledEventTypes": [], + "adminEventsEnabled": false, + "adminEventsDetailsEnabled": false, + "identityProviders": [], + "identityProviderMappers": [], + "components": { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy": [ + { + "id": "4489b316-ab01-42cf-a224-f853a310c7c5", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-address-mapper", + "saml-user-property-mapper", + "oidc-usermodel-property-mapper", + "oidc-usermodel-attribute-mapper", + "saml-user-attribute-mapper", + "oidc-sha256-pairwise-sub-mapper", + "oidc-full-name-mapper", + "saml-role-list-mapper" + ] + } + }, + { + "id": "d355f2ac-bd0d-4c5c-8e2d-52f14f332354", + "name": "Allowed Protocol Mapper Types", + "providerId": "allowed-protocol-mappers", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allowed-protocol-mapper-types": [ + "oidc-full-name-mapper", + "oidc-usermodel-attribute-mapper", + "oidc-address-mapper", + "oidc-sha256-pairwise-sub-mapper", + "saml-role-list-mapper", + "saml-user-property-mapper", + "saml-user-attribute-mapper", + "oidc-usermodel-property-mapper" + ] + } + }, + { + "id": "cefbb851-d3bd-4b6d-b582-7fc4fa51d9b6", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "anonymous", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + }, + { + "id": "b7fe23c6-f82b-4920-8062-3d9fa6fb838f", + "name": "Full Scope Disabled", + "providerId": "scope", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "8332b5b7-5643-4c9f-afdb-4bda506bac55", + "name": "Allowed Client Scopes", + "providerId": "allowed-client-templates", + "subType": "authenticated", + "subComponents": {}, + "config": { + "allow-default-scopes": [ + "true" + ] + } + }, + { + "id": "bb595923-ec43-4cbc-be3c-6d65c250956d", + "name": "Consent Required", + "providerId": "consent-required", + "subType": "anonymous", + "subComponents": {}, + "config": {} + }, + { + "id": "4d9901ea-7d1e-484a-a5de-abb36d8a5cc8", + "name": "Trusted Hosts", + "providerId": "trusted-hosts", + "subType": "anonymous", + "subComponents": {}, + "config": { + "host-sending-registration-request-must-match": [ + "true" + ], + "client-uris-must-match": [ + "true" + ] + } + }, + { + "id": "44bd2392-250b-4b4a-9772-7478fbd68f67", + "name": "Max Clients Limit", + "providerId": "max-clients", + "subType": "anonymous", + "subComponents": {}, + "config": { + "max-clients": [ + "200" + ] + } + } + ], + "org.keycloak.userprofile.UserProfileProvider": [ + { + "id": "0ffb33e9-2f5b-4e54-b188-19b3da816f0f", + "providerId": "declarative-user-profile", + "subComponents": {}, + "config": {} + } + ], + "org.keycloak.keys.KeyProvider": [ + { + "id": "d80cf13c-e402-49e5-b347-0175bdcac7b6", + "name": "rsa-generated", + "providerId": "rsa-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + }, + { + "id": "03442c52-879c-4b3d-89c4-db02a0ad3c52", + "name": "aes-generated", + "providerId": "aes-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ] + } + }, + { + "id": "30ce0596-cbce-4633-a422-15873bc363bf", + "name": "hmac-generated", + "providerId": "hmac-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ], + "algorithm": [ + "HS256" + ] + } + }, + { + "id": "012d037c-b22a-48f2-a80a-927424d75f00", + "name": "rsa-enc-generated", + "providerId": "rsa-enc-generated", + "subComponents": {}, + "config": { + "priority": [ + "100" + ], + "algorithm": [ + "RSA-OAEP" + ] + } + } + ] + }, + "internationalizationEnabled": false, + "supportedLocales": [], + "authenticationFlows": [ + { + "id": "865c209a-a080-4162-9b49-795345294601", + "alias": "Account verification options", + "description": "Method with which to verity the existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-email-verification", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Verify Existing Account by Re-authentication", + "userSetupAllowed": false + } + ] + }, + { + "id": "85fc1de2-5b82-40ec-bda0-91a4fec38584", + "alias": "Authentication Options", + "description": "Authentication options.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "basic-auth", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "basic-auth-otp", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-spnego", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "2476a340-9459-4541-9e61-226f06ec66c4", + "alias": "Browser - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "2e424297-19dd-4c09-a06a-876ef5183ca6", + "alias": "Direct Grant - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "direct-grant-validate-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "3966485f-3455-47d2-86a2-d990b41c7953", + "alias": "First broker login - Conditional OTP", + "description": "Flow to determine if the OTP is required for the authentication", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-otp-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "29098fa4-d254-44cb-a6c5-63b36b8bfdc1", + "alias": "Handle Existing Account", + "description": "Handle what to do if there is existing account with same email/username like authenticated identity provider", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-confirm-link", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Account verification options", + "userSetupAllowed": false + } + ] + }, + { + "id": "03569e80-3083-4147-ad49-445466c191d8", + "alias": "Reset - Conditional OTP", + "description": "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "conditional-user-configured", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-otp", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "7e660682-5eb3-4527-a5fa-53fc14a0f2f5", + "alias": "User creation or linking", + "description": "Flow for the existing/non-existing user alternatives", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "create unique user config", + "authenticator": "idp-create-user-if-unique", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Handle Existing Account", + "userSetupAllowed": false + } + ] + }, + { + "id": "4deed0f8-02f9-4581-9a91-05bc58fbffba", + "alias": "Verify Existing Account by Re-authentication", + "description": "Reauthentication of existing account", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "idp-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "First broker login - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "3c101100-d2dc-4e0d-beea-7bb41ee08849", + "alias": "browser", + "description": "browser based authentication", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-cookie", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "auth-spnego", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "identity-provider-redirector", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 25, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "ALTERNATIVE", + "priority": 30, + "autheticatorFlow": true, + "flowAlias": "forms", + "userSetupAllowed": false + } + ] + }, + { + "id": "f38f76db-5399-4231-82e0-bf31cf748d14", + "alias": "clients", + "description": "Base authentication for clients", + "providerId": "client-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "client-secret", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-secret-jwt", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "client-x509", + "authenticatorFlow": false, + "requirement": "ALTERNATIVE", + "priority": 40, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "ed29eed7-d490-4985-be20-2444da957182", + "alias": "direct grant", + "description": "OpenID Connect Resource Owner Grant", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "direct-grant-validate-username", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "direct-grant-validate-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 30, + "autheticatorFlow": true, + "flowAlias": "Direct Grant - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "e41f4bae-aad9-4821-8597-514e6dc5796c", + "alias": "docker auth", + "description": "Used by Docker clients to authenticate against the IDP", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "docker-http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "3149ebc7-481d-43bd-9d09-9e66bd9b2b7f", + "alias": "first broker login", + "description": "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticatorConfig": "review profile config", + "authenticator": "idp-review-profile", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "User creation or linking", + "userSetupAllowed": false + } + ] + }, + { + "id": "7aaab1d1-5357-4e71-8553-52d5613acf7d", + "alias": "forms", + "description": "Username, password, otp and other auth forms.", + "providerId": "basic-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "auth-username-password-form", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Browser - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "c2ffc5bb-568a-4648-bc13-8e7d6a4a53b4", + "alias": "http challenge", + "description": "An authentication flow based on challenge-response HTTP Authentication Schemes", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "no-cookie-redirect", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": true, + "flowAlias": "Authentication Options", + "userSetupAllowed": false + } + ] + }, + { + "id": "c6b78806-13ee-4010-b282-a6e527dda66c", + "alias": "registration", + "description": "registration flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-page-form", + "authenticatorFlow": true, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": true, + "flowAlias": "registration form", + "userSetupAllowed": false + } + ] + }, + { + "id": "b5f00ead-4f6f-4454-a925-154c1e9c2cc5", + "alias": "registration form", + "description": "registration form", + "providerId": "form-flow", + "topLevel": false, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "registration-user-creation", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-profile-action", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 40, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-password-action", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 50, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "registration-recaptcha-action", + "authenticatorFlow": false, + "requirement": "DISABLED", + "priority": 60, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + }, + { + "id": "f8370f63-f996-4039-9fbf-86e87da675dd", + "alias": "reset credentials", + "description": "Reset credentials for a user if they forgot their password or something", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "reset-credentials-choose-user", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-credential-email", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 20, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticator": "reset-password", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 30, + "autheticatorFlow": false, + "userSetupAllowed": false + }, + { + "authenticatorFlow": true, + "requirement": "CONDITIONAL", + "priority": 40, + "autheticatorFlow": true, + "flowAlias": "Reset - Conditional OTP", + "userSetupAllowed": false + } + ] + }, + { + "id": "7f156260-804d-4848-9ea0-347281aea644", + "alias": "saml ecp", + "description": "SAML ECP Profile Authentication Flow", + "providerId": "basic-flow", + "topLevel": true, + "builtIn": true, + "authenticationExecutions": [ + { + "authenticator": "http-basic-authenticator", + "authenticatorFlow": false, + "requirement": "REQUIRED", + "priority": 10, + "autheticatorFlow": false, + "userSetupAllowed": false + } + ] + } + ], + "authenticatorConfig": [ + { + "id": "17f2c0cc-8dd8-4774-adcc-cd294cdadac8", + "alias": "create unique user config", + "config": { + "require.password.update.after.registration": "false" + } + }, + { + "id": "d5be3f6c-d096-4232-88e8-b0f46e608cff", + "alias": "review profile config", + "config": { + "update.profile.on.first.login": "missing" + } + } + ], + "requiredActions": [ + { + "alias": "CONFIGURE_TOTP", + "name": "Configure OTP", + "providerId": "CONFIGURE_TOTP", + "enabled": true, + "defaultAction": false, + "priority": 10, + "config": {} + }, + { + "alias": "TERMS_AND_CONDITIONS", + "name": "Terms and Conditions", + "providerId": "TERMS_AND_CONDITIONS", + "enabled": false, + "defaultAction": false, + "priority": 20, + "config": {} + }, + { + "alias": "UPDATE_PASSWORD", + "name": "Update Password", + "providerId": "UPDATE_PASSWORD", + "enabled": true, + "defaultAction": false, + "priority": 30, + "config": {} + }, + { + "alias": "UPDATE_PROFILE", + "name": "Update Profile", + "providerId": "UPDATE_PROFILE", + "enabled": true, + "defaultAction": false, + "priority": 40, + "config": {} + }, + { + "alias": "VERIFY_EMAIL", + "name": "Verify Email", + "providerId": "VERIFY_EMAIL", + "enabled": true, + "defaultAction": false, + "priority": 50, + "config": {} + }, + { + "alias": "delete_account", + "name": "Delete Account", + "providerId": "delete_account", + "enabled": false, + "defaultAction": false, + "priority": 60, + "config": {} + }, + { + "alias": "webauthn-register", + "name": "Webauthn Register", + "providerId": "webauthn-register", + "enabled": true, + "defaultAction": false, + "priority": 70, + "config": {} + }, + { + "alias": "webauthn-register-passwordless", + "name": "Webauthn Register Passwordless", + "providerId": "webauthn-register-passwordless", + "enabled": true, + "defaultAction": false, + "priority": 80, + "config": {} + }, + { + "alias": "update_user_locale", + "name": "Update User Locale", + "providerId": "update_user_locale", + "enabled": true, + "defaultAction": false, + "priority": 1000, + "config": {} + } + ], + "browserFlow": "browser", + "registrationFlow": "registration", + "directGrantFlow": "direct grant", + "resetCredentialsFlow": "reset credentials", + "clientAuthenticationFlow": "clients", + "dockerAuthenticationFlow": "docker auth", + "attributes": { + "cibaBackchannelTokenDeliveryMode": "poll", + "cibaAuthRequestedUserHint": "login_hint", + "oauth2DevicePollingInterval": "5", + "clientOfflineSessionMaxLifespan": "0", + "clientSessionIdleTimeout": "0", + "clientOfflineSessionIdleTimeout": "0", + "cibaInterval": "5", + "realmReusableOtpCode": "false", + "cibaExpiresIn": "120", + "oauth2DeviceCodeLifespan": "600", + "parRequestUriLifespan": "60", + "clientSessionMaxLifespan": "0", + "frontendUrl": "", + "acr.loa.map": "{}" + }, + "keycloakVersion": "21.0.2", + "userManagedAccessAllowed": false, + "clientProfiles": { + "profiles": [] + }, + "clientPolicies": { + "policies": [] + } +} \ No newline at end of file diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/KeycloakGateway.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/KeycloakGateway.java index 53216689d5486ffc821da61eec9dfc4d6e441728..a4823fe1247e0ffc5c3acda15715e27cdd437bf6 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/KeycloakGateway.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/KeycloakGateway.java @@ -5,13 +5,14 @@ import at.tuwien.api.keycloak.UserDto; import at.tuwien.api.user.UserPasswordDto; import at.tuwien.exception.AccessDeniedException; import at.tuwien.exception.KeycloakRemoteException; +import at.tuwien.exception.UserAlreadyExistsException; import at.tuwien.exception.UserNotFoundException; import java.util.UUID; public interface KeycloakGateway { - void createUser(UserCreateDto data) throws AccessDeniedException, KeycloakRemoteException; + void createUser(UserCreateDto data) throws AccessDeniedException, KeycloakRemoteException, UserAlreadyExistsException; void updateUserCredentials(UUID id, UserPasswordDto password) throws AccessDeniedException, KeycloakRemoteException; diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/KeycloakGatewayImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/KeycloakGatewayImpl.java index 6068f1e1064410552dfcf2adefe42e93cee74311..9fb667381844506d01b7b3fdbddd27827e04d3d8 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/KeycloakGatewayImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/KeycloakGatewayImpl.java @@ -5,6 +5,7 @@ import at.tuwien.api.user.UserPasswordDto; import at.tuwien.config.KeycloakConfig; import at.tuwien.exception.AccessDeniedException; import at.tuwien.exception.KeycloakRemoteException; +import at.tuwien.exception.UserAlreadyExistsException; import at.tuwien.exception.UserNotFoundException; import at.tuwien.gateway.KeycloakGateway; import at.tuwien.mapper.UserMapper; @@ -14,6 +15,7 @@ import org.springframework.http.*; import org.springframework.stereotype.Service; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; +import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.HttpServerErrorException; import org.springframework.web.client.ResourceAccessException; import org.springframework.web.client.RestTemplate; @@ -44,7 +46,7 @@ public class KeycloakGatewayImpl implements KeycloakGateway { payload.add("client_id", "admin-cli"); final ResponseEntity<TokenDto> response; try { - response = restTemplate.exchange("/api/auth/realms/master/protocol/openid-connect/token", + response = restTemplate.exchange("/realms/master/protocol/openid-connect/token", HttpMethod.POST, new HttpEntity<>(payload, headers), TokenDto.class); } catch (ResourceAccessException | HttpServerErrorException.ServiceUnavailable e) { log.error("Failed to obtain admin token: {}", e.getMessage()); @@ -54,18 +56,22 @@ public class KeycloakGatewayImpl implements KeycloakGateway { } @Override - public void createUser(UserCreateDto data) throws AccessDeniedException, KeycloakRemoteException { + public void createUser(UserCreateDto data) throws AccessDeniedException, KeycloakRemoteException, + UserAlreadyExistsException { /* obtain admin token */ final HttpHeaders headers = new HttpHeaders(); headers.set("Accept", "application/json"); headers.set("Authorization", "Bearer " + obtainToken().getAccessToken()); final ResponseEntity<Void> response; try { - response = restTemplate.exchange("/api/auth/admin/realms/dbrepo/users", HttpMethod.POST, + response = restTemplate.exchange("/admin/realms/dbrepo/users", HttpMethod.POST, new HttpEntity<>(data, headers), Void.class); } catch (ResourceAccessException | HttpServerErrorException.ServiceUnavailable e) { log.error("Failed to create user: {}", e.getMessage()); throw new KeycloakRemoteException("Failed to create user: " + e.getMessage()); + } catch (HttpClientErrorException.Conflict e) { + log.error("Conflict when creating user: {}", e.getMessage()); + throw new UserAlreadyExistsException("Conflict when creating user: " + e.getMessage()); } if (!response.getStatusCode().equals(HttpStatus.CREATED)) { log.error("Failed to create user: status {} was not expected", response.getStatusCode().value()); @@ -83,13 +89,13 @@ public class KeycloakGatewayImpl implements KeycloakGateway { final UpdateCredentialsDto payload = userMapper.passwordToUpdateCredentialsDto(data.getPassword()); final ResponseEntity<Void> response; try { - response = restTemplate.exchange("/api/auth/admin/realms/dbrepo/users/" + id, HttpMethod.PUT, + response = restTemplate.exchange("/admin/realms/dbrepo/users/" + id, HttpMethod.PUT, new HttpEntity<>(payload, headers), Void.class); } catch (ResourceAccessException | HttpServerErrorException.ServiceUnavailable e) { log.error("Failed to update user credentials: {}", e.getMessage()); throw new KeycloakRemoteException("Failed to update user credentials: " + e.getMessage()); } - if (!response.getStatusCode().equals(HttpStatus.ACCEPTED)) { + if (!response.getStatusCode().equals(HttpStatus.NO_CONTENT)) { log.error("Failed to update user credentials: status {} was not expected", response.getStatusCode().value()); throw new KeycloakRemoteException("Failed to update user credentials: status " + response.getStatusCode().value() + "was not expected"); } @@ -104,7 +110,7 @@ public class KeycloakGatewayImpl implements KeycloakGateway { headers.set("Authorization", "Bearer " + obtainToken().getAccessToken()); final ResponseEntity<UserDto[]> response; try { - response = restTemplate.exchange("/api/auth/admin/realms/dbrepo/users/?username=" + username, + response = restTemplate.exchange("/admin/realms/dbrepo/users/?username=" + username, HttpMethod.GET, new HttpEntity<>(null, headers), UserDto[].class); } catch (ResourceAccessException | HttpServerErrorException.ServiceUnavailable e) { log.error("Failed to find user: {}", e.getMessage()); diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/interceptor/KeycloakInterceptor.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/interceptor/KeycloakInterceptor.java index f9e9e1d69db037a8e0402b6bb2e6ec358862b7f5..8f5ff4f024cf789556f988d93428f52707cd9053 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/interceptor/KeycloakInterceptor.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/interceptor/KeycloakInterceptor.java @@ -47,7 +47,7 @@ public class KeycloakInterceptor implements ClientHttpRequestInterceptor { payload.add("client_id", "admin-cli"); final ResponseEntity<TokenDto> response; try { - response = restTemplate.exchange(keycloakEndpoint + "/api/auth/realms/master/protocol/openid-connect/token", + response = restTemplate.exchange(keycloakEndpoint + "/realms/master/protocol/openid-connect/token", HttpMethod.POST, new HttpEntity<>(payload, headers), TokenDto.class); } catch (ResourceAccessException | HttpServerErrorException.ServiceUnavailable e) { log.error("Failed to obtain admin token: {}", e.getMessage()); diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/UserService.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/UserService.java index 4b0b453617dcd5062825343fe226fc01f27a0dd2..6a35daf1c0861e32062d84afe4a149182b528da3 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/UserService.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/UserService.java @@ -61,7 +61,7 @@ public interface UserService { * @param id The user id. * @param data The new password. */ - void updatePassword(UUID id, UserPasswordDto data) throws KeycloakRemoteException, AccessDeniedException; + void updatePassword(UUID id, UserPasswordDto data) throws KeycloakRemoteException, AccessDeniedException, UserNotFoundException; /** * Updates the user theme for a user with given id. diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java index c29f1b7d01ca19e7805b63de2e5f33b1a68e06b1..a151801be3b08456734b6a8eecd351b9649ebf5e 100644 --- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java +++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java @@ -10,9 +10,11 @@ import at.tuwien.repository.mdb.UserRepository; import at.tuwien.repository.sdb.UserIdxRepository; import at.tuwien.service.UserService; import lombok.extern.log4j.Log4j2; +import org.apache.commons.codec.digest.DigestUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Optional; import java.util.UUID; @@ -68,6 +70,7 @@ public class UserServiceImpl implements UserService { .username(data.getUsername()) .email(data.getEmail()) .themeDark(true) + .mariadbPassword(getMariaDbPassword(data.getPassword())) .build(); keycloakGateway.createUser(userMapper.signupRequestDtoToUserCreateDto(data)); /* create at metadata database */ @@ -93,8 +96,14 @@ public class UserServiceImpl implements UserService { } @Override - public void updatePassword(UUID id, UserPasswordDto data) throws KeycloakRemoteException, AccessDeniedException { + public void updatePassword(UUID id, UserPasswordDto data) throws KeycloakRemoteException, AccessDeniedException, + UserNotFoundException { + final User user = find(id); + user.setMariadbPassword(getMariaDbPassword(data.getPassword())); + userRepository.save(user); + log.debug("updated password in metadata database"); keycloakGateway.updateUserCredentials(id, data); + log.debug("updated password in keycloak"); log.info("Updated user password with id {}", id); } @@ -122,4 +131,9 @@ public class UserServiceImpl implements UserService { throw new UserEmailAlreadyExistsException("User with email " + email + " already exists in metadata database"); } } + + protected String getMariaDbPassword(String password) { + final byte[] utf8 = password.getBytes(StandardCharsets.UTF_8); + return "*" + DigestUtils.sha1Hex(DigestUtils.sha1(utf8)).toUpperCase(); + } }