From ea900c43b7a4bbd0ed8bbf1c127e9b59288198c7 Mon Sep 17 00:00:00 2001
From: Martin Weise <martin.weise@tuwien.ac.at>
Date: Thu, 13 Feb 2025 14:16:30 +0100
Subject: [PATCH] Fixed the sync

Signed-off-by: Martin Weise <martin.weise@tuwien.ac.at>
---
 dbrepo-auth-service/dbrepo-realm.json         |  61 +-
 .../src/main/java/at/tuwien/Client.java       |   3 +-
 .../tuwien/CreateEventListenerProvider.java   |   1 -
 .../target/create-event-listener.jar          | Bin 10130 -> 10238 bytes
 .../at/tuwien/api/auth/CreateUserDto.java     |   3 -
 .../java/at/tuwien/entities/user/User.java    |   2 +-
 .../at/tuwien/endpoints/UserEndpoint.java     |  44 +-
 .../tuwien/validation/EndpointValidator.java  |   2 +-
 .../at/tuwien/ApplicationIntegrationTest.java |  23 +
 .../endpoints/DatabaseEndpointUnitTest.java   |  79 +-
 .../endpoints/TableEndpointUnitTest.java      |  68 +-
 .../endpoints/UserEndpointUnitTest.java       |  73 +-
 .../KeycloakGatewayIntegrationTest.java       |  19 -
 .../handlers/ApiExceptionHandlerTest.java     | 907 +++++++++++++++++-
 .../service/UserServiceIntegrationTest.java   |  85 ++
 .../service/UserServicePersistenceTest.java   |  14 +-
 .../tuwien/service/UserServiceUnitTest.java   |   4 +-
 .../validator/EndpointValidatorUnitTest.java  |  94 +-
 .../at/tuwien/gateway/KeycloakGateway.java    |   9 +-
 .../gateway/impl/KeycloakGatewayImpl.java     |  33 +-
 .../tuwien/service/AuthenticationService.java |   4 +-
 .../java/at/tuwien/service/UserService.java   |   3 +-
 .../impl/AuthenticationServiceImpl.java       |   7 +-
 .../tuwien/service/impl/UserServiceImpl.java  |   4 +-
 .../main/java/at/tuwien/test/BaseTest.java    |  57 +-
 .../composables/authentication-service.ts     |  24 -
 dbrepo-ui/composables/axios-instance.ts       |   2 +-
 dbrepo-ui/composables/user-service.ts         |  32 -
 dbrepo-ui/layouts/default.vue                 |  29 +-
 dbrepo-ui/locales/en-US.json                  |  12 +-
 dbrepo-ui/pages/user/authentication.vue       |  13 +-
 dbrepo-ui/pages/user/info.vue                 |  14 +-
 docker-compose.yml                            |   2 +-
 helm/dbrepo/files/create-event-listener.jar   | Bin 10130 -> 10238 bytes
 34 files changed, 1476 insertions(+), 251 deletions(-)
 create mode 100644 dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/ApplicationIntegrationTest.java
 create mode 100644 dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServiceIntegrationTest.java
 delete mode 100644 dbrepo-ui/composables/authentication-service.ts

diff --git a/dbrepo-auth-service/dbrepo-realm.json b/dbrepo-auth-service/dbrepo-realm.json
index 1c703b8375..bac2ddc978 100644
--- a/dbrepo-auth-service/dbrepo-realm.json
+++ b/dbrepo-auth-service/dbrepo-realm.json
@@ -1475,6 +1475,39 @@
         "claim.name" : "language",
         "jsonType.label" : "String"
       }
+    }, {
+      "id" : "9bdc3e60-09b8-4241-915e-29f083434026",
+      "name" : "provider",
+      "protocol" : "openid-connect",
+      "protocolMapper" : "oidc-usersessionmodel-note-mapper",
+      "consentRequired" : false,
+      "config" : {
+        "user.session.note" : "identity_provider",
+        "introspection.token.claim" : "true",
+        "userinfo.token.claim" : "true",
+        "id.token.claim" : "true",
+        "lightweight.claim" : "false",
+        "access.token.claim" : "true",
+        "claim.name" : "identity_provider",
+        "jsonType.label" : "String",
+        "access.tokenResponse.claim" : "false"
+      }
+    }, {
+      "id" : "e567cb5c-8856-4124-8b86-f19cd53d7c71",
+      "name" : "setup_finished",
+      "protocol" : "openid-connect",
+      "protocolMapper" : "oidc-usermodel-attribute-mapper",
+      "consentRequired" : false,
+      "config" : {
+        "introspection.token.claim" : "true",
+        "userinfo.token.claim" : "true",
+        "user.attribute" : "SETUP_FINISHED",
+        "id.token.claim" : "true",
+        "lightweight.claim" : "false",
+        "access.token.claim" : "true",
+        "claim.name" : "setup_finished",
+        "jsonType.label" : "boolean"
+      }
     }, {
       "id" : "b817424d-7f91-43d8-b7d0-6a32582377fb",
       "name" : "family name",
@@ -2376,7 +2409,7 @@
       "subType" : "anonymous",
       "subComponents" : { },
       "config" : {
-        "allowed-protocol-mapper-types" : [ "oidc-usermodel-property-mapper", "saml-user-property-mapper", "saml-user-attribute-mapper", "saml-role-list-mapper", "oidc-full-name-mapper", "oidc-usermodel-attribute-mapper", "oidc-address-mapper", "oidc-sha256-pairwise-sub-mapper" ]
+        "allowed-protocol-mapper-types" : [ "saml-role-list-mapper", "saml-user-property-mapper", "oidc-address-mapper", "oidc-usermodel-property-mapper", "oidc-full-name-mapper", "saml-user-attribute-mapper", "oidc-usermodel-attribute-mapper", "oidc-sha256-pairwise-sub-mapper" ]
       }
     }, {
       "id" : "1849e52a-b8c9-44a8-af3d-ee19376a1ed1",
@@ -2402,7 +2435,7 @@
       "subType" : "authenticated",
       "subComponents" : { },
       "config" : {
-        "allowed-protocol-mapper-types" : [ "oidc-usermodel-property-mapper", "oidc-usermodel-attribute-mapper", "oidc-full-name-mapper", "saml-role-list-mapper", "oidc-address-mapper", "saml-user-property-mapper", "saml-user-attribute-mapper", "oidc-sha256-pairwise-sub-mapper" ]
+        "allowed-protocol-mapper-types" : [ "oidc-full-name-mapper", "saml-role-list-mapper", "saml-user-attribute-mapper", "oidc-usermodel-attribute-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-property-mapper", "saml-user-property-mapper", "oidc-address-mapper" ]
       }
     } ],
     "org.keycloak.userprofile.UserProfileProvider" : [ {
@@ -2426,8 +2459,8 @@
           "config" : {
             "ldap.attribute" : [ "createTimestamp" ],
             "is.mandatory.in.ldap" : [ "false" ],
-            "always.read.value.from.ldap" : [ "true" ],
             "read.only" : [ "true" ],
+            "always.read.value.from.ldap" : [ "true" ],
             "user.model.attribute" : [ "createTimestamp" ]
           }
         }, {
@@ -2438,8 +2471,8 @@
           "config" : {
             "ldap.attribute" : [ "sn" ],
             "is.mandatory.in.ldap" : [ "true" ],
-            "read.only" : [ "false" ],
             "always.read.value.from.ldap" : [ "true" ],
+            "read.only" : [ "false" ],
             "user.model.attribute" : [ "lastName" ]
           }
         }, {
@@ -2450,8 +2483,8 @@
           "config" : {
             "ldap.attribute" : [ "cn" ],
             "is.mandatory.in.ldap" : [ "true" ],
-            "always.read.value.from.ldap" : [ "true" ],
             "read.only" : [ "false" ],
+            "always.read.value.from.ldap" : [ "true" ],
             "user.model.attribute" : [ "firstName" ]
           }
         }, {
@@ -2462,8 +2495,8 @@
           "config" : {
             "ldap.attribute" : [ "mail" ],
             "is.mandatory.in.ldap" : [ "false" ],
-            "always.read.value.from.ldap" : [ "false" ],
             "read.only" : [ "false" ],
+            "always.read.value.from.ldap" : [ "false" ],
             "user.model.attribute" : [ "email" ]
           }
         }, {
@@ -2476,15 +2509,15 @@
             "membership.attribute.type" : [ "DN" ],
             "user.roles.retrieve.strategy" : [ "LOAD_GROUPS_BY_MEMBER_ATTRIBUTE" ],
             "group.name.ldap.attribute" : [ "cn" ],
-            "membership.user.ldap.attribute" : [ "uid" ],
-            "ignore.missing.groups" : [ "false" ],
             "preserve.group.inheritance" : [ "false" ],
             "membership.ldap.attribute" : [ "member" ],
-            "memberof.ldap.attribute" : [ "memberOf" ],
-            "group.object.classes" : [ "groupOfNames" ],
+            "ignore.missing.groups" : [ "false" ],
+            "membership.user.ldap.attribute" : [ "uid" ],
             "groups.dn" : [ "ou=users,dc=dbrepo,dc=at" ],
-            "groups.path" : [ "/" ],
-            "drop.non.existing.groups.during.sync" : [ "false" ]
+            "group.object.classes" : [ "groupOfNames" ],
+            "memberof.ldap.attribute" : [ "memberOf" ],
+            "drop.non.existing.groups.during.sync" : [ "false" ],
+            "groups.path" : [ "/" ]
           }
         }, {
           "id" : "b6ff3285-35af-4e86-8bb4-d94b8e0d70bb",
@@ -2518,8 +2551,8 @@
         "fullSyncPeriod" : [ "-1" ],
         "pagination" : [ "false" ],
         "startTls" : [ "false" ],
-        "connectionPooling" : [ "true" ],
         "usersDn" : [ "ou=users,dc=dbrepo,dc=at" ],
+        "connectionPooling" : [ "true" ],
         "cachePolicy" : [ "DEFAULT" ],
         "useKerberosForPasswordAuthentication" : [ "false" ],
         "importEnabled" : [ "true" ],
@@ -2531,8 +2564,8 @@
         "lastSync" : [ "1719252666" ],
         "vendor" : [ "other" ],
         "uuidLDAPAttribute" : [ "entryUUID" ],
-        "allowKerberosAuthentication" : [ "false" ],
         "connectionUrl" : [ "ldap://identity-service:1389" ],
+        "allowKerberosAuthentication" : [ "false" ],
         "syncRegistrations" : [ "true" ],
         "authType" : [ "simple" ],
         "useTruststoreSpi" : [ "always" ],
diff --git a/dbrepo-auth-service/listeners/src/main/java/at/tuwien/Client.java b/dbrepo-auth-service/listeners/src/main/java/at/tuwien/Client.java
index 769ec49097..c63e88618b 100644
--- a/dbrepo-auth-service/listeners/src/main/java/at/tuwien/Client.java
+++ b/dbrepo-auth-service/listeners/src/main/java/at/tuwien/Client.java
@@ -31,8 +31,7 @@ public class Client {
             if (systemPassword == null || systemPassword.isEmpty()) {
                 throw new IllegalArgumentException("Environment variable SYSTEM_PASSWORD is not set or is empty.");
             }
-
-            URL url = URI.create(urlString).toURL();
+            final URL url = URI.create(urlString + "/api/user").toURL();
             HttpURLConnection conn = (HttpURLConnection) url.openConnection();
             conn.setDoOutput(true);
             conn.setRequestMethod("POST");
diff --git a/dbrepo-auth-service/listeners/src/main/java/at/tuwien/CreateEventListenerProvider.java b/dbrepo-auth-service/listeners/src/main/java/at/tuwien/CreateEventListenerProvider.java
index 93f2b2919b..ea4aa7794b 100644
--- a/dbrepo-auth-service/listeners/src/main/java/at/tuwien/CreateEventListenerProvider.java
+++ b/dbrepo-auth-service/listeners/src/main/java/at/tuwien/CreateEventListenerProvider.java
@@ -57,7 +57,6 @@ public class CreateEventListenerProvider implements EventListenerProvider {
         final String userData = "{" +
                 quoteAttr("id", user.getId()) + ", " +
                 quoteAttr("username", user.getUsername()) + ", " +
-                quoteAttr("email", user.getEmail()) + ", " +
                 quoteAttr("ldap_id", user.getFirstAttribute("LDAP_ID")) + ", " +
                 quoteAttr("given_name", user.getFirstName()) + ", " +
                 quoteAttr("family_name", user.getLastName()) +
diff --git a/dbrepo-auth-service/listeners/target/create-event-listener.jar b/dbrepo-auth-service/listeners/target/create-event-listener.jar
index a23243d39509ec3821219e5799a25740c93e2ca1..a970096eeaa5015d9e95064f6b341fffdf8aae5f 100644
GIT binary patch
delta 6843
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
T<fSgnST{LMU5!mh8Dtm$g_hbd

delta 6774
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
X3>z637=%&G5Sn~RU5#y-5=amLiEW)w

diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/CreateUserDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/CreateUserDto.java
index 16f45aec4d..9742986ae0 100644
--- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/CreateUserDto.java
+++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/auth/CreateUserDto.java
@@ -40,7 +40,4 @@ public class CreateUserDto {
     @Schema(example = "bar")
     private String familyName;
 
-    @Schema(example = "foo.bar@example.com")
-    private String email;
-
 }
diff --git a/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/user/User.java b/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/user/User.java
index ba86e3d29c..156fc3b4c8 100644
--- a/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/user/User.java
+++ b/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/user/User.java
@@ -65,7 +65,7 @@ public class User {
     @Column(name = "mariadb_password", nullable = false)
     private String mariadbPassword;
 
-    @Column(name = "is_internal", nullable = false, updatable = false)
+    @Column(name = "is_internal", nullable = false, updatable = false, columnDefinition = "bool default false")
     private Boolean isInternal;
 
 }
diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java
index 51f323c30f..5ca14a5a34 100644
--- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java
+++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java
@@ -106,47 +106,15 @@ public class UserEndpoint extends AbstractEndpoint {
             @ApiResponse(responseCode = "400",
                     description = "Parameters are not well-formed (likely email)",
                     content = {@Content(mediaType = "application/json")}),
-            @ApiResponse(responseCode = "403",
-                    description = "Internal authentication to the auth service is invalid",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = ApiErrorDto.class))}),
-            @ApiResponse(responseCode = "404",
-                    description = "Default role not found",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = ApiErrorDto.class))}),
-            @ApiResponse(responseCode = "409",
-                    description = "User with username already exists",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = ApiErrorDto.class))}),
-            @ApiResponse(responseCode = "417",
-                    description = "User with e-mail already exists",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = ApiErrorDto.class))}),
-            @ApiResponse(responseCode = "502",
-                    description = "Failed to create in auth service",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = ApiErrorDto.class))}),
-            @ApiResponse(responseCode = "503",
-                    description = "Failed to create in auth service",
-                    content = {@Content(
-                            mediaType = "application/json",
-                            schema = @Schema(implementation = ApiErrorDto.class))}),
     })
-    public ResponseEntity<UserBriefDto> create(@NotNull @Valid @RequestBody CreateUserDto data)
-            throws UserExistsException, EmailExistsException, AuthServiceException, AuthServiceConnectionException,
-            UserNotFoundException, CredentialsInvalidException {
+    public ResponseEntity<UserBriefDto> create(@NotNull @Valid @RequestBody CreateUserDto data) {
         log.debug("endpoint create user, data.id={}, data.username={}", data.getId(), data.getUsername());
         return ResponseEntity.status(HttpStatus.CREATED)
                 .body(userMapper.userToUserBriefDto(
                         userService.create(data)));
     }
 
-    @GetMapping("/{userId}")
+    @RequestMapping(value = "/{userId}", method = {RequestMethod.GET, RequestMethod.HEAD})
     @Transactional(readOnly = true)
     @PreAuthorize("isAuthenticated()")
     @Observed(name = "dbrepo_user_find")
@@ -181,12 +149,14 @@ public class UserEndpoint extends AbstractEndpoint {
             throw new NotAllowedException("Failed to find user: foreign user");
         }
         if (user.getIsInternal()) {
-            throw new UserNotFoundException("Failed to find user with username: " + user.getUsername());
+            log.error("Failed to find user: internal user");
+            throw new NotAllowedException("Failed to find user: internal user");
         }
         final HttpHeaders headers = new HttpHeaders();
         if (isSystem(principal)) {
             headers.set("X-Username", user.getUsername());
             headers.set("X-Password", user.getMariadbPassword());
+            headers.set("Access-Control-Expose-Headers", "X-Username X-Password");
         }
         return ResponseEntity.status(HttpStatus.OK)
                 .headers(headers)
@@ -282,18 +252,18 @@ public class UserEndpoint extends AbstractEndpoint {
                                          @NotNull @Valid @RequestBody UserPasswordDto data,
                                          @NotNull Principal principal) throws NotAllowedException,
             UserNotFoundException, DatabaseNotFoundException, DataServiceException,
-            DataServiceConnectionException {
+            DataServiceConnectionException, AuthServiceException {
         log.debug("endpoint modify a user password, userId={}, principal.name={}", userId, principal.getName());
         final User user = userService.findById(userId);
         if (!user.getUsername().equals(principal.getName())) {
             log.error("Failed to modify user password: not current user");
             throw new NotAllowedException("Failed to modify user password: not current user");
         }
-        authenticationService.updatePassword(user, data);
         for (Database database : databaseService.findAllAtLestReadAccess(userId)) {
             databaseService.updatePassword(database, user);
         }
         userService.updatePassword(user, data);
+        authenticationService.setupFinished(user);
         return ResponseEntity.accepted()
                 .build();
     }
diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java
index a54f616b01..6fe29c118b 100644
--- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java
+++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java
@@ -131,7 +131,7 @@ public class EndpointValidator extends AbstractEndpoint {
         final Optional<CreateTableColumnDto> optional3 = data.getColumns()
                 .stream()
                 .filter(c -> c.getType().equals(ColumnTypeDto.SET))
-                .filter(c -> c.getEnums() == null || c.getSets().isEmpty())
+                .filter(c -> c.getSets() == null || c.getSets().isEmpty())
                 .findFirst();
         if (optional3.isPresent()) {
             log.error("Validation failed: column {} needs at least 1 allowed set value", optional3.get().getName());
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/ApplicationIntegrationTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/ApplicationIntegrationTest.java
new file mode 100644
index 0000000000..33c7bc76c5
--- /dev/null
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/ApplicationIntegrationTest.java
@@ -0,0 +1,23 @@
+package at.tuwien;
+
+import lombok.extern.log4j.Log4j2;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+@Log4j2
+@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
+@SpringBootTest
+@ExtendWith(SpringExtension.class)
+public class ApplicationIntegrationTest {
+
+    @Test
+    public void main_succeeds() {
+
+        /* test */
+        DbrepoMetadataServiceApplication.main(new String[]{});
+    }
+
+}
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 fd91fb5655..7ab4d2c16a 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
@@ -269,7 +269,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
 
     @Test
     @WithAnonymousUser
-    public void list_anonymous_succeeds() throws DatabaseNotFoundException, UserNotFoundException {
+    public void list_anonymous_succeeds() {
 
         /* mock */
         when(databaseService.findAllPublicOrSchemaPublic())
@@ -281,7 +281,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
 
     @Test
     @WithMockUser(username = USER_1_USERNAME, authorities = {"list-databases"})
-    public void list_hasRole_succeeds() throws DatabaseNotFoundException, UserNotFoundException {
+    public void list_hasRole_succeeds() {
 
         /* pre-condition */
         assertTrue(DATABASE_3_PUBLIC);
@@ -296,7 +296,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
 
     @Test
     @WithMockUser(username = USER_1_USERNAME, authorities = {"list-databases"})
-    public void list_hasRoleForeign_succeeds() throws DatabaseNotFoundException, UserNotFoundException {
+    public void list_hasRoleForeign_succeeds() {
 
         /* pre-condition */
         assertTrue(DATABASE_3_PUBLIC);
@@ -311,7 +311,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
 
     @Test
     @WithMockUser(username = USER_1_USERNAME, authorities = {"list-databases"})
-    public void list_hasRoleFilter_succeeds() throws DatabaseNotFoundException, UserNotFoundException {
+    public void list_hasRoleFilter_succeeds() {
 
         /* mock */
         when(databaseService.findAllPublicOrSchemaPublicOrReadAccessByInternalName(USER_1_ID, DATABASE_3_INTERNALNAME))
@@ -323,7 +323,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
 
     @Test
     @WithMockUser(username = USER_1_USERNAME, authorities = {"list-databases"})
-    public void list_hasRoleFilterNoResult_succeeds() throws DatabaseNotFoundException, UserNotFoundException {
+    public void list_hasRoleFilterNoResult_succeeds() {
 
         /* mock */
         when(databaseService.findAllPublicOrSchemaPublicOrReadAccessByInternalName(USER_1_ID, "i_do_not_exist"))
@@ -333,6 +333,18 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
         list_generic("i_do_not_exist", USER_1_PRINCIPAL, 0);
     }
 
+    @Test
+    @WithAnonymousUser
+    public void list_filterNoResult_succeeds() {
+
+        /* mock */
+        when(databaseService.findAllPublicOrSchemaPublicByInternalName("i_do_not_exist"))
+                .thenReturn(List.of());
+
+        /* test */
+        list_generic("i_do_not_exist", null, 0);
+    }
+
     @Test
     @WithAnonymousUser
     public void visibility_anonymous_fails() {
@@ -569,7 +581,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         final DatabaseDto database = findById_generic(DATABASE_1_ID, DATABASE_1, USER_LOCAL_ADMIN_PRINCIPAL);
-        assertEquals(4, database.getTables().size());
+        assertEquals(2, database.getTables().size());
         assertEquals(2, database.getViews().size());
         assertNotEquals(0, database.getAccesses().size());
     }
@@ -610,6 +622,58 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
         assertEquals(3, database.getAccesses().size());
     }
 
+    @Test
+    @WithMockUser(username = USER_2_USERNAME)
+    public void findById_hiddenAccessRights_succeeds() throws DataServiceException, DataServiceConnectionException,
+            DatabaseNotFoundException, ExchangeNotFoundException, UserNotFoundException, NotAllowedException {
+
+        /* mock */
+        when(accessService.list(DATABASE_1))
+                .thenReturn(List.of(DATABASE_1_USER_1_WRITE_ALL_ACCESS, DATABASE_1_USER_2_READ_ACCESS));
+
+        /* test */
+        final DatabaseDto database = findById_generic(DATABASE_1_ID, DATABASE_1, USER_2_PRINCIPAL);
+        assertEquals(4, database.getTables().size());
+        assertEquals(3, database.getViews().size());
+        assertEquals(0, database.getAccesses().size());
+    }
+
+    @Test
+    @WithMockUser(username = USER_1_USERNAME)
+    public void findById_hiddenAccessRightsSeesOwn_succeeds() throws DataServiceException, DataServiceConnectionException,
+            DatabaseNotFoundException, ExchangeNotFoundException, UserNotFoundException, NotAllowedException {
+
+        /* mock */
+        when(accessService.list(DATABASE_1))
+                .thenReturn(List.of(DATABASE_1_USER_1_WRITE_ALL_ACCESS, DATABASE_1_USER_2_READ_ACCESS));
+
+        /* test */
+        final DatabaseDto database = findById_generic(DATABASE_1_ID, DATABASE_1, USER_1_PRINCIPAL);
+        assertEquals(4, database.getTables().size());
+        assertEquals(3, database.getViews().size());
+        assertEquals(3, database.getAccesses().size());
+    }
+
+    @Test
+    @WithMockUser(username = USER_1_USERNAME)
+    public void findById_privateDataPrivateSchemaNoAccess_fails() {
+
+        /* test */
+        assertThrows(NotAllowedException.class, () -> {
+            findById_generic(DATABASE_1_ID, DATABASE_1, USER_4_PRINCIPAL);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_1_USERNAME)
+    public void findById_anonymousPrivateDataPrivateSchema_fails() {
+
+        /* test */
+        assertThrows(NotAllowedException.class, () -> {
+            findById_generic(DATABASE_1_ID, DATABASE_1, null);
+        });
+    }
+
     @Test
     @WithAnonymousUser
     public void findPreviewImage_anonymous_succeeds() throws DatabaseNotFoundException {
@@ -660,8 +724,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
     /* ## GENERIC TEST CASES                                                                            ## */
     /* ################################################################################################### */
 
-    public void list_generic(String internalName, Principal principal, Integer expectedSize)
-            throws DatabaseNotFoundException, UserNotFoundException {
+    public void list_generic(String internalName, Principal principal, Integer expectedSize) {
 
         /* test */
         final ResponseEntity<List<DatabaseBriefDto>> response = databaseEndpoint.list(internalName, principal);
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableEndpointUnitTest.java
index a17d31649e..094673416b 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableEndpointUnitTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableEndpointUnitTest.java
@@ -1,12 +1,12 @@
 package at.tuwien.endpoints;
 
-import at.tuwien.api.database.table.TableBriefDto;
 import at.tuwien.api.database.table.CreateTableDto;
+import at.tuwien.api.database.table.TableBriefDto;
 import at.tuwien.api.database.table.TableDto;
 import at.tuwien.api.database.table.TableUpdateDto;
-import at.tuwien.api.database.table.columns.CreateTableColumnDto;
 import at.tuwien.api.database.table.columns.ColumnDto;
 import at.tuwien.api.database.table.columns.ColumnTypeDto;
+import at.tuwien.api.database.table.columns.CreateTableColumnDto;
 import at.tuwien.api.database.table.columns.concepts.ColumnSemanticsUpdateDto;
 import at.tuwien.api.database.table.constraints.CreateTableConstraintsDto;
 import at.tuwien.api.semantics.EntityDto;
@@ -40,6 +40,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension;
 
 import java.security.Principal;
 import java.util.List;
+import java.util.UUID;
 import java.util.stream.Stream;
 
 import static org.junit.jupiter.api.Assertions.*;
@@ -524,7 +525,8 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
     @Test
     @WithAnonymousUser
     public void findById_publicDatabasePrivateDataPrivateSchemaAnonymous_succeeds() throws UserNotFoundException,
-            TableNotFoundException, NotAllowedException, DataServiceException, DatabaseNotFoundException, AccessNotFoundException, QueueNotFoundException, DataServiceConnectionException {
+            TableNotFoundException, NotAllowedException, DataServiceException, DatabaseNotFoundException,
+            AccessNotFoundException, QueueNotFoundException, DataServiceConnectionException {
 
         /* test */
         final ResponseEntity<TableDto> response = generic_findById(DATABASE_3_ID, DATABASE_3, TABLE_8_ID, TABLE_8, null, null, null);
@@ -596,6 +598,16 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
         });
     }
 
+    @Test
+    @WithMockUser(username = USER_4_USERNAME, authorities = {"table-semantic-analyse"})
+    public void analyseTable_notOwner_fails() {
+
+        /* test */
+        assertThrows(NotAllowedException.class, () -> {
+            analyseTable_generic(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, USER_4_PRINCIPAL);
+        });
+    }
+
     @Test
     @WithMockUser(username = USER_4_USERNAME)
     public void findAll_noRole_fails() {
@@ -930,6 +942,29 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
         generic_findById(DATABASE_1_ID, DATABASE_1, TABLE_2_ID, TABLE_2, null, null, null);
     }
 
+    @Test
+    @WithMockUser(username = USER_4_USERNAME)
+    public void findById_privateSchemaNotOwnerNoAccess_fails() {
+
+        /* test */
+        assertThrows(NotAllowedException.class, () -> {
+            generic_findById(DATABASE_1_ID, DATABASE_1, TABLE_4_ID, TABLE_4, USER_4_PRINCIPAL, USER_4, null);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_4_USERNAME)
+    public void findById_publicSchemaNotOwnerNoAccess_succeeds() throws UserNotFoundException, TableNotFoundException,
+            NotAllowedException, DataServiceException, DatabaseNotFoundException, AccessNotFoundException,
+            QueueNotFoundException, DataServiceConnectionException {
+
+        /* test */
+        final ResponseEntity<TableDto> response = generic_findById(DATABASE_1_ID, DATABASE_1, TABLE_2_ID, TABLE_2, USER_4_PRINCIPAL, USER_4, null);
+        assertEquals(HttpStatus.OK, response.getStatusCode());
+        final TableDto body = response.getBody();
+        assertNotNull(body);
+    }
+
     @Test
     @WithMockUser(username = USER_1_USERNAME, authorities = "find-table")
     public void findById_privateHasRoleTableNotFound_fails() {
@@ -970,7 +1005,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
             AccessNotFoundException, QueueNotFoundException, DataServiceConnectionException {
 
         /* test */
-        generic_findById(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, USER_4_PRINCIPAL, USER_4, null);
+        generic_findById(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, USER_4_PRINCIPAL, USER_4, DATABASE_1_USER_4_READ_ACCESS);
     }
 
     @Test
@@ -1081,6 +1116,17 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
         assertEquals(HttpStatus.ACCEPTED, response.getStatusCode());
     }
 
+    @Test
+    @WithMockUser(username = USER_LOCAL_ADMIN_USERNAME, authorities = {"system", "update-table-statistic"})
+    public void updateStatistic_internalUser_succeeds() throws TableNotFoundException, SearchServiceException,
+            MalformedException, NotAllowedException, DataServiceException, DatabaseNotFoundException,
+            SearchServiceConnectionException, DataServiceConnectionException {
+
+        /* test */
+        final ResponseEntity<Void> response = generic_updateStatistic(USER_LOCAL_ADMIN_PRINCIPAL);
+        assertEquals(HttpStatus.ACCEPTED, response.getStatusCode());
+    }
+
     /* ################################################################################################### */
     /* ## GENERIC TEST CASES                                                                            ## */
     /* ################################################################################################### */
@@ -1148,7 +1194,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
     }
 
     protected ResponseEntity<TableBriefDto> generic_create(Long databaseId, Database database, CreateTableDto data,
-                                                      Principal principal, User user, DatabaseAccess access)
+                                                           Principal principal, User user, DatabaseAccess access)
             throws MalformedException, NotAllowedException, DataServiceException, DataServiceConnectionException,
             UserNotFoundException, DatabaseNotFoundException, AccessNotFoundException, TableNotFoundException,
             TableExistsException, SearchServiceException, SearchServiceConnectionException, OntologyNotFoundException,
@@ -1203,11 +1249,21 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
                     .when(tableService)
                     .findById(any(Database.class), eq(tableId));
         }
-        if (principal != null) {
+        if (user != null) {
             when(userService.findById(user.getId()))
                     .thenReturn(user);
+        } else {
+            doThrow(UserNotFoundException.class)
+                    .when(userService)
+                    .findById(any(UUID.class));
+        }
+        if (access != null) {
             when(accessService.find(any(Database.class), eq(user)))
                     .thenReturn(access);
+        } else {
+            doThrow(AccessNotFoundException.class)
+                    .when(accessService)
+                    .find(any(Database.class), eq(user));
         }
 
         /* test */
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/UserEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/UserEndpointUnitTest.java
index 6ef4bd8779..de5c8993a1 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/UserEndpointUnitTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/UserEndpointUnitTest.java
@@ -1,5 +1,6 @@
 package at.tuwien.endpoints;
 
+import at.tuwien.api.auth.CreateUserDto;
 import at.tuwien.api.user.UserBriefDto;
 import at.tuwien.api.user.UserDto;
 import at.tuwien.api.user.UserPasswordDto;
@@ -19,6 +20,7 @@ import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.boot.test.mock.mockito.MockBean;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.AccessDeniedException;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.authority.SimpleGrantedAuthority;
 import org.springframework.security.test.context.support.WithAnonymousUser;
@@ -63,6 +65,15 @@ public class UserEndpointUnitTest extends AbstractUnitTest {
         assertEquals(2, response.size());
     }
 
+    @Test
+    @WithAnonymousUser
+    public void findAll_filterInternalUserEmptyList_succeeds() throws UserNotFoundException {
+
+        /* test */
+        final List<UserBriefDto> response = findAll_generic(USER_LOCAL_ADMIN_USERNAME, null);
+        assertEquals(0, response.size());
+    }
+
     @Test
     @WithMockUser(username = USER_1_USERNAME)
     public void findAll_noRole_succeeds() throws UserNotFoundException {
@@ -142,6 +153,18 @@ public class UserEndpointUnitTest extends AbstractUnitTest {
         assertEquals(USER_3_DATABASE_PASSWORD, response.getHeaders().get("X-Password").get(0));
     }
 
+    @Test
+    @WithMockUser(username = USER_LOCAL_ADMIN_USERNAME, authorities = {"system"})
+    public void find_internalUser_fails() {
+        final Principal principal = new UsernamePasswordAuthenticationToken(USER_LOCAL_ADMIN_DETAILS, USER_LOCAL_ADMIN_PASSWORD, List.of(
+                new SimpleGrantedAuthority("system")));
+
+        /* test */
+        assertThrows(NotAllowedException.class, () -> {
+            find_generic(USER_LOCAL_ADMIN_ID, USER_LOCAL, principal);
+        });
+    }
+
     @Test
     @WithAnonymousUser
     public void modify_anonymous_fails() {
@@ -233,7 +256,7 @@ public class UserEndpointUnitTest extends AbstractUnitTest {
     @Test
     @WithMockUser(username = USER_1_USERNAME)
     public void password_succeeds() throws NotAllowedException, DataServiceException, DataServiceConnectionException,
-            UserNotFoundException, DatabaseNotFoundException {
+            UserNotFoundException, DatabaseNotFoundException, AuthServiceException {
         final UserPasswordDto request = UserPasswordDto.builder()
                 .password(USER_1_PASSWORD)
                 .build();
@@ -242,6 +265,38 @@ public class UserEndpointUnitTest extends AbstractUnitTest {
         password_generic(USER_1_PRINCIPAL, request);
     }
 
+    @Test
+    @WithAnonymousUser
+    public void create_anonymous_fails() {
+
+        /* test */
+        assertThrows(AccessDeniedException.class, () -> {
+            generic_create(USER_1_CREATE_USER_DTO);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_2_USERNAME)
+    public void create_notInternalUser_fails() {
+
+        /* test */
+        assertThrows(AccessDeniedException.class, () -> {
+            generic_create(USER_1_CREATE_USER_DTO);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_LOCAL_ADMIN_USERNAME, authorities = {"system"})
+    public void create_succeeds() {
+
+        /* mock */
+        when(userService.create(USER_1_CREATE_USER_DTO))
+                .thenReturn(USER_1);
+
+        /* test */
+        generic_create(USER_1_CREATE_USER_DTO);
+    }
+
     /* ################################################################################################### */
     /* ## GENERIC TEST CASES                                                                            ## */
     /* ################################################################################################### */
@@ -260,7 +315,7 @@ public class UserEndpointUnitTest extends AbstractUnitTest {
             }
         } else {
             when(userService.findAll())
-                    .thenReturn(List.of(USER_1, USER_2));
+                    .thenReturn(List.of(USER_1, USER_2, USER_LOCAL));
         }
 
         /* test */
@@ -310,14 +365,15 @@ public class UserEndpointUnitTest extends AbstractUnitTest {
     }
 
     protected void password_generic(Principal principal, UserPasswordDto data) throws NotAllowedException,
-            DataServiceException, DataServiceConnectionException, UserNotFoundException, DatabaseNotFoundException {
+            DataServiceException, DataServiceConnectionException, UserNotFoundException, DatabaseNotFoundException,
+            AuthServiceException {
 
         /* mock */
         when(userService.findById(USER_1_ID))
                 .thenReturn(USER_1);
         doNothing()
                 .when(authenticationService)
-                .updatePassword(USER_1, data);
+                .setupFinished(USER_1);
         doNothing()
                 .when(userService)
                 .updatePassword(USER_1, data);
@@ -331,4 +387,13 @@ public class UserEndpointUnitTest extends AbstractUnitTest {
         final ResponseEntity<?> response = userEndpoint.password(USER_1_ID, data, principal);
         assertEquals(HttpStatus.ACCEPTED, response.getStatusCode());
     }
+
+    protected void generic_create(CreateUserDto data) {
+
+        /* test */
+        final ResponseEntity<UserBriefDto> response = userEndpoint.create(data);
+        assertEquals(HttpStatus.CREATED, response.getStatusCode());
+        final UserBriefDto body = response.getBody();
+        assertNotNull(body);
+    }
 }
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/KeycloakGatewayIntegrationTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/KeycloakGatewayIntegrationTest.java
index e72cd7fa75..58701adfc3 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/KeycloakGatewayIntegrationTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/gateway/KeycloakGatewayIntegrationTest.java
@@ -92,23 +92,4 @@ public class KeycloakGatewayIntegrationTest extends AbstractUnitTest {
         });
     }
 
-    @Test
-    public void updateUserCredentials_succeeds() throws UserNotFoundException {
-
-        /* mock */
-        keycloakUtils.createUser(USER_1_ID, USER_1_KEYCLOAK_SIGNUP_REQUEST);
-
-        /* test */
-        keycloakGateway.updateUserCredentials(keycloakUtils.getUserId(USER_1_USERNAME), USER_1_PASSWORD_DTO);
-    }
-
-    @Test
-    public void updateUserCredentials_notFound_fails() {
-
-        /* test */
-        assertThrows(UserNotFoundException.class, () -> {
-            keycloakGateway.updateUserCredentials(keycloakUtils.getUserId(USER_1_USERNAME), USER_1_PASSWORD_DTO);
-        });
-    }
-
 }
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/handlers/ApiExceptionHandlerTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/handlers/ApiExceptionHandlerTest.java
index 9075ec2a02..a58a4b49da 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/handlers/ApiExceptionHandlerTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/handlers/ApiExceptionHandlerTest.java
@@ -1,12 +1,16 @@
 package at.tuwien.handlers;
 
+import at.tuwien.api.error.ApiErrorDto;
+import at.tuwien.exception.*;
 import at.tuwien.test.AbstractUnitTest;
 import lombok.extern.log4j.Log4j2;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
 import org.springframework.test.context.junit.jupiter.SpringExtension;
 import org.springframework.web.bind.annotation.ResponseStatus;
 
@@ -18,12 +22,17 @@ import java.util.Optional;
 
 import static at.tuwien.test.utils.EndpointUtils.getErrorCodes;
 import static at.tuwien.test.utils.EndpointUtils.getExceptions;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 
 @Log4j2
 @ExtendWith(SpringExtension.class)
 @SpringBootTest
 public class ApiExceptionHandlerTest extends AbstractUnitTest {
 
+    @Autowired
+    private ApiExceptionHandler apiExceptionHandler;
+
     @Test
     public void handle_succeeds() throws ClassNotFoundException, IOException {
         final List<Method> handlers = Arrays.asList(ApiExceptionHandler.class.getMethods());
@@ -42,7 +51,903 @@ public class ApiExceptionHandlerTest extends AbstractUnitTest {
             Assertions.assertNotNull(exception.getDeclaredAnnotation(ResponseStatus.class).reason(), "Exception " + exception.getName() + " does not provide a reason code");
             Assertions.assertTrue(errorCodes.contains(exception.getDeclaredAnnotation(ResponseStatus.class).reason()), "Exception code " + exception.getDeclaredAnnotation(ResponseStatus.class).reason() + " does have a reason code mapped in localized ui error messages");
             /* handler method */
-            Assertions.assertEquals(method.getDeclaredAnnotation(ResponseStatus.class).code(), exception.getDeclaredAnnotation(ResponseStatus.class).code());
+            assertEquals(method.getDeclaredAnnotation(ResponseStatus.class).code(), exception.getDeclaredAnnotation(ResponseStatus.class).code());
         }
     }
+
+    @Test
+    public void handle_accessNotFoundException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new AccessNotFoundException("msg"));
+        assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.NOT_FOUND, body.getStatus());
+        assertEquals("error.access.missing", body.getCode());
+    }
+
+
+    @Test
+    public void handle_accountNotSetupException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new AccountNotSetupException("msg"));
+        assertEquals(HttpStatus.PRECONDITION_REQUIRED, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.PRECONDITION_REQUIRED, body.getStatus());
+        assertEquals("error.user.setup", body.getCode());
+    }
+
+
+    @Test
+    public void handle_analyseServiceException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new AnalyseServiceException("msg"));
+        assertEquals(HttpStatus.SERVICE_UNAVAILABLE, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.SERVICE_UNAVAILABLE, body.getStatus());
+        assertEquals("error.analyse.invalid", body.getCode());
+    }
+
+
+    @Test
+    public void handle_authServiceConnectionException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new AuthServiceConnectionException("msg"));
+        assertEquals(HttpStatus.BAD_GATEWAY, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.BAD_GATEWAY, body.getStatus());
+        assertEquals("error.auth.connection", body.getCode());
+    }
+
+
+    @Test
+    public void handle_authServiceException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new AuthServiceException("msg"));
+        assertEquals(HttpStatus.SERVICE_UNAVAILABLE, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.SERVICE_UNAVAILABLE, body.getStatus());
+        assertEquals("error.auth.invalid", body.getCode());
+    }
+
+
+    @Test
+    public void handle_brokerServiceConnectionException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new BrokerServiceConnectionException("msg"));
+        assertEquals(HttpStatus.BAD_GATEWAY, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.BAD_GATEWAY, body.getStatus());
+        assertEquals("error.broker.connection", body.getCode());
+    }
+
+
+    @Test
+    public void handle_brokerServiceException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new BrokerServiceException("msg"));
+        assertEquals(HttpStatus.SERVICE_UNAVAILABLE, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.SERVICE_UNAVAILABLE, body.getStatus());
+        assertEquals("error.broker.invalid", body.getCode());
+    }
+
+
+    @Test
+    public void handle_conceptNotFoundException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new ConceptNotFoundException("msg"));
+        assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.NOT_FOUND, body.getStatus());
+        assertEquals("error.concept.missing", body.getCode());
+    }
+
+
+    @Test
+    public void handle_containerAlreadyExistsException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new ContainerAlreadyExistsException("msg"));
+        assertEquals(HttpStatus.CONFLICT, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.CONFLICT, body.getStatus());
+        assertEquals("error.container.exists", body.getCode());
+    }
+
+
+    @Test
+    public void handle_containerNotFoundException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new ContainerNotFoundException("msg"));
+        assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.NOT_FOUND, body.getStatus());
+        assertEquals("error.container.missing", body.getCode());
+    }
+
+
+    @Test
+    public void handle_containerQuotaException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new ContainerQuotaException("msg"));
+        assertEquals(HttpStatus.LOCKED, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.LOCKED, body.getStatus());
+        assertEquals("error.container.quota", body.getCode());
+    }
+
+
+    @Test
+    public void handle_credentialsInvalidException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new CredentialsInvalidException("msg"));
+        assertEquals(HttpStatus.FORBIDDEN, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.FORBIDDEN, body.getStatus());
+        assertEquals("error.user.credentials", body.getCode());
+    }
+
+
+    @Test
+    public void handle_dataServiceConnectionException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new DataServiceConnectionException("msg"));
+        assertEquals(HttpStatus.BAD_GATEWAY, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.BAD_GATEWAY, body.getStatus());
+        assertEquals("error.data.connection", body.getCode());
+    }
+
+
+    @Test
+    public void handle_dataServiceException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new DataServiceException("msg"));
+        assertEquals(HttpStatus.SERVICE_UNAVAILABLE, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.SERVICE_UNAVAILABLE, body.getStatus());
+        assertEquals("error.data.invalid", body.getCode());
+    }
+
+
+    @Test
+    public void handle_databaseMalformedException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new DatabaseMalformedException("msg"));
+        assertEquals(HttpStatus.EXPECTATION_FAILED, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.EXPECTATION_FAILED, body.getStatus());
+        assertEquals("error.database.invalid", body.getCode());
+    }
+
+
+    @Test
+    public void handle_databaseNotFoundException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new DatabaseNotFoundException("msg"));
+        assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.NOT_FOUND, body.getStatus());
+        assertEquals("error.database.missing", body.getCode());
+    }
+
+
+    @Test
+    public void handle_databaseUnavailableException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new DatabaseUnavailableException("msg"));
+        assertEquals(HttpStatus.SERVICE_UNAVAILABLE, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.SERVICE_UNAVAILABLE, body.getStatus());
+        assertEquals("error.database.connection", body.getCode());
+    }
+
+
+    @Test
+    public void handle_doiNotFoundException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new DoiNotFoundException("msg"));
+        assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.NOT_FOUND, body.getStatus());
+        assertEquals("error.doi.missing", body.getCode());
+    }
+
+
+    @Test
+    public void handle_emailExistsException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new EmailExistsException("msg"));
+        assertEquals(HttpStatus.EXPECTATION_FAILED, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.EXPECTATION_FAILED, body.getStatus());
+        assertEquals("error.user.email-exists", body.getCode());
+    }
+
+
+    @Test
+    public void handle_exchangeNotFoundException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new ExchangeNotFoundException("msg"));
+        assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.NOT_FOUND, body.getStatus());
+        assertEquals("error.exchange.missing", body.getCode());
+    }
+
+
+    @Test
+    public void handle_externalServiceException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new ExternalServiceException("msg"));
+        assertEquals(HttpStatus.SERVICE_UNAVAILABLE, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.SERVICE_UNAVAILABLE, body.getStatus());
+        assertEquals("error.external.invalid", body.getCode());
+    }
+
+
+    @Test
+    public void handle_filterBadRequestException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new FilterBadRequestException("msg"));
+        assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.BAD_REQUEST, body.getStatus());
+        assertEquals("error.semantic.filter", body.getCode());
+    }
+
+
+    @Test
+    public void handle_formatNotAvailableException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new FormatNotAvailableException("msg"));
+        assertEquals(HttpStatus.NOT_ACCEPTABLE, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.NOT_ACCEPTABLE, body.getStatus());
+        assertEquals("error.identifier.format", body.getCode());
+    }
+
+
+    @Test
+    public void handle_identifierNotFoundException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new IdentifierNotFoundException("msg"));
+        assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.NOT_FOUND, body.getStatus());
+        assertEquals("error.identifier.missing", body.getCode());
+    }
+
+
+    @Test
+    public void handle_identifierNotSupportedException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new IdentifierNotSupportedException("msg"));
+        assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.NOT_FOUND, body.getStatus());
+        assertEquals("error.identifier.unsupported", body.getCode());
+    }
+
+
+    @Test
+    public void handle_imageAlreadyExistsException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new ImageAlreadyExistsException("msg"));
+        assertEquals(HttpStatus.CONFLICT, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.CONFLICT, body.getStatus());
+        assertEquals("error.image.exists", body.getCode());
+    }
+
+
+    @Test
+    public void handle_imageInvalidException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new ImageInvalidException("msg"));
+        assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.BAD_REQUEST, body.getStatus());
+        assertEquals("error.image.invalid", body.getCode());
+    }
+
+
+    @Test
+    public void handle_imageNotFoundException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new ImageNotFoundException("msg"));
+        assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.NOT_FOUND, body.getStatus());
+        assertEquals("error.image.missing", body.getCode());
+    }
+
+
+    @Test
+    public void handle_licenseNotFoundException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new LicenseNotFoundException("msg"));
+        assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.NOT_FOUND, body.getStatus());
+        assertEquals("error.license.missing", body.getCode());
+    }
+
+
+    @Test
+    public void handle_malformedException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new MalformedException("msg"));
+        assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.BAD_REQUEST, body.getStatus());
+        assertEquals("error.request.invalid", body.getCode());
+    }
+
+
+    @Test
+    public void handle_messageNotFoundException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new MessageNotFoundException("msg"));
+        assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.NOT_FOUND, body.getStatus());
+        assertEquals("error.message.missing", body.getCode());
+    }
+
+
+    @Test
+    public void handle_metadataServiceConnectionException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new MetadataServiceConnectionException("msg"));
+        assertEquals(HttpStatus.BAD_GATEWAY, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.BAD_GATEWAY, body.getStatus());
+        assertEquals("error.metadata.connection", body.getCode());
+    }
+
+
+    @Test
+    public void handle_metadataServiceException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new MetadataServiceException("msg"));
+        assertEquals(HttpStatus.SERVICE_UNAVAILABLE, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.SERVICE_UNAVAILABLE, body.getStatus());
+        assertEquals("error.metadata.invalid", body.getCode());
+    }
+
+
+    @Test
+    public void handle_notAllowedException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new NotAllowedException("msg"));
+        assertEquals(HttpStatus.FORBIDDEN, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.FORBIDDEN, body.getStatus());
+        assertEquals("error.request.forbidden", body.getCode());
+    }
+
+
+    @Test
+    public void handle_ontologyNotFoundException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new OntologyNotFoundException("msg"));
+        assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.NOT_FOUND, body.getStatus());
+        assertEquals("error.ontology.missing", body.getCode());
+    }
+
+
+    @Test
+    public void handle_orcidNotFoundException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new OrcidNotFoundException("msg"));
+        assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.NOT_FOUND, body.getStatus());
+        assertEquals("error.orcid.missing", body.getCode());
+    }
+
+
+    @Test
+    public void handle_paginationException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new PaginationException("msg"));
+        assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.BAD_REQUEST, body.getStatus());
+        assertEquals("error.request.pagination", body.getCode());
+    }
+
+
+    @Test
+    public void handle_queryMalformedException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new QueryMalformedException("msg"));
+        assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.BAD_REQUEST, body.getStatus());
+        assertEquals("error.query.invalid", body.getCode());
+    }
+
+
+    @Test
+    public void handle_queryNotFoundException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new QueryNotFoundException("msg"));
+        assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.NOT_FOUND, body.getStatus());
+        assertEquals("error.query.missing", body.getCode());
+    }
+
+
+    @Test
+    public void handle_queryNotSupportedException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new QueryNotSupportedException("msg"));
+        assertEquals(HttpStatus.NOT_IMPLEMENTED, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.NOT_IMPLEMENTED, body.getStatus());
+        assertEquals("error.query.invalid", body.getCode());
+    }
+
+
+    @Test
+    public void handle_queryStoreCreateException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new QueryStoreCreateException("msg"));
+        assertEquals(HttpStatus.EXPECTATION_FAILED, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.EXPECTATION_FAILED, body.getStatus());
+        assertEquals("error.store.invalid", body.getCode());
+    }
+
+
+    @Test
+    public void handle_queryStoreGCException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new QueryStoreGCException("msg"));
+        assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.BAD_REQUEST, body.getStatus());
+        assertEquals("error.store.clean", body.getCode());
+    }
+
+
+    @Test
+    public void handle_queryStoreInsertException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new QueryStoreInsertException("msg"));
+        assertEquals(HttpStatus.EXPECTATION_FAILED, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.EXPECTATION_FAILED, body.getStatus());
+        assertEquals("error.store.insert", body.getCode());
+    }
+
+
+    @Test
+    public void handle_queryStorePersistException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new QueryStorePersistException("msg"));
+        assertEquals(HttpStatus.EXPECTATION_FAILED, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.EXPECTATION_FAILED, body.getStatus());
+        assertEquals("error.store.persist", body.getCode());
+    }
+
+
+    @Test
+    public void handle_queueNotFoundException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new QueueNotFoundException("msg"));
+        assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.NOT_FOUND, body.getStatus());
+        assertEquals("error.queue.missing", body.getCode());
+    }
+
+
+    @Test
+    public void handle_remoteUnavailableException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new RemoteUnavailableException("msg"));
+        assertEquals(HttpStatus.SERVICE_UNAVAILABLE, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.SERVICE_UNAVAILABLE, body.getStatus());
+        assertEquals("error.metadata.privileged", body.getCode());
+    }
+
+
+    @Test
+    public void handle_rorNotFoundException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new RorNotFoundException("msg"));
+        assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.NOT_FOUND, body.getStatus());
+        assertEquals("error.ror.missing", body.getCode());
+    }
+
+
+    @Test
+    public void handle_searchServiceConnectionException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new SearchServiceConnectionException("msg"));
+        assertEquals(HttpStatus.BAD_GATEWAY, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.BAD_GATEWAY, body.getStatus());
+        assertEquals("error.search.connection", body.getCode());
+    }
+
+
+    @Test
+    public void handle_searchServiceException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new SearchServiceException("msg"));
+        assertEquals(HttpStatus.SERVICE_UNAVAILABLE, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.SERVICE_UNAVAILABLE, body.getStatus());
+        assertEquals("error.search.invalid", body.getCode());
+    }
+
+
+    @Test
+    public void handle_semanticEntityNotFoundException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new SemanticEntityNotFoundException("msg"));
+        assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.NOT_FOUND, body.getStatus());
+        assertEquals("error.semantic.missing", body.getCode());
+    }
+
+
+    @Test
+    public void handle_sortException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new SortException("msg"));
+        assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.BAD_REQUEST, body.getStatus());
+        assertEquals("error.request.sort", body.getCode());
+    }
+
+
+    @Test
+    public void handle_storageNotFoundException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new StorageNotFoundException("msg"));
+        assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.NOT_FOUND, body.getStatus());
+        assertEquals("error.storage.missing", body.getCode());
+    }
+
+
+    @Test
+    public void handle_storageUnavailableException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new StorageUnavailableException("msg"));
+        assertEquals(HttpStatus.SERVICE_UNAVAILABLE, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.SERVICE_UNAVAILABLE, body.getStatus());
+        assertEquals("error.storage.invalid", body.getCode());
+    }
+
+
+    @Test
+    public void handle_tableExistsException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new TableExistsException("msg"));
+        assertEquals(HttpStatus.CONFLICT, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.CONFLICT, body.getStatus());
+        assertEquals("error.table.exists", body.getCode());
+    }
+
+
+    @Test
+    public void handle_tableMalformedException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new TableMalformedException("msg"));
+        assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.BAD_REQUEST, body.getStatus());
+        assertEquals("error.table.invalid", body.getCode());
+    }
+
+
+    @Test
+    public void handle_tableNotFoundException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new TableNotFoundException("msg"));
+        assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.NOT_FOUND, body.getStatus());
+        assertEquals("error.table.missing", body.getCode());
+    }
+
+
+    @Test
+    public void handle_tableSchemaException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new TableSchemaException("msg"));
+        assertEquals(HttpStatus.CONFLICT, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.CONFLICT, body.getStatus());
+        assertEquals("error.schema.table", body.getCode());
+    }
+
+
+    @Test
+    public void handle_unitNotFoundException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new UnitNotFoundException("msg"));
+        assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.NOT_FOUND, body.getStatus());
+        assertEquals("error.unit.missing", body.getCode());
+    }
+
+
+    @Test
+    public void handle_uriMalformedException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new UriMalformedException("msg"));
+        assertEquals(HttpStatus.EXPECTATION_FAILED, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.EXPECTATION_FAILED, body.getStatus());
+        assertEquals("error.semantics.uri", body.getCode());
+    }
+
+
+    @Test
+    public void handle_userExistsException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new UserExistsException("msg"));
+        assertEquals(HttpStatus.CONFLICT, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.CONFLICT, body.getStatus());
+        assertEquals("error.user.exists", body.getCode());
+    }
+
+
+    @Test
+    public void handle_userNotFoundException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new UserNotFoundException("msg"));
+        assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.NOT_FOUND, body.getStatus());
+        assertEquals("error.user.missing", body.getCode());
+    }
+
+
+    @Test
+    public void handle_viewMalformedException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new ViewMalformedException("msg"));
+        assertEquals(HttpStatus.BAD_REQUEST, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.BAD_REQUEST, body.getStatus());
+        assertEquals("error.view.invalid", body.getCode());
+    }
+
+
+    @Test
+    public void handle_viewNotFoundException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new ViewNotFoundException("msg"));
+        assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.NOT_FOUND, body.getStatus());
+        assertEquals("error.view.missing", body.getCode());
+    }
+
+
+    @Test
+    public void handle_viewSchemaException_succeeds() {
+
+        /* test */
+        final ResponseEntity<ApiErrorDto> response = apiExceptionHandler.handle(new ViewSchemaException("msg"));
+        assertEquals(HttpStatus.CONFLICT, response.getStatusCode());
+        final ApiErrorDto body = response.getBody();
+        assertNotNull(body);
+        assertEquals("msg", body.getMessage());
+        assertEquals(HttpStatus.CONFLICT, body.getStatus());
+        assertEquals("error.schema.view", body.getCode());
+    }
+
 }
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
new file mode 100644
index 0000000000..39aed0d28e
--- /dev/null
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServiceIntegrationTest.java
@@ -0,0 +1,85 @@
+package at.tuwien.service;
+
+import at.tuwien.entities.user.User;
+import at.tuwien.exception.AuthServiceException;
+import at.tuwien.exception.UserNotFoundException;
+import at.tuwien.gateway.KeycloakGateway;
+import at.tuwien.repository.UserRepository;
+import at.tuwien.test.AbstractUnitTest;
+import at.tuwien.utils.KeycloakUtils;
+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.test.context.SpringBootTest;
+import org.springframework.boot.test.mock.mockito.MockBean;
+import org.springframework.test.context.DynamicPropertyRegistry;
+import org.springframework.test.context.DynamicPropertySource;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+import org.testcontainers.images.PullPolicy;
+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.*;
+
+@Log4j2
+@Testcontainers
+@ExtendWith(SpringExtension.class)
+@SpringBootTest
+public class UserServiceIntegrationTest extends AbstractUnitTest {
+
+    @Autowired
+    private UserRepository userRepository;
+
+    @Autowired
+    private UserService userService;
+
+    @Autowired
+    private KeycloakUtils keycloakUtils;
+
+    @BeforeEach
+    public void beforeEach() {
+        genesis();
+        /* keycloak */
+        userRepository.deleteAll();
+        keycloakUtils.deleteUser(USER_1_USERNAME);
+    }
+
+    @Container
+    private static KeycloakContainer keycloakContainer = new KeycloakContainer(KEYCLOAK_IMAGE)
+            .withImagePullPolicy(PullPolicy.alwaysPull())
+            .withAdminUsername("admin")
+            .withAdminPassword("admin")
+            .withRealmImportFile("./init/dbrepo-realm.json")
+            .withEnv("KC_HOSTNAME_STRICT_HTTPS", "false");
+
+    @DynamicPropertySource
+    static void keycloakProperties(DynamicPropertyRegistry registry) {
+        final String authServiceEndpoint = "http://localhost:" + keycloakContainer.getMappedPort(8080);
+        log.trace("set auth endpoint: {}", authServiceEndpoint);
+        registry.add("dbrepo.endpoints.authService", () -> authServiceEndpoint);
+    }
+
+    @Test
+    public void create_succeeds() throws UserNotFoundException, AuthServiceException {
+
+        /* test */
+        final User response = userService.create(USER_1_CREATE_USER_DTO);
+        assertEquals(USER_1_ID, response.getId());
+        assertEquals(USER_1_KEYCLOAK_ID, response.getKeycloakId());
+        assertEquals(USER_1_USERNAME, response.getUsername());
+        assertEquals(USER_1_THEME, response.getTheme());
+        assertNotNull(response.getMariadbPassword());
+        assertEquals(USER_1_LANGUAGE, response.getLanguage());
+        assertEquals(USER_1_FIRSTNAME, response.getFirstname());
+        assertEquals(USER_1_LASTNAME, response.getLastname());
+        assertEquals(USER_1_IS_INTERNAL, response.getIsInternal());
+    }
+
+}
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServicePersistenceTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServicePersistenceTest.java
index 514d23b227..eb228ab3c3 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServicePersistenceTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/UserServicePersistenceTest.java
@@ -42,7 +42,7 @@ public class UserServicePersistenceTest extends AbstractUnitTest {
     public void beforeEach() {
         genesis();
         /* metadata database */
-        userRepository.save(USER_1);
+        userRepository.saveAll(List.of(USER_1, USER_LOCAL));
     }
 
     @Test
@@ -54,6 +54,16 @@ public class UserServicePersistenceTest extends AbstractUnitTest {
         assertEquals(USER_1_USERNAME, response.getUsername());
     }
 
+    @Test
+    public void findAllInternalUsers_succeeds() {
+
+        /* test */
+        final List<User> response = userService.findAllInternalUsers();
+        assertEquals(1, response.size());
+        final User user0 = response.get(0);
+        assertEquals(USER_LOCAL_ADMIN_ID, user0.getId());
+    }
+
     @Test
     public void findByUsername_fails() {
 
@@ -68,7 +78,7 @@ public class UserServicePersistenceTest extends AbstractUnitTest {
 
         /* test */
         final List<User> response = userService.findAll();
-        assertEquals(1, response.size());
+        assertEquals(2, response.size());
     }
 
     @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 58d7cdc5e4..df13d00b08 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
@@ -93,12 +93,12 @@ public class UserServiceUnitTest extends AbstractUnitTest {
     }
 
     @Test
-    public void updatePassword_succeeds() throws UserNotFoundException {
+    public void updatePassword_succeeds() throws UserNotFoundException, AuthServiceException {
 
         /* mock */
         doNothing()
                 .when(keycloakGateway)
-                .updateUserCredentials(USER_1_ID, USER_1_PASSWORD_DTO);
+                .setupFinished(USER_1_ID);
         when(userRepository.findById(USER_1_ID))
                 .thenReturn(Optional.of(USER_1));
         when(userRepository.save(any(User.class)))
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/validator/EndpointValidatorUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/validator/EndpointValidatorUnitTest.java
index 486db28e59..8105a7fb89 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/validator/EndpointValidatorUnitTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/validator/EndpointValidatorUnitTest.java
@@ -52,9 +52,27 @@ public class EndpointValidatorUnitTest extends AbstractUnitTest {
 
     public static Stream<Arguments> needSize_parameters() {
         return Stream.of(
-                Arguments.arguments(ColumnTypeDto.VARCHAR),
-                Arguments.arguments(ColumnTypeDto.BINARY),
-                Arguments.arguments(ColumnTypeDto.VARBINARY)
+                Arguments.arguments("varchar", ColumnTypeDto.VARCHAR),
+                Arguments.arguments("binary", ColumnTypeDto.BINARY),
+                Arguments.arguments("varbinary", ColumnTypeDto.VARBINARY)
+        );
+    }
+
+    public static Stream<Arguments> needSizeAndD_parameters() {
+        return Stream.of(
+                Arguments.arguments("double_size", ColumnTypeDto.DOUBLE, 40L, null),
+                Arguments.arguments("double_d", ColumnTypeDto.DOUBLE, null, 10L),
+                Arguments.arguments("decimal_size", ColumnTypeDto.DECIMAL, 40L, null),
+                Arguments.arguments("decimal_d", ColumnTypeDto.DECIMAL, null, 10L)
+        );
+    }
+
+    public static Stream<Arguments> enums_parameters() {
+        return Stream.of(
+                Arguments.arguments("enums_null", ColumnTypeDto.ENUM, null),
+                Arguments.arguments("enums_empty", ColumnTypeDto.ENUM, List.of()),
+                Arguments.arguments("sets_null", ColumnTypeDto.SET, null),
+                Arguments.arguments("sets_empty", ColumnTypeDto.SET, List.of())
         );
     }
 
@@ -244,6 +262,20 @@ public class EndpointValidatorUnitTest extends AbstractUnitTest {
         endpointValidator.validateOnlyWriteOwnOrWriteAllAccess(TABLE_1, USER_1);
     }
 
+    @Test
+    public void validateOnlyWriteOwnOrWriteAllAccess_writeOwnAccess_succeeds() throws DatabaseNotFoundException,
+            TableNotFoundException, AccessNotFoundException, NotAllowedException {
+
+        /* mock */
+        when(tableService.findById(DATABASE_1, TABLE_1_ID))
+                .thenReturn(TABLE_1);
+        when(accessService.find(eq(DATABASE_1), any(User.class)))
+                .thenReturn(DATABASE_1_USER_1_WRITE_OWN_ACCESS);
+
+        /* test */
+        endpointValidator.validateOnlyWriteOwnOrWriteAllAccess(TABLE_1, USER_1);
+    }
+
     @Test
     public void validateOnlyWriteOwnOrWriteAllAccess_privateHasReadAccess_fails() throws DatabaseNotFoundException,
             TableNotFoundException, AccessNotFoundException {
@@ -285,7 +317,7 @@ public class EndpointValidatorUnitTest extends AbstractUnitTest {
 
     @ParameterizedTest
     @MethodSource("needSize_parameters")
-    public void validateColumnCreateConstraints_needSize_fails(ColumnTypeDto type) {
+    public void validateColumnCreateConstraints_needSize_fails(String name, ColumnTypeDto type) {
         final CreateTableDto request = CreateTableDto.builder()
                 .columns(List.of(CreateTableColumnDto.builder()
                         .type(type)
@@ -299,12 +331,13 @@ public class EndpointValidatorUnitTest extends AbstractUnitTest {
         });
     }
 
-    @Test
-    public void validateColumnCreateConstraints_needEnum_fails() {
+    @ParameterizedTest
+    @MethodSource("enums_parameters")
+    public void validateColumnCreateConstraints_needEnum_fails(String name, ColumnTypeDto type, List<String> enums) {
         final CreateTableDto request = CreateTableDto.builder()
                 .columns(List.of(CreateTableColumnDto.builder()
-                        .type(ColumnTypeDto.ENUM)
-                        .enums(null) // <<<<<<<
+                        .type(type)
+                        .enums(enums)
                         .build()))
                 .build();
 
@@ -314,12 +347,14 @@ public class EndpointValidatorUnitTest extends AbstractUnitTest {
         });
     }
 
-    @Test
-    public void validateColumnCreateConstraints_needSet_fails() {
+    @ParameterizedTest
+    @MethodSource("needSizeAndD_parameters")
+    public void validateColumnCreateConstraints_needSizeAndD_fails(String name, ColumnTypeDto type, Long size, Long d) {
         final CreateTableDto request = CreateTableDto.builder()
                 .columns(List.of(CreateTableColumnDto.builder()
-                        .type(ColumnTypeDto.SET)
-                        .sets(null) // <<<<<<<
+                        .type(type)
+                        .size(size)
+                        .d(d)
                         .build()))
                 .build();
 
@@ -345,6 +380,34 @@ public class EndpointValidatorUnitTest extends AbstractUnitTest {
         });
     }
 
+    @Test
+    public void validateOnlyOwnerOrWriteAll_writeOwnAccess_succeeds() throws DatabaseNotFoundException,
+            TableNotFoundException, AccessNotFoundException, NotAllowedException {
+
+        /* mock */
+        when(tableService.findById(DATABASE_1, TABLE_1_ID))
+                .thenReturn(TABLE_1);
+        when(accessService.find(DATABASE_1, USER_1))
+                .thenReturn(DATABASE_1_USER_1_WRITE_OWN_ACCESS);
+
+        /* test */
+        endpointValidator.validateOnlyOwnerOrWriteAll(TABLE_1, USER_1);
+    }
+
+    @Test
+    public void validateOnlyOwnerOrWriteAll_writeAllAccess_succeeds() throws DatabaseNotFoundException,
+            TableNotFoundException, AccessNotFoundException, NotAllowedException {
+
+        /* mock */
+        when(tableService.findById(DATABASE_1, TABLE_1_ID))
+                .thenReturn(TABLE_1);
+        when(accessService.find(DATABASE_1, USER_2))
+                .thenReturn(DATABASE_1_USER_2_WRITE_ALL_ACCESS);
+
+        /* test */
+        endpointValidator.validateOnlyOwnerOrWriteAll(TABLE_1, USER_2);
+    }
+
     @Test
     public void validateOnlyPrivateDataHasRole_publicDatabase_succeeds() throws NotAllowedException {
 
@@ -555,6 +618,13 @@ public class EndpointValidatorUnitTest extends AbstractUnitTest {
         assertTrue(endpointValidator.validateOnlyMineOrWriteAccessOrHasRole(USER_1, USER_1_PRINCIPAL, DATABASE_1_USER_1_WRITE_OWN_ACCESS, "nobody-role"));
     }
 
+    @Test
+    public void validateOnlyMineOrWriteAccessOrHasRole_ownerOnlyWriteAll_succeeds() {
+
+        /* test */
+        assertTrue(endpointValidator.validateOnlyMineOrWriteAccessOrHasRole(USER_1, USER_1_PRINCIPAL, DATABASE_1_USER_1_WRITE_ALL_ACCESS, "nobody-role"));
+    }
+
     @Test
     public void validateOnlyMineOrWriteAccessOrHasRole_notOwnerOnlyWriteOwn_fails() {
 
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 cd5fd08a7e..296457b3d8 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
@@ -1,7 +1,6 @@
 package at.tuwien.gateway;
 
 import at.tuwien.api.keycloak.TokenDto;
-import at.tuwien.api.user.UserPasswordDto;
 import at.tuwien.api.user.UserUpdateDto;
 import at.tuwien.exception.AuthServiceException;
 import at.tuwien.exception.UserNotFoundException;
@@ -22,13 +21,7 @@ public interface KeycloakGateway {
      */
     void deleteUser(UUID id) throws UserNotFoundException;
 
-    /**
-     * Update the credentials for a given user.
-     *
-     * @param id       The user id.
-     * @param password The user credential.
-     */
-    void updateUserCredentials(UUID id, UserPasswordDto password) throws UserNotFoundException;
+    void setupFinished(UUID id) throws AuthServiceException, UserNotFoundException;
 
     void updateUser(UUID id, UserUpdateDto data) throws AuthServiceException, UserNotFoundException;
 }
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 af54651d6c..270653ee8f 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
@@ -1,14 +1,12 @@
 package at.tuwien.gateway.impl;
 
 import at.tuwien.api.keycloak.TokenDto;
-import at.tuwien.api.user.UserPasswordDto;
 import at.tuwien.api.user.UserUpdateDto;
 import at.tuwien.config.KeycloakConfig;
 import at.tuwien.exception.AuthServiceException;
 import at.tuwien.exception.UserNotFoundException;
 import at.tuwien.gateway.KeycloakGateway;
 import at.tuwien.mapper.MetadataMapper;
-import jakarta.ws.rs.BadRequestException;
 import jakarta.ws.rs.ForbiddenException;
 import jakarta.ws.rs.NotFoundException;
 import jakarta.ws.rs.core.Response;
@@ -17,7 +15,6 @@ import org.keycloak.OAuth2Constants;
 import org.keycloak.admin.client.Keycloak;
 import org.keycloak.admin.client.KeycloakBuilder;
 import org.keycloak.admin.client.resource.UserResource;
-import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.representations.idm.UserRepresentation;
 import org.springframework.stereotype.Service;
 
@@ -80,21 +77,25 @@ public class KeycloakGatewayImpl implements KeycloakGateway {
     }
 
     @Override
-    public void updateUserCredentials(UUID id, UserPasswordDto data) throws UserNotFoundException {
-        final CredentialRepresentation credential = new CredentialRepresentation();
-        credential.setTemporary(false);
-        credential.setValue(data.getPassword());
-        credential.setType(CredentialRepresentation.PASSWORD);
+    public void setupFinished(UUID id) throws AuthServiceException, UserNotFoundException {
+        final UserResource resource = keycloak.realm(keycloakConfig.getRealm())
+                .users()
+                .get(String.valueOf(id));
+        final UserRepresentation user;
         try {
-            keycloak.realm(keycloakConfig.getRealm())
-                    .users()
-                    .get(String.valueOf(id))
-                    .resetPassword(credential);
+            user = resource.toRepresentation();
         } catch (NotFoundException e) {
-            log.error("Failed to update user password: not found");
-            throw new UserNotFoundException("Failed to update user password: not found", e);
+            log.error("Failed to update user setup: not found: {}", e.getMessage());
+            throw new UserNotFoundException("Failed to update user setup: not found", e);
+        }
+        user.singleAttribute("SETUP_FINISHED", "true");
+        try {
+            resource.update(user);
+        } catch (ForbiddenException e) {
+            log.error("Failed to update user setup: forbidden: {}", e.getMessage());
+            throw new AuthServiceException("Failed to update user setup: forbidden", e);
         }
-        log.info("Updated user {} password at auth service", id);
+        log.info("Updated user {} setup at auth service", id);
     }
 
     @Override
@@ -102,7 +103,7 @@ public class KeycloakGatewayImpl implements KeycloakGateway {
         final UserResource resource = keycloak.realm(keycloakConfig.getRealm())
                 .users()
                 .get(String.valueOf(id));
-        UserRepresentation user;
+        final UserRepresentation user;
         try {
             user = resource.toRepresentation();
         } catch (NotFoundException e) {
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/AuthenticationService.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/AuthenticationService.java
index 75b647bf95..3abe07a10a 100644
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/AuthenticationService.java
+++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/AuthenticationService.java
@@ -1,6 +1,5 @@
 package at.tuwien.service;
 
-import at.tuwien.api.user.UserPasswordDto;
 import at.tuwien.entities.user.User;
 import at.tuwien.exception.AuthServiceConnectionException;
 import at.tuwien.exception.AuthServiceException;
@@ -23,8 +22,7 @@ public interface AuthenticationService {
      * Updates the password of a user with given id.
      *
      * @param user The user.
-     * @param data The new password.
      * @throws UserNotFoundException      The user was not found after creation in the auth database.
      */
-    void updatePassword(User user, UserPasswordDto data) throws UserNotFoundException;
+    void setupFinished(User user) throws UserNotFoundException, AuthServiceException;
 }
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 581641a93a..c6ca0ff21e 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
@@ -4,7 +4,6 @@ import at.tuwien.api.auth.CreateUserDto;
 import at.tuwien.api.user.UserPasswordDto;
 import at.tuwien.api.user.UserUpdateDto;
 import at.tuwien.entities.user.User;
-import at.tuwien.exception.AuthServiceConnectionException;
 import at.tuwien.exception.AuthServiceException;
 import at.tuwien.exception.UserExistsException;
 import at.tuwien.exception.UserNotFoundException;
@@ -47,7 +46,7 @@ public interface UserService {
      * @param data The user data.
      * @return The user, if successful.
      */
-    User create(CreateUserDto data) throws UserNotFoundException, AuthServiceException;
+    User create(CreateUserDto data);
 
     /**
      * Updates the user information for a user with given id in the metadata database.
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/AuthenticationServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/AuthenticationServiceImpl.java
index 1159913039..dec3577886 100644
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/AuthenticationServiceImpl.java
+++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/AuthenticationServiceImpl.java
@@ -1,10 +1,7 @@
 package at.tuwien.service.impl;
 
-import at.tuwien.api.user.UserPasswordDto;
 import at.tuwien.entities.user.User;
-import at.tuwien.exception.AuthServiceConnectionException;
 import at.tuwien.exception.AuthServiceException;
-import at.tuwien.exception.CredentialsInvalidException;
 import at.tuwien.exception.UserNotFoundException;
 import at.tuwien.gateway.KeycloakGateway;
 import at.tuwien.service.AuthenticationService;
@@ -29,8 +26,8 @@ public class AuthenticationServiceImpl implements AuthenticationService {
     }
 
     @Override
-    public void updatePassword(User user, UserPasswordDto data) throws UserNotFoundException {
-        keycloakGateway.updateUserCredentials(user.getKeycloakId(), data);
+    public void setupFinished(User user) throws AuthServiceException, UserNotFoundException {
+        keycloakGateway.setupFinished(user.getKeycloakId());
     }
 
 }
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 1d582bb975..e79dd9bd84 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
@@ -65,14 +65,14 @@ public class UserServiceImpl implements UserService {
     }
 
     @Override
-    public User create(CreateUserDto data) throws UserNotFoundException, AuthServiceException {
+    public User create(CreateUserDto data) {
         /* create at authentication service */
         final User entity = User.builder()
                 .id(data.getLdapId())
                 .keycloakId(data.getId())
                 .username(data.getUsername())
                 .theme("light")
-                .mariadbPassword(getMariaDbPassword(RandomStringUtils.randomAlphabetic(10))) /* user needs to set it later to access */
+                .mariadbPassword(getMariaDbPassword(RandomStringUtils.randomAlphabetic(10)))
                 .language("en")
                 .firstname(data.getGivenName())
                 .lastname(data.getFamilyName())
diff --git a/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java b/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java
index bccf903b8b..860eeb8253 100644
--- a/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java
+++ b/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java
@@ -5,6 +5,7 @@ import at.tuwien.api.amqp.CreateVirtualHostDto;
 import at.tuwien.api.amqp.ExchangeDto;
 import at.tuwien.api.amqp.GrantVirtualHostPermissionsDto;
 import at.tuwien.api.amqp.QueueDto;
+import at.tuwien.api.auth.CreateUserDto;
 import at.tuwien.api.auth.LoginRequestDto;
 import at.tuwien.api.auth.RefreshTokenRequestDto;
 import at.tuwien.api.container.ContainerBriefDto;
@@ -478,8 +479,6 @@ public abstract class BaseTest {
     @SuppressWarnings("java:S2068")
     public final static String USER_1_PASSWORD = "junit1";
     @SuppressWarnings("java:S2068")
-    public final static String USER_1_PASSWORD_ENCODED = "$2a$10$0dtdedA/RLTrFbUsvpbUw.I73AXOKeQP3t5UXj96OvnDEaDb3d3M6";
-    @SuppressWarnings("java:S2068")
     public final static String USER_1_DATABASE_PASSWORD = "*440BA4FD1A87A0999647DB67C0EE258198B247BA" /* junit1 */;
     public final static String USER_1_FIRSTNAME = "John";
     public final static String USER_1_LASTNAME = "Doe";
@@ -487,18 +486,11 @@ public abstract class BaseTest {
     public final static String USER_1_NAME = "John Doe";
     public final static String USER_1_AFFILIATION = "TU Graz";
     public final static String USER_1_ORCID_URL = "https://orcid.org/0000-0003-4216-302X";
-    public final static String USER_1_TITLES_BEFORE = "Dr.";
-    public final static String USER_1_TITLES_AFTER = "MSc BSc";
-    public final static Boolean USER_1_VERIFIED = false;
-    public final static Boolean USER_1_TOTP = false;
-    public final static Long USER_1_NOT_BEFORE = 0L;
     public final static Boolean USER_1_ENABLED = true;
     public final static Boolean USER_1_IS_INTERNAL = false;
     public final static String USER_1_THEME = "light";
     public final static String USER_1_LANGUAGE = "en";
     public final static Instant USER_1_CREATED = Instant.ofEpochSecond(1677399441L) /* 2023-02-26 08:17:21 (UTC) */;
-    public final static Instant USER_1_LAST_MODIFIED = USER_1_CREATED;
-    public final static UUID USER_1_REALM_ID = REALM_DBREPO_ID;
 
     public final static UpdateUserPasswordDto USER_1_UPDATE_PASSWORD_DTO = UpdateUserPasswordDto.builder()
             .username(USER_1_USERNAME)
@@ -568,6 +560,14 @@ public abstract class BaseTest {
             .qualifiedName(USER_1_QUALIFIED_NAME)
             .build();
 
+    public final static CreateUserDto USER_1_CREATE_USER_DTO = CreateUserDto.builder()
+            .id(USER_1_KEYCLOAK_ID)
+            .ldapId(USER_1_ID)
+            .givenName(USER_1_FIRSTNAME)
+            .familyName(USER_1_LASTNAME)
+            .username(USER_1_USERNAME)
+            .build();
+
     public final static UserUpdateDto USER_1_UPDATE_DTO = UserUpdateDto.builder()
             .firstname(USER_1_FIRSTNAME)
             .lastname(USER_1_LASTNAME)
@@ -608,7 +608,6 @@ public abstract class BaseTest {
 
     public final static UUID USER_2_ID = UUID.fromString("eeb9a51b-4cd8-4039-90bf-e24f17372f7c");
     public final static UUID USER_2_KEYCLOAK_ID = UUID.fromString("eeb9a51b-4cd8-4039-90bf-e24f17372f7c");
-    public final static String USER_2_EMAIL = "jane.doe@example.com";
     public final static String USER_2_USERNAME = "junit2";
     public final static String USER_2_FIRSTNAME = "Jane";
     public final static String USER_2_LASTNAME = "Doe";
@@ -620,16 +619,9 @@ public abstract class BaseTest {
     @SuppressWarnings("java:S2068")
     public final static String USER_2_DATABASE_PASSWORD = "*9AA70A8B0EEFAFCB5BED5BDEF6EE264D5DA915AE" /* junit2 */;
     public final static String USER_2_QUALIFIED_NAME = USER_2_FIRSTNAME + " " + USER_2_LASTNAME + " — @" + USER_2_USERNAME;
-    public final static Boolean USER_2_VERIFIED = true;
-    public final static Boolean USER_2_TOTP = false;
-    public final static Long USER_2_NOT_BEFORE = 0L;
-    public final static Boolean USER_2_ENABLED = true;
     public final static Boolean USER_2_IS_INTERNAL = false;
     public final static String USER_2_THEME = "light";
     public final static String USER_2_LANGUAGE = "de";
-    public final static Instant USER_2_CREATED = Instant.ofEpochSecond(1677399528L) /* 2023-02-26 08:18:48 (UTC) */;
-    public final static Instant USER_2_LAST_MODIFIED = USER_1_CREATED;
-    public final static UUID USER_2_REALM_ID = REALM_DBREPO_ID;
 
     public final static UserAttributesDto USER_2_ATTRIBUTES_DTO = UserAttributesDto.builder()
             .theme(USER_2_THEME)
@@ -697,20 +689,13 @@ public abstract class BaseTest {
     public final static String USER_3_AFFILIATION = "TU Wien";
     public final static String USER_3_ORCID_URL = null;
     public final static String USER_3_ORCID_UNCOMPRESSED = null;
-    public final static String USER_3_EMAIL = "system@example.com";
     @SuppressWarnings("java:S2068")
     public final static String USER_3_PASSWORD = "password";
     @SuppressWarnings("java:S2068")
     public final static String USER_3_DATABASE_PASSWORD = "*D65FCA043964B63E849DD6334699ECB065905DA4" /* junit3 */;
     public final static String USER_3_QUALIFIED_NAME = USER_3_FIRSTNAME + " " + USER_3_LASTNAME + " — @" + USER_3_USERNAME;
-    public final static Boolean USER_3_VERIFIED = true;
-    public final static Boolean USER_3_TOTP = false;
-    public final static Long USER_3_NOT_BEFORE = 0L;
-    public final static Boolean USER_3_ENABLED = true;
     public final static Boolean USER_3_IS_INTERNAL = false;
     public final static String USER_3_THEME = "light";
-    public final static Instant USER_3_CREATED = Instant.ofEpochSecond(1677399559L) /* 2023-02-26 08:19:19 (UTC) */;
-    public final static UUID USER_3_REALM_ID = REALM_DBREPO_ID;
 
     public final static UserAttributesDto USER_3_ATTRIBUTES_DTO = UserAttributesDto.builder()
             .theme(USER_3_THEME)
@@ -779,12 +764,8 @@ public abstract class BaseTest {
     @SuppressWarnings("java:S2068")
     public final static String USER_4_DATABASE_PASSWORD = "*C20EF5C6875857DEFA9BE6E9B62DD76AAAE51882" /* junit4 */;
     public final static String USER_4_QUALIFIED_NAME = USER_4_FIRSTNAME + " " + USER_4_LASTNAME + " — @" + USER_4_USERNAME;
-    public final static Boolean USER_4_VERIFIED = true;
-    public final static Boolean USER_4_ENABLED = true;
     public final static Boolean USER_4_IS_INTERNAL = false;
     public final static String USER_4_THEME = "light";
-    public final static Instant USER_4_CREATED = Instant.ofEpochSecond(1677399592L) /* 2023-02-26 08:19:52 (UTC) */;
-    public final static UUID USER_4_REALM_ID = REALM_DBREPO_ID;
 
     public final static UserAttributesDto USER_4_ATTRIBUTES_DTO = UserAttributesDto.builder()
             .theme(USER_4_THEME)
@@ -842,18 +823,13 @@ public abstract class BaseTest {
     public final static String USER_5_LASTNAME = "Body";
     public final static String USER_5_NAME = "No Body";
     public final static String USER_5_AFFILIATION = "TU Wien";
-    public final static String USER_5_ORCID = null;
     @SuppressWarnings("java:S2068")
     public final static String USER_5_PASSWORD = "junit5";
     @SuppressWarnings("java:S2068")
     public final static String USER_5_DATABASE_PASSWORD = "*C20EF5C6875857DEFA9BE6E9B62DD76AAAE51882" /* junit5 */;
     public final static String USER_5_QUALIFIED_NAME = USER_5_FIRSTNAME + " " + USER_5_LASTNAME + " — @" + USER_5_USERNAME;
-    public final static Boolean USER_5_VERIFIED = true;
-    public final static Boolean USER_5_ENABLED = true;
     public final static Boolean USER_5_IS_INTERNAL = false;
     public final static String USER_5_THEME = "dark";
-    public final static Instant USER_5_CREATED = Instant.ofEpochSecond(1677399592L) /* 2023-02-26 08:19:52 (UTC) */;
-    public final static UUID USER_5_REALM_ID = REALM_DBREPO_ID;
 
     public final static UserAttributesDto USER_5_ATTRIBUTES_DTO = UserAttributesDto.builder()
             .theme(USER_5_THEME)
@@ -7680,6 +7656,21 @@ public abstract class BaseTest {
             .user(USER_3_BRIEF_DTO)
             .build();
 
+    public final static DatabaseAccess DATABASE_1_USER_4_READ_ACCESS = DatabaseAccess.builder()
+            .type(AccessType.READ)
+            .hdbid(DATABASE_1_ID)
+            .database(DATABASE_1)
+            .huserid(USER_4_ID)
+            .user(USER_4)
+            .build();
+
+    public final static DatabaseAccessDto DATABASE_1_USER_4_READ_ACCESS_DTO = DatabaseAccessDto.builder()
+            .type(AccessTypeDto.READ)
+            .hdbid(DATABASE_1_ID)
+            .huserid(USER_4_ID)
+            .user(USER_4_BRIEF_DTO)
+            .build();
+
     public final static Database DATABASE_2 = Database.builder()
             .id(DATABASE_2_ID)
             .created(DATABASE_2_CREATED)
diff --git a/dbrepo-ui/composables/authentication-service.ts b/dbrepo-ui/composables/authentication-service.ts
deleted file mode 100644
index 39f6cc5a3f..0000000000
--- a/dbrepo-ui/composables/authentication-service.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import {jwtDecode} from 'jwt-decode'
-
-export const useAuthenticationService = (): any => {
-
-  function isExpiredToken(token: string): boolean {
-    if (!token) {
-      return false
-    }
-    return tokenToExpiryDate(token) < Date.now()
-  }
-
-  function tokenToExpiryDate(token: string): number {
-    if (!token) {
-      return -1
-    }
-    const exp: number = jwtDecode<Token>(token).exp
-    if (exp) {
-      return exp * 1000
-    }
-    return -1
-  }
-
-  return {isExpiredToken, tokenToExpiryDate}
-}
diff --git a/dbrepo-ui/composables/axios-instance.ts b/dbrepo-ui/composables/axios-instance.ts
index ca7a7b111c..cd3737f2bf 100644
--- a/dbrepo-ui/composables/axios-instance.ts
+++ b/dbrepo-ui/composables/axios-instance.ts
@@ -16,7 +16,7 @@ export const useAxiosInstance = () => {
       baseURL: config.public.api.client
     });
     instance.interceptors.request.use((config) => {
-      const { loggedIn, user, login, logout } = useOidcAuth()
+      const { loggedIn, user } = useOidcAuth()
       if (!loggedIn) {
         return config
       }
diff --git a/dbrepo-ui/composables/user-service.ts b/dbrepo-ui/composables/user-service.ts
index 3425dbaa5c..b90ee34033 100644
--- a/dbrepo-ui/composables/user-service.ts
+++ b/dbrepo-ui/composables/user-service.ts
@@ -1,5 +1,3 @@
-import {jwtDecode} from 'jwt-decode'
-import axios from 'axios'
 import {axiosErrorToApiError} from '@/utils'
 
 export const useUserService = (): any => {
@@ -80,36 +78,6 @@ export const useUserService = (): any => {
     })
   }
 
-  async function refreshToken(refreshToken: string): Promise<KeycloakOpenIdTokenDto> {
-    console.debug('refresh user token')
-    return new Promise<KeycloakOpenIdTokenDto>((resolve, reject) => {
-      const config = useRuntimeConfig()
-      const instance = axios.create({
-        timeout: 90_000,
-        params: {},
-        baseURL: config.public.api.client
-      })
-      instance.put<KeycloakOpenIdTokenDto>('/api/user/token', {refresh_token: refreshToken})
-        .then((response) => {
-          console.info('Refreshed user token')
-          const userStore = useUserStore()
-          // eslint-disable-next-line camelcase
-          const {access_token, refresh_token} = response.data
-          userStore.setToken(access_token)
-          userStore.setRefreshToken(refresh_token)
-          resolve(response.data)
-        }).catch((error) => {
-          console.error('Failed to refresh user token', error)
-          reject(axiosErrorToApiError(error))
-      })
-    })
-  }
-
-  function tokenToRoles(token: string): string[] {
-    const data: Token = jwtDecode<Token>(token)
-    return data.realm_access.roles || []
-  }
-
   function nameIdentifierToNameIdentifierScheme(nameIdentifier: string) {
     if (nameIdentifier.includes('orcid.org')) {
       return 'ORCID'
diff --git a/dbrepo-ui/layouts/default.vue b/dbrepo-ui/layouts/default.vue
index a26c6d2539..85d530e74d 100644
--- a/dbrepo-ui/layouts/default.vue
+++ b/dbrepo-ui/layouts/default.vue
@@ -141,12 +141,30 @@
     </v-form>
     <v-main>
       <v-container>
-        <slot />
+        <div
+          v-cloak>
+          <v-alert
+            v-if="isNotFinishedAccountSetup"
+            border="start"
+            color="info"
+            class="mb-4">
+            {{ $t('pages.settings.subpages.authentication.setup.text') }}
+            <v-btn
+              variant="flat"
+              size="small"
+              to="/user/authentication">
+              {{ $t('pages.settings.subpages.authentication.setup.action') }}
+            </v-btn>
+            .
+          </v-alert>
+        </div>
         <JumboBox
           v-if="error"
           :title="$t(errorCodeKey(error).title, { resource })"
           :subtitle="$t(errorCodeKey(error).subtitle)"
           :text="$t(errorCodeKey(error).text, { resource })" />
+        <slot
+          v-else />
       </v-container>
     </v-main>
   </v-app>
@@ -251,6 +269,15 @@ export default {
     commitShort () {
       return this.$config.public.commit.substr(0, 8)
     },
+    isNotFinishedAccountSetup () {
+      if (!this.cacheUser) {
+        return false
+      }
+      if (!('setup_finished' in this.cacheUser)) {
+        return true
+      }
+      return this.cacheUser.setup_finished === false
+    },
     error () {
       if (this.identifier) {
         return null
diff --git a/dbrepo-ui/locales/en-US.json b/dbrepo-ui/locales/en-US.json
index 07ac0163ef..d17da2341d 100644
--- a/dbrepo-ui/locales/en-US.json
+++ b/dbrepo-ui/locales/en-US.json
@@ -794,11 +794,11 @@
           },
           "firstname": {
             "label": "Given Name",
-            "hint": ""
+            "hint": "Managed by your identity provider: {provider}"
           },
           "lastname": {
             "label": "Family Name",
-            "hint": ""
+            "hint": "Managed by your identity provider: {provider}"
           },
           "affiliation": {
             "label": "Affiliation Identifier",
@@ -834,8 +834,12 @@
     "settings": {
       "subpages": {
         "authentication": {
-          "title": "User Password",
-          "subtitle": "Update the user password used for basic authentication with all interfaces",
+          "title": "API Password",
+          "subtitle": "Update the user password used for authentication with all interfaces (e.g. HTTP API, AMQP API, MQTT API)",
+          "setup": {
+            "text": "Finish your account setup by setting the",
+            "action": "API Password"
+          },
           "password": {
             "label": "Password",
             "hint": "Required"
diff --git a/dbrepo-ui/pages/user/authentication.vue b/dbrepo-ui/pages/user/authentication.vue
index 50008d3c5d..912c1878c4 100644
--- a/dbrepo-ui/pages/user/authentication.vue
+++ b/dbrepo-ui/pages/user/authentication.vue
@@ -61,7 +61,7 @@
 </template>
 
 <script setup>
-const { loggedIn } = useOidcAuth()
+const { loggedIn, user } = useOidcAuth()
 </script>
 <script>
 import UserToolbar from '@/components/user/UserToolbar.vue'
@@ -113,11 +113,20 @@ export default {
       const userService = useUserService()
       userService.updatePassword(this.cacheUser.uid, {'password': this.password})
         .then(() => {
+          const user = Object.assign({}, this.cacheUser)
+          user.setup_finished = true
+          this.cacheStore.setUser(user)
+          // fixme [mweise]: currently nuxt-oidc-auth cannot refresh the session correctly
           const toast = useToastInstance()
           toast.success(this.$t('success.user.password'))
           this.loadingUpdate = false
         })
-        .catch(() => {
+        .catch(({code, message}) => {
+          const toast = useToastInstance()
+          if (typeof code !== 'string') {
+            return
+          }
+          toast.error(message)
           this.loadingUpdate = false
         })
         .finally(() => {
diff --git a/dbrepo-ui/pages/user/info.vue b/dbrepo-ui/pages/user/info.vue
index 8674c57e2d..9c8dbf873d 100644
--- a/dbrepo-ui/pages/user/info.vue
+++ b/dbrepo-ui/pages/user/info.vue
@@ -74,24 +74,24 @@
                 <v-col md="6">
                   <v-text-field
                     v-model="model.firstname"
-                    :disabled="!canModifyInformation"
+                    :disabled="!canModifyInformation || identityProvider"
                     clearable
                     persistent-hint
                     :variant="inputVariant"
                     :label="$t('pages.user.subpages.info.firstname.label')"
-                    :hint="$t('pages.user.subpages.info.firstname.hint')" />
+                    :hint="identityProvider ? $t('pages.user.subpages.info.firstname.hint', { provider: identityProvider }) : ''" />
                 </v-col>
               </v-row>
               <v-row dense>
                 <v-col md="6">
                   <v-text-field
                     v-model="model.lastname"
-                    :disabled="!canModifyInformation"
+                    :disabled="!canModifyInformation || identityProvider"
                     clearable
                     persistent-hint
                     :variant="inputVariant"
                     :label="$t('pages.user.subpages.info.lastname.label')"
-                    :hint="$t('pages.user.subpages.info.lastname.hint')" />
+                    :hint="identityProvider ? $t('pages.user.subpages.info.lastname.hint', { provider: identityProvider }) : ''" />
                 </v-col>
               </v-row>
               <v-row dense>
@@ -191,6 +191,12 @@ export default {
     cacheUser () {
       return this.cacheStore.getUser
     },
+    identityProvider () {
+      if (!this.cacheUser || !('identity_provider' in this.cacheUser)) {
+        return false
+      }
+      return this.cacheUser.identity_provider
+    },
     canModifyInformation () {
       if (!this.roles) {
         return false
diff --git a/docker-compose.yml b/docker-compose.yml
index 9176f6404a..94b5fc30a6 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -96,7 +96,7 @@ services:
       KEYCLOAK_DATABASE_NAME: "${AUTH_DB_NAME:-keycloak}"
       KEYCLOAK_DATABASE_USER: "${AUTH_DB_USERNAME:-keycloak}"
       KEYCLOAK_DATABASE_PASSWORD: "${AUTH_DB_PASSWORD:-dbrepo}"
-      METADATA_SERVICE_ENDPOINT: "${METADATA_SERVICE_ENDPOINT:-http://metadata-service:8080}/api/user"
+      METADATA_SERVICE_ENDPOINT: "${METADATA_SERVICE_ENDPOINT:-http://metadata-service:8080}"
       SYSTEM_USERNAME: "${SYSTEM_USERNAME:-admin}"
       SYSTEM_PASSWORD: "${SYSTEM_PASSWORD:-admin}"
     healthcheck:
diff --git a/helm/dbrepo/files/create-event-listener.jar b/helm/dbrepo/files/create-event-listener.jar
index a23243d39509ec3821219e5799a25740c93e2ca1..a970096eeaa5015d9e95064f6b341fffdf8aae5f 100644
GIT binary patch
delta 6843
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
T<fSgnST{LMU5!mh8Dtm$g_hbd

delta 6774
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
X3>z637=%&G5Sn~RU5#y-5=amLiEW)w

-- 
GitLab