From 99ba7e3a34c43877816be2e6e8227cefd946a312 Mon Sep 17 00:00:00 2001
From: Martin Weise <martin.weise@tuwien.ac.at>
Date: Mon, 6 Jan 2025 19:12:41 +0100
Subject: [PATCH] Increased coverage

---
 .../at/tuwien/endpoints/AccessEndpoint.java   |  45 +-
 .../tuwien/endpoints/IdentifierEndpoint.java  | 139 ++--
 .../at/tuwien/endpoints/ImageEndpoint.java    |   7 +-
 .../at/tuwien/endpoints/TableEndpoint.java    | 103 ++-
 .../at/tuwien/endpoints/UserEndpoint.java     |   2 +-
 .../at/tuwien/endpoints/ViewEndpoint.java     |   8 +-
 .../endpoints/AccessEndpointUnitTest.java     | 168 +++--
 .../endpoints/DatabaseEndpointUnitTest.java   |  46 +-
 .../endpoints/IdentifierEndpointUnitTest.java | 600 +++++++++++++++---
 .../endpoints/ImageEndpointUnitTest.java      |  19 +-
 .../endpoints/MessageEndpointUnitTest.java    |  12 +-
 .../endpoints/TableEndpointUnitTest.java      | 238 +++++--
 .../endpoints/UserEndpointUnitTest.java       | 214 ++++++-
 .../endpoints/ViewEndpointUnitTest.java       |  95 ++-
 .../tuwien/mvc/PrometheusEndpointMvcTest.java |   8 +-
 ...aCiteIdentifierServicePersistenceTest.java |   5 +-
 .../IdentifierServicePersistenceTest.java     |   5 +-
 .../tuwien/service/TableServiceUnitTest.java  |  21 +-
 .../validator/EndpointValidatorUnitTest.java  |   6 +-
 .../at/tuwien/gateway/DataServiceGateway.java |  96 +--
 .../gateway/impl/DataServiceGatewayImpl.java  |   5 +-
 .../at/tuwien/service/IdentifierService.java  |   7 +-
 .../java/at/tuwien/service/TableService.java  |  11 +-
 .../impl/DataCiteIdentifierServiceImpl.java   |   5 +-
 .../service/impl/IdentifierServiceImpl.java   |   5 +-
 .../tuwien/service/impl/TableServiceImpl.java |  18 +-
 .../java/at/tuwien/test/AbstractUnitTest.java |   4 +
 .../main/java/at/tuwien/test/BaseTest.java    |  16 +-
 28 files changed, 1410 insertions(+), 498 deletions(-)

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 812ec7bc21..99e4d2e6b1 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
@@ -99,20 +99,20 @@ 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)) {
+        final User caller = userService.findByUsername(principal.getName());
+        if (!database.getOwner().getId().equals(caller.getId())) {
             log.error("Failed to create access: not owner");
             throw new NotAllowedException("Failed to create access: not owner");
         }
-        final User otherUser = userService.findById(userId);
+        final User user = userService.findById(userId);
         try {
-            accessService.find(database, otherUser);
+            accessService.find(database, user);
             log.error("Failed to create access to user with id {}: already has access", userId);
             throw new NotAllowedException("Failed to create access to user with id " + userId + ": already has access");
         } catch (AccessNotFoundException e) {
             /* ignore */
         }
-        accessService.create(database, otherUser, data.getType());
+        accessService.create(database, user, data.getType());
         return ResponseEntity.accepted()
                 .build();
     }
@@ -159,17 +159,17 @@ public class AccessEndpoint {
                                        @NotNull Principal principal) throws NotAllowedException,
             DataServiceException, DataServiceConnectionException, DatabaseNotFoundException, UserNotFoundException,
             AccessNotFoundException, SearchServiceException, SearchServiceConnectionException {
-        log.debug("endpoint modify database access, databaseId={}, userId={}, access.type={}", databaseId, userId,
-                data.getType());
+        log.debug("endpoint modify database access, databaseId={}, userId={}, access.type={}, principal.name={}",
+                databaseId, userId, data.getType(), principal.getName());
         final Database database = databaseService.findById(databaseId);
-        final User user = userService.findByUsername(principal.getName());
-        if (!database.getOwner().equals(user)) {
+        final User caller = userService.findByUsername(principal.getName());
+        if (!database.getOwner().getId().equals(caller.getId())) {
             log.error("Failed to update access: not owner");
             throw new NotAllowedException("Failed to update access: not owner");
         }
-        final User otherUser = userService.findById(userId);
-        accessService.find(database, otherUser);
-        accessService.update(database, otherUser, data.getType());
+        final User user = userService.findById(userId);
+        accessService.find(database, user);
+        accessService.update(database, user, data.getType());
         return ResponseEntity.accepted()
                 .build();
     }
@@ -204,7 +204,8 @@ public class AccessEndpoint {
             UserNotFoundException, AccessNotFoundException, NotAllowedException {
         log.debug("endpoint get database access, databaseId={}, userId={}, principal.name={}", databaseId, userId,
                 principal.getName());
-        if (!userId.equals(UserUtil.getId(principal))) {
+        final User caller = userService.findByUsername(principal.getName());
+        if (!userId.equals(caller.getId())) {
             if (!UserUtil.hasRole(principal, "check-foreign-database-access")) {
                 log.error("Failed to find access: foreign user");
                 throw new NotAllowedException("Failed to find access: foreign user");
@@ -212,11 +213,9 @@ public class AccessEndpoint {
             log.trace("principal is allowed to check foreign user access");
         }
         final Database database = databaseService.findById(databaseId);
-        final User otherUser = userService.findById(userId);
-        final DatabaseAccess access = accessService.find(database, otherUser);
-        final DatabaseAccessDto dto = databaseMapper.databaseAccessToDatabaseAccessDto(access);
-        log.trace("check access resulted in dto {}", dto);
-        return ResponseEntity.ok(dto);
+        final User user = userService.findById(userId);
+        final DatabaseAccess access = accessService.find(database, user);
+        return ResponseEntity.ok(databaseMapper.databaseAccessToDatabaseAccessDto(access));
     }
 
     @DeleteMapping("/{userId}")
@@ -262,14 +261,14 @@ 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)) {
+        final User caller = userService.findByUsername(principal.getName());
+        if (!database.getOwner().getId().equals(caller.getId())) {
             log.error("Failed to revoke access: not owner");
             throw new NotAllowedException("Failed to revoke access: not owner");
         }
-        final User otherUser = userService.findById(userId);
-        accessService.find(database, otherUser);
-        accessService.delete(database, otherUser);
+        final User user = userService.findById(userId);
+        accessService.find(database, user);
+        accessService.delete(database, user);
         return ResponseEntity.accepted()
                 .build();
     }
diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java
index e84b7161f8..31fcd67305 100644
--- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java
+++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/IdentifierEndpoint.java
@@ -1,6 +1,5 @@
 package at.tuwien.endpoints;
 
-import at.tuwien.api.database.query.QueryDto;
 import at.tuwien.api.error.ApiErrorDto;
 import at.tuwien.api.identifier.*;
 import at.tuwien.api.identifier.ld.LdDatasetDto;
@@ -8,8 +7,6 @@ import at.tuwien.api.user.external.ExternalMetadataDto;
 import at.tuwien.config.EndpointConfig;
 import at.tuwien.entities.database.Database;
 import at.tuwien.entities.database.DatabaseAccess;
-import at.tuwien.entities.database.View;
-import at.tuwien.entities.database.table.Table;
 import at.tuwien.entities.identifier.Identifier;
 import at.tuwien.entities.identifier.IdentifierStatusType;
 import at.tuwien.entities.identifier.IdentifierType;
@@ -137,8 +134,10 @@ public class IdentifierEndpoint {
                         .toList();
                 log.debug("find identifier resulted in identifiers {}", resource2);
                 return ResponseEntity.ok(resource2);
+            default:
+                log.error("accept header {} is not supported", accept);
+                throw new FormatNotAvailableException("Must provide either application/json or application/ld+json headers");
         }
-        throw new FormatNotAvailableException("Must provide either application/json or application/ld+json headers");
     }
 
     @GetMapping(value = "/{identifierId}", produces = {MediaType.APPLICATION_JSON_VALUE, "application/ld+json",
@@ -202,56 +201,54 @@ public class IdentifierEndpoint {
             DataServiceException, DataServiceConnectionException, MalformedException, FormatNotAvailableException,
             QueryNotFoundException {
         log.debug("endpoint find identifier, identifierId={}, accept={}", identifierId, accept);
+        if (accept == null) {
+            accept = "";
+        }
         final Identifier identifier = identifierService.find(identifierId);
-        log.info("Found persistent identifier with id {}", identifier.getId());
-        log.trace("found persistent identifier {}", identifier);
-        if (accept != null) {
-            log.trace("accept header present: {}", accept);
-            switch (accept) {
-                case "application/json":
-                    log.trace("accept header matches json");
-                    final IdentifierDto resource1 = metadataMapper.identifierToIdentifierDto(identifier);
-                    log.debug("find identifier resulted in identifier {}", resource1);
-                    return ResponseEntity.ok(resource1);
-                case "application/ld+json":
-                    log.trace("accept header matches json-ld");
-                    final LdDatasetDto resource2 = metadataMapper.identifierToLdDatasetDto(identifier, endpointConfig.getWebsiteUrl());
-                    log.debug("find identifier resulted in identifier {}", resource2);
-                    return ResponseEntity.ok(resource2);
-                case "text/csv":
-                    log.trace("accept header matches csv");
-                    if (identifier.getType().equals(IdentifierType.DATABASE)) {
-                        log.error("Failed to export dataset: identifier type is database");
-                        throw new FormatNotAvailableException("Failed to export dataset: identifier type is database");
-                    }
-                    final InputStreamResource resource3;
-                    resource3 = identifierService.exportResource(identifier);
-                    log.debug("find identifier resulted in resource {}", resource3);
-                    return ResponseEntity.ok(resource3);
-                case "text/xml":
-                    log.trace("accept header matches xml");
-                    final InputStreamResource resource4 = identifierService.exportMetadata(identifier);
-                    log.debug("find identifier resulted in resource {}", resource4);
-                    return ResponseEntity.ok(resource4);
-            }
-            final Pattern regex = Pattern.compile("text\\/bibliography(; ?style=(apa|ieee|bibtex))?");
-            final Matcher matcher = regex.matcher(accept);
-            if (matcher.find()) {
-                log.trace("accept header matches bibliography");
-                final BibliographyTypeDto style;
-                if (matcher.group(2) != null) {
-                    style = BibliographyTypeDto.valueOf(matcher.group(2).toUpperCase());
-                    log.trace("bibliography style matches {}", style);
-                } else {
-                    style = BibliographyTypeDto.APA;
-                    log.trace("no bibliography style provided, default: {}", style);
+        log.info("Found persistent identifier with id: {}", identifier.getId());
+        switch (accept) {
+            case "application/json":
+                log.trace("accept header matches json");
+                final IdentifierDto resource1 = metadataMapper.identifierToIdentifierDto(identifier);
+                log.debug("find identifier resulted in identifier {}", resource1);
+                return ResponseEntity.ok(resource1);
+            case "application/ld+json":
+                log.trace("accept header matches json-ld");
+                final LdDatasetDto resource2 = metadataMapper.identifierToLdDatasetDto(identifier, endpointConfig.getWebsiteUrl());
+                log.debug("find identifier resulted in identifier {}", resource2);
+                log.debug("find identifier resulted in identifier {}", resource2);
+                return ResponseEntity.ok(resource2);
+            case "text/csv":
+                log.trace("accept header matches csv");
+                if (identifier.getType().equals(IdentifierType.DATABASE)) {
+                    log.error("Failed to export dataset: identifier type is database");
+                    throw new FormatNotAvailableException("Failed to export dataset: identifier type is database");
                 }
-                final String resource = identifierService.exportBibliography(identifier, style);
-                log.debug("find identifier resulted in resource {}", resource);
-                return ResponseEntity.ok(resource);
+                final InputStreamResource resource3;
+                resource3 = identifierService.exportResource(identifier);
+                log.debug("find identifier resulted in resource {}", resource3);
+                return ResponseEntity.ok(resource3);
+            case "text/xml":
+                log.trace("accept header matches xml");
+                final InputStreamResource resource4 = identifierService.exportMetadata(identifier);
+                log.debug("find identifier resulted in resource {}", resource4);
+                return ResponseEntity.ok(resource4);
+        }
+        final Pattern regex = Pattern.compile("text\\/bibliography(; ?style=(apa|ieee|bibtex))?");
+        final Matcher matcher = regex.matcher(accept);
+        if (matcher.find()) {
+            log.trace("accept header matches bibliography");
+            final BibliographyTypeDto style;
+            if (matcher.group(2) != null) {
+                style = BibliographyTypeDto.valueOf(matcher.group(2).toUpperCase());
+                log.trace("bibliography style matches {}", style);
+            } else {
+                style = BibliographyTypeDto.APA;
+                log.trace("no bibliography style provided, default: {}", style);
             }
-        } else {
-            log.trace("no accept header present");
+            final String resource = identifierService.exportBibliography(identifier, style);
+            log.debug("find identifier resulted in resource {}", resource);
+            return ResponseEntity.ok(resource);
         }
         final HttpHeaders headers = new HttpHeaders();
         final String url = metadataMapper.identifierToLocationUrl(endpointConfig.getWebsiteUrl(), identifier);
@@ -351,9 +348,9 @@ public class IdentifierEndpoint {
             throws SearchServiceException, DatabaseNotFoundException, SearchServiceConnectionException,
             MalformedException, DataServiceConnectionException, IdentifierNotFoundException, ExternalServiceException {
         log.debug("endpoint publish identifier, identifierId={}", identifierId);
-        identifierService.find(identifierId);
+        final Identifier identifier = identifierService.find(identifierId);
         return ResponseEntity.status(HttpStatus.CREATED)
-                .body(metadataMapper.identifierToIdentifierDto(identifierService.publish(identifierId)));
+                .body(metadataMapper.identifierToIdentifierDto(identifierService.publish(identifier)));
     }
 
     @PutMapping("/{identifierId}")
@@ -405,10 +402,10 @@ public class IdentifierEndpoint {
         log.debug("endpoint save identifier, identifierId={}, data.id={}, principal.name={}", identifierId,
                 data.getId(), principal.getName());
         final Database database = databaseService.findById(data.getDatabaseId());
-        final User user = userService.findByUsername(principal.getName());
+        final User caller = userService.findByUsername(principal.getName());
         final Identifier identifier = identifierService.find(identifierId);
         /* check owner */
-        if (!identifier.getOwner().equals(user) && !UserUtil.hasRole(principal, "create-foreign-identifier")) {
+        if (!identifier.getOwner().getId().equals(caller.getId()) && !UserUtil.hasRole(principal, "create-foreign-identifier")) {
             log.error("Failed to save identifier: foreign user");
             throw new NotAllowedException("Failed to save identifier: foreign user");
         }
@@ -418,9 +415,8 @@ public class IdentifierEndpoint {
             throw new MalformedException("Failed to save identifier: publication date is invalid");
         }
         /* check access */
-        DatabaseAccess access = null;
         try {
-            access = accessService.find(database, user);
+            final DatabaseAccess access = accessService.find(database, caller);
             log.trace("found access: {}", access);
         } catch (AccessNotFoundException e) {
             if (!UserUtil.hasRole(principal, "create-foreign-identifier")) {
@@ -434,22 +430,12 @@ public class IdentifierEndpoint {
                     log.error("Failed to save view identifier: only parameters database_id & view_id must be present");
                     throw new MalformedException("Failed to save view identifier: only parameters database_id & view_id must be present");
                 }
-                final View view = viewService.findById(database, data.getViewId());
-                if (!endpointValidator.validateOnlyMineOrReadAccessOrHasRole(view.getOwner(), principal, access, "create-foreign-identifier")) {
-                    log.error("Failed to save view identifier: insufficient access or role");
-                    throw new MalformedException("Failed to save view identifier: insufficient access or role");
-                }
             }
             case TABLE -> {
                 if (data.getQueryId() != null || data.getViewId() != null || data.getTableId() == null) {
                     log.error("Failed to save table identifier: only parameters database_id & table_id must be present");
                     throw new MalformedException("Failed to save table identifier: only parameters database_id & table_id must be present");
                 }
-                final Table table = tableService.findById(data.getDatabaseId(), data.getTableId());
-                if (!endpointValidator.validateOnlyMineOrReadAccessOrHasRole(table.getOwner(), principal, access, "create-foreign-identifier")) {
-                    log.error("Failed to save table identifier: insufficient access or role");
-                    throw new MalformedException("Failed to save table identifier: insufficient access or role");
-                }
             }
             case SUBSET -> {
                 if (data.getQueryId() == null || data.getViewId() != null || data.getTableId() != null) {
@@ -457,26 +443,16 @@ public class IdentifierEndpoint {
                     throw new MalformedException("Failed to save subset identifier: only parameters database_id & query_id must be present");
                 }
                 log.debug("retrieving subset from data service: data.database_id={}, data.query_id={}", data.getDatabaseId(), data.getQueryId());
-                final QueryDto query = dataServiceGateway.findQuery(data.getDatabaseId(), data.getQueryId());
-                final User queryCreator = userService.findById(query.getOwner().getId());
-                if (!endpointValidator.validateOnlyMineOrReadAccessOrHasRole(queryCreator, principal, access, "create-foreign-identifier")) {
-                    log.error("Failed to create subset identifier: insufficient access or role");
-                    throw new MalformedException("Failed to create subset identifier: insufficient access or role");
-                }
             }
             case DATABASE -> {
                 if (data.getQueryId() != null || data.getViewId() != null || data.getTableId() != null) {
                     log.error("Failed to save database identifier: only parameters database_id must be present");
                     throw new MalformedException("Failed to save database identifier: only parameters database_id must be present");
                 }
-                if (!endpointValidator.validateOnlyMineOrReadAccessOrHasRole(database.getOwner(), principal, access, "create-foreign-identifier")) {
-                    log.error("Failed to save database identifier: insufficient access or role");
-                    throw new MalformedException("Failed to save database identifier: insufficient access or role");
-                }
             }
         }
         return ResponseEntity.accepted()
-                .body(metadataMapper.identifierToIdentifierDto(identifierService.save(database, user, data)));
+                .body(metadataMapper.identifierToIdentifierDto(identifierService.save(database, caller, data)));
     }
 
     @PostMapping
@@ -525,18 +501,17 @@ public class IdentifierEndpoint {
             IdentifierNotFoundException, ViewNotFoundException, ExternalServiceException {
         log.debug("endpoint create identifier, data.databaseId={}", data.getDatabaseId());
         final Database database = databaseService.findById(data.getDatabaseId());
-        final User user = userService.findByUsername(principal.getName());
+        final User caller = userService.findByUsername(principal.getName());
         /* check access */
         try {
-            final DatabaseAccess access = accessService.find(database, user);
-            log.trace("found access: {}", access.getType());
+            accessService.find(database, caller);
         } catch (AccessNotFoundException e) {
             if (!UserUtil.hasRole(principal, "create-foreign-identifier")) {
                 log.error("Failed to create identifier: insufficient role");
                 throw new NotAllowedException("Failed to create identifier: insufficient role");
             }
         }
-        final Identifier identifier = identifierService.create(database, user, data);
+        final Identifier identifier = identifierService.create(database, caller, data);
         return ResponseEntity.status(HttpStatus.CREATED)
                 .body(metadataMapper.identifierToIdentifierDto(identifier));
     }
diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ImageEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ImageEndpoint.java
index 088dc2981b..2a9dd56870 100644
--- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ImageEndpoint.java
+++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/ImageEndpoint.java
@@ -31,7 +31,6 @@ import org.springframework.web.bind.annotation.*;
 
 import java.security.Principal;
 import java.util.List;
-import java.util.stream.Collectors;
 
 @Log4j2
 @RestController
@@ -97,11 +96,7 @@ public class ImageEndpoint {
     public ResponseEntity<ImageDto> create(@Valid @RequestBody ImageCreateDto data,
                                            @NotNull Principal principal) throws ImageAlreadyExistsException,
             ImageInvalidException {
-        log.debug("endpoint create image, data={}", data);
-        if (data.getDefaultPort() == null) {
-            log.error("Failed to create image, default port is null");
-            throw new ImageInvalidException("Failed to create image, default port is null");
-        }
+        log.debug("endpoint create image, data={}, principal.name={}", data, principal.getName());
         final ContainerImage image = imageService.create(data, principal);
         final ImageDto dto = metadataMapper.containerImageToImageDto(image);
         log.trace("create image resulted in image {}", dto);
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 e04cba85ce..822581bb99 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
@@ -112,7 +112,7 @@ public class TableEndpoint {
     @PreAuthorize("hasAuthority('table-semantic-analyse')")
     @Observed(name = "dbrepo_semantic_table_analyse")
     @Operation(summary = "Suggest semantics",
-            description = "Suggests semantic concepts for a table. Requires role `table-semantic-analyse`.",
+            description = "Suggests semantic concepts for a table. This action can only be performed by the table owner. Requires role `table-semantic-analyse`.",
             security = {@SecurityRequirement(name = "bearerAuth"), @SecurityRequirement(name = "basicAuth")})
     @ApiResponses(value = {
             @ApiResponse(responseCode = "200",
@@ -125,6 +125,11 @@ public class TableEndpoint {
                     content = {@Content(
                             mediaType = "application/json",
                             schema = @Schema(implementation = ApiErrorDto.class))}),
+            @ApiResponse(responseCode = "403",
+                    description = "Not the table owner.",
+                    content = {@Content(
+                            mediaType = "application/json",
+                            schema = @Schema(implementation = ApiErrorDto.class))}),
             @ApiResponse(responseCode = "404",
                     description = "Failed to find database/table in metadata database",
                     content = {@Content(
@@ -142,13 +147,19 @@ public class TableEndpoint {
                             schema = @Schema(implementation = ApiErrorDto.class))}),
     })
     public ResponseEntity<List<EntityDto>> analyseTable(@NotNull @PathVariable("databaseId") Long databaseId,
-                                                        @NotNull @PathVariable("tableId") Long tableId)
-            throws MalformedException, TableNotFoundException, DatabaseNotFoundException {
-        log.debug("endpoint analyse table semantics, databaseId={}, tableId={}", databaseId, tableId);
-        final Table table = tableService.findById(databaseId, tableId);
-        final List<EntityDto> dtos = entityService.suggestByTable(table);
+                                                        @NotNull @PathVariable("tableId") Long tableId,
+                                                        @NotNull Principal principal)
+            throws MalformedException, TableNotFoundException, DatabaseNotFoundException, NotAllowedException {
+        log.debug("endpoint analyse table semantics, databaseId={}, tableId={}, principal.name={}", databaseId, tableId,
+                principal);
+        final Database database = databaseService.findById(databaseId);
+        final Table table = tableService.findById(database, tableId);
+        if (!table.getOwner().getUsername().equals(principal.getName())) {
+            log.error("Failed to analyse table semantics: not owner");
+            throw new NotAllowedException("Failed to analyse table semantics: not owner");
+        }
         return ResponseEntity.ok()
-                .body(dtos);
+                .body(entityService.suggestByTable(table));
     }
 
     @PutMapping("/{tableId}/statistic")
@@ -156,18 +167,23 @@ public class TableEndpoint {
     @PreAuthorize("hasAuthority('update-table-statistic')")
     @Observed(name = "dbrepo_statistic_table_update")
     @Operation(summary = "Update statistics",
-            description = "Updates basic statistical properties (min, max, mean, median, std.dev) for numerical columns in a table with id. Requires role `update-table-statistic`.",
+            description = "Updates basic statistical properties (min, max, mean, median, std.dev) for numerical columns in a table with id. This action can only be performed by the table owner. Requires role `update-table-statistic`.",
             security = {@SecurityRequirement(name = "bearerAuth"), @SecurityRequirement(name = "basicAuth")})
     @ApiResponses(value = {
             @ApiResponse(responseCode = "202",
                     description = "Updated table statistics successfully"),
-            @ApiResponse(responseCode = "404",
-                    description = "Failed to find database/table in metadata database",
+            @ApiResponse(responseCode = "400",
+                    description = "Failed to map column statistic to known columns",
                     content = {@Content(
                             mediaType = "application/json",
                             schema = @Schema(implementation = ApiErrorDto.class))}),
-            @ApiResponse(responseCode = "400",
-                    description = "Failed to map column statistic to known columns",
+            @ApiResponse(responseCode = "403",
+                    description = "Not the owner",
+                    content = {@Content(
+                            mediaType = "application/json",
+                            schema = @Schema(implementation = ApiErrorDto.class))}),
+            @ApiResponse(responseCode = "404",
+                    description = "Failed to find database/table in metadata database",
                     content = {@Content(
                             mediaType = "application/json",
                             schema = @Schema(implementation = ApiErrorDto.class))}),
@@ -183,11 +199,18 @@ public class TableEndpoint {
                             schema = @Schema(implementation = ApiErrorDto.class))}),
     })
     public ResponseEntity<Void> updateStatistic(@NotNull @PathVariable("databaseId") Long databaseId,
-                                                @NotNull @PathVariable("tableId") Long tableId)
-            throws TableNotFoundException, DatabaseNotFoundException, SearchServiceException,
+                                                @NotNull @PathVariable("tableId") Long tableId,
+                                                @NotNull Principal principal)
+            throws TableNotFoundException, DatabaseNotFoundException, SearchServiceException, NotAllowedException,
             SearchServiceConnectionException, MalformedException, DataServiceException, DataServiceConnectionException {
-        log.debug("endpoint update table statistics, databaseId={}, tableId={}", databaseId, tableId);
-        final Table table = tableService.findById(databaseId, tableId);
+        log.debug("endpoint update table statistics, databaseId={}, tableId={}, principal.name={}", databaseId, tableId,
+                principal.getName());
+        final Database database = databaseService.findById(databaseId);
+        final Table table = tableService.findById(database, tableId);
+        if (!table.getOwner().getUsername().equals(principal.getName())) {
+            log.error("Failed to update table statistics: not owner");
+            throw new NotAllowedException("Failed to update table statistics: not owner");
+        }
         tableService.updateStatistics(table);
         return ResponseEntity.accepted()
                 .build();
@@ -232,17 +255,19 @@ public class TableEndpoint {
                             mediaType = "application/json",
                             schema = @Schema(implementation = ApiErrorDto.class))}),
     })
-    public ResponseEntity<ColumnDto> update(@NotNull @PathVariable("databaseId") Long databaseId,
-                                            @NotNull @PathVariable("tableId") Long tableId,
-                                            @NotNull @PathVariable("columnId") Long columnId,
-                                            @NotNull @Valid @RequestBody ColumnSemanticsUpdateDto updateDto,
-                                            @NotNull Principal principal) throws NotAllowedException,
+    public ResponseEntity<ColumnDto> updateColumn(@NotNull @PathVariable("databaseId") Long databaseId,
+                                                  @NotNull @PathVariable("tableId") Long tableId,
+                                                  @NotNull @PathVariable("columnId") Long columnId,
+                                                  @NotNull @Valid @RequestBody ColumnSemanticsUpdateDto updateDto,
+                                                  @NotNull Principal principal) throws NotAllowedException,
             MalformedException, DataServiceException, DataServiceConnectionException, UserNotFoundException,
             TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException, SearchServiceException,
             SearchServiceConnectionException, OntologyNotFoundException, SemanticEntityNotFoundException {
-        log.debug("endpoint update table, databaseId={}, tableId={}, columnId={}", databaseId, tableId, columnId);
+        log.debug("endpoint update table, databaseId={}, tableId={}, columnId={}, principal.name={}", databaseId,
+                tableId, columnId, principal.getName());
+        final Database database = databaseService.findById(databaseId);
         final User user = userService.findByUsername(principal.getName());
-        final Table table = tableService.findById(databaseId, tableId);
+        final Table table = tableService.findById(database, tableId);
         if (!UserUtil.hasRole(principal, "modify-foreign-table-column-semantics")) {
             endpointValidator.validateOnlyAccess(table.getDatabase(), principal, true);
             endpointValidator.validateOnlyOwnerOrWriteAll(table, user);
@@ -287,10 +312,13 @@ public class TableEndpoint {
     })
     public ResponseEntity<List<TableColumnEntityDto>> analyseTableColumn(@NotNull @PathVariable("databaseId") Long databaseId,
                                                                          @NotNull @PathVariable("tableId") Long tableId,
-                                                                         @NotNull @PathVariable("columnId") Long columnId)
+                                                                         @NotNull @PathVariable("columnId") Long columnId,
+                                                                         @NotNull Principal principal)
             throws MalformedException, TableNotFoundException, DatabaseNotFoundException {
-        log.debug("endpoint analyse table column semantics, databaseId={}, tableId={}, columnId={}", databaseId, tableId, columnId);
-        final Table table = tableService.findById(databaseId, tableId);
+        log.debug("endpoint analyse table column semantics, databaseId={}, tableId={}, columnId={}, principal.name={}",
+                databaseId, tableId, columnId, principal.getName());
+        final Database database = databaseService.findById(databaseId);
+        final Table table = tableService.findById(database, tableId);
         TableColumn column = tableService.findColumnById(table, columnId);
         final List<TableColumnEntityDto> dtos = entityService.suggestByColumn(column);
         return ResponseEntity.ok()
@@ -347,7 +375,8 @@ public class TableEndpoint {
             DataServiceException, DataServiceConnectionException, DatabaseNotFoundException, UserNotFoundException,
             AccessNotFoundException, TableNotFoundException, TableExistsException, SearchServiceException,
             SearchServiceConnectionException, OntologyNotFoundException, SemanticEntityNotFoundException {
-        log.debug("endpoint create table, databaseId={}, data.name={}", databaseId, data.getName());
+        log.debug("endpoint create table, databaseId={}, data.name={}, principal.name={}", databaseId, data.getName(),
+                principal.getName());
         final Database database = databaseService.findById(databaseId);
         endpointValidator.validateOnlyAccess(database, principal, true);
         endpointValidator.validateColumnCreateConstraints(data);
@@ -403,10 +432,11 @@ public class TableEndpoint {
                                            @NotNull Principal principal) throws NotAllowedException,
             DataServiceException, DataServiceConnectionException, DatabaseNotFoundException, TableNotFoundException,
             SearchServiceException, SearchServiceConnectionException {
-        log.debug("endpoint update table, databaseId={}, data.is_public={}, data.is_schema_public={}", databaseId,
-                data.getIsPublic(), data.getIsSchemaPublic());
-        final Table table = tableService.findById(databaseId, tableId);
-        if (!table.getOwner().equals(principal)) {
+        log.debug("endpoint update table, databaseId={}, data.is_public={}, data.is_schema_public={}, principal.name={}",
+                databaseId, data.getIsPublic(), data.getIsSchemaPublic(), principal.getName());
+        final Database database = databaseService.findById(databaseId);
+        final Table table = tableService.findById(database, tableId);
+        if (!table.getOwner().getUsername().equals(principal.getName())) {
             log.error("Failed to update table: not owner");
             throw new NotAllowedException("Failed to update table: not owner");
         }
@@ -462,7 +492,8 @@ public class TableEndpoint {
                                              Principal principal) throws DataServiceException,
             DataServiceConnectionException, TableNotFoundException, DatabaseNotFoundException, QueueNotFoundException {
         log.debug("endpoint find table, databaseId={}, tableId={}", databaseId, tableId);
-        final Table table = tableService.findById(databaseId, tableId);
+        final Database database = databaseService.findById(databaseId);
+        final Table table = tableService.findById(database, tableId);
         boolean hasAccess = UserUtil.isSystem(principal);
         boolean isOwner = false;
         try {
@@ -470,7 +501,7 @@ public class TableEndpoint {
                 final User user = userService.findByUsername(principal.getName());
                 accessService.find(table.getDatabase(), user);
                 hasAccess = true;
-                isOwner = table.getOwner().equals(user);
+                isOwner = table.getOwner().getId().equals(user.getId());
             }
         } catch (UserNotFoundException | AccessNotFoundException e) {
             /* ignore */
@@ -537,8 +568,10 @@ public class TableEndpoint {
                                        @NotNull Principal principal) throws NotAllowedException,
             DataServiceException, DataServiceConnectionException, TableNotFoundException, DatabaseNotFoundException,
             SearchServiceException, SearchServiceConnectionException {
-        log.debug("endpoint delete table, databaseId={}, tableId={}", databaseId, tableId);
-        final Table table = tableService.findById(databaseId, tableId);
+        log.debug("endpoint delete table, databaseId={}, tableId={}, principal.name={}", databaseId, tableId,
+                principal.getName());
+        final Database database = databaseService.findById(databaseId);
+        final Table table = tableService.findById(database, tableId);
         /* roles */
         if (!table.getOwner().getUsername().equals(principal.getName()) && !UserUtil.hasRole(principal, "delete-foreign-table")) {
             log.error("Failed to delete table: not owned by current user");
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 fe2d0a343a..151169c244 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
@@ -205,7 +205,7 @@ public class UserEndpoint {
                     .password(data.getPassword())
                     .build();
             final at.tuwien.api.keycloak.UserDto user = authenticationService.findByUsername(data.getUsername());
-            if (user.getAttributes().getLdapId().length != 1) {
+            if (user.getAttributes().getLdapId() == null || user.getAttributes().getLdapId().length != 1) {
                 log.error("Failed to map ldap id for user with username: {}", data.getUsername());
                 throw new UserNotFoundException("Failed to map ldap id");
             }
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 359c1be796..012206289e 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
@@ -141,14 +141,14 @@ public class ViewEndpoint {
             UserNotFoundException, SearchServiceException, SearchServiceConnectionException {
         log.debug("endpoint create view, databaseId={}, data={}", databaseId, data);
         final Database database = databaseService.findById(databaseId);
-        if (!database.getOwner().equals(principal)) {
+        final User caller = userService.findByUsername(principal.getName());
+        if (!database.getOwner().getId().equals(caller.getId())) {
             log.error("Failed to create view: not the database owner");
             throw new NotAllowedException("Failed to create view: not the database owner");
         }
-        final User user = userService.findByUsername(principal.getName());
         log.trace("create view for database {}", database);
         final View view;
-        view = viewService.create(database, user, data);
+        view = viewService.create(database, caller, data);
         final ViewBriefDto dto = metadataMapper.viewToViewBriefDto(view);
         return ResponseEntity.status(HttpStatus.CREATED)
                 .body(dto);
@@ -311,7 +311,7 @@ public class ViewEndpoint {
         log.debug("endpoint update view, databaseId={}, viewId={}", databaseId, viewId);
         final Database database = databaseService.findById(databaseId);
         final View view = viewService.findById(database, viewId);
-        if (!database.getOwner().equals(principal) && !view.getOwner().equals(principal)) {
+        if (!database.getOwner().getUsername().equals(principal.getName()) && !view.getOwner().getUsername().equals(principal.getName())) {
             log.error("Failed to update view: not the database- or view owner");
             throw new NotAllowedException("Failed to update view: not the database- or view owner");
         }
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/AccessEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/AccessEndpointUnitTest.java
index 7c6061ed1e..f4a700e859 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/AccessEndpointUnitTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/AccessEndpointUnitTest.java
@@ -7,9 +7,9 @@ import at.tuwien.entities.database.DatabaseAccess;
 import at.tuwien.entities.user.User;
 import at.tuwien.exception.*;
 import at.tuwien.mapper.MetadataMapper;
-import at.tuwien.repository.DatabaseRepository;
-import at.tuwien.repository.UserRepository;
 import at.tuwien.service.AccessService;
+import at.tuwien.service.DatabaseService;
+import at.tuwien.service.UserService;
 import at.tuwien.test.AbstractUnitTest;
 import lombok.extern.log4j.Log4j2;
 import org.junit.jupiter.api.BeforeEach;
@@ -20,12 +20,14 @@ 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.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
 import org.springframework.security.test.context.support.WithAnonymousUser;
 import org.springframework.security.test.context.support.WithMockUser;
 import org.springframework.test.context.junit.jupiter.SpringExtension;
 
 import java.security.Principal;
-import java.util.Optional;
+import java.util.List;
 import java.util.UUID;
 
 import static org.junit.jupiter.api.Assertions.*;
@@ -41,10 +43,10 @@ public class AccessEndpointUnitTest extends AbstractUnitTest {
     private AccessService accessService;
 
     @MockBean
-    private DatabaseRepository databaseRepository;
+    private DatabaseService databaseService;
 
     @MockBean
-    private UserRepository userRepository;
+    private UserService userService;
 
     @Autowired
     private AccessEndpoint accessEndpoint;
@@ -63,7 +65,7 @@ public class AccessEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> {
-            generic_create(null, null, null, null);
+            generic_create(null, null, null, null, null);
         });
     }
 
@@ -73,7 +75,37 @@ public class AccessEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> {
-            generic_create(USER_2_PRINCIPAL, USER_2, USER_4_ID, USER_4);
+            generic_create(USER_2_PRINCIPAL, USER_2, USER_4_ID, USER_4, null);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_4_USERNAME)
+    public void create_noRole_fails() {
+
+        /* test */
+        assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> {
+            generic_create(USER_2_PRINCIPAL, USER_2, USER_4_ID, USER_4, null);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_4_USERNAME, authorities = {"create-database-access"})
+    public void create_notOwner_fails() {
+
+        /* test */
+        assertThrows(NotAllowedException.class, () -> {
+            generic_create(USER_2_PRINCIPAL, USER_2, USER_4_ID, USER_4, null);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_1_USERNAME, authorities = {"create-database-access"})
+    public void create_alreadyAccess_fails() {
+
+        /* test */
+        assertThrows(NotAllowedException.class, () -> {
+            generic_create(USER_1_PRINCIPAL, USER_1, USER_2_ID, USER_2, DATABASE_1_USER_2_READ_ACCESS);
         });
     }
 
@@ -88,7 +120,7 @@ public class AccessEndpointUnitTest extends AbstractUnitTest {
                 .thenReturn(DATABASE_1_USER_1_READ_ACCESS);
 
         /* test */
-        generic_create(USER_1_PRINCIPAL, USER_1, USER_2_ID, USER_2);
+        generic_create(USER_1_PRINCIPAL, USER_1, USER_2_ID, USER_2, null);
     }
 
     @Test
@@ -97,7 +129,7 @@ public class AccessEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(AccessNotFoundException.class, () -> {
-            generic_find(DATABASE_1_ID, DATABASE_1, null, USER_2_PRINCIPAL, USER_2_ID, USER_2);
+            generic_find(DATABASE_1_ID, DATABASE_1, null, USER_2_PRINCIPAL, USER_2, USER_2_ID, USER_2);
         });
     }
 
@@ -107,7 +139,7 @@ public class AccessEndpointUnitTest extends AbstractUnitTest {
             AccessNotFoundException, NotAllowedException {
 
         /* test */
-        generic_find(DATABASE_1_ID, DATABASE_1, DATABASE_1_USER_1_READ_ACCESS, USER_1_PRINCIPAL, USER_1_ID, USER_1);
+        generic_find(DATABASE_1_ID, DATABASE_1, DATABASE_1_USER_1_READ_ACCESS, USER_1_PRINCIPAL, USER_1, USER_1_ID, USER_1);
     }
 
     @Test
@@ -116,17 +148,20 @@ public class AccessEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            generic_find(DATABASE_1_ID, DATABASE_1, DATABASE_1_USER_1_READ_ACCESS, USER_1_PRINCIPAL, USER_2_ID, USER_2);
+            generic_find(DATABASE_1_ID, DATABASE_1, DATABASE_1_USER_1_READ_ACCESS, USER_1_PRINCIPAL, USER_1, USER_2_ID, USER_2);
         });
     }
 
     @Test
-    @WithMockUser(username = USER_1_USERNAME, authorities = {"check-foreign-database-access"})
+    @WithMockUser(username = USER_1_USERNAME, authorities = {"check-database-access", "check-foreign-database-access"})
     public void find_hasRoleHasAccessForeign_succeeds() throws UserNotFoundException, NotAllowedException,
             DatabaseNotFoundException, AccessNotFoundException {
+        final Principal principal = new UsernamePasswordAuthenticationToken(USER_1_DETAILS, USER_1_PASSWORD, List.of(
+                new SimpleGrantedAuthority("check-database-access"),
+                new SimpleGrantedAuthority("check-foreign-database-access")));
 
         /* test */
-        generic_find(DATABASE_1_ID, DATABASE_1, DATABASE_1_USER_1_READ_ACCESS, USER_1_PRINCIPAL, USER_1_ID, USER_1);
+        generic_find(DATABASE_1_ID, DATABASE_1, DATABASE_1_USER_2_READ_ACCESS, principal, USER_1, USER_2_ID, USER_2);
     }
 
     @Test
@@ -159,6 +194,16 @@ public class AccessEndpointUnitTest extends AbstractUnitTest {
         });
     }
 
+    @Test
+    @WithMockUser(username = USER_4_USERNAME, authorities = {"update-database-access"})
+    public void update_notOwner_fails() {
+
+        /* test */
+        assertThrows(NotAllowedException.class, () -> {
+            generic_update(USER_4_PRINCIPAL, USER_4, USER_1_ID, USER_1, null);
+        });
+    }
+
     @Test
     @WithMockUser(username = USER_1_USERNAME, authorities = {"update-database-access"})
     public void update_succeeds() throws NotAllowedException, DataServiceException, DataServiceConnectionException,
@@ -194,6 +239,16 @@ public class AccessEndpointUnitTest extends AbstractUnitTest {
         });
     }
 
+    @Test
+    @WithMockUser(username = USER_4_USERNAME, authorities = {"delete-database-access"})
+    public void revoke_notOwner_fails() {
+
+        /* test */
+        assertThrows(NotAllowedException.class, () -> {
+            generic_revoke(USER_4_PRINCIPAL, USER_4, USER_1_ID, USER_1);
+        });
+    }
+
     @Test
     @WithMockUser(username = USER_1_USERNAME, authorities = {"delete-database-access"})
     public void revoke_succeeds() throws NotAllowedException, DataServiceException, DataServiceConnectionException,
@@ -213,30 +268,37 @@ public class AccessEndpointUnitTest extends AbstractUnitTest {
     /* ## GENERIC TEST CASES                                                                            ## */
     /* ################################################################################################### */
 
-    protected void generic_create(Principal principal, User principalUser, UUID userId, User user)
+    protected void generic_create(Principal principal, User principalUser, UUID userId, User user, DatabaseAccess access)
             throws NotAllowedException, DataServiceException, DataServiceConnectionException, UserNotFoundException,
             DatabaseNotFoundException, AccessNotFoundException, SearchServiceException,
             SearchServiceConnectionException {
 
         /* mock */
-        when(databaseRepository.findById(DATABASE_1_ID))
-                .thenReturn(Optional.of(DATABASE_1));
-        doThrow(AccessNotFoundException.class)
-                .when(accessService)
-                .find(DATABASE_1, user);
+        when(databaseService.findById(DATABASE_1_ID))
+                .thenReturn(DATABASE_1);
+        if (access != null) {
+            when(accessService.find(DATABASE_1, user))
+                    .thenReturn(access);
+        } else {
+            doThrow(AccessNotFoundException.class)
+                    .when(accessService)
+                    .find(DATABASE_1, user);
+        }
         if (principalUser != null) {
-            when(userRepository.findByUsername(principal.getName()))
-                    .thenReturn(Optional.of(principalUser));
+            when(userService.findByUsername(principal.getName()))
+                    .thenReturn(principalUser);
         } else {
-            when(userRepository.findByUsername(anyString()))
-                    .thenReturn(Optional.empty());
+            doThrow(UserNotFoundException.class)
+                    .when(userService)
+                    .findByUsername(anyString());
         }
         if (user != null) {
-            when(userRepository.findById(userId))
-                    .thenReturn(Optional.of(user));
+            when(userService.findById(userId))
+                    .thenReturn(user);
         } else {
-            when(userRepository.findById(any(UUID.class)))
-                    .thenReturn(Optional.empty());
+            doThrow(UserNotFoundException.class)
+                    .when(userService)
+                    .findById(any(UUID.class));
         }
 
         /* test */
@@ -246,14 +308,16 @@ public class AccessEndpointUnitTest extends AbstractUnitTest {
     }
 
     protected void generic_find(Long databaseId, Database database, DatabaseAccess access, Principal principal,
-                                UUID userId, User user) throws UserNotFoundException, DatabaseNotFoundException,
-            AccessNotFoundException, NotAllowedException {
+                                User caller, UUID userId, User user) throws UserNotFoundException,
+            DatabaseNotFoundException, AccessNotFoundException, NotAllowedException {
 
         /* mock */
-        when(databaseRepository.findById(databaseId))
-                .thenReturn(Optional.of(database));
-        when(userRepository.findById(userId))
-                .thenReturn(Optional.of(user));
+        when(userService.findByUsername(principal.getName()))
+                .thenReturn(caller);
+        when(databaseService.findById(databaseId))
+                .thenReturn(database);
+        when(userService.findById(userId))
+                .thenReturn(user);
         if (access != null) {
             log.trace("mock access {} for user with id {} for database with id {}", access.getType(), userId, databaseId);
             when(accessService.find(database, user))
@@ -264,10 +328,6 @@ public class AccessEndpointUnitTest extends AbstractUnitTest {
                     .when(accessService)
                     .find(database, user);
         }
-        if (principal != null) {
-            when(userRepository.findByUsername(principal.getName()))
-                    .thenReturn(Optional.of(user));
-        }
 
         /* test */
         final ResponseEntity<DatabaseAccessDto> response = accessEndpoint.find(databaseId, userId, principal);
@@ -287,8 +347,8 @@ public class AccessEndpointUnitTest extends AbstractUnitTest {
             SearchServiceException, SearchServiceConnectionException {
 
         /* mock */
-        when(databaseRepository.findById(DATABASE_1_ID))
-                .thenReturn(Optional.of(DATABASE_1));
+        when(databaseService.findById(DATABASE_1_ID))
+                .thenReturn(DATABASE_1);
         if (access != null) {
             log.trace("mock access {} for user with id {} for database with id {}", access.getType(), userId, DATABASE_1_ID);
             when(accessService.find(DATABASE_1, user))
@@ -300,18 +360,20 @@ public class AccessEndpointUnitTest extends AbstractUnitTest {
                     .find(DATABASE_1, user);
         }
         if (userId != null) {
-            when(userRepository.findById(userId))
-                    .thenReturn(Optional.of(user));
+            when(userService.findById(userId))
+                    .thenReturn(user);
         } else {
-            when(userRepository.findById(any(UUID.class)))
-                    .thenReturn(Optional.empty());
+            doThrow(UserNotFoundException.class)
+                    .when(userService)
+                    .findById(any(UUID.class));
         }
         if (principal != null) {
-            when(userRepository.findByUsername(principal.getName()))
-                    .thenReturn(Optional.of(principalUser));
+            when(userService.findByUsername(principal.getName()))
+                    .thenReturn(principalUser);
         } else {
-            when(userRepository.findByUsername(anyString()))
-                    .thenReturn(Optional.empty());
+            doThrow(UserNotFoundException.class)
+                    .when(userService)
+                    .findByUsername(anyString());
         }
 
         /* test */
@@ -326,14 +388,14 @@ public class AccessEndpointUnitTest extends AbstractUnitTest {
             SearchServiceConnectionException {
 
         /* mock */
-        when(databaseRepository.findById(DATABASE_1_ID))
-                .thenReturn(Optional.of(DATABASE_1));
+        when(databaseService.findById(DATABASE_1_ID))
+                .thenReturn(DATABASE_1);
         if (principal != null) {
-            when(userRepository.findByUsername(principal.getName()))
-                    .thenReturn(Optional.of(principalUser));
+            when(userService.findByUsername(principal.getName()))
+                    .thenReturn(principalUser);
         }
-        when(userRepository.findById(userId))
-                .thenReturn(Optional.of(user));
+        when(userService.findById(userId))
+                .thenReturn(user);
 
         /* test */
         final ResponseEntity<?> response = accessEndpoint.revoke(DATABASE_1_ID, userId, principal);
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 861863a315..b788fd206b 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
@@ -386,7 +386,7 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
     }
 
     @Test
-    @WithMockUser(username = USER_4_USERNAME)
+    @WithMockUser(username = USER_3_USERNAME)
     public void modifyImage_noRole_fails() {
         final DatabaseModifyImageDto request = DatabaseModifyImageDto.builder()
                 .key("s3key_here")
@@ -394,13 +394,32 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(AccessDeniedException.class, () -> {
-            databaseEndpoint.modifyImage(DATABASE_3_ID, request, USER_4_PRINCIPAL);
+            databaseEndpoint.modifyImage(DATABASE_3_ID, request, USER_3_PRINCIPAL);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_2_USERNAME, authorities = {"modify-database-image"})
+    public void modifyImage_notOwner_fails() throws DatabaseNotFoundException, UserNotFoundException {
+        final DatabaseModifyImageDto request = DatabaseModifyImageDto.builder()
+                .key("s3key_here")
+                .build();
+
+        /* mock */
+        when(databaseService.findById(DATABASE_3_ID))
+                .thenReturn(DATABASE_3);
+        when(userService.findByUsername(USER_2_USERNAME))
+                .thenReturn(USER_2);
+
+        /* test */
+        assertThrows(NotAllowedException.class, () -> {
+            databaseEndpoint.modifyImage(DATABASE_3_ID, request, USER_2_PRINCIPAL);
         });
     }
 
     @Test
     @WithMockUser(username = USER_1_USERNAME, authorities = {"modify-database-image"})
-    public void modifyImage_hasRole_succeeds() throws NotAllowedException, UserNotFoundException,
+    public void modifyImage_succeeds() throws NotAllowedException, UserNotFoundException,
             DatabaseNotFoundException, SearchServiceException, SearchServiceConnectionException,
             StorageUnavailableException, StorageNotFoundException {
         final DatabaseModifyImageDto request = DatabaseModifyImageDto.builder()
@@ -413,7 +432,26 @@ public class DatabaseEndpointUnitTest extends AbstractUnitTest {
         when(userService.findByUsername(USER_1_USERNAME))
                 .thenReturn(USER_1);
         when(storageService.getBytes(request.getKey()))
-                .thenReturn(new byte[]{});
+                .thenReturn(new byte[]{1, 2, 3, 4, 5});
+
+        /* test */
+        databaseEndpoint.modifyImage(DATABASE_1_ID, request, USER_1_PRINCIPAL);
+    }
+
+    @Test
+    @WithMockUser(username = USER_1_USERNAME, authorities = {"modify-database-image"})
+    public void modifyImage_empty_succeeds() throws NotAllowedException, UserNotFoundException,
+            DatabaseNotFoundException, SearchServiceException, SearchServiceConnectionException,
+            StorageUnavailableException, StorageNotFoundException {
+        final DatabaseModifyImageDto request = DatabaseModifyImageDto.builder()
+                .key(null)
+                .build();
+
+        /* mock */
+        when(databaseService.findById(DATABASE_1_ID))
+                .thenReturn(DATABASE_1);
+        when(userService.findByUsername(USER_1_USERNAME))
+                .thenReturn(USER_1);
 
         /* test */
         databaseEndpoint.modifyImage(DATABASE_1_ID, request, USER_1_PRINCIPAL);
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/IdentifierEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/IdentifierEndpointUnitTest.java
index ba3debd79d..243f0e9aed 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/IdentifierEndpointUnitTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/IdentifierEndpointUnitTest.java
@@ -1,6 +1,7 @@
 package at.tuwien.endpoints;
 
 import at.tuwien.api.identifier.*;
+import at.tuwien.api.identifier.ld.LdDatasetDto;
 import at.tuwien.config.EndpointConfig;
 import at.tuwien.entities.database.Database;
 import at.tuwien.entities.database.DatabaseAccess;
@@ -9,25 +10,27 @@ import at.tuwien.entities.identifier.IdentifierType;
 import at.tuwien.entities.user.User;
 import at.tuwien.exception.*;
 import at.tuwien.gateway.DataServiceGateway;
-import at.tuwien.service.AccessService;
-import at.tuwien.service.DatabaseService;
-import at.tuwien.service.IdentifierService;
-import at.tuwien.service.UserService;
+import at.tuwien.service.*;
 import at.tuwien.test.AbstractUnitTest;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import lombok.extern.log4j.Log4j2;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOUtils;
 import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
 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.core.io.InputStreamResource;
 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;
 import org.springframework.security.test.context.support.WithMockUser;
 import org.springframework.test.context.junit.jupiter.SpringExtension;
@@ -38,6 +41,7 @@ import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
 import java.security.Principal;
 import java.util.List;
+import java.util.stream.Stream;
 
 import static org.junit.jupiter.api.Assertions.*;
 import static org.mockito.Mockito.*;
@@ -62,6 +66,12 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
     @MockBean
     private UserService userService;
 
+    @MockBean
+    private ViewService viewService;
+
+    @MockBean
+    private TableService tableService;
+
     @Autowired
     private IdentifierEndpoint identifierEndpoint;
 
@@ -71,11 +81,162 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
     @Autowired
     private EndpointConfig endpointConfig;
 
+    public static Stream<Arguments> save_parameters() {
+        return Stream.of(
+                Arguments.arguments("foreign_subset", DATABASE_2_ID, DATABASE_2, null, IDENTIFIER_5, IDENTIFIER_5_SAVE_DTO, USER_1_PRINCIPAL, USER_1),
+                Arguments.arguments("foreign_database", DATABASE_1_ID, DATABASE_1, null, IDENTIFIER_1, IDENTIFIER_1_SAVE_DTO, USER_1_PRINCIPAL, USER_1),
+                Arguments.arguments("foreign_view", DATABASE_1_ID, DATABASE_1, null, IDENTIFIER_3, IDENTIFIER_3_SAVE_DTO, USER_1_PRINCIPAL, USER_1),
+                Arguments.arguments("foreign_table", DATABASE_1_ID, DATABASE_1, null, IDENTIFIER_4, IDENTIFIER_4_SAVE_DTO, USER_1_PRINCIPAL, USER_1)
+        );
+    }
+
+    public static Stream<Arguments> malformedDatabase_parameters() {
+        return Stream.of(
+                Arguments.arguments("queryId", 9999L, null, null),
+                Arguments.arguments("viewId", null, 9999L, null),
+                Arguments.arguments("tableId", null, null, 9999L)
+        );
+    }
+
+    public static Stream<Arguments> malformedSubset_parameters() {
+        return Stream.of(
+                Arguments.arguments("queryId", null, null, null),
+                Arguments.arguments("viewId", null, 9999L, null),
+                Arguments.arguments("tableId", null, null, 9999L)
+        );
+    }
+
+    public static Stream<Arguments> malformedView_parameters() {
+        return Stream.of(
+                Arguments.arguments("queryId", 9999L, null, null),
+                Arguments.arguments("viewId", null, null, null),
+                Arguments.arguments("tableId", null, null, 9999L)
+        );
+    }
+
+    public static Stream<Arguments> malformedTable_parameters() {
+        return Stream.of(
+                Arguments.arguments("queryId", 9999L, null, null),
+                Arguments.arguments("viewId", null, 9999L, null),
+                Arguments.arguments("tableId", null, null, null)
+        );
+    }
+
+    public static Stream<Arguments> findAll_filterDatabase_parameters() {
+        return Stream.of(
+                Arguments.arguments("dbid", DATABASE_1_ID, null, null, null, 4),
+                Arguments.arguments("qid", DATABASE_1_ID, QUERY_1_ID, null, null, 1),
+                Arguments.arguments("vid", DATABASE_1_ID, null, VIEW_1_ID, null, 1),
+                Arguments.arguments("tid", DATABASE_1_ID, null, null, TABLE_1_ID, 1)
+        );
+    }
+
+    public static Stream<Arguments> save_foreign_parameters() {
+        return Stream.of(
+                Arguments.arguments("view", IDENTIFIER_3, IDENTIFIER_3_SAVE_DTO),
+                Arguments.arguments("table", IDENTIFIER_4, IDENTIFIER_4_SAVE_DTO),
+                Arguments.arguments("subset", IDENTIFIER_2, IDENTIFIER_2_SAVE_DTO)
+        );
+    }
+
     @BeforeEach
     public void beforeEach() {
         genesis();
     }
 
+    @Test
+    @WithAnonymousUser
+    public void findAll_empty_succeeds() throws FormatNotAvailableException {
+
+        /* mock */
+        when(identifierService.findAll())
+                .thenReturn(List.of());
+
+        /* test */
+        final ResponseEntity<?> response = identifierEndpoint.findAll(null, null, null, null, "application/json");
+        assertEquals(HttpStatus.OK, response.getStatusCode());
+        assertNotNull(response.getBody());
+        final List<IdentifierBriefDto> identifiers = (List<IdentifierBriefDto>) response.getBody();
+        assertNotNull(identifiers);
+        assertEquals(0, identifiers.size());
+    }
+
+    @ParameterizedTest
+    @MethodSource("findAll_filterDatabase_parameters")
+    @WithAnonymousUser
+    public void findAll_filterDatabase_succeeds(String name, Long databaseId, Long queryId, Long viewId, Long tableId,
+                                                Integer expectedSize) throws FormatNotAvailableException,
+            ViewNotFoundException, TableNotFoundException, DatabaseNotFoundException {
+
+        /* mock */
+        when(identifierService.findAll())
+                .thenReturn(List.of(IDENTIFIER_1, IDENTIFIER_2, IDENTIFIER_3, IDENTIFIER_4, IDENTIFIER_5, IDENTIFIER_6, IDENTIFIER_7));
+        if (viewId != null) {
+            when(viewService.findById(DATABASE_1, VIEW_1_ID))
+                    .thenReturn(VIEW_1);
+        }
+        if (tableId != null) {
+            when(tableService.findById(DATABASE_1, TABLE_1_ID))
+                    .thenReturn(TABLE_1);
+        }
+
+        /* test */
+        final ResponseEntity<?> response = identifierEndpoint.findAll(databaseId, queryId, viewId, tableId, "application/json");
+        assertEquals(HttpStatus.OK, response.getStatusCode());
+        assertNotNull(response.getBody());
+        final List<IdentifierBriefDto> identifiers = (List<IdentifierBriefDto>) response.getBody();
+        assertNotNull(identifiers);
+        assertEquals(expectedSize, identifiers.size());
+    }
+
+    @Test
+    @WithAnonymousUser
+    public void findAll_json_succeeds() throws FormatNotAvailableException {
+
+        /* mock */
+        when(identifierService.findAll())
+                .thenReturn(List.of(IDENTIFIER_1));
+
+        /* test */
+        final ResponseEntity<?> response = identifierEndpoint.findAll(null, null, null, null, "application/json");
+        assertEquals(HttpStatus.OK, response.getStatusCode());
+        assertNotNull(response.getBody());
+        final List<IdentifierBriefDto> identifiers = (List<IdentifierBriefDto>) response.getBody();
+        assertNotNull(identifiers);
+        assertEquals(1, identifiers.size());
+    }
+
+    @Test
+    @WithAnonymousUser
+    public void findAll_jsonLd_succeeds() throws FormatNotAvailableException {
+
+        /* mock */
+        when(identifierService.findAll())
+                .thenReturn(List.of(IDENTIFIER_1));
+
+        /* test */
+        final ResponseEntity<?> response = identifierEndpoint.findAll(null, null, null, null, "application/ld+json");
+        assertEquals(HttpStatus.OK, response.getStatusCode());
+        assertNotNull(response.getBody());
+        final List<LdDatasetDto> identifiers = (List<LdDatasetDto>) response.getBody();
+        assertNotNull(identifiers);
+        assertEquals(1, identifiers.size());
+    }
+
+    @Test
+    @WithAnonymousUser
+    public void findAll_format_fails() {
+
+        /* mock */
+        when(identifierService.findAll())
+                .thenReturn(List.of(IDENTIFIER_1));
+
+        /* test */
+        assertThrows(FormatNotAvailableException.class, () -> {
+            identifierEndpoint.findAll(null, null, null, null, "text/csv");
+        });
+    }
+
     @Test
     @WithAnonymousUser
     public void find_json0_succeeds() throws IOException, MalformedException, DataServiceException,
@@ -179,47 +340,6 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
         assertEquals(inputStreamToString(compare.getInputStream()), inputStreamToString(body.getInputStream()));
     }
 
-    @Test
-    @Disabled("not testable with xml")
-    public void find_xml0_succeeds() throws IOException, MalformedException, DataServiceException,
-            DataServiceConnectionException, IdentifierNotFoundException, QueryNotFoundException,
-            FormatNotAvailableException {
-        final String accept = "text/xml";
-        final InputStreamResource compare = new InputStreamResource(FileUtils.openInputStream(new File("src/test/resources/xml/metadata0.xml")));
-
-        /* mock */
-        when(identifierService.find(IDENTIFIER_1_ID))
-                .thenReturn(IDENTIFIER_1);
-
-        /* test */
-        final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_1_ID, accept);
-        assertEquals(HttpStatus.OK, response.getStatusCode());
-        final InputStreamResource body = (InputStreamResource) response.getBody();
-        assertNotNull(body);
-        assertEquals(inputStreamToString(compare.getInputStream()), inputStreamToString(body.getInputStream()));
-    }
-
-    @Test
-    @Disabled("not testable with xml")
-    public void find_xml1_succeeds() throws IOException, MalformedException, DataServiceException,
-            DataServiceConnectionException, QueryNotFoundException, IdentifierNotFoundException,
-            FormatNotAvailableException {
-        final String accept = "text/xml";
-        final InputStreamResource compare = new InputStreamResource(FileUtils.openInputStream(new File("src/test/resources/xml/metadata1.xml")));
-
-        /* mock */
-        when(identifierService.find(IDENTIFIER_1_ID))
-                .thenReturn(IDENTIFIER_1);
-
-        /* test */
-        final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_1_ID, accept);
-        assertEquals(HttpStatus.OK, response.getStatusCode());
-        final InputStreamResource body = (InputStreamResource) response.getBody();
-        assertNotNull(body);
-        assertEquals(inputStreamToString(body.getInputStream()), inputStreamToString(compare.getInputStream()));
-
-    }
-
     @Test
     @WithAnonymousUser
     public void find_bibliography_succeeds() throws IOException, MalformedException, DataServiceException,
@@ -542,6 +662,52 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
         assertEquals(compare, body);
     }
 
+    @Test
+    @WithAnonymousUser
+    public void find_jsonLd_succeeds() throws MalformedException, DataServiceException, DataServiceConnectionException,
+            QueryNotFoundException, IdentifierNotFoundException, FormatNotAvailableException {
+        final String accept = "application/ld+json";
+
+        /* mock */
+        when(identifierService.find(IDENTIFIER_1_ID))
+                .thenReturn(IDENTIFIER_1);
+
+        /* test */
+        final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_1_ID, accept);
+        assertEquals(HttpStatus.OK, response.getStatusCode());
+        final LdDatasetDto body = (LdDatasetDto) response.getBody();
+        assertNotNull(body);
+    }
+
+    @Test
+    @WithAnonymousUser
+    public void find_jsonDatabase_fails() throws IdentifierNotFoundException {
+        final String accept = "text/csv";
+
+        /* mock */
+        when(identifierService.find(IDENTIFIER_7_ID))
+                .thenReturn(IDENTIFIER_7);
+
+        /* test */
+        assertThrows(FormatNotAvailableException.class, () -> {
+            identifierEndpoint.find(IDENTIFIER_7_ID, accept);
+        });
+    }
+
+    @Test
+    @WithAnonymousUser
+    public void find_move_succeeds() throws MalformedException, DataServiceException, DataServiceConnectionException,
+            QueryNotFoundException, IdentifierNotFoundException, FormatNotAvailableException {
+
+        /* mock */
+        when(identifierService.find(IDENTIFIER_1_ID))
+                .thenReturn(IDENTIFIER_1);
+
+        /* test */
+        final ResponseEntity<?> response = identifierEndpoint.find(IDENTIFIER_1_ID, null);
+        assertEquals(HttpStatus.MOVED_PERMANENTLY, response.getStatusCode());
+    }
+
     @Test
     @WithAnonymousUser
     public void delete_anonymous_fails() {
@@ -568,6 +734,117 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
         this.generic_delete();
     }
 
+    @Test
+    @WithMockUser(username = USER_2_USERNAME, authorities = {"delete-identifier"})
+    public void delete_alreadyPublished_fails() throws DataServiceException, DataServiceConnectionException,
+            DatabaseNotFoundException, IdentifierNotFoundException, SearchServiceException,
+            SearchServiceConnectionException {
+
+        /* mock */
+        when(identifierService.find(IDENTIFIER_1_ID))
+                .thenReturn(IDENTIFIER_1);
+        doNothing()
+                .when(identifierService)
+                .delete(IDENTIFIER_1);
+
+        /* test */
+        assertThrows(NotAllowedException.class, () -> {
+            identifierEndpoint.delete(IDENTIFIER_1_ID);
+        });
+    }
+
+    @Test
+    @WithAnonymousUser
+    public void publish_anonymous_fails() {
+
+        /* test */
+        assertThrows(AccessDeniedException.class, () -> {
+            identifierEndpoint.publish(IDENTIFIER_1_ID);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_1_USERNAME)
+    public void publish_noRole_fails() {
+
+        /* test */
+        assertThrows(AccessDeniedException.class, () -> {
+            identifierEndpoint.publish(IDENTIFIER_1_ID);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_1_USERNAME, authorities = {"publish-identifier"})
+    public void publish_succeeds() throws IdentifierNotFoundException, SearchServiceException, MalformedException,
+            DatabaseNotFoundException, ExternalServiceException, SearchServiceConnectionException,
+            DataServiceConnectionException {
+
+        /* mock */
+        when(identifierService.find(IDENTIFIER_1_ID))
+                .thenReturn(IDENTIFIER_1);
+        when(identifierService.publish(IDENTIFIER_1))
+                .thenReturn(IDENTIFIER_1);
+
+        /* test */
+        final ResponseEntity<IdentifierDto> response = identifierEndpoint.publish(IDENTIFIER_1_ID);
+        assertEquals(HttpStatus.CREATED, response.getStatusCode());
+        final IdentifierDto body = response.getBody();
+        assertNotNull(body);
+    }
+
+    @Test
+    @WithMockUser(username = USER_1_USERNAME, authorities = {"publish-identifier"})
+    public void publish_searchService_fails() throws IdentifierNotFoundException, SearchServiceException, MalformedException,
+            DatabaseNotFoundException, ExternalServiceException, SearchServiceConnectionException,
+            DataServiceConnectionException {
+
+        /* mock */
+        when(identifierService.find(IDENTIFIER_1_ID))
+                .thenReturn(IDENTIFIER_1);
+        doThrow(SearchServiceException.class)
+                .when(identifierService)
+                .publish(IDENTIFIER_1);
+
+        /* test */
+        assertThrows(SearchServiceException.class, () -> {
+            identifierEndpoint.publish(IDENTIFIER_1_ID);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_1_USERNAME, authorities = {"publish-identifier"})
+    public void publish_searchServiceConnection_fails() throws IdentifierNotFoundException, SearchServiceException,
+            MalformedException, DatabaseNotFoundException, ExternalServiceException, SearchServiceConnectionException,
+            DataServiceConnectionException {
+
+        /* mock */
+        when(identifierService.find(IDENTIFIER_1_ID))
+                .thenReturn(IDENTIFIER_1);
+        doThrow(SearchServiceConnectionException.class)
+                .when(identifierService)
+                .publish(IDENTIFIER_1);
+
+        /* test */
+        assertThrows(SearchServiceConnectionException.class, () -> {
+            identifierEndpoint.publish(IDENTIFIER_1_ID);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_1_USERNAME, authorities = {"publish-identifier"})
+    public void publish_notFound_fails() throws IdentifierNotFoundException {
+
+        /* mock */
+        doThrow(IdentifierNotFoundException.class)
+                .when(identifierService)
+                .find(IDENTIFIER_1_ID);
+
+        /* test */
+        assertThrows(IdentifierNotFoundException.class, () -> {
+            identifierEndpoint.publish(IDENTIFIER_1_ID);
+        });
+    }
+
     @Test
     @WithAnonymousUser
     public void find_json_succeeds() throws MalformedException, DataServiceException, DataServiceConnectionException,
@@ -627,7 +904,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
 
     @Test
     @WithMockUser(username = USER_1_USERNAME, authorities = {"create-identifier"})
-    public void save_hasRoleDatabase_succeeds() throws MalformedException, NotAllowedException, DataServiceException,
+    public void save_succeeds() throws MalformedException, NotAllowedException, DataServiceException,
             DataServiceConnectionException, UserNotFoundException, DatabaseNotFoundException, AccessNotFoundException,
             QueryNotFoundException, IdentifierNotFoundException, ViewNotFoundException, SearchServiceException,
             SearchServiceConnectionException, TableNotFoundException, ExternalServiceException {
@@ -638,7 +915,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
 
     @Test
     @WithMockUser(username = USER_1_USERNAME, authorities = {"create-identifier"})
-    public void save_hasRoleDatabaseNoAccess_fails() {
+    public void save_noAccess_fails() {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
@@ -648,7 +925,7 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
 
     @Test
     @WithMockUser(username = USER_2_USERNAME, authorities = {"create-identifier"})
-    public void save_hasRoleReadAccessQuery_succeeds() throws MalformedException, NotAllowedException,
+    public void save_readAccessQuery_succeeds() throws MalformedException, NotAllowedException,
             DataServiceException, DataServiceConnectionException, UserNotFoundException, DatabaseNotFoundException,
             AccessNotFoundException, QueryNotFoundException, IdentifierNotFoundException, ViewNotFoundException,
             SearchServiceException, SearchServiceConnectionException, TableNotFoundException, ExternalServiceException {
@@ -661,11 +938,14 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
         generic_save(DATABASE_2_ID, DATABASE_2, DATABASE_2_USER_1_READ_ACCESS, IDENTIFIER_5, IDENTIFIER_5_SAVE_DTO, USER_2_PRINCIPAL, USER_2);
     }
 
-    @Test
+    @ParameterizedTest
+    @MethodSource("malformedSubset_parameters")
     @WithMockUser(username = USER_1_USERNAME, authorities = {"create-identifier"})
-    public void save_invalidSubset_fails() {
+    public void save_malformedSubset_fails(String name, Long queryId, Long viewId, Long tableId) {
         final IdentifierSaveDto request = IdentifierSaveDto.builder()
-                .queryId(null)  // <--
+                .queryId(queryId)
+                .viewId(viewId)
+                .tableId(tableId)
                 .databaseId(IDENTIFIER_1_DATABASE_ID)
                 .descriptions(List.of(IDENTIFIER_1_DESCRIPTION_1_CREATE_DTO))
                 .titles(List.of(IDENTIFIER_1_TITLE_1_CREATE_DTO))
@@ -683,21 +963,23 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
         });
     }
 
-    @Test
+    @ParameterizedTest
+    @MethodSource("malformedView_parameters")
     @WithMockUser(username = USER_1_USERNAME, authorities = {"create-identifier"})
-    public void save_invalidDatabase_fails() {
+    public void save_malformedView_fails(String name, Long queryId, Long viewId, Long tableId) {
         final IdentifierSaveDto request = IdentifierSaveDto.builder()
-                .queryId(1L) // <--
+                .queryId(queryId)
+                .viewId(viewId)
+                .tableId(tableId)
                 .databaseId(IDENTIFIER_1_DATABASE_ID)
                 .descriptions(List.of(IDENTIFIER_1_DESCRIPTION_1_CREATE_DTO))
                 .titles(List.of(IDENTIFIER_1_TITLE_1_CREATE_DTO))
-                .relatedIdentifiers(List.of(IDENTIFIER_1_RELATED_IDENTIFIER_5_CREATE_DTO))
-                .publicationDay(IDENTIFIER_1_PUBLICATION_DAY)
+                .relatedIdentifiers(List.of())
                 .publicationMonth(IDENTIFIER_1_PUBLICATION_MONTH)
                 .publicationYear(IDENTIFIER_1_PUBLICATION_YEAR)
                 .creators(List.of(IDENTIFIER_1_CREATOR_1_CREATE_DTO))
                 .publisher(IDENTIFIER_1_PUBLISHER)
-                .type(IdentifierTypeDto.DATABASE)
+                .type(IdentifierTypeDto.VIEW)
                 .build();
 
         /* test */
@@ -706,21 +988,48 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
         });
     }
 
-    @Test
+    @ParameterizedTest
+    @MethodSource("malformedTable_parameters")
     @WithMockUser(username = USER_1_USERNAME, authorities = {"create-identifier"})
-    public void save_invalidView_fails() {
+    public void save_malformedTable_fails(String name, Long queryId, Long viewId, Long tableId) {
         final IdentifierSaveDto request = IdentifierSaveDto.builder()
-                .tableId(1L)  // <--
-                .databaseId(DATABASE_1_ID)
+                .queryId(queryId)
+                .viewId(viewId)
+                .tableId(tableId)
+                .databaseId(IDENTIFIER_1_DATABASE_ID)
                 .descriptions(List.of(IDENTIFIER_1_DESCRIPTION_1_CREATE_DTO))
                 .titles(List.of(IDENTIFIER_1_TITLE_1_CREATE_DTO))
-                .relatedIdentifiers(List.of(IDENTIFIER_1_RELATED_IDENTIFIER_5_CREATE_DTO))
-                .publicationDay(IDENTIFIER_1_PUBLICATION_DAY)
+                .relatedIdentifiers(List.of())
                 .publicationMonth(IDENTIFIER_1_PUBLICATION_MONTH)
                 .publicationYear(IDENTIFIER_1_PUBLICATION_YEAR)
                 .creators(List.of(IDENTIFIER_1_CREATOR_1_CREATE_DTO))
                 .publisher(IDENTIFIER_1_PUBLISHER)
-                .type(IdentifierTypeDto.VIEW)
+                .type(IdentifierTypeDto.TABLE)
+                .build();
+
+        /* test */
+        assertThrows(MalformedException.class, () -> {
+            generic_save(DATABASE_1_ID, DATABASE_1, DATABASE_1_USER_1_READ_ACCESS, IDENTIFIER_1, request, USER_1_PRINCIPAL, USER_1);
+        });
+    }
+
+    @ParameterizedTest
+    @MethodSource("malformedDatabase_parameters")
+    @WithMockUser(username = USER_1_USERNAME, authorities = {"create-identifier"})
+    public void save_malformedDatabase_fails(String name, Long queryId, Long viewId, Long tableId) {
+        final IdentifierSaveDto request = IdentifierSaveDto.builder()
+                .queryId(queryId)
+                .viewId(viewId)
+                .tableId(tableId)
+                .databaseId(IDENTIFIER_1_DATABASE_ID)
+                .descriptions(List.of(IDENTIFIER_1_DESCRIPTION_1_CREATE_DTO))
+                .titles(List.of(IDENTIFIER_1_TITLE_1_CREATE_DTO))
+                .relatedIdentifiers(List.of())
+                .publicationMonth(IDENTIFIER_1_PUBLICATION_MONTH)
+                .publicationYear(IDENTIFIER_1_PUBLICATION_YEAR)
+                .creators(List.of(IDENTIFIER_1_CREATOR_1_CREATE_DTO))
+                .publisher(IDENTIFIER_1_PUBLISHER)
+                .type(IdentifierTypeDto.DATABASE)
                 .build();
 
         /* test */
@@ -731,42 +1040,40 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
 
     @Test
     @WithMockUser(username = USER_1_USERNAME, authorities = {"create-identifier"})
-    public void save_foreignUser_fails() {
+    public void save_invalidDateDay_fails() {
         final IdentifierSaveDto request = IdentifierSaveDto.builder()
-                .viewId(9999L)  // <--
-                .databaseId(DATABASE_1_ID)
+                .databaseId(IDENTIFIER_1_DATABASE_ID)
                 .descriptions(List.of(IDENTIFIER_1_DESCRIPTION_1_CREATE_DTO))
                 .titles(List.of(IDENTIFIER_1_TITLE_1_CREATE_DTO))
                 .relatedIdentifiers(List.of(IDENTIFIER_1_RELATED_IDENTIFIER_5_CREATE_DTO))
-                .publicationDay(IDENTIFIER_1_PUBLICATION_DAY)
+                .publicationDay(32) // <<<
                 .publicationMonth(IDENTIFIER_1_PUBLICATION_MONTH)
                 .publicationYear(IDENTIFIER_1_PUBLICATION_YEAR)
                 .creators(List.of(IDENTIFIER_1_CREATOR_1_CREATE_DTO))
                 .publisher(IDENTIFIER_1_PUBLISHER)
-                .type(IdentifierTypeDto.VIEW)
+                .type(IdentifierTypeDto.DATABASE)
                 .build();
 
         /* test */
-        assertThrows(NotAllowedException.class, () -> {
-            generic_save(DATABASE_1_ID, DATABASE_1, DATABASE_1_USER_1_READ_ACCESS, IDENTIFIER_5, request, USER_1_PRINCIPAL, USER_1);
+        assertThrows(MalformedException.class, () -> {
+            generic_save(DATABASE_1_ID, DATABASE_1, DATABASE_1_USER_1_READ_ACCESS, IDENTIFIER_1, request, USER_1_PRINCIPAL, USER_1);
         });
     }
 
     @Test
     @WithMockUser(username = USER_1_USERNAME, authorities = {"create-identifier"})
-    public void save_invalidTable_fails() {
+    public void save_invalidDateMonth_fails() {
         final IdentifierSaveDto request = IdentifierSaveDto.builder()
-                .viewId(1L)  // <--
-                .databaseId(DATABASE_1_ID)
+                .databaseId(IDENTIFIER_1_DATABASE_ID)
                 .descriptions(List.of(IDENTIFIER_1_DESCRIPTION_1_CREATE_DTO))
                 .titles(List.of(IDENTIFIER_1_TITLE_1_CREATE_DTO))
                 .relatedIdentifiers(List.of(IDENTIFIER_1_RELATED_IDENTIFIER_5_CREATE_DTO))
                 .publicationDay(IDENTIFIER_1_PUBLICATION_DAY)
-                .publicationMonth(IDENTIFIER_1_PUBLICATION_MONTH)
+                .publicationMonth(13) // <<<
                 .publicationYear(IDENTIFIER_1_PUBLICATION_YEAR)
                 .creators(List.of(IDENTIFIER_1_CREATOR_1_CREATE_DTO))
                 .publisher(IDENTIFIER_1_PUBLISHER)
-                .type(IdentifierTypeDto.TABLE)
+                .type(IdentifierTypeDto.DATABASE)
                 .build();
 
         /* test */
@@ -775,11 +1082,27 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
         });
     }
 
+    @ParameterizedTest
+    @MethodSource("save_foreign_parameters")
+    @WithMockUser(username = USER_1_USERNAME, authorities = {"create-identifier"})
+    public void save_foreign_fails(String name, Identifier identifier, IdentifierSaveDto data)
+            throws UserNotFoundException {
+
+        /* mock */
+        when(userService.findByUsername(USER_1_USERNAME))
+                .thenReturn(USER_1);
+
+        /* test */
+        assertThrows(NotAllowedException.class, () -> {
+            generic_save(DATABASE_1_ID, DATABASE_1, null, identifier, data, USER_1_PRINCIPAL, USER_1);
+        });
+    }
+
     @Test
     @WithMockUser(username = USER_1_USERNAME, authorities = {"create-identifier"})
-    public void save_tableNotFound_fails() {
+    public void save_invalidTable_fails() {
         final IdentifierSaveDto request = IdentifierSaveDto.builder()
-                .tableId(9999L)  // <--
+                .viewId(1L)  // <--
                 .databaseId(DATABASE_1_ID)
                 .descriptions(List.of(IDENTIFIER_1_DESCRIPTION_1_CREATE_DTO))
                 .titles(List.of(IDENTIFIER_1_TITLE_1_CREATE_DTO))
@@ -793,18 +1116,121 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
                 .build();
 
         /* test */
-        assertThrows(TableNotFoundException.class, () -> {
+        assertThrows(MalformedException.class, () -> {
             generic_save(DATABASE_1_ID, DATABASE_1, DATABASE_1_USER_1_READ_ACCESS, IDENTIFIER_1, request, USER_1_PRINCIPAL, USER_1);
         });
     }
 
+    @ParameterizedTest
+    @MethodSource("save_parameters")
+    @WithMockUser(username = USER_1_USERNAME, authorities = {"create-identifier"})
+    public void save_noForeign_fails(String name, Long databaseId, Database database, DatabaseAccess access,
+                                     Identifier identifier, IdentifierSaveDto data, Principal principal, User user) {
+
+        /* test */
+        assertThrows(NotAllowedException.class, () -> {
+            generic_save(databaseId, database, access, identifier, data, principal, user);
+        });
+    }
+
+    @Test
+    @WithAnonymousUser
+    public void create_anonymous_fails() {
+
+        /* test */
+        assertThrows(AccessDeniedException.class, () -> {
+            identifierEndpoint.create(IDENTIFIER_1_CREATE_DTO, USER_1_PRINCIPAL);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_1_USERNAME)
+    public void create_noRole_fails() {
+
+        /* test */
+        assertThrows(AccessDeniedException.class, () -> {
+            identifierEndpoint.create(IDENTIFIER_1_CREATE_DTO, USER_1_PRINCIPAL);
+        });
+    }
+
     @Test
     @WithMockUser(username = USER_1_USERNAME, authorities = {"create-identifier"})
-    public void save_queryForeign_fails() {
+    public void create_succeeds() throws DatabaseNotFoundException, UserNotFoundException, AccessNotFoundException,
+            SearchServiceException, MalformedException, DataServiceException, QueryNotFoundException,
+            ExternalServiceException, SearchServiceConnectionException, DataServiceConnectionException,
+            IdentifierNotFoundException, ViewNotFoundException, NotAllowedException {
+
+        /* mock */
+        when(databaseService.findById(DATABASE_1_ID))
+                .thenReturn(DATABASE_1);
+        when(userService.findByUsername(USER_1_USERNAME))
+                .thenReturn(USER_1);
+        when(accessService.find(DATABASE_1, USER_1))
+                .thenReturn(DATABASE_1_USER_1_READ_ACCESS);
+        when(identifierService.create(DATABASE_1, USER_1, IDENTIFIER_1_CREATE_DTO))
+                .thenReturn(IDENTIFIER_1);
+
+        /* test */
+        final ResponseEntity<IdentifierDto> response = identifierEndpoint.create(IDENTIFIER_1_CREATE_DTO, USER_1_PRINCIPAL);
+        assertEquals(HttpStatus.CREATED, response.getStatusCode());
+        final IdentifierDto body = response.getBody();
+        assertNotNull(body);
+    }
+
+    @Test
+    @WithMockUser(username = USER_1_USERNAME, authorities = {"create-identifier"})
+    public void create_noAccess_fails() throws DatabaseNotFoundException, UserNotFoundException,
+            AccessNotFoundException {
+
+        /* mock */
+        when(databaseService.findById(DATABASE_1_ID))
+                .thenReturn(DATABASE_1);
+        when(userService.findByUsername(USER_1_USERNAME))
+                .thenReturn(USER_1);
+        doThrow(AccessNotFoundException.class)
+                .when(accessService)
+                .find(DATABASE_1, USER_1);
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            generic_save(DATABASE_2_ID, DATABASE_2, null, IDENTIFIER_5, IDENTIFIER_5_SAVE_DTO, USER_1_PRINCIPAL, USER_1);
+            identifierEndpoint.create(IDENTIFIER_1_CREATE_DTO, USER_1_PRINCIPAL);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_2_USERNAME, authorities = {"create-foreign-identifier"})
+    public void create_hasForeign_succeeds() throws DatabaseNotFoundException, UserNotFoundException,
+            AccessNotFoundException, SearchServiceException, MalformedException, NotAllowedException,
+            DataServiceException, QueryNotFoundException, ExternalServiceException, SearchServiceConnectionException,
+            DataServiceConnectionException, IdentifierNotFoundException, ViewNotFoundException {
+        final Principal principal = new UsernamePasswordAuthenticationToken(USER_2_DETAILS, USER_2_PASSWORD, List.of(
+                new SimpleGrantedAuthority("create-foreign-identifier")));
+
+        /* mock */
+        when(databaseService.findById(DATABASE_1_ID))
+                .thenReturn(DATABASE_1);
+        when(userService.findByUsername(USER_2_USERNAME))
+                .thenReturn(USER_2);
+        doThrow(AccessNotFoundException.class)
+                .when(accessService)
+                .find(DATABASE_1, USER_2);
+
+        /* test */
+        identifierEndpoint.create(IDENTIFIER_1_CREATE_DTO, principal);
+    }
+
+    @Test
+    @WithMockUser(username = USER_1_USERNAME, authorities = {"create-identifier"})
+    public void create_databaseNotFound_fails() throws DatabaseNotFoundException {
+
+        /* mock */
+        doThrow(DatabaseNotFoundException.class)
+                .when(databaseService)
+                .findById(DATABASE_1_ID);
+
+        /* test */
+        assertThrows(DatabaseNotFoundException.class, () -> {
+            identifierEndpoint.create(IDENTIFIER_1_CREATE_DTO, USER_1_PRINCIPAL);
         });
     }
 
@@ -820,6 +1246,14 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
             SearchServiceConnectionException, TableNotFoundException, ExternalServiceException {
 
         /* mock */
+        if (database != null) {
+            when(databaseService.findById(databaseId))
+                    .thenReturn(database);
+        } else {
+            doThrow(DatabaseNotFoundException.class)
+                    .when(databaseService)
+                    .findById(databaseId);
+        }
         if (access != null) {
             log.trace("mock access: {}", access);
             when(accessService.find(any(Database.class), any(User.class)))
@@ -833,8 +1267,6 @@ public class IdentifierEndpointUnitTest extends AbstractUnitTest {
         if (identifier.getType().equals(IdentifierType.SUBSET)) {
             when(dataServiceGateway.findQuery(databaseId, QUERY_2_ID))
                     .thenReturn(QUERY_2_DTO);
-            when(userService.findById(USER_1_ID))
-                    .thenReturn(USER_1);
         }
         when(identifierService.find(identifier.getId()))
                 .thenReturn(identifier);
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/ImageEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/ImageEndpointUnitTest.java
index c5c3c24cfd..bf1b2ffa55 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/ImageEndpointUnitTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/ImageEndpointUnitTest.java
@@ -1,13 +1,15 @@
 package at.tuwien.endpoints;
 
-import at.tuwien.test.AbstractUnitTest;
 import at.tuwien.api.container.image.ImageBriefDto;
 import at.tuwien.api.container.image.ImageChangeDto;
 import at.tuwien.api.container.image.ImageCreateDto;
 import at.tuwien.api.container.image.ImageDto;
 import at.tuwien.entities.container.image.ContainerImage;
-import at.tuwien.exception.*;
+import at.tuwien.exception.ImageAlreadyExistsException;
+import at.tuwien.exception.ImageInvalidException;
+import at.tuwien.exception.ImageNotFoundException;
 import at.tuwien.service.impl.ImageServiceImpl;
+import at.tuwien.test.AbstractUnitTest;
 import lombok.extern.log4j.Log4j2;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -120,19 +122,10 @@ public class ImageEndpointUnitTest extends AbstractUnitTest {
 
     @Test
     @WithMockUser(username = USER_1_USERNAME, authorities = {"create-image"})
-    public void create_missingEssentialInfo_fails() {
-        final ImageCreateDto request = ImageCreateDto.builder()
-                .name(IMAGE_1_NAME)
-                .version(IMAGE_1_VERSION)
-                .defaultPort(null)
-                .dialect(IMAGE_1_DIALECT)
-                .jdbcMethod(IMAGE_1_JDBC)
-                .build();
+    public void create_succeeds() throws ImageAlreadyExistsException, ImageInvalidException {
 
         /* test */
-        assertThrows(ImageInvalidException.class, () -> {
-            create_generic(request, USER_1_PRINCIPAL);
-        });
+        create_generic(IMAGE_1_CREATE_DTO, USER_1_PRINCIPAL);
     }
 
     @Test
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/MessageEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/MessageEndpointUnitTest.java
index 59166b7200..22516074ae 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/MessageEndpointUnitTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/MessageEndpointUnitTest.java
@@ -58,11 +58,17 @@ public class MessageEndpointUnitTest extends AbstractUnitTest {
     }
 
     @Test
-    @WithMockUser(username = USER_1_USERNAME, authorities = {"list-maintenance-messages"})
-    public void list_hasRole_succeeds() {
+    @WithAnonymousUser
+    public void list_onlyActive_succeeds() {
+
+        /* mock */
+        when(bannerMessageService.getActive())
+                .thenReturn(List.of(BANNER_MESSAGE_1));
 
         /* test */
-        list_generic();
+        final ResponseEntity<List<BannerMessageDto>> response = messageEndpoint.list(true);
+        assertEquals(HttpStatus.OK, response.getStatusCode());
+        assertNotNull(response.getBody());
     }
 
     @Test
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 1ceb6fd75f..dcc10f61a7 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
@@ -3,6 +3,7 @@ package at.tuwien.endpoints;
 import at.tuwien.api.database.table.TableBriefDto;
 import at.tuwien.api.database.table.TableCreateDto;
 import at.tuwien.api.database.table.TableDto;
+import at.tuwien.api.database.table.TableUpdateDto;
 import at.tuwien.api.database.table.columns.ColumnCreateDto;
 import at.tuwien.api.database.table.columns.ColumnDto;
 import at.tuwien.api.database.table.columns.ColumnTypeDto;
@@ -29,8 +30,6 @@ import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.Arguments;
 import org.junit.jupiter.params.provider.MethodSource;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.boot.test.mock.mockito.MockBean;
 import org.springframework.http.HttpStatus;
@@ -47,7 +46,6 @@ import static org.junit.jupiter.api.Assertions.*;
 import static org.mockito.Mockito.*;
 
 @Log4j2
-@EnableAutoConfiguration(exclude = RabbitAutoConfiguration.class)
 @SpringBootTest
 @ExtendWith(SpringExtension.class)
 public class TableEndpointUnitTest extends AbstractUnitTest {
@@ -468,10 +466,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
                 .name("Some Table")
                 .description("Some Description")
                 .columns(List.of(ColumnCreateDto.builder()
-                                .name("ID")
-                                .type(ColumnTypeDto.SERIAL)
-                                .nullAllowed(true) // <<<
-                                .build()))
+                        .name("ID")
+                        .type(ColumnTypeDto.SERIAL)
+                        .nullAllowed(true) // <<<
+                        .build()))
                 .constraints(ConstraintsCreateDto.builder()
                         .uniques(List.of(List.of("ID")))
                         .build())
@@ -514,7 +512,8 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
     @Test
     @WithAnonymousUser
     public void findById_publicAnonymous_succeeds() throws DataServiceException, DataServiceConnectionException,
-            TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException, QueueNotFoundException {
+            TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException, QueueNotFoundException,
+            UserNotFoundException {
 
         /* test */
         generic_findById(DATABASE_3_ID, DATABASE_3, TABLE_8_ID, TABLE_8, null, null, null);
@@ -532,17 +531,19 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
     @Test
     @WithMockUser(username = USER_1_USERNAME, authorities = "find-table")
-    public void findById_publicHasRoleDatabaseNotFound_succeeds() throws DataServiceException, DataServiceConnectionException,
-            TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException, QueueNotFoundException {
+    public void findById_publicHasRoleDatabaseNotFound_fails() {
 
         /* test */
-        generic_findById(DATABASE_3_ID, null, TABLE_8_ID, TABLE_8, USER_1_PRINCIPAL, USER_1, DATABASE_1_USER_1_READ_ACCESS);
+        assertThrows(DatabaseNotFoundException.class, () -> {
+            generic_findById(DATABASE_3_ID, null, TABLE_8_ID, TABLE_8, USER_1_PRINCIPAL, USER_1, DATABASE_1_USER_1_READ_ACCESS);
+        });
     }
 
     @Test
     @WithMockUser(username = USER_1_USERNAME, authorities = "find-table")
     public void findById_publicHasRole_succeeds() throws DataServiceException, DataServiceConnectionException,
-            TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException, QueueNotFoundException {
+            TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException, QueueNotFoundException,
+            UserNotFoundException {
 
         /* test */
         final ResponseEntity<TableDto> response = generic_findById(DATABASE_3_ID, DATABASE_3, TABLE_8_ID, TABLE_8, USER_1_PRINCIPAL, USER_1, DATABASE_1_USER_1_READ_ACCESS);
@@ -554,7 +555,8 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
     @Test
     @WithMockUser(username = USER_4_USERNAME)
     public void findById_publicNoRole_succeeds() throws DataServiceException, DataServiceConnectionException,
-            TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException, QueueNotFoundException {
+            TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException, QueueNotFoundException,
+            UserNotFoundException {
 
         /* test */
         generic_findById(DATABASE_3_ID, DATABASE_3, TABLE_8_ID, TABLE_8, USER_1_PRINCIPAL, USER_1, null);
@@ -566,7 +568,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> {
-            analyseTable_generic(DATABASE_1_ID, TABLE_1_ID, TABLE_1);
+            analyseTable_generic(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, null);
         });
     }
 
@@ -576,17 +578,17 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> {
-            analyseTable_generic(DATABASE_1_ID, TABLE_1_ID, TABLE_1);
+            analyseTable_generic(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, USER_4_PRINCIPAL);
         });
     }
 
     @Test
     @WithMockUser(username = USER_1_USERNAME, authorities = {"table-semantic-analyse"})
     public void findAll_hasRole_succeeds() throws MalformedException, TableNotFoundException,
-            DatabaseNotFoundException {
+            DatabaseNotFoundException, NotAllowedException {
 
         /* test */
-        analyseTable_generic(DATABASE_1_ID, TABLE_1_ID, TABLE_1);
+        analyseTable_generic(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, USER_1_PRINCIPAL);
     }
 
     @Test
@@ -595,7 +597,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> {
-            analyseTableColumn_generic(DATABASE_1_ID, TABLE_1_ID, TABLE_1_COLUMNS.get(0).getId(), TABLE_1_COLUMNS.get(0));
+            analyseTableColumn_generic(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1_COLUMNS.get(0).getId(), TABLE_1_COLUMNS.get(0), null);
         });
     }
 
@@ -605,7 +607,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> {
-            analyseTableColumn_generic(DATABASE_1_ID, TABLE_1_ID, TABLE_1_COLUMNS.get(0).getId(), TABLE_1_COLUMNS.get(0));
+            analyseTableColumn_generic(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1_COLUMNS.get(0).getId(), TABLE_1_COLUMNS.get(0), USER_4_PRINCIPAL);
         });
     }
 
@@ -615,7 +617,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
             DatabaseNotFoundException {
 
         /* test */
-        analyseTableColumn_generic(DATABASE_1_ID, TABLE_1_ID, TABLE_1_COLUMNS.get(0).getId(), TABLE_1_COLUMNS.get(0));
+        analyseTableColumn_generic(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1_COLUMNS.get(0).getId(), TABLE_1_COLUMNS.get(0), USER_1_PRINCIPAL);
     }
 
     @Test
@@ -627,7 +629,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> {
-            generic_update(DATABASE_3_ID, DATABASE_3, TABLE_8_ID, TABLE_8, TABLE_8_COLUMNS.get(0).getId(),
+            generic_updateColumn(DATABASE_3_ID, DATABASE_3, TABLE_8_ID, TABLE_8, TABLE_8_COLUMNS.get(0).getId(),
                     TABLE_8_COLUMNS.get(0), null, null, request, null);
         });
     }
@@ -641,7 +643,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(AccessNotFoundException.class, () -> {
-            generic_update(DATABASE_3_ID, DATABASE_3, TABLE_8_ID, TABLE_8, TABLE_8_COLUMNS.get(0).getId(),
+            generic_updateColumn(DATABASE_3_ID, DATABASE_3, TABLE_8_ID, TABLE_8, TABLE_8_COLUMNS.get(0).getId(),
                     TABLE_8_COLUMNS.get(0), USER_1_PRINCIPAL, USER_1, request, null);
         });
     }
@@ -655,7 +657,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            generic_update(DATABASE_3_ID, DATABASE_3, TABLE_8_ID, TABLE_8, TABLE_8_COLUMNS.get(0).getId(),
+            generic_updateColumn(DATABASE_3_ID, DATABASE_3, TABLE_8_ID, TABLE_8, TABLE_8_COLUMNS.get(0).getId(),
                     TABLE_8_COLUMNS.get(0), USER_1_PRINCIPAL, USER_1, request, DATABASE_3_USER_1_READ_ACCESS);
         });
     }
@@ -671,7 +673,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
                 .build();
 
         /* test */
-        generic_update(DATABASE_3_ID, DATABASE_3, TABLE_8_ID, TABLE_8, TABLE_8_COLUMNS.get(0).getId(),
+        generic_updateColumn(DATABASE_3_ID, DATABASE_3, TABLE_8_ID, TABLE_8, TABLE_8_COLUMNS.get(0).getId(),
                 TABLE_8_COLUMNS.get(0), USER_1_PRINCIPAL, USER_1, request, DATABASE_3_USER_1_WRITE_OWN_ACCESS);
     }
 
@@ -684,7 +686,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            generic_update(DATABASE_3_ID, DATABASE_3, TABLE_8_ID, TABLE_8, TABLE_8_COLUMNS.get(0).getId(),
+            generic_updateColumn(DATABASE_3_ID, DATABASE_3, TABLE_8_ID, TABLE_8, TABLE_8_COLUMNS.get(0).getId(),
                     TABLE_8_COLUMNS.get(0), USER_2_PRINCIPAL, USER_2, request, DATABASE_3_USER_2_WRITE_OWN_ACCESS);
         });
     }
@@ -698,7 +700,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(TableNotFoundException.class, () -> {
-            generic_update(DATABASE_3_ID, DATABASE_3, TABLE_8_ID, null, TABLE_8_COLUMNS.get(0).getId(),
+            generic_updateColumn(DATABASE_3_ID, DATABASE_3, TABLE_8_ID, null, TABLE_8_COLUMNS.get(0).getId(),
                     TABLE_8_COLUMNS.get(0), USER_1_PRINCIPAL, USER_1, request, DATABASE_3_USER_1_WRITE_OWN_ACCESS);
         });
     }
@@ -714,7 +716,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
                 .build();
 
         /* test */
-        generic_update(DATABASE_3_ID, DATABASE_3, TABLE_8_ID, TABLE_8, TABLE_8_COLUMNS.get(0).getId(),
+        generic_updateColumn(DATABASE_3_ID, DATABASE_3, TABLE_8_ID, TABLE_8, TABLE_8_COLUMNS.get(0).getId(),
                 TABLE_8_COLUMNS.get(0), USER_2_PRINCIPAL, USER_2, request, DATABASE_3_USER_2_WRITE_ALL_ACCESS);
     }
 
@@ -731,7 +733,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> {
-            generic_update(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, TABLE_1_COLUMNS.get(0).getId(),
+            generic_updateColumn(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, TABLE_1_COLUMNS.get(0).getId(),
                     TABLE_1_COLUMNS.get(0), null, null, request, null);
         });
     }
@@ -745,7 +747,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(AccessNotFoundException.class, () -> {
-            generic_update(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, TABLE_1_COLUMNS.get(0).getId(),
+            generic_updateColumn(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, TABLE_1_COLUMNS.get(0).getId(),
                     TABLE_1_COLUMNS.get(0), USER_1_PRINCIPAL, USER_1, request, null);
         });
     }
@@ -759,7 +761,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            generic_update(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, TABLE_1_COLUMNS.get(0).getId(),
+            generic_updateColumn(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, TABLE_1_COLUMNS.get(0).getId(),
                     TABLE_1_COLUMNS.get(0), USER_1_PRINCIPAL, USER_1, request, DATABASE_1_USER_1_READ_ACCESS);
         });
     }
@@ -775,7 +777,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
                 .build();
 
         /* test */
-        generic_update(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, TABLE_1_COLUMNS.get(0).getId(),
+        generic_updateColumn(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, TABLE_1_COLUMNS.get(0).getId(),
                 TABLE_1_COLUMNS.get(0), USER_1_PRINCIPAL, USER_1, request, DATABASE_1_USER_1_WRITE_OWN_ACCESS);
     }
 
@@ -788,7 +790,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(NotAllowedException.class, () -> {
-            generic_update(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, TABLE_1_COLUMNS.get(0).getId(),
+            generic_updateColumn(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, TABLE_1_COLUMNS.get(0).getId(),
                     TABLE_1_COLUMNS.get(0), USER_2_PRINCIPAL, USER_2, request, DATABASE_1_USER_2_WRITE_OWN_ACCESS);
         });
     }
@@ -802,7 +804,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(TableNotFoundException.class, () -> {
-            generic_update(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, null, TABLE_1_COLUMNS.get(0).getId(),
+            generic_updateColumn(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, null, TABLE_1_COLUMNS.get(0).getId(),
                     TABLE_1_COLUMNS.get(0), USER_2_PRINCIPAL, USER_2, request, DATABASE_1_USER_2_WRITE_ALL_ACCESS);
         });
     }
@@ -818,7 +820,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
                 .build();
 
         /* test */
-        generic_update(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, TABLE_1_COLUMNS.get(0).getId(),
+        generic_updateColumn(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, TABLE_1_COLUMNS.get(0).getId(),
                 TABLE_1_COLUMNS.get(0), USER_2_PRINCIPAL, USER_2, request, DATABASE_1_USER_2_WRITE_ALL_ACCESS);
     }
 
@@ -897,7 +899,8 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
     @Test
     @WithAnonymousUser
     public void findById_privateAnonymous_succeeds() throws DataServiceException, DataServiceConnectionException,
-            TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException, QueueNotFoundException {
+            TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException, QueueNotFoundException,
+            UserNotFoundException {
 
         /* test */
         generic_findById(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, null, null, null);
@@ -915,17 +918,19 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
 
     @Test
     @WithMockUser(username = USER_1_USERNAME, authorities = "find-table")
-    public void findById_privateHasRoleDatabaseNotFound_succeeds() throws DataServiceException, DataServiceConnectionException,
-            TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException, QueueNotFoundException {
+    public void findById_privateHasRoleDatabaseNotFound_fails() {
 
         /* test */
-        generic_findById(DATABASE_1_ID, null, TABLE_1_ID, TABLE_1, USER_1_PRINCIPAL, USER_1, DATABASE_1_USER_1_READ_ACCESS);
+        assertThrows(DatabaseNotFoundException.class, () -> {
+            generic_findById(DATABASE_1_ID, null, TABLE_1_ID, TABLE_1, USER_1_PRINCIPAL, USER_1, DATABASE_1_USER_1_READ_ACCESS);
+        });
     }
 
     @Test
     @WithMockUser(username = USER_1_USERNAME, authorities = "find-table")
     public void findById_privateHasRole_succeeds() throws DataServiceException, DataServiceConnectionException,
-            TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException, QueueNotFoundException {
+            TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException, QueueNotFoundException,
+            UserNotFoundException {
         /* test */
         final ResponseEntity<TableDto> response = generic_findById(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1,
                 USER_1_PRINCIPAL, USER_1, DATABASE_1_USER_1_READ_ACCESS);
@@ -937,7 +942,8 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
     @Test
     @WithMockUser(username = USER_4_USERNAME)
     public void findById_privateNoRole_succeeds() throws DataServiceException, DataServiceConnectionException,
-            TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException, QueueNotFoundException {
+            TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException, QueueNotFoundException,
+            UserNotFoundException {
 
         /* test */
         generic_findById(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, USER_4_PRINCIPAL, USER_4, null);
@@ -998,25 +1004,83 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
         });
     }
 
+    @Test
+    @WithMockUser(username = USER_1_USERNAME, authorities = {"update-table"})
+    public void update_succeeds() throws TableNotFoundException, SearchServiceException, NotAllowedException,
+            DataServiceException, DatabaseNotFoundException, SearchServiceConnectionException,
+            DataServiceConnectionException {
+        final TableUpdateDto request = TableUpdateDto.builder()
+                .isPublic(true)
+                .isSchemaPublic(true)
+                .description("Debug")
+                .build();
+
+        /* test */
+        final ResponseEntity<TableDto> response = generic_update(request, USER_1_PRINCIPAL);
+        assertEquals(HttpStatus.ACCEPTED, response.getStatusCode());
+        assertNotNull(response.getBody());
+    }
+
+    @Test
+    @WithMockUser(username = USER_2_USERNAME, authorities = {"update-table"})
+    public void update_notOwner_fails() {
+        final TableUpdateDto request = TableUpdateDto.builder()
+                .isPublic(true)
+                .isSchemaPublic(true)
+                .description("Debug")
+                .build();
+
+        /* test */
+        assertThrows(NotAllowedException.class, () -> {
+            generic_update(request, USER_2_PRINCIPAL);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_2_USERNAME, authorities = {"update-table-statistic"})
+    public void updateStatistic_notOwner_fails() {
+
+        /* test */
+        assertThrows(NotAllowedException.class, () -> {
+            generic_updateStatistic(USER_2_PRINCIPAL);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_1_USERNAME, authorities = {"update-table-statistic"})
+    public void updateStatistic_succeeds() throws TableNotFoundException, SearchServiceException, MalformedException,
+            NotAllowedException, DataServiceException, DatabaseNotFoundException, SearchServiceConnectionException,
+            DataServiceConnectionException {
+
+        /* test */
+        final ResponseEntity<Void> response = generic_updateStatistic(USER_1_PRINCIPAL);
+        assertEquals(HttpStatus.ACCEPTED, response.getStatusCode());
+    }
+
     /* ################################################################################################### */
     /* ## GENERIC TEST CASES                                                                            ## */
     /* ################################################################################################### */
 
-    public void analyseTable_generic(Long databaseId, Long tableId, Table table) throws MalformedException,
-            TableNotFoundException, DatabaseNotFoundException {
+    public void analyseTable_generic(Long databaseId, Database database, Long tableId, Table table, Principal principal)
+            throws MalformedException, TableNotFoundException, DatabaseNotFoundException, NotAllowedException {
 
         /* mock */
+        when(databaseService.findById(databaseId))
+                .thenReturn(database);
+        when(tableService.findById(database, tableId))
+                .thenReturn(table);
         when(entityService.suggestByTable(table))
                 .thenReturn(List.of());
 
         /* test */
-        final ResponseEntity<List<EntityDto>> response = tableEndpoint.analyseTable(databaseId, tableId);
+        final ResponseEntity<List<EntityDto>> response = tableEndpoint.analyseTable(databaseId, tableId, principal);
         assertEquals(HttpStatus.OK, response.getStatusCode());
         final List<EntityDto> body = response.getBody();
         assertNotNull(body);
     }
 
-    public void analyseTableColumn_generic(Long databaseId, Long tableId, Long columnId, TableColumn tableColumn)
+    public void analyseTableColumn_generic(Long databaseId, Database database, Long tableId, Long columnId,
+                                           TableColumn tableColumn, Principal principal)
             throws MalformedException, TableNotFoundException, DatabaseNotFoundException {
 
         /* mock */
@@ -1024,7 +1088,8 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
                 .thenReturn(List.of());
 
         /* test */
-        final ResponseEntity<List<TableColumnEntityDto>> response = tableEndpoint.analyseTableColumn(databaseId, tableId, columnId);
+        final ResponseEntity<List<TableColumnEntityDto>> response = tableEndpoint.analyseTableColumn(databaseId,
+                tableId, columnId, principal);
         assertEquals(HttpStatus.OK, response.getStatusCode());
         final List<TableColumnEntityDto> body = response.getBody();
         assertNotNull(body);
@@ -1095,19 +1160,9 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
                                                         Table table, Principal principal, User user,
                                                         DatabaseAccess access) throws DataServiceException,
             DataServiceConnectionException, TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException,
-            QueueNotFoundException {
+            QueueNotFoundException, UserNotFoundException {
 
         /* mock */
-        if (table != null) {
-            when(tableService.findById(databaseId, tableId))
-                    .thenReturn(table);
-            when(databaseService.findById(databaseId))
-                    .thenReturn(database);
-        } else {
-            doThrow(TableNotFoundException.class)
-                    .when(tableService)
-                    .findById(databaseId, tableId);
-        }
         if (database != null) {
             when(databaseService.findById(databaseId))
                     .thenReturn(database);
@@ -1116,13 +1171,19 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
                     .when(databaseService)
                     .findById(databaseId);
         }
-        if (access != null) {
-            when(accessService.find(database, user))
-                    .thenReturn(access);
+        if (table != null) {
+            when(tableService.findById(any(Database.class), eq(tableId)))
+                    .thenReturn(table);
         } else {
-            doThrow(AccessNotFoundException.class)
-                    .when(accessService)
-                    .find(database, user);
+            doThrow(TableNotFoundException.class)
+                    .when(tableService)
+                    .findById(any(Database.class), eq(tableId));
+        }
+        if (principal != null) {
+            when(userService.findByUsername(principal.getName()))
+                    .thenReturn(user);
+            when(accessService.find(any(Database.class), eq(user)))
+                    .thenReturn(access);
         }
 
         /* test */
@@ -1134,24 +1195,29 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
             SearchServiceException, SearchServiceConnectionException {
 
         /* mock */
-        when(tableService.findById(anyLong(), anyLong()))
+        when(databaseService.findById(DATABASE_1_ID))
+                .thenReturn(DATABASE_1);
+        when(tableService.findById(any(Database.class), anyLong()))
                 .thenReturn(table);
 
         /* test */
         return tableEndpoint.delete(DATABASE_1_ID, TABLE_1_ID, principal);
     }
 
-    protected ResponseEntity<ColumnDto> generic_update(Long databaseId, Database database, Long tableId, Table table,
-                                                       Long columnId, TableColumn column, Principal principal,
-                                                       User user, ColumnSemanticsUpdateDto data, DatabaseAccess access)
+    protected ResponseEntity<ColumnDto> generic_updateColumn(Long databaseId, Database database, Long tableId,
+                                                             Table table, Long columnId, TableColumn column,
+                                                             Principal principal, User user,
+                                                             ColumnSemanticsUpdateDto data, DatabaseAccess access)
             throws DataServiceException, DataServiceConnectionException, MalformedException, NotAllowedException,
             UserNotFoundException, TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException,
             SearchServiceException, SearchServiceConnectionException, OntologyNotFoundException,
             SemanticEntityNotFoundException {
 
         /* mock */
+        when(databaseService.findById(databaseId))
+                .thenReturn(database);
         if (table != null) {
-            when(tableService.findById(databaseId, tableId))
+            when(tableService.findById(database, tableId))
                     .thenReturn(table);
             when(tableService.update(column, data))
                     .thenReturn(column);
@@ -1161,7 +1227,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
                     .update(column, data);
             doThrow(TableNotFoundException.class)
                     .when(tableService)
-                    .findById(databaseId, tableId);
+                    .findById(database, tableId);
         }
         if (principal != null) {
             log.trace("mock user {}", user);
@@ -1180,6 +1246,40 @@ public class TableEndpointUnitTest extends AbstractUnitTest {
         }
 
         /* test */
-        return tableEndpoint.update(databaseId, tableId, columnId, data, principal);
+        return tableEndpoint.updateColumn(databaseId, tableId, columnId, data, principal);
+    }
+
+    protected ResponseEntity<TableDto> generic_update(TableUpdateDto data, Principal caller)
+            throws TableNotFoundException, SearchServiceException, NotAllowedException, DataServiceException,
+            DatabaseNotFoundException, SearchServiceConnectionException, DataServiceConnectionException {
+
+        /* mock */
+        when(databaseService.findById(DATABASE_1_ID))
+                .thenReturn(DATABASE_1);
+        when(tableService.findById(DATABASE_1, TABLE_1_ID))
+                .thenReturn(TABLE_1);
+        when(tableService.updateTable(any(Table.class), eq(data)))
+                .thenReturn(TABLE_1);
+
+        /* test */
+        return tableEndpoint.update(DATABASE_1_ID, TABLE_1_ID, data, caller);
+    }
+
+    protected ResponseEntity<Void> generic_updateStatistic(Principal caller)
+            throws TableNotFoundException, SearchServiceException, NotAllowedException, DataServiceException,
+            DatabaseNotFoundException, SearchServiceConnectionException, DataServiceConnectionException,
+            MalformedException {
+
+        /* mock */
+        when(databaseService.findById(DATABASE_1_ID))
+                .thenReturn(DATABASE_1);
+        when(tableService.findById(DATABASE_1, TABLE_1_ID))
+                .thenReturn(TABLE_1);
+        doNothing()
+                .when(tableService)
+                .updateStatistics(TABLE_1);
+
+        /* test */
+        return tableEndpoint.updateStatistic(DATABASE_1_ID, TABLE_1_ID, caller);
     }
 }
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 da86dd9344..54cef2ca4b 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,23 +1,32 @@
 package at.tuwien.endpoints;
 
-import at.tuwien.test.AbstractUnitTest;
+import at.tuwien.api.auth.LoginRequestDto;
 import at.tuwien.api.auth.SignupRequestDto;
-import at.tuwien.api.user.*;
+import at.tuwien.api.keycloak.UserAttributesDto;
+import at.tuwien.api.user.UserBriefDto;
+import at.tuwien.api.user.UserDto;
+import at.tuwien.api.user.UserPasswordDto;
+import at.tuwien.api.user.UserUpdateDto;
 import at.tuwien.entities.user.User;
 import at.tuwien.exception.*;
 import at.tuwien.service.AuthenticationService;
+import at.tuwien.service.DatabaseService;
 import at.tuwien.service.UserService;
+import at.tuwien.test.AbstractUnitTest;
 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.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.boot.test.mock.mockito.MockBean;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
 import org.springframework.security.test.context.support.WithAnonymousUser;
 import org.springframework.security.test.context.support.WithMockUser;
 import org.springframework.test.context.junit.jupiter.SpringExtension;
@@ -25,12 +34,12 @@ 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.*;
 import static org.mockito.Mockito.*;
 
 @Log4j2
-@EnableAutoConfiguration(exclude = RabbitAutoConfiguration.class)
 @SpringBootTest
 @ExtendWith(SpringExtension.class)
 public class UserEndpointUnitTest extends AbstractUnitTest {
@@ -41,9 +50,19 @@ public class UserEndpointUnitTest extends AbstractUnitTest {
     @MockBean
     private AuthenticationService authenticationService;
 
+    @MockBean
+    private DatabaseService databaseService;
+
     @Autowired
     private UserEndpoint userEndpoint;
 
+    public static Stream<Arguments> getToken_parameters() {
+        return Stream.of(
+                Arguments.arguments("null", null),
+                Arguments.arguments("empty", new UUID[]{})
+        );
+    }
+
     @BeforeEach
     public void beforeEach() {
         genesis();
@@ -142,13 +161,28 @@ public class UserEndpointUnitTest extends AbstractUnitTest {
     }
 
     @Test
-    @WithMockUser(username = USER_3_USERNAME, authorities = {"find-user"})
-    public void find_hasRoleForeign_succeeds() {
+    @WithMockUser(username = USER_3_USERNAME, authorities = {"find-foreign-user"})
+    public void find_hasRoleForeign_succeeds() throws UserNotFoundException, NotAllowedException {
+        final Principal principal = new UsernamePasswordAuthenticationToken(USER_3_DETAILS, USER_3_PASSWORD, List.of(
+                new SimpleGrantedAuthority("find-foreign-user")));
 
         /* test */
-        assertThrows(NotAllowedException.class, () -> {
-            find_generic(USER_2_ID, USER_2, USER_3_PRINCIPAL);
-        });
+        find_generic(USER_2_ID, USER_2, principal);
+    }
+
+    @Test
+    @WithMockUser(username = USER_3_USERNAME, authorities = {"system"})
+    public void find_system_succeeds() throws UserNotFoundException, NotAllowedException {
+        final Principal principal = new UsernamePasswordAuthenticationToken(USER_3_DETAILS, USER_3_PASSWORD, List.of(
+                new SimpleGrantedAuthority("system")));
+
+        /* test */
+        final ResponseEntity<UserDto> response = find_generic(USER_3_ID, USER_3, principal);
+        assertNotNull(response.getHeaders().get("X-Username"));
+        assertEquals(USER_3_USERNAME, response.getHeaders().get("X-Username").get(0));
+        assertNotNull(response.getHeaders().get("X-Password"));
+        assertNotEquals(USER_3_PASSWORD, response.getHeaders().get("X-Password").get(0));
+        assertEquals(USER_3_DATABASE_PASSWORD, response.getHeaders().get("X-Password").get(0));
     }
 
     @Test
@@ -252,6 +286,136 @@ public class UserEndpointUnitTest extends AbstractUnitTest {
         password_generic(USER_1_PRINCIPAL, request);
     }
 
+    @Test
+    @WithAnonymousUser
+    public void getToken_anonymous_succeeds() throws UserNotFoundException, AuthServiceException,
+            AuthServiceConnectionException, AccountNotSetupException, CredentialsInvalidException {
+
+        /* test */
+        getToken_generic(USER_1_LOGIN_REQUEST_DTO, USER_1_PRINCIPAL, USER_1);
+    }
+
+    @Test
+    @WithMockUser(username = USER_1_USERNAME)
+    public void getToken_loggedIn_succeeds() throws UserNotFoundException, AuthServiceException,
+            AuthServiceConnectionException, AccountNotSetupException, CredentialsInvalidException {
+
+        /* test */
+        getToken_generic(USER_1_LOGIN_REQUEST_DTO, USER_1_PRINCIPAL, USER_1);
+    }
+
+    @Test
+    @WithAnonymousUser
+    public void getToken_notExists_succeeds() throws UserNotFoundException, AuthServiceException,
+            AuthServiceConnectionException, AccountNotSetupException, CredentialsInvalidException {
+
+        /* mock */
+        when(authenticationService.findByUsername(USER_1_USERNAME))
+                .thenReturn(USER_1_KEYCLOAK_DTO);
+        when(userService.create(any(SignupRequestDto.class), any(UUID.class)))
+                .thenReturn(USER_1);
+
+        /* test */
+        getToken_generic(USER_1_LOGIN_REQUEST_DTO, USER_1_PRINCIPAL, null);
+    }
+
+    @Test
+    @WithAnonymousUser
+    public void getToken_notExists_fails() throws UserNotFoundException, AuthServiceException,
+            AuthServiceConnectionException, CredentialsInvalidException {
+
+        /* mock */
+        doThrow(UserNotFoundException.class)
+                .when(authenticationService)
+                .findByUsername(USER_1_USERNAME);
+
+        /* test */
+        assertThrows(UserNotFoundException.class, () -> {
+            getToken_generic(USER_1_LOGIN_REQUEST_DTO, USER_1_PRINCIPAL, null);
+        });
+    }
+
+    @ParameterizedTest
+    @MethodSource("getToken_parameters")
+    @WithAnonymousUser
+    public void getToken_missingLdapId_fails(String name, UUID[] ldapId) throws UserNotFoundException, AuthServiceException,
+            AuthServiceConnectionException, CredentialsInvalidException {
+        final at.tuwien.api.keycloak.UserDto mock = at.tuwien.api.keycloak.UserDto.builder()
+                .attributes(UserAttributesDto.builder()
+                        .ldapId(ldapId)
+                        .build())
+                .build();
+
+        /* mock */
+        when(authenticationService.findByUsername(USER_1_USERNAME))
+                .thenReturn(mock);
+
+        /* test */
+        assertThrows(UserNotFoundException.class, () -> {
+            getToken_generic(USER_1_LOGIN_REQUEST_DTO, USER_1_PRINCIPAL, null);
+        });
+    }
+
+    @Test
+    @WithAnonymousUser
+    public void refreshToken_anonymous_succeeds() throws AuthServiceConnectionException, CredentialsInvalidException {
+
+        /* mock */
+        when(authenticationService.refreshToken(anyString()))
+                .thenReturn(TOKEN_DTO);
+
+        /* test */
+        final ResponseEntity<?> response = userEndpoint.refreshToken(REFRESH_TOKEN_REQUEST_DTO);
+        assertEquals(HttpStatus.ACCEPTED, response.getStatusCode());
+        assertNotNull(response.getBody());
+    }
+
+    @Test
+    @WithMockUser(username = USER_1_USERNAME)
+    public void refreshToken_loggedIn_succeeds() throws AuthServiceConnectionException, CredentialsInvalidException {
+
+        /* mock */
+        when(authenticationService.refreshToken(anyString()))
+                .thenReturn(TOKEN_DTO);
+
+        /* test */
+        final ResponseEntity<?> response = userEndpoint.refreshToken(REFRESH_TOKEN_REQUEST_DTO);
+        assertEquals(HttpStatus.ACCEPTED, response.getStatusCode());
+        assertNotNull(response.getBody());
+    }
+
+    @Test
+    @WithMockUser(username = USER_1_USERNAME)
+    public void refreshToken_authServiceConnection_fails() throws AuthServiceConnectionException,
+            CredentialsInvalidException {
+
+        /* mock */
+        doThrow(AuthServiceConnectionException.class)
+                .when(authenticationService)
+                .refreshToken(anyString());
+
+        /* test */
+        assertThrows(AuthServiceConnectionException.class, () -> {
+            userEndpoint.refreshToken(REFRESH_TOKEN_REQUEST_DTO);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_1_USERNAME)
+    public void refreshToken_invalidCredentials_fails() throws AuthServiceConnectionException,
+            CredentialsInvalidException {
+
+        /* mock */
+        doThrow(CredentialsInvalidException.class)
+                .when(authenticationService)
+                .refreshToken(anyString());
+
+        /* test */
+        assertThrows(CredentialsInvalidException.class, () -> {
+            userEndpoint.refreshToken(REFRESH_TOKEN_REQUEST_DTO);
+        });
+    }
+
     /* ################################################################################################### */
     /* ## GENERIC TEST CASES                                                                            ## */
     /* ################################################################################################### */
@@ -300,7 +464,7 @@ public class UserEndpointUnitTest extends AbstractUnitTest {
         assertNotNull(body);
     }
 
-    protected void find_generic(UUID id, User user, Principal principal) throws NotAllowedException,
+    protected ResponseEntity<UserDto> find_generic(UUID id, User user, Principal principal) throws NotAllowedException,
             UserNotFoundException {
 
         /* mock */
@@ -318,6 +482,7 @@ public class UserEndpointUnitTest extends AbstractUnitTest {
         assertEquals(HttpStatus.OK, response.getStatusCode());
         final UserDto body = response.getBody();
         assertNotNull(body);
+        return response;
     }
 
     protected void modify_generic(UUID userId, User user, Principal principal, UserUpdateDto data)
@@ -347,9 +512,36 @@ public class UserEndpointUnitTest extends AbstractUnitTest {
         doNothing()
                 .when(userService)
                 .updatePassword(USER_1, data);
+        when(databaseService.findAllAccess(USER_1_ID))
+                .thenReturn(List.of(DATABASE_1));
+        doNothing()
+                .when(databaseService)
+                .updatePassword(DATABASE_1, USER_1);
 
         /* test */
         final ResponseEntity<?> response = userEndpoint.password(USER_1_ID, data, principal);
         assertEquals(HttpStatus.ACCEPTED, response.getStatusCode());
     }
+
+    protected void getToken_generic(LoginRequestDto request, Principal principal, User user)
+            throws UserNotFoundException, AuthServiceConnectionException, AccountNotSetupException,
+            CredentialsInvalidException, AuthServiceException {
+
+        /* mock */
+        when(authenticationService.obtainToken(any(LoginRequestDto.class)))
+                .thenReturn(TOKEN_DTO);
+        if (user != null) {
+            when(userService.findByUsername(principal.getName()))
+                    .thenReturn(user);
+        } else {
+            doThrow(UserNotFoundException.class)
+                    .when(userService)
+                    .findByUsername(principal.getName());
+        }
+
+        /* test */
+        final ResponseEntity<?> response = userEndpoint.getToken(request);
+        assertEquals(HttpStatus.ACCEPTED, response.getStatusCode());
+        assertNotNull(response.getBody());
+    }
 }
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/ViewEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/ViewEndpointUnitTest.java
index 7f1855b91e..ccd8b067de 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/ViewEndpointUnitTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/ViewEndpointUnitTest.java
@@ -3,6 +3,7 @@ package at.tuwien.endpoints;
 import at.tuwien.api.database.ViewBriefDto;
 import at.tuwien.api.database.ViewCreateDto;
 import at.tuwien.api.database.ViewDto;
+import at.tuwien.api.database.ViewUpdateDto;
 import at.tuwien.entities.database.Database;
 import at.tuwien.entities.database.DatabaseAccess;
 import at.tuwien.entities.database.View;
@@ -22,6 +23,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.test.context.support.WithAnonymousUser;
 import org.springframework.security.test.context.support.WithMockUser;
 import org.springframework.test.context.junit.jupiter.SpringExtension;
@@ -99,7 +101,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest {
     public void create_publicAnonymous_succeeds() {
 
         /* test */
-        assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> {
+        assertThrows(AccessDeniedException.class, () -> {
             create_generic(DATABASE_3_ID, DATABASE_3, null, null, null, null);
         });
     }
@@ -129,11 +131,21 @@ public class ViewEndpointUnitTest extends AbstractUnitTest {
     public void create_publicNoRole_fails() {
 
         /* test */
-        assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> {
+        assertThrows(AccessDeniedException.class, () -> {
             create_generic(DATABASE_3_ID, DATABASE_3, USER_2_PRINCIPAL, USER_2_ID, USER_2, null);
         });
     }
 
+    @Test
+    @WithMockUser(username = USER_1_USERNAME, authorities = {"create-database-view"})
+    public void create_succeeds() throws UserNotFoundException, SearchServiceException, MalformedException,
+            NotAllowedException, DataServiceException, DatabaseNotFoundException, AccessNotFoundException,
+            SearchServiceConnectionException, DataServiceConnectionException {
+
+        /* test */
+        create_generic(DATABASE_1_ID, DATABASE_1, USER_1_PRINCIPAL, USER_1_ID, USER_1, DATABASE_1_USER_1_WRITE_ALL_ACCESS);
+    }
+
     @Test
     @WithAnonymousUser
     public void find_publicAnonymous_succeeds() throws UserNotFoundException, DatabaseNotFoundException,
@@ -175,7 +187,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest {
     public void delete_publicAnonymous_fails() {
 
         /* test */
-        assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> {
+        assertThrows(AccessDeniedException.class, () -> {
             delete_generic(DATABASE_3_ID, DATABASE_3, VIEW_1_ID, VIEW_1, null, null, null, null);
         });
     }
@@ -195,7 +207,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest {
     public void delete_publicNoRole_fails() {
 
         /* test */
-        assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> {
+        assertThrows(AccessDeniedException.class, () -> {
             delete_generic(DATABASE_3_ID, DATABASE_3, VIEW_1_ID, VIEW_1, USER_2_PRINCIPAL, USER_2_ID, USER_2, DATABASE_2_USER_1_READ_ACCESS);
         });
     }
@@ -252,10 +264,10 @@ public class ViewEndpointUnitTest extends AbstractUnitTest {
 
     @Test
     @WithAnonymousUser
-    public void create_privateAnonymous_succeeds() {
+    public void create_privateAnonymous_fails() {
 
         /* test */
-        assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> {
+        assertThrows(AccessDeniedException.class, () -> {
             create_generic(DATABASE_1_ID, DATABASE_1, null, null, null, null);
         });
     }
@@ -285,7 +297,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest {
     public void create_privateNoRole_fails() {
 
         /* test */
-        assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> {
+        assertThrows(AccessDeniedException.class, () -> {
             create_generic(DATABASE_1_ID, DATABASE_1, USER_2_PRINCIPAL, USER_2_ID, USER_2, null);
         });
     }
@@ -331,7 +343,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest {
     public void delete_privateAnonymous_fails() {
 
         /* test */
-        assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> {
+        assertThrows(AccessDeniedException.class, () -> {
             delete_generic(DATABASE_1_ID, DATABASE_1, VIEW_1_ID, VIEW_1, null, null, null, null);
         });
     }
@@ -351,7 +363,7 @@ public class ViewEndpointUnitTest extends AbstractUnitTest {
     public void delete_privateNoRole_fails() {
 
         /* test */
-        assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> {
+        assertThrows(AccessDeniedException.class, () -> {
             delete_generic(DATABASE_1_ID, DATABASE_1, VIEW_1_ID, VIEW_1, USER_2_PRINCIPAL, USER_2_ID, USER_2, DATABASE_2_USER_1_READ_ACCESS);
         });
     }
@@ -366,6 +378,45 @@ public class ViewEndpointUnitTest extends AbstractUnitTest {
         delete_generic(DATABASE_1_ID, DATABASE_1, VIEW_1_ID, VIEW_1, USER_1_PRINCIPAL, USER_1_ID, USER_1, DATABASE_1_USER_1_WRITE_ALL_ACCESS);
     }
 
+    @Test
+    @WithAnonymousUser
+    public void update_anonymous_succeeds() {
+
+        /* test */
+        assertThrows(AccessDeniedException.class, () -> {
+            update_generic(USER_1_PRINCIPAL);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_1_USERNAME)
+    public void update_noRole_fails() {
+
+        /* test */
+        assertThrows(AccessDeniedException.class, () -> {
+            update_generic(USER_1_PRINCIPAL);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_2_USERNAME, authorities = {"modify-view-visibility"})
+    public void update_notOwner_fails() {
+
+        /* test */
+        assertThrows(NotAllowedException.class, () -> {
+            update_generic(USER_2_PRINCIPAL);
+        });
+    }
+
+    @Test
+    @WithMockUser(username = USER_1_USERNAME, authorities = {"modify-view-visibility"})
+    public void update_succeeds() throws NotAllowedException, DataServiceConnectionException, DatabaseNotFoundException,
+            SearchServiceException, SearchServiceConnectionException, ViewNotFoundException {
+
+        /* test */
+        update_generic(USER_1_PRINCIPAL);
+    }
+
     /* ################################################################################################### */
     /* ## GENERIC TEST CASES                                                                            ## */
     /* ################################################################################################### */
@@ -419,6 +470,10 @@ public class ViewEndpointUnitTest extends AbstractUnitTest {
         /* mock */
         when(databaseService.findById(databaseId))
                 .thenReturn(database);
+        if (principal != null) {
+            when(userService.findByUsername(principal.getName()))
+                    .thenReturn(user);
+        }
         if (access != null) {
             log.trace("mock access of database with id {} and user id {}", databaseId, userId);
             when(accessService.find(database, user))
@@ -499,4 +554,26 @@ public class ViewEndpointUnitTest extends AbstractUnitTest {
         assertEquals(HttpStatus.ACCEPTED, response.getStatusCode());
     }
 
+    protected void update_generic(Principal principal) throws SearchServiceException, NotAllowedException,
+            DatabaseNotFoundException, SearchServiceConnectionException, DataServiceConnectionException,
+            ViewNotFoundException {
+        final ViewUpdateDto request = ViewUpdateDto.builder()
+                .isPublic(true)
+                .isSchemaPublic(true)
+                .build();
+
+        /* mock */
+        when(databaseService.findById(DATABASE_1_ID))
+                .thenReturn(DATABASE_1);
+        when(viewService.findById(DATABASE_1, VIEW_1_ID))
+                .thenReturn(VIEW_1);
+        when(viewService.update(DATABASE_1, VIEW_1, request))
+                .thenReturn(VIEW_1);
+
+        /* test */
+        final ResponseEntity<ViewDto> response = viewEndpoint.update(DATABASE_1_ID, VIEW_1_ID, request, principal);
+        assertEquals(HttpStatus.ACCEPTED, response.getStatusCode());
+        assertNotNull(response.getBody());
+    }
+
 }
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java
index 9a43b234d9..578f6276c7 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java
@@ -535,22 +535,22 @@ public class PrometheusEndpointMvcTest extends AbstractUnitTest {
             /* ignore */
         }
         try {
-            tableEndpoint.analyseTable(DATABASE_1_ID, TABLE_1_ID);
+            tableEndpoint.analyseTable(DATABASE_1_ID, TABLE_1_ID, USER_1_PRINCIPAL);
         } catch (Exception e) {
             /* ignore */
         }
         try {
-            tableEndpoint.updateStatistic(DATABASE_1_ID, TABLE_1_ID);
+            tableEndpoint.updateStatistic(DATABASE_1_ID, TABLE_1_ID, USER_1_PRINCIPAL);
         } catch (Exception e) {
             /* ignore */
         }
         try {
-            tableEndpoint.analyseTableColumn(DATABASE_1_ID, TABLE_1_ID, TABLE_1_COLUMNS.get(0).getId());
+            tableEndpoint.analyseTableColumn(DATABASE_1_ID, TABLE_1_ID, TABLE_1_COLUMNS.get(0).getId(), USER_1_PRINCIPAL);
         } catch (Exception e) {
             /* ignore */
         }
         try {
-            tableEndpoint.update(DATABASE_1_ID, TABLE_1_ID, TABLE_1_COLUMNS.get(3).getId(), request, USER_1_PRINCIPAL);
+            tableEndpoint.updateColumn(DATABASE_1_ID, TABLE_1_ID, TABLE_1_COLUMNS.get(3).getId(), request, USER_1_PRINCIPAL);
         } catch (Exception e) {
             /* ignore */
         }
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DataCiteIdentifierServicePersistenceTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DataCiteIdentifierServicePersistenceTest.java
index 43f99276f5..182fe8e14a 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DataCiteIdentifierServicePersistenceTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/DataCiteIdentifierServicePersistenceTest.java
@@ -217,8 +217,7 @@ public class DataCiteIdentifierServicePersistenceTest extends AbstractUnitTest {
 
     @Test
     public void publish_succeeds() throws MalformedException, DataServiceConnectionException, SearchServiceException,
-            DatabaseNotFoundException, SearchServiceConnectionException, IdentifierNotFoundException,
-            ExternalServiceException {
+            DatabaseNotFoundException, SearchServiceConnectionException, ExternalServiceException {
         final ResponseEntity<DataCiteBody<DataCiteDoi>> mock = ResponseEntity.status(HttpStatus.CREATED)
                 .body(IDENTIFIER_7_DATA_CITE);
 
@@ -227,7 +226,7 @@ public class DataCiteIdentifierServicePersistenceTest extends AbstractUnitTest {
                 .thenReturn(mock);
 
         /* test */
-        final Identifier response = dataCiteIdentifierService.publish(IDENTIFIER_7_ID);
+        final Identifier response = dataCiteIdentifierService.publish(IDENTIFIER_7);
         assertEquals(IDENTIFIER_7_ID, response.getId());
         assertEquals(IdentifierStatusType.PUBLISHED, response.getStatus());
     }
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/IdentifierServicePersistenceTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/IdentifierServicePersistenceTest.java
index 8947e823fc..0c87dcdd69 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/IdentifierServicePersistenceTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/IdentifierServicePersistenceTest.java
@@ -493,11 +493,10 @@ public class IdentifierServicePersistenceTest extends AbstractUnitTest {
 
     @Test
     public void publish_succeeds() throws MalformedException, DataServiceConnectionException, SearchServiceException,
-            DatabaseNotFoundException, SearchServiceConnectionException, IdentifierNotFoundException,
-            ExternalServiceException {
+            DatabaseNotFoundException, SearchServiceConnectionException, ExternalServiceException {
 
         /* test */
-        final Identifier response = identifierService.publish(IDENTIFIER_7_ID);
+        final Identifier response = identifierService.publish(IDENTIFIER_7);
         assertEquals(IDENTIFIER_7_ID, response.getId());
         assertEquals(IdentifierStatusType.PUBLISHED, response.getStatus());
     }
diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/TableServiceUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/TableServiceUnitTest.java
index 477d2bd17d..d975e808e3 100644
--- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/TableServiceUnitTest.java
+++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/service/TableServiceUnitTest.java
@@ -78,7 +78,7 @@ public class TableServiceUnitTest extends AbstractUnitTest {
                 .thenReturn(Optional.of(DATABASE_3));
 
         /* test */
-        final Table response = tableService.findById(DATABASE_3_ID, TABLE_8_ID);
+        final Table response = tableService.findById(DATABASE_3, TABLE_8_ID);
         assertEquals(TABLE_8_ID, response.getId());
         assertEquals(TABLE_8_NAME, response.getName());
         assertEquals(TABLE_8_INTERNAL_NAME, response.getInternalName());
@@ -93,7 +93,7 @@ public class TableServiceUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(TableNotFoundException.class, () -> {
-            tableService.findById(DATABASE_3_ID, TABLE_1_ID);
+            tableService.findById(DATABASE_3, TABLE_1_ID);
         });
     }
 
@@ -105,7 +105,7 @@ public class TableServiceUnitTest extends AbstractUnitTest {
                 .thenReturn(Optional.of(DATABASE_3));
 
         /* test */
-        final Table response = tableService.findByName(DATABASE_3_ID, TABLE_8_INTERNAL_NAME);
+        final Table response = tableService.findByName(DATABASE_3, TABLE_8_INTERNAL_NAME);
         assertEquals(TABLE_8_ID, response.getId());
         assertEquals(TABLE_8_NAME, response.getName());
         assertEquals(TABLE_8_INTERNAL_NAME, response.getInternalName());
@@ -119,8 +119,8 @@ public class TableServiceUnitTest extends AbstractUnitTest {
                 .thenReturn(Optional.empty());
 
         /* test */
-        assertThrows(DatabaseNotFoundException.class, () -> {
-            tableService.findByName(DATABASE_3_ID, TABLE_1_INTERNAL_NAME);
+        assertThrows(TableNotFoundException.class, () -> {
+            tableService.findByName(DATABASE_3, TABLE_1_INTERNAL_NAME);
         });
     }
 
@@ -542,16 +542,7 @@ public class TableServiceUnitTest extends AbstractUnitTest {
 
         /* test */
         assertThrows(TableNotFoundException.class, () -> {
-            tableService.findByName(DATABASE_1_ID, "i_do_not_exist");
-        });
-    }
-
-    @Test
-    public void findById_databaseNotFound_fails() {
-
-        /* test */
-        assertThrows(DatabaseNotFoundException.class, () -> {
-            tableService.findByName(99999L, TABLE_3_INTERNALNAME);
+            tableService.findByName(DATABASE_1, "i_do_not_exist");
         });
     }
 
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 9444cea6e9..29718b0962 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
@@ -227,7 +227,7 @@ public class EndpointValidatorUnitTest extends AbstractUnitTest {
             TableNotFoundException, AccessNotFoundException {
 
         /* mock */
-        when(tableService.findById(DATABASE_1_ID, TABLE_1_ID))
+        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_READ_ACCESS);
@@ -243,7 +243,7 @@ public class EndpointValidatorUnitTest extends AbstractUnitTest {
             DatabaseNotFoundException, AccessNotFoundException, TableNotFoundException {
 
         /* mock */
-        when(tableService.findById(DATABASE_1_ID, TABLE_1_ID))
+        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);
@@ -312,7 +312,7 @@ public class EndpointValidatorUnitTest extends AbstractUnitTest {
             TableNotFoundException, AccessNotFoundException {
 
         /* mock */
-        when(tableService.findById(DATABASE_1_ID, TABLE_1_ID))
+        when(tableService.findById(DATABASE_1, TABLE_1_ID))
                 .thenReturn(TABLE_1);
         when(accessService.find(DATABASE_1, USER_1))
                 .thenReturn(DATABASE_1_USER_1_READ_ACCESS);
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/DataServiceGateway.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/DataServiceGateway.java
index 621ee53dfe..976830cebb 100644
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/DataServiceGateway.java
+++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/gateway/DataServiceGateway.java
@@ -21,57 +21,62 @@ public interface DataServiceGateway {
 
     /**
      * Create r/w access for a given user to a given database.
+     *
      * @param databaseId The database id.
-     * @param userId The user id.
-     * @param access The access.
+     * @param userId     The user id.
+     * @param access     The access.
      * @throws DataServiceConnectionException The connection to the data service could not be established.
-     * @throws DataServiceException The data service responded unexpectedly.
-     * @throws DatabaseNotFoundException Some of the privileged parameters of the given database were not provided by the metadata service.
+     * @throws DataServiceException           The data service responded unexpectedly.
+     * @throws DatabaseNotFoundException      Some of the privileged parameters of the given database were not provided by the metadata service.
      */
     void createAccess(Long databaseId, UUID userId, AccessTypeDto access) throws DataServiceConnectionException,
             DataServiceException, DatabaseNotFoundException;
 
     /**
      * Update r/w access for a given user to a given database.
+     *
      * @param databaseId The database id.
-     * @param userId The user id.
-     * @param access The access.
+     * @param userId     The user id.
+     * @param access     The access.
      * @throws DataServiceConnectionException The connection to the data service could not be established.
-     * @throws DataServiceException The data service responded unexpectedly.
-     * @throws AccessNotFoundException Some of the privileged parameters of the given database were not provided by the metadata service.
+     * @throws DataServiceException           The data service responded unexpectedly.
+     * @throws AccessNotFoundException        Some of the privileged parameters of the given database were not provided by the metadata service.
      */
     void updateAccess(Long databaseId, UUID userId, AccessTypeDto access) throws DataServiceConnectionException,
             DataServiceException, AccessNotFoundException;
 
     /**
      * Deletes access for a given user to a given database.
+     *
      * @param databaseId The database id.
-     * @param userId The user id.
+     * @param userId     The user id.
      * @throws DataServiceConnectionException The connection to the data service could not be established.
-     * @throws DataServiceException The data service responded unexpectedly.
-     * @throws AccessNotFoundException Some of the privileged parameters of the given database were not provided by the metadata service.
+     * @throws DataServiceException           The data service responded unexpectedly.
+     * @throws AccessNotFoundException        Some of the privileged parameters of the given database were not provided by the metadata service.
      */
     void deleteAccess(Long databaseId, UUID userId) throws DataServiceConnectionException, DataServiceException,
             AccessNotFoundException;
 
     /**
      * Creates a database in the data service.
+     *
      * @param data The data.
      * @return The created database, if successful.
      * @throws DataServiceConnectionException The connection to the data service could not be established.
-     * @throws DataServiceException The data service responded unexpectedly.
-     * @throws DatabaseNotFoundException Some of the privileged parameters of the given database were not provided by the metadata service.
+     * @throws DataServiceException           The data service responded unexpectedly.
+     * @throws DatabaseNotFoundException      Some of the privileged parameters of the given database were not provided by the metadata service.
      */
     DatabaseDto createDatabase(CreateDatabaseDto data) throws DataServiceConnectionException, DataServiceException,
             DatabaseNotFoundException;
 
     /**
      * Updates the user password in the given database in the data service.
+     *
      * @param databaseId The database id.
-     * @param data The user password.
+     * @param data       The user password.
      * @throws DataServiceConnectionException The connection to the data service could not be established.
-     * @throws DataServiceException The data service responded unexpectedly.
-     * @throws DatabaseNotFoundException Some of the privileged parameters of the given database were not provided by the metadata service.
+     * @throws DataServiceException           The data service responded unexpectedly.
+     * @throws DatabaseNotFoundException      Some of the privileged parameters of the given database were not provided by the metadata service.
      */
     void updateDatabase(Long databaseId, UpdateUserPasswordDto data) throws DataServiceConnectionException,
             DataServiceException, DatabaseNotFoundException;
@@ -81,102 +86,111 @@ public interface DataServiceGateway {
 
     /**
      * Creates a table in a given database.
+     *
      * @param databaseId The database id.
-     * @param data The table data.
+     * @param data       The table data.
      * @throws DataServiceConnectionException The connection to the data service could not be established.
-     * @throws DataServiceException The data service responded unexpectedly.
-     * @throws DatabaseNotFoundException Some of the privileged parameters of the given database were not provided by the metadata service.
-     * @throws TableExistsException  A table with this internal name exists already in the database.
+     * @throws DataServiceException           The data service responded unexpectedly.
+     * @throws DatabaseNotFoundException      Some of the privileged parameters of the given database were not provided by the metadata service.
+     * @throws TableExistsException           A table with this internal name exists already in the database.
      */
     void createTable(Long databaseId, TableCreateDto data) throws DataServiceConnectionException, DataServiceException,
             DatabaseNotFoundException, TableExistsException;
 
     /**
      * Deletes a given table in a given database.
+     *
      * @param databaseId The database id.
-     * @param tableId The table id.
+     * @param tableId    The table id.
      * @throws DataServiceConnectionException The connection to the data service could not be established.
-     * @throws DataServiceException The data service responded unexpectedly.
-     * @throws TableNotFoundException The given table was not found in the database.
+     * @throws DataServiceException           The data service responded unexpectedly.
+     * @throws TableNotFoundException         The given table was not found in the database.
      */
     void deleteTable(Long databaseId, Long tableId) throws DataServiceConnectionException, DataServiceException,
             TableNotFoundException;
 
     /**
      * Creates a view in the given database.
+     *
      * @param databaseId The database id.
-     * @param data The view data.
+     * @param data       The view data.
      * @return The created view, if successful.
      * @throws DataServiceConnectionException The connection to the data service could not be established.
-     * @throws DataServiceException The data service responded unexpectedly.
+     * @throws DataServiceException           The data service responded unexpectedly.
      */
     ViewDto createView(Long databaseId, ViewCreateDto data) throws DataServiceConnectionException, DataServiceException;
 
     /**
      * Deletes a given view in the given database.
+     *
      * @param databaseId The database id.
-     * @param viewId The view id.
+     * @param viewId     The view id.
      * @throws DataServiceConnectionException The connection to the data service could not be established.
-     * @throws DataServiceException The data service responded unexpectedly.
-     * @throws ViewNotFoundException The given view was not found in the database.
+     * @throws DataServiceException           The data service responded unexpectedly.
+     * @throws ViewNotFoundException          The given view was not found in the database.
      */
     void deleteView(Long databaseId, Long viewId) throws DataServiceConnectionException, DataServiceException,
             ViewNotFoundException;
 
     /**
      * Finds a given query in a given database.
+     *
      * @param databaseId The database id.
-     * @param queryId The query id.
+     * @param queryId    The query id.
      * @return The query, if successful.
      * @throws DataServiceConnectionException The connection to the data service could not be established.
-     * @throws DataServiceException The data service responded unexpectedly.
-     * @throws QueryNotFoundException The given query was not found in the query store.
+     * @throws DataServiceException           The data service responded unexpectedly.
+     * @throws QueryNotFoundException         The given query was not found in the query store.
      */
     QueryDto findQuery(Long databaseId, Long queryId) throws DataServiceConnectionException, DataServiceException,
             QueryNotFoundException;
 
     /**
      * Exports a given query.
+     *
      * @param databaseId The database id.
-     * @param queryId The query id.
+     * @param queryId    The query id.
      * @return The exported resource, if successful.
      * @throws DataServiceConnectionException The connection to the data service could not be established.
-     * @throws DataServiceException The data service responded unexpectedly.
-     * @throws QueryNotFoundException The given query was not found in the query store.
+     * @throws DataServiceException           The data service responded unexpectedly.
+     * @throws QueryNotFoundException         The given query was not found in the query store.
      */
     ExportResourceDto exportQuery(Long databaseId, Long queryId) throws DataServiceConnectionException,
             DataServiceException, QueryNotFoundException;
 
     /**
      * Obtain table schemas from a given database.
+     *
      * @param databaseId The database id.
      * @return The list of tables, if successful.
      * @throws DataServiceConnectionException The connection to the data service could not be established.
-     * @throws DataServiceException The data service responded unexpectedly.
-     * @throws TableNotFoundException The table was not found in the database.
+     * @throws DataServiceException           The data service responded unexpectedly.
+     * @throws TableNotFoundException         The table was not found in the database.
      */
     List<TableDto> getTableSchemas(Long databaseId) throws DataServiceConnectionException, DataServiceException,
             TableNotFoundException;
 
     /**
      * Obtain view schemas from a given database.
+     *
      * @param databaseId The database id.
      * @return The list of tables, if successful.
      * @throws DataServiceConnectionException The connection to the data service could not be established.
-     * @throws DataServiceException The data service responded unexpectedly.
-     * @throws ViewNotFoundException The table was not found in the database.
+     * @throws DataServiceException           The data service responded unexpectedly.
+     * @throws ViewNotFoundException          The table was not found in the database.
      */
     List<ViewDto> getViewSchemas(Long databaseId) throws DataServiceConnectionException, DataServiceException,
             ViewNotFoundException;
 
     /**
      * Obtain table statistics for a given table in a given database.
+     *
      * @param databaseId The database id.
-     * @param tableId The table id.
+     * @param tableId    The table id.
      * @return The statistic, if successful.
      * @throws DataServiceConnectionException The connection to the data service could not be established.
-     * @throws DataServiceException The data service responded unexpectedly.
-     * @throws TableNotFoundException The table was not found in the database.
+     * @throws DataServiceException           The data service responded unexpectedly.
+     * @throws TableNotFoundException         The table was not found in the database.
      */
     TableStatisticDto getTableStatistics(Long databaseId, Long tableId) throws DataServiceConnectionException,
             DataServiceException, TableNotFoundException;
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 09ad1e55da..2b0cd72dda 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
@@ -14,7 +14,10 @@ import at.tuwien.exception.*;
 import at.tuwien.gateway.DataServiceGateway;
 import lombok.extern.log4j.Log4j2;
 import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.http.*;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Service;
 import org.springframework.web.client.HttpClientErrorException;
 import org.springframework.web.client.HttpServerErrorException;
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/IdentifierService.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/IdentifierService.java
index 4d0228f410..ded8204f32 100644
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/IdentifierService.java
+++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/IdentifierService.java
@@ -87,7 +87,8 @@ public interface IdentifierService {
 
     /**
      * Publishes a draft identifier with DataCite.
-     * @param identifierId The identifier id.
+     *
+     * @param identifier The identifier.
      * @return The resulting identifier.
      * @throws SearchServiceException
      * @throws DatabaseNotFoundException
@@ -96,9 +97,9 @@ public interface IdentifierService {
      * @throws DataServiceConnectionException
      * @throws IdentifierNotFoundException
      */
-    Identifier publish(Long identifierId) throws SearchServiceException, DatabaseNotFoundException,
+    Identifier publish(Identifier identifier) throws SearchServiceException, DatabaseNotFoundException,
             SearchServiceConnectionException, MalformedException, DataServiceConnectionException,
-            IdentifierNotFoundException, ExternalServiceException;
+            ExternalServiceException;
 
     /**
      * Creates a new identifier in the metadata database for a query or database.
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/TableService.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/TableService.java
index 9a5c395c72..299283e68b 100644
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/TableService.java
+++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/TableService.java
@@ -7,7 +7,6 @@ import at.tuwien.entities.database.Database;
 import at.tuwien.entities.database.table.Table;
 import at.tuwien.entities.database.table.columns.TableColumn;
 import at.tuwien.exception.*;
-import org.springframework.transaction.annotation.Transactional;
 
 import java.security.Principal;
 
@@ -16,20 +15,20 @@ public interface TableService {
     /**
      * Find a table in the metadata database by database and table id.
      *
-     * @param databaseId The database id.
-     * @param tableId    The table id.
+     * @param database The database.
+     * @param tableId  The table id.
      * @return The table, if successful.
      */
-    Table findById(Long databaseId, Long tableId) throws TableNotFoundException, DatabaseNotFoundException;
+    Table findById(Database database, Long tableId) throws TableNotFoundException, DatabaseNotFoundException;
 
     /**
      * Find a table in the metadata database by database id and table name.
      *
-     * @param databaseId   The database id.
+     * @param database     The database.
      * @param internalName The table name.
      * @return The table, if successful.
      */
-    Table findByName(Long databaseId, String internalName) throws TableNotFoundException, DatabaseNotFoundException;
+    Table findByName(Database database, String internalName) throws TableNotFoundException, DatabaseNotFoundException;
 
 
     /**
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/DataCiteIdentifierServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/DataCiteIdentifierServiceImpl.java
index ed58148707..0151ea92f7 100644
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/DataCiteIdentifierServiceImpl.java
+++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/DataCiteIdentifierServiceImpl.java
@@ -70,9 +70,8 @@ public class DataCiteIdentifierServiceImpl implements IdentifierService {
 
     @Override
     @Transactional
-    public Identifier publish(Long identifierId) throws MalformedException, DataServiceConnectionException,
-            IdentifierNotFoundException, ExternalServiceException {
-        final Identifier identifier = find(identifierId);
+    public Identifier publish(Identifier identifier) throws MalformedException, DataServiceConnectionException,
+            ExternalServiceException {
         identifier.setStatus(IdentifierStatusType.PUBLISHED);
         identifier.setDoi(remoteSave(identifier, DataCiteDoiEvent.PUBLISH));
         return identifierRepository.save(identifier);
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java
index 0682225e8a..8706e18bbc 100644
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java
+++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/IdentifierServiceImpl.java
@@ -147,9 +147,8 @@ public class IdentifierServiceImpl implements IdentifierService {
 
     @Override
     @Transactional
-    public Identifier publish(Long identifierId) throws SearchServiceException, DatabaseNotFoundException,
-            SearchServiceConnectionException, IdentifierNotFoundException {
-        Identifier identifier = find(identifierId);
+    public Identifier publish(Identifier identifier) throws SearchServiceException, DatabaseNotFoundException,
+            SearchServiceConnectionException {
         /* publish identifier */
         identifier.setStatus(IdentifierStatusType.PUBLISHED);
         identifier = identifierRepository.save(identifier);
diff --git a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java
index 57b546fcea..52a9a63667 100644
--- a/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java
+++ b/dbrepo-metadata-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java
@@ -40,7 +40,6 @@ public class TableServiceImpl implements TableService {
     private final EntityService entityService;
     private final ConceptService conceptService;
     private final MetadataMapper metadataMapper;
-    private final DatabaseService databaseService;
     private final DataServiceGateway dataServiceGateway;
     private final DatabaseRepository databaseRepository;
     private final SearchServiceGateway searchServiceGateway;
@@ -48,15 +47,14 @@ public class TableServiceImpl implements TableService {
     @Autowired
     public TableServiceImpl(UserService userService, UnitService unitService, RabbitConfig rabbitConfig,
                             EntityService entityService, ConceptService conceptService, MetadataMapper metadataMapper,
-                            DatabaseService databaseService, DataServiceGateway dataServiceGateway,
-                            DatabaseRepository databaseRepository, SearchServiceGateway searchServiceGateway) {
+                            DataServiceGateway dataServiceGateway, DatabaseRepository databaseRepository,
+                            SearchServiceGateway searchServiceGateway) {
         this.userService = userService;
         this.unitService = unitService;
         this.rabbitConfig = rabbitConfig;
         this.entityService = entityService;
         this.conceptService = conceptService;
         this.metadataMapper = metadataMapper;
-        this.databaseService = databaseService;
         this.dataServiceGateway = dataServiceGateway;
         this.databaseRepository = databaseRepository;
         this.searchServiceGateway = searchServiceGateway;
@@ -64,10 +62,8 @@ public class TableServiceImpl implements TableService {
 
     @Override
     @Transactional(readOnly = true)
-    public Table findById(Long databaseId, Long tableId) throws TableNotFoundException,
-            DatabaseNotFoundException {
-        final Optional<Table> table = databaseService.findById(databaseId)
-                .getTables()
+    public Table findById(Database database, Long tableId) throws TableNotFoundException {
+        final Optional<Table> table = database.getTables()
                 .stream()
                 .filter(t -> t.getId().equals(tableId))
                 .findFirst();
@@ -80,10 +76,8 @@ public class TableServiceImpl implements TableService {
 
     @Override
     @Transactional(readOnly = true)
-    public Table findByName(Long databaseId, String internalName) throws TableNotFoundException,
-            DatabaseNotFoundException {
-        final Optional<Table> table = databaseService.findById(databaseId)
-                .getTables()
+    public Table findByName(Database database, String internalName) throws TableNotFoundException {
+        final Optional<Table> table = database.getTables()
                 .stream()
                 .filter(t -> t.getInternalName().equals(internalName))
                 .findFirst();
diff --git a/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/AbstractUnitTest.java b/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/AbstractUnitTest.java
index b0f5d3f85c..51b0e89b35 100644
--- a/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/AbstractUnitTest.java
+++ b/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/AbstractUnitTest.java
@@ -42,6 +42,10 @@ public abstract class AbstractUnitTest extends BaseTest {
         TABLE_1_PRIVILEGED_DTO.setDatabase(DATABASE_1_PRIVILEGED_DTO);
         VIEW_1_DTO.setDatabase(DATABASE_1_DTO);
         DATABASE_1.setIdentifiers(new LinkedList<>(List.of(IDENTIFIER_1, IDENTIFIER_2, IDENTIFIER_3, IDENTIFIER_4)));
+        IDENTIFIER_1.setDatabase(DATABASE_1);
+        IDENTIFIER_2.setDatabase(DATABASE_1);
+        IDENTIFIER_3.setDatabase(DATABASE_1);
+        IDENTIFIER_4.setDatabase(DATABASE_1);
         DATABASE_1.setTables(new LinkedList<>(List.of(TABLE_1, TABLE_2, TABLE_3, TABLE_4)));
         DATABASE_1.setViews(new LinkedList<>(List.of(VIEW_1, VIEW_2, VIEW_3)));
         DATABASE_1_PRIVILEGED_DTO.setIdentifiers(new LinkedList<>(List.of(IDENTIFIER_1_DTO, IDENTIFIER_2_DTO, IDENTIFIER_3_DTO, IDENTIFIER_4_DTO)));
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 f706dd021d..5e5b653852 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
@@ -6,6 +6,7 @@ import at.tuwien.api.amqp.ExchangeDto;
 import at.tuwien.api.amqp.GrantVirtualHostPermissionsDto;
 import at.tuwien.api.amqp.QueueDto;
 import at.tuwien.api.auth.LoginRequestDto;
+import at.tuwien.api.auth.RefreshTokenRequestDto;
 import at.tuwien.api.auth.SignupRequestDto;
 import at.tuwien.api.container.ContainerBriefDto;
 import at.tuwien.api.container.ContainerDto;
@@ -287,11 +288,15 @@ public abstract class BaseTest {
             .scope("openid")
             .build();
 
+    public final static RefreshTokenRequestDto REFRESH_TOKEN_REQUEST_DTO = RefreshTokenRequestDto.builder()
+            .refreshToken("ey.yee.skrr")
+            .build();
+
     public final static Long CONCEPT_1_ID = 1L;
     public final static String CONCEPT_1_NAME = "precipitation";
     public final static String CONCEPT_1_URI = "http://www.wikidata.org/entity/Q25257";
     public final static String CONCEPT_1_DESCRIPTION = null;
-    public final static Instant CONCEPT_1_CREATED = Instant.ofEpochSecond(1701976048L) /* 2023-12-07 19:07:27 */;
+    public final static Instant CONCEPT_1_CREATED = Instant.ofEpochSecond(1701976048L) /* 2023-12-07 19:07:27 (UTC) */;
 
     public final static ConceptSaveDto CONCEPT_1_SAVE_DTO = ConceptSaveDto.builder()
             .uri(CONCEPT_1_URI)
@@ -5946,6 +5951,7 @@ public abstract class BaseTest {
             .firstname(IDENTIFIER_1_CREATOR_1_FIRSTNAME)
             .lastname(IDENTIFIER_1_CREATOR_1_LASTNAME)
             .creatorName(IDENTIFIER_1_CREATOR_1_NAME)
+            .nameType(NameType.PERSONAL)
             .nameIdentifier(IDENTIFIER_1_CREATOR_1_ORCID)
             .nameIdentifierScheme(IDENTIFIER_1_CREATOR_1_IDENTIFIER_SCHEME_TYPE)
             .affiliation(IDENTIFIER_1_CREATOR_1_AFFILIATION)
@@ -5959,6 +5965,7 @@ public abstract class BaseTest {
             .firstname(IDENTIFIER_1_CREATOR_1_FIRSTNAME)
             .lastname(IDENTIFIER_1_CREATOR_1_LASTNAME)
             .creatorName(IDENTIFIER_1_CREATOR_1_NAME)
+            .nameType(NameTypeDto.PERSONAL)
             .nameIdentifier(IDENTIFIER_1_CREATOR_1_ORCID)
             .nameIdentifierScheme(IDENTIFIER_1_CREATOR_1_IDENTIFIER_SCHEME_TYPE_DTO)
             .affiliation(IDENTIFIER_1_CREATOR_1_AFFILIATION)
@@ -5972,6 +5979,7 @@ public abstract class BaseTest {
             .firstname(IDENTIFIER_1_CREATOR_1_FIRSTNAME)
             .lastname(IDENTIFIER_1_CREATOR_1_LASTNAME)
             .creatorName(IDENTIFIER_1_CREATOR_1_NAME)
+            .nameType(NameTypeDto.PERSONAL)
             .nameIdentifier(IDENTIFIER_1_CREATOR_1_ORCID)
             .nameIdentifierScheme(IDENTIFIER_1_CREATOR_1_IDENTIFIER_SCHEME_TYPE_DTO)
             .affiliation(IDENTIFIER_1_CREATOR_1_AFFILIATION)
@@ -7142,7 +7150,7 @@ public abstract class BaseTest {
     public final static String BANNER_MESSAGE_1_MESSAGE = "Next maintenance in 7 days!";
     public final static BannerMessageType BANNER_MESSAGE_1_TYPE = BannerMessageType.INFO;
     public final static BannerMessageTypeDto BANNER_MESSAGE_1_TYPE_DTO = BannerMessageTypeDto.INFO;
-    public final static Instant BANNER_MESSAGE_1_START = Instant.ofEpochSecond(1684577786L);
+    public final static Instant BANNER_MESSAGE_1_START = Instant.ofEpochSecond(1684577786L) /* 2022-12-23 22:00:00 (UTC) */;
     public final static Instant BANNER_MESSAGE_1_END = null;
 
     public final static BannerMessage BANNER_MESSAGE_1 = BannerMessage.builder()
@@ -7171,8 +7179,8 @@ public abstract class BaseTest {
     public final static String BANNER_MESSAGE_2_MESSAGE = "No operation on Christmas 2022!";
     public final static BannerMessageType BANNER_MESSAGE_2_TYPE = BannerMessageType.ERROR;
     public final static BannerMessageTypeDto BANNER_MESSAGE_2_TYPE_DTO = BannerMessageTypeDto.ERROR;
-    public final static Instant BANNER_MESSAGE_2_START = Instant.ofEpochSecond(1671836400L);
-    public final static Instant BANNER_MESSAGE_2_END = Instant.ofEpochSecond(1672009200L);
+    public final static Instant BANNER_MESSAGE_2_START = Instant.ofEpochSecond(1671836400L) /* 2022-12-23 22:00:00 (UTC) */;
+    public final static Instant BANNER_MESSAGE_2_END = Instant.ofEpochSecond(1672009200L) /* 2022-12-25 22:00:00 (UTC) */;
 
     public final static BannerMessage BANNER_MESSAGE_2 = BannerMessage.builder()
             .id(BANNER_MESSAGE_2_ID)
-- 
GitLab