From 3dc42c8f39e61c5054f9ac5b8f61dfe7afd9b45c Mon Sep 17 00:00:00 2001
From: Martin Weise <martin.weise@tuwien.ac.at>
Date: Tue, 3 Sep 2024 12:01:38 +0800
Subject: [PATCH] Hotfix access

---
 .gitignore                                    |  3 ++
 dbrepo-analyse-service/.gitignore             |  5 ++-
 dbrepo-auth-service/dbrepo-realm.json         | 18 ++++-----
 .../impl/MetadataServiceGatewayImpl.java      | 16 +++++---
 .../main/java/at/tuwien/utils/ListUtil.java   | 20 ++++++++++
 .../at/tuwien/endpoints/AccessEndpoint.java   | 29 ++++++++------
 .../tuwien/endpoints/ContainerEndpoint.java   |  2 +-
 .../at/tuwien/endpoints/DatabaseEndpoint.java |  1 +
 .../at/tuwien/endpoints/TableEndpoint.java    |  1 +
 .../at/tuwien/endpoints/UserEndpoint.java     |  9 +++++
 .../at/tuwien/endpoints/ViewEndpoint.java     |  1 +
 .../service/DatabaseServiceUnitTest.java      |  2 +
 .../gateway/impl/DataServiceGatewayImpl.java  |  2 +-
 .../service/impl/AccessServiceImpl.java       |  1 +
 .../service/impl/DatabaseServiceImpl.java     |  2 +
 dbrepo-ui/components/dialogs/EditAccess.vue   | 40 +++++++++++++++----
 dbrepo-ui/composables/access-service.ts       |  2 +-
 dbrepo-ui/dto/index.ts                        |  2 +-
 dbrepo-ui/locales/en-US.json                  | 10 ++---
 .../pages/database/[database_id]/settings.vue |  8 ++--
 .../[database_id]/table/[table_id]/schema.vue |  2 +-
 21 files changed, 126 insertions(+), 50 deletions(-)
 create mode 100644 dbrepo-data-service/services/src/main/java/at/tuwien/utils/ListUtil.java

diff --git a/.gitignore b/.gitignore
index b8d8c03487..021c030c50 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,6 +21,9 @@ tmp.yaml
 site/
 final/
 
+# newer
+dbrepo-somapper/
+
 .$*
 
 # Notebooks
diff --git a/dbrepo-analyse-service/.gitignore b/dbrepo-analyse-service/.gitignore
index eb26996516..b6abc6eae3 100644
--- a/dbrepo-analyse-service/.gitignore
+++ b/dbrepo-analyse-service/.gitignore
@@ -14,6 +14,9 @@ venv/
 .venv/
 env*
 
+# model
+bge-m3/*.bin
+
 # Test
 report.xml
 coverage.html
@@ -36,4 +39,4 @@ docs/_build/
 htmlcov/
 .coverage
 .coverage.*
-*,cover
\ No newline at end of file
+*,cover
diff --git a/dbrepo-auth-service/dbrepo-realm.json b/dbrepo-auth-service/dbrepo-realm.json
index a39f7de1b0..7e8ac84325 100644
--- a/dbrepo-auth-service/dbrepo-realm.json
+++ b/dbrepo-auth-service/dbrepo-realm.json
@@ -2143,7 +2143,7 @@
       "subType" : "anonymous",
       "subComponents" : { },
       "config" : {
-        "allowed-protocol-mapper-types" : [ "oidc-full-name-mapper", "saml-user-attribute-mapper", "saml-user-property-mapper", "saml-role-list-mapper", "oidc-usermodel-attribute-mapper", "oidc-usermodel-property-mapper", "oidc-address-mapper", "oidc-sha256-pairwise-sub-mapper" ]
+        "allowed-protocol-mapper-types" : [ "oidc-address-mapper", "oidc-usermodel-attribute-mapper", "oidc-full-name-mapper", "saml-user-attribute-mapper", "saml-role-list-mapper", "oidc-usermodel-property-mapper", "saml-user-property-mapper", "oidc-sha256-pairwise-sub-mapper" ]
       }
     }, {
       "id" : "1849e52a-b8c9-44a8-af3d-ee19376a1ed1",
@@ -2169,7 +2169,7 @@
       "subType" : "authenticated",
       "subComponents" : { },
       "config" : {
-        "allowed-protocol-mapper-types" : [ "saml-user-property-mapper", "oidc-usermodel-property-mapper", "saml-role-list-mapper", "oidc-address-mapper", "saml-user-attribute-mapper", "oidc-full-name-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-attribute-mapper" ]
+        "allowed-protocol-mapper-types" : [ "saml-user-property-mapper", "oidc-address-mapper", "saml-user-attribute-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-full-name-mapper", "saml-role-list-mapper", "oidc-usermodel-attribute-mapper", "oidc-usermodel-property-mapper" ]
       }
     } ],
     "org.keycloak.storage.UserStorageProvider" : [ {
@@ -2185,8 +2185,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" ]
           }
         }, {
@@ -2209,8 +2209,8 @@
           "config" : {
             "ldap.attribute" : [ "cn" ],
             "is.mandatory.in.ldap" : [ "true" ],
-            "read.only" : [ "false" ],
             "always.read.value.from.ldap" : [ "true" ],
+            "read.only" : [ "false" ],
             "user.model.attribute" : [ "firstName" ]
           }
         }, {
@@ -2221,8 +2221,8 @@
           "config" : {
             "ldap.attribute" : [ "mail" ],
             "is.mandatory.in.ldap" : [ "false" ],
-            "read.only" : [ "false" ],
             "always.read.value.from.ldap" : [ "false" ],
+            "read.only" : [ "false" ],
             "user.model.attribute" : [ "email" ]
           }
         }, {
@@ -2238,8 +2238,8 @@
             "groups.dn" : [ "ou=users,dc=dbrepo,dc=at" ],
             "mode" : [ "LDAP_ONLY" ],
             "user.roles.retrieve.strategy" : [ "LOAD_GROUPS_BY_MEMBER_ATTRIBUTE" ],
-            "membership.ldap.attribute" : [ "member" ],
             "ignore.missing.groups" : [ "false" ],
+            "membership.ldap.attribute" : [ "member" ],
             "group.object.classes" : [ "groupOfNames" ],
             "memberof.ldap.attribute" : [ "memberOf" ],
             "groups.path" : [ "/" ],
@@ -2253,8 +2253,8 @@
           "config" : {
             "ldap.attribute" : [ "modifyTimestamp" ],
             "is.mandatory.in.ldap" : [ "false" ],
-            "always.read.value.from.ldap" : [ "true" ],
             "read.only" : [ "true" ],
+            "always.read.value.from.ldap" : [ "true" ],
             "user.model.attribute" : [ "modifyTimestamp" ]
           }
         }, {
@@ -2274,8 +2274,8 @@
         } ]
       },
       "config" : {
-        "pagination" : [ "false" ],
         "fullSyncPeriod" : [ "-1" ],
+        "pagination" : [ "false" ],
         "startTls" : [ "false" ],
         "usersDn" : [ "ou=users,dc=dbrepo,dc=at" ],
         "connectionPooling" : [ "true" ],
@@ -2284,8 +2284,8 @@
         "importEnabled" : [ "true" ],
         "enabled" : [ "true" ],
         "usernameLDAPAttribute" : [ "uid" ],
-        "bindCredential" : [ "admin" ],
         "bindDn" : [ "cn=admin,dc=dbrepo,dc=at" ],
+        "bindCredential" : [ "admin" ],
         "changedSyncPeriod" : [ "-1" ],
         "lastSync" : [ "1719252666" ],
         "vendor" : [ "other" ],
diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java
index b4cb2ff504..85c179801b 100644
--- a/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java
+++ b/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java
@@ -14,6 +14,7 @@ import at.tuwien.api.user.UserDto;
 import at.tuwien.exception.*;
 import at.tuwien.gateway.MetadataServiceGateway;
 import at.tuwien.mapper.MetadataMapper;
+import at.tuwien.utils.ListUtil;
 import jakarta.validation.constraints.NotNull;
 import lombok.extern.log4j.Log4j2;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -61,9 +62,10 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway {
             log.error("Failed to find container with id {}: service responded unsuccessful: {}", containerId, response.getStatusCode());
             throw new MetadataServiceException("Failed to find container: service responded unsuccessful: " + response.getStatusCode());
         }
-        if (!response.getHeaders().keySet().containsAll(List.of("X-Username", "X-Password"))) {
+        final List<String> headers = List.of("X-Username", "X-Password");
+        if (!response.getHeaders().keySet().containsAll(headers)) {
             log.error("Failed to find all privileged container headers");
-            throw new MetadataServiceException("Failed to find all privileged container headers");
+            throw new MetadataServiceException("Failed to find all privileged container headers: missing" + ListUtil.missingHeaders(headers, response.getHeaders().keySet()));
         }
         if (response.getBody() == null) {
             log.error("Failed to find container with id {}: body is empty", containerId);
@@ -93,9 +95,10 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway {
             log.error("Failed to find database with id {}: service responded unsuccessful: {}", id, response.getStatusCode());
             throw new MetadataServiceException("Failed to find database: service responded unsuccessful: " + response.getStatusCode());
         }
-        if (!response.getHeaders().keySet().containsAll(List.of("X-Username", "X-Password"))) {
+        final List<String> headers = List.of("X-Username", "X-Password");
+        if (!response.getHeaders().keySet().containsAll(headers)) {
             log.error("Failed to find all privileged database headers");
-            throw new MetadataServiceException("Failed to find all privileged database headers");
+            throw new MetadataServiceException("Failed to find all privileged database headers: missing" + ListUtil.missingHeaders(headers, response.getHeaders().keySet()));
         }
         if (response.getBody() == null) {
             log.error("Failed to find database with id {}: body is empty", id);
@@ -243,9 +246,10 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway {
             log.error("Failed to find user with id {}: service responded unsuccessful: {}", userId, response.getStatusCode());
             throw new MetadataServiceException("Failed to find user: service responded unsuccessful: " + response.getStatusCode());
         }
-        if (!response.getHeaders().keySet().containsAll(List.of("X-Username", "X-Password"))) {
+        final List<String> headers = List.of("X-Username", "X-Password");
+        if (!response.getHeaders().keySet().containsAll(headers)) {
             log.error("Failed to find all privileged user headers");
-            throw new MetadataServiceException("Failed to find all privileged user headers");
+            throw new MetadataServiceException("Failed to find all privileged user headers: missing " + ListUtil.missingHeaders(headers, response.getHeaders().keySet()));
         }
         if (response.getBody() == null) {
             log.error("Failed to find user with id {}: body is empty", userId);
diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/utils/ListUtil.java b/dbrepo-data-service/services/src/main/java/at/tuwien/utils/ListUtil.java
new file mode 100644
index 0000000000..3d679f3aec
--- /dev/null
+++ b/dbrepo-data-service/services/src/main/java/at/tuwien/utils/ListUtil.java
@@ -0,0 +1,20 @@
+package at.tuwien.utils;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+public class ListUtil {
+
+    public static List<String> missingHeaders(List<String> expected, Set<String> actual) {
+        final List<String> missing = new LinkedList<>();
+        for (String exp : expected) {
+            if (actual.contains(exp)) {
+                continue;
+            }
+            missing.add(exp);
+        }
+        return missing;
+    }
+
+}
diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java
index 9cdcfdedf9..34215aa7ff 100644
--- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java
+++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java
@@ -100,10 +100,11 @@ public class AccessEndpoint {
         log.debug("endpoint give access to database, databaseId={}, userId={}, access.type={}", databaseId, userId,
                 data.getType());
         final Database database = databaseService.findById(databaseId);
-        final User user = userService.findByUsername(principal.getName());
-        if (database.getOwner().equals(user)) {
-            log.error("Failed to give access to user with id {}: not owner", userId);
-            throw new NotAllowedException("Failed to give access to user with id " + userId + ": not owner");
+        final User caller = userService.findByUsername(principal.getName());
+        final User user = userService.findById(userId);
+        if (!database.getOwner().equals(caller)) {
+            log.error("Failed to give access to user with id {}: not owner", caller.getId());
+            throw new NotAllowedException("Failed to give access to user with id " + caller.getId() + ": not owner");
         }
         try {
             accessService.find(database, user);
@@ -162,10 +163,11 @@ public class AccessEndpoint {
         log.debug("endpoint modify database access, databaseId={}, userId={}, access.type={}", databaseId, userId,
                 data.getType());
         final Database database = databaseService.findById(databaseId);
-        final User user = userService.findByUsername(principal.getName());
-        if (database.getOwner().equals(user)) {
-            log.error("Failed to give access to user with id {}: not owner", userId);
-            throw new NotAllowedException("Failed to give access to user with id " + userId + ": not owner");
+        final User caller = userService.findByUsername(principal.getName());
+        final User user = userService.findById(userId);
+        if (!database.getOwner().equals(caller)) {
+            log.error("Failed to give access to user with id {}: not owner", caller.getId());
+            throw new NotAllowedException("Failed to give access to user with id " + caller.getId() + ": not owner");
         }
         accessService.find(database, user);
         accessService.update(database, user, data.getType());
@@ -203,6 +205,7 @@ public class AccessEndpoint {
             UserNotFoundException, AccessNotFoundException, NotAllowedException {
         log.debug("endpoint get database access, databaseId={}, userId={}, principal.name={}", databaseId, userId,
                 principal.getName());
+        final User user = userService.findById(userId);
         if (!userId.equals(UserUtil.getId(principal))) {
             if (!UserUtil.hasRole(principal, "check-foreign-database-access")) {
                 log.error("Failed to find access: foreign user");
@@ -211,7 +214,6 @@ public class AccessEndpoint {
             log.trace("principal is allowed to check foreign user access");
         }
         final Database database = databaseService.findById(databaseId);
-        final User user = userService.findById(userId);
         final DatabaseAccess access = accessService.find(database, user);
         final DatabaseAccessDto dto = databaseMapper.databaseAccessToDatabaseAccessDto(access);
         log.trace("check access resulted in dto {}", dto);
@@ -261,10 +263,11 @@ public class AccessEndpoint {
             SearchServiceException, SearchServiceConnectionException {
         log.debug("endpoint revoke database access, databaseId={}, userId={}", databaseId, userId);
         final Database database = databaseService.findById(databaseId);
-        final User user = userService.findByUsername(principal.getName());
-        if (!database.getOwner().equals(user)) {
-            log.error("Failed to revoke access to user with id {}: not owner", user.getId());
-            throw new NotAllowedException("Failed to revoke access to user with id " + user.getId() + ": not owner");
+        final User caller = userService.findByUsername(principal.getName());
+        final User user = userService.findById(userId);
+        if (!database.getOwner().equals(caller)) {
+            log.error("Failed to revoke access to user with id {}: not owner", caller.getId());
+            throw new NotAllowedException("Failed to revoke access to user with id " + caller.getId() + ": not owner");
         }
         accessService.find(database, user);
         accessService.delete(database, user);
diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java
index 294d471e8f..b9460ee12d 100644
--- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java
+++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ContainerEndpoint.java
@@ -145,7 +145,7 @@ public class ContainerEndpoint {
         log.trace("find container resulted in container {}", dto);
         final HttpHeaders headers = new HttpHeaders();
         if (UserUtil.isSystem(principal)) {
-            log.trace("attach privileged credential information");
+            log.trace("principal matches system-scoped user: expose privileged information");
             headers.set("X-Username", container.getPrivilegedUsername());
             headers.set("X-Password", container.getPrivilegedPassword());
             headers.set("Access-Control-Expose-Headers", "X-Username X-Password");
diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java
index 8be62ea5c4..415b7392a2 100644
--- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java
+++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java
@@ -469,6 +469,7 @@ public class DatabaseEndpoint {
         }
         final HttpHeaders headers = new HttpHeaders();
         if (UserUtil.isSystem(principal)) {
+            log.trace("principal matches system-scoped user: expose privileged information");
             headers.set("X-Username", database.getContainer().getPrivilegedUsername());
             headers.set("X-Password", database.getContainer().getPrivilegedPassword());
             headers.set("Access-Control-Expose-Headers", "X-Username X-Password");
diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java
index 4fb8240b1d..0d92d47061 100644
--- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java
+++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java
@@ -407,6 +407,7 @@ public class TableEndpoint {
         final TableDto dto = metadataMapper.customTableToTableDto(table);
         final HttpHeaders headers = new HttpHeaders();
         if (UserUtil.isSystem(principal)) {
+            log.trace("principal matches system-scoped user: expose privileged information");
             headers.set("X-Username", table.getDatabase().getContainer().getPrivilegedUsername());
             headers.set("X-Password", table.getDatabase().getContainer().getPrivilegedPassword());
             headers.set("X-Host", table.getDatabase().getContainer().getHost());
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 4be54d5edd..ef1cb74a40 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
@@ -26,6 +26,7 @@ import jakarta.validation.Valid;
 import jakarta.validation.constraints.NotNull;
 import lombok.extern.log4j.Log4j2;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -275,8 +276,16 @@ public class UserEndpoint {
                 throw new NotAllowedException("Failed to find user: foreign user");
             }
         }
+        final HttpHeaders headers = new HttpHeaders();
+        if (UserUtil.isSystem(principal)) {
+            log.trace("principal matches system-scoped user: expose privileged information");
+            headers.set("X-Username", user.getUsername());
+            headers.set("X-Password", user.getMariadbPassword());
+            headers.set("Access-Control-Expose-Headers", "X-Username X-Password");
+        }
         final UserDto dto = userMapper.userToUserDto(user);
         return ResponseEntity.ok()
+                .headers(headers)
                 .body(dto);
     }
 
diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java
index 79981ee6d1..04d98578a5 100644
--- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java
+++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java
@@ -184,6 +184,7 @@ public class ViewEndpoint {
         final View view = viewService.findById(database, viewId);
         final HttpHeaders headers = new HttpHeaders();
         if (UserUtil.isSystem(principal)) {
+            log.trace("principal matches system-scoped user: expose privileged information");
             headers.set("X-Username", view.getDatabase().getContainer().getPrivilegedUsername());
             headers.set("X-Password", view.getDatabase().getContainer().getPrivilegedPassword());
             headers.set("X-Host", view.getDatabase().getContainer().getHost());
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceUnitTest.java
index ea58ae16e4..e6f08ee889 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceUnitTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DatabaseServiceUnitTest.java
@@ -346,6 +346,8 @@ public class DatabaseServiceUnitTest extends AbstractUnitTest {
         /* test */
         final Database response = databaseService.modifyOwner(database, newOwner);
         assertNotNull(response);
+        assertEquals(newOwner.getId(), response.getOwnedBy());
+        assertEquals(newOwner.getId(), response.getOwner().getId());
         return response;
     }
 
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/DataServiceGatewayImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/DataServiceGatewayImpl.java
index 886911d9f4..e483c6346a 100644
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/DataServiceGatewayImpl.java
+++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/impl/DataServiceGatewayImpl.java
@@ -55,7 +55,7 @@ public class DataServiceGatewayImpl implements DataServiceGateway {
             log.error("Failed to create access: {}", e.getMessage());
             throw new DataServiceException("Failed to create access: " + e.getMessage(), e);
         }
-        if (!response.getStatusCode().equals(HttpStatus.CREATED)) {
+        if (!response.getStatusCode().equals(HttpStatus.ACCEPTED)) {
             log.error("Failed to create access: wrong http code: {}", response.getStatusCode());
             throw new DataServiceException("Failed to create access: wrong http code: " + response.getStatusCode());
         }
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/AccessServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/AccessServiceImpl.java
index aaa50251c3..f6cf096499 100644
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/AccessServiceImpl.java
+++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/AccessServiceImpl.java
@@ -72,6 +72,7 @@ public class AccessServiceImpl implements AccessService {
                 .hdbid(database.getId())
                 .database(database)
                 .huserid(user.getId())
+                .user(user)
                 .type(metadataMapper.accessTypeDtoToAccessType(type))
                 .build();
         database.getAccesses()
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceImpl.java
index 8c835864db..5fc4b85162 100644
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceImpl.java
+++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/DatabaseServiceImpl.java
@@ -170,6 +170,8 @@ public class DatabaseServiceImpl implements DatabaseService {
             SearchServiceConnectionException {
         /* update in metadata database */
         database.setOwnedBy(user.getId());
+        database.setContact(user);
+        database.setContactPerson(user.getId());
         database = databaseRepository.save(database);
         /* save in search service */
         searchServiceGateway.update(database);
diff --git a/dbrepo-ui/components/dialogs/EditAccess.vue b/dbrepo-ui/components/dialogs/EditAccess.vue
index 8132adddf5..97f5dff003 100644
--- a/dbrepo-ui/components/dialogs/EditAccess.vue
+++ b/dbrepo-ui/components/dialogs/EditAccess.vue
@@ -12,7 +12,7 @@
             <v-col>
               <v-autocomplete
                 v-if="!isModification"
-                v-model="modify.userId"
+                v-model="localUserId"
                 :items="eligibleUsers"
                 :disabled="loadingUsers"
                 :loading="loadingUsers"
@@ -90,6 +90,7 @@ export default {
       loadingUsers: false,
       users: [],
       error: false,
+      localUserId: null,
       types: [
         { title: this.$t('pages.database.subpages.access.read'), value: 'read' },
         { title: this.$t('pages.database.subpages.access.write-own'), value: 'write_own' },
@@ -169,36 +170,60 @@ export default {
     },
     revokeAccess () {
       const accessService = useAccessService()
-      accessService.remove(this.$route.params.database_id, this.userId)
+      accessService.remove(this.$route.params.database_id, this.localUserId)
         .then(() => {
           const toast = useToastInstance()
-          toast.success(this.$t('notifications.access.revoked'))
+          toast.success(this.$t('success.access.revoked'))
           this.$emit('close-dialog', { success: true })
         })
+        .catch(({code}) => {
+          this.loading = false
+          const toast = useToastInstance()
+          if (typeof code !== 'string') {
+            return
+          }
+          toast.error(this.$t(code))
+        })
         .finally(() => {
           this.loading = false
         })
     },
     modifyAccess () {
       const accessService = useAccessService()
-      accessService.modify(this.$route.params.database_id, this.userId, this.modify)
+      accessService.modify(this.$route.params.database_id, this.localUserId, this.modify)
         .then(() => {
           const toast = useToastInstance()
-          toast.success(this.$t('notifications.access.modified'))
+          toast.success(this.$t('success.access.modified'))
           this.$emit('close-dialog', { success: true })
         })
+        .catch(({code}) => {
+          this.loading = false
+          const toast = useToastInstance()
+          if (typeof code !== 'string') {
+            return
+          }
+          toast.error(this.$t(code))
+        })
         .finally(() => {
           this.loading = false
         })
     },
     giveAccess () {
       const accessService = useAccessService()
-      accessService.create(this.$route.params.database_id, this.userId, this.modify)
+      accessService.create(this.$route.params.database_id, this.localUserId, this.modify)
         .then(() => {
           const toast = useToastInstance()
-          toast.success(this.$t('notifications.access.created'))
+          toast.success(this.$t('success.access.created'))
           this.$emit('close-dialog', { success: true })
         })
+        .catch(({code}) => {
+          this.loading = false
+          const toast = useToastInstance()
+          if (typeof code !== 'string') {
+            return
+          }
+          toast.error(this.$t(code))
+        })
         .finally(() => {
           this.loading = false
         })
@@ -218,6 +243,7 @@ export default {
       if (!this.userId) {
         this.loadUsers()
       }
+      this.localUserId = this.userId
       if (!this.accessType) {
         this.modify.type = null
       } else {
diff --git a/dbrepo-ui/composables/access-service.ts b/dbrepo-ui/composables/access-service.ts
index c08e5d0b9f..056efec117 100644
--- a/dbrepo-ui/composables/access-service.ts
+++ b/dbrepo-ui/composables/access-service.ts
@@ -21,7 +21,7 @@ export const useAccessService = (): any => {
     const axios = useAxiosInstance()
     console.debug('create access for user with id', userId, 'of database with id', databaseId)
     return new Promise<DatabaseAccessDto>((resolve, reject) => {
-      axios.post<DatabaseAccessDto>(`/api/database/${databaseId}/access`, payload)
+      axios.post<DatabaseAccessDto>(`/api/database/${databaseId}/access/${userId}`, payload)
         .then((response) => {
           console.info('Created access for user with id', userId, 'of database with id', databaseId)
           resolve(response.data)
diff --git a/dbrepo-ui/dto/index.ts b/dbrepo-ui/dto/index.ts
index df0babcfe1..c1d63fa200 100644
--- a/dbrepo-ui/dto/index.ts
+++ b/dbrepo-ui/dto/index.ts
@@ -382,7 +382,7 @@ interface DatabaseModifyVisibilityDto {
 }
 
 interface DatabaseTransferDto {
-  username: string;
+  id: string;
 }
 
 interface DatabaseModifyImageDto {
diff --git a/dbrepo-ui/locales/en-US.json b/dbrepo-ui/locales/en-US.json
index be8ddfcc23..57f88bb2f9 100644
--- a/dbrepo-ui/locales/en-US.json
+++ b/dbrepo-ui/locales/en-US.json
@@ -282,8 +282,8 @@
         "secure": "secure",
         "insecure": "insecure",
         "permissions": {
-          "write": "You can write to this table",
-          "read": "You can read all contents of this table"
+          "write": "Can write to this table",
+          "read": "Can read all contents of this table"
         }
       },
       "description": {
@@ -611,9 +611,9 @@
         "access": {
           "title": "Database Access",
           "subtitle": "Overview on users with their access to the database",
-          "read": "You can read all contents",
-          "write-own": "You can write own tables and read all contents",
-          "write-all": "You can write own tables and read all contents",
+          "read": "Can read all contents",
+          "write-own": "Can write own tables and read all contents",
+          "write-all": "Can write own tables and read all contents",
           "revoke": "Revoke",
           "action": "Action",
           "username": {
diff --git a/dbrepo-ui/pages/database/[database_id]/settings.vue b/dbrepo-ui/pages/database/[database_id]/settings.vue
index 09c3d8263b..e22ad34522 100644
--- a/dbrepo-ui/pages/database/[database_id]/settings.vue
+++ b/dbrepo-ui/pages/database/[database_id]/settings.vue
@@ -95,7 +95,7 @@
               <v-btn
                 v-if="item && item.user && item.user.username !== user.username"
                 size="x-small"
-                variant="flat"
+                :variant="buttonVariant"
                 :disabled="!canModifyAccess"
                 :text="$t('pages.database.subpages.access.submit.text')"
                 @click="modifyAccess(item)" />
@@ -422,8 +422,8 @@ export default {
       this.$refs.form.validate()
     },
     closeDialog () {
-      this.reloadDatabase()
       this.editAccessDialog = false
+      this.cacheStore.reloadDatabase()
     },
     updateDatabaseVisibility () {
       this.loading = true
@@ -510,11 +510,11 @@ export default {
     updateDatabaseOwner () {
       this.loading = true
       const databaseService = useDatabaseService()
-      databaseService.updateOwner(this.$route.params.database_id, this.modifyOwner.id)
+      databaseService.updateOwner(this.$route.params.database_id, { id: this.modifyOwner.id })
         .then(() => {
           const toast = useToastInstance()
           toast.success(this.$t('success.database.transfer'))
-          location.reload()
+          this.$router.push(`/database/${this.$route.params.database_id}`)
         })
         .catch(() => {
           this.loading = false
diff --git a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/schema.vue b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/schema.vue
index 3a821a730b..68d149ed08 100644
--- a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/schema.vue
+++ b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/schema.vue
@@ -200,7 +200,7 @@ export default {
       if (!this.access) {
         return false
       }
-      return this.roles.includes('modify-table-column-semantics') && (this.access.type === 'write_all' || this.table.owner.username === this.user.username)
+      return this.roles.includes('modify-table-column-semantics') && (this.access.type === 'write_all' || this.table.owner.id === this.user.id)
     },
     inputVariant () {
       const runtimeConfig = useRuntimeConfig()
-- 
GitLab