diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/AbstractEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/AbstractEndpoint.java index 334128637ec5113e6478567d30e44b2f61ea3f5b..87a4d32532c586c0f2517862f0cd3cc104f4f054 100644 --- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/AbstractEndpoint.java +++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/AbstractEndpoint.java @@ -1,14 +1,50 @@ package at.tuwien.endpoints; +import at.tuwien.api.user.UserDetailsDto; import org.apache.spark.sql.Dataset; import org.apache.spark.sql.Row; +import org.springframework.security.core.Authentication; +import java.security.Principal; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.UUID; public abstract class AbstractEndpoint { + public boolean hasRole(Principal principal, String role) { + if (principal == null || role == null) { + return false; + } + final Authentication authentication = (Authentication) principal; + return authentication.getAuthorities() + .stream() + .anyMatch(a -> a.getAuthority().equals(role)); + } + + public boolean isSystem(Principal principal) { + if (principal == null) { + return false; + } + final Authentication authentication = (Authentication) principal; + return authentication.getAuthorities() + .stream() + .anyMatch(a -> a.getAuthority().equals("system")); + } + + public UUID getId(Principal principal) { + if (principal == null) { + return null; + } + final Authentication authentication = (Authentication) principal; + final UserDetailsDto user = (UserDetailsDto) authentication.getPrincipal(); + if (user.getId() == null) { + return null; + } + return UUID.fromString(user.getId()); + } + public List<Map<String, Object>> transform(Dataset<Row> dataset) { return dataset.collectAsList() .stream() diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java index 64d69a9013647c3bd886a690172994fcc382de95..5ca740f1cd32bae2a362cee03a81b7e9be9d6fba 100644 --- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java +++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/AccessEndpoint.java @@ -29,7 +29,7 @@ import java.util.UUID; @RestController @CrossOrigin(origins = "*") @RequestMapping(path = "/api/database/{databaseId}/access") -public class AccessEndpoint { +public class AccessEndpoint extends AbstractEndpoint { private final AccessService accessService; private final CredentialService credentialService; diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java index 0043b64dfe742fa97ea8a9bdbef671b5cc6118c1..d101a8c97393a41ecf4e76ea4e4fb4aa3c1f4993 100644 --- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java +++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/DatabaseEndpoint.java @@ -35,7 +35,7 @@ import java.sql.SQLException; @RestController @CrossOrigin(origins = "*") @RequestMapping(path = "/api/database") -public class DatabaseEndpoint { +public class DatabaseEndpoint extends AbstractEndpoint { private final SubsetService queryService; private final AccessService accessService; diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java index 4f1b5d59c94cee09b4666a8171367cd9d737ea27..8194ca624cde4b55f407e01ed716b899eb6c69b7 100644 --- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java +++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/SubsetEndpoint.java @@ -9,9 +9,9 @@ import at.tuwien.api.database.query.QueryDto; import at.tuwien.api.database.query.QueryPersistDto; import at.tuwien.api.error.ApiErrorDto; import at.tuwien.exception.*; +import at.tuwien.gateway.MetadataServiceGateway; import at.tuwien.mapper.MetadataMapper; import at.tuwien.service.*; -import at.tuwien.utils.UserUtil; import at.tuwien.validation.EndpointValidator; import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; @@ -56,11 +56,13 @@ public class SubsetEndpoint extends AbstractEndpoint { private final StorageService storageService; private final CredentialService credentialService; private final EndpointValidator endpointValidator; + private final MetadataServiceGateway metadataServiceGateway; @Autowired public SubsetEndpoint(SchemaService schemaService, SubsetService subsetService, MetadataMapper metadataMapper, MetricsService metricsService, StorageService storageService, - CredentialService credentialService, EndpointValidator endpointValidator) { + CredentialService credentialService, EndpointValidator endpointValidator, + MetadataServiceGateway metadataServiceGateway) { this.schemaService = schemaService; this.subsetService = subsetService; this.metadataMapper = metadataMapper; @@ -68,6 +70,7 @@ public class SubsetEndpoint extends AbstractEndpoint { this.storageService = storageService; this.credentialService = credentialService; this.endpointValidator = endpointValidator; + this.metadataServiceGateway = metadataServiceGateway; } @GetMapping @@ -98,11 +101,19 @@ public class SubsetEndpoint extends AbstractEndpoint { schema = @Schema(implementation = ApiErrorDto.class))}), }) public ResponseEntity<List<QueryDto>> list(@NotNull @PathVariable("databaseId") Long databaseId, - @RequestParam(name = "persisted", required = false) Boolean filterPersisted) + @RequestParam(name = "persisted", required = false) Boolean filterPersisted, + Principal principal) throws DatabaseUnavailableException, DatabaseNotFoundException, RemoteUnavailableException, QueryNotFoundException, NotAllowedException, MetadataServiceException { log.debug("endpoint find subsets in database, databaseId={}, filterPersisted={}", databaseId, filterPersisted); final PrivilegedDatabaseDto database = credentialService.getDatabase(databaseId); + if (!database.getIsPublic() || !database.getIsSchemaPublic()) { + if (principal == null) { + log.error("Failed to find subset: database is private & missing authentication"); + throw new NotAllowedException("Failed to find subset: database is private & missing authentication"); + } + metadataServiceGateway.getAccess(databaseId, getId(principal)); + } final List<QueryDto> queries; try { queries = subsetService.findAll(database, filterPersisted); @@ -154,15 +165,23 @@ public class SubsetEndpoint extends AbstractEndpoint { }) public ResponseEntity<?> findById(@NotNull @PathVariable("databaseId") Long databaseId, @NotNull @PathVariable("subsetId") Long subsetId, - @NotNull HttpServletRequest httpServletRequest, - @RequestParam(required = false) Instant timestamp) + @NotNull @RequestHeader("Accept") String accept, + @RequestParam(required = false) Instant timestamp, + Principal principal) throws DatabaseUnavailableException, DatabaseNotFoundException, RemoteUnavailableException, QueryNotFoundException, FormatNotAvailableException, StorageUnavailableException, UserNotFoundException, - MetadataServiceException, TableNotFoundException, ViewMalformedException, QueryMalformedException { - String accept = httpServletRequest.getHeader("Accept"); + MetadataServiceException, TableNotFoundException, ViewMalformedException, QueryMalformedException, + NotAllowedException { log.debug("endpoint find subset in database, databaseId={}, subsetId={}, accept={}, timestamp={}", databaseId, subsetId, accept, timestamp); final PrivilegedDatabaseDto database = credentialService.getDatabase(databaseId); + if (!database.getIsPublic() || !database.getIsSchemaPublic()) { + if (principal == null) { + log.error("Failed to find subset: database is private & missing authentication"); + throw new NotAllowedException("Failed to find subset: database is private & missing authentication"); + } + metadataServiceGateway.getAccess(databaseId, getId(principal)); + } final QueryDto subset; try { subset = subsetService.findById(database, subsetId); @@ -206,7 +225,7 @@ public class SubsetEndpoint extends AbstractEndpoint { @PostMapping @Observed(name = "dbrepo_subset_create") @Operation(summary = "Create subset", - description = "Creates a subset in the query store of the data database. Requires role `execute-query` for private databases.", + description = "Creates a subset in the query store of the data database. Can also be used without authentication if (and only if) the database is marked as public (i.e. when `is_public` = `is_schema_public` is set to `true`). Otherwise at least read access is required.", security = {@SecurityRequirement(name = "basicAuth"), @SecurityRequirement(name = "bearerAuth")}) @ApiResponses(value = { @ApiResponse(responseCode = "201", @@ -264,7 +283,12 @@ public class SubsetEndpoint extends AbstractEndpoint { endpointValidator.validateDataParams(page, size); endpointValidator.validateForbiddenStatements(data.getStatement()); /* parameters */ - final UUID userId = principal != null ? UserUtil.getId(principal) : null; + final UUID userId; + if (principal != null) { + userId = getId(principal); + } else { + userId = null; + } if (page == null) { page = 0L; log.debug("page not set: default to {}", page); @@ -279,6 +303,13 @@ public class SubsetEndpoint extends AbstractEndpoint { } /* create */ final PrivilegedDatabaseDto database = credentialService.getDatabase(databaseId); + if (!database.getIsPublic() || !database.getIsSchemaPublic()) { + if (principal == null) { + log.error("Failed to find subset: database is private & missing authentication"); + throw new NotAllowedException("Failed to find subset: database is private & missing authentication"); + } + metadataServiceGateway.getAccess(databaseId, getId(principal)); + } try { final Long subsetId = subsetService.create(database, data.getStatement(), timestamp, userId); return getData(databaseId, subsetId, principal, request, page, size); @@ -342,7 +373,7 @@ public class SubsetEndpoint extends AbstractEndpoint { log.error("Failed to re-execute query: no authentication found"); throw new NotAllowedException("Failed to re-execute query: no authentication found"); } - credentialService.getAccess(databaseId, UserUtil.getId(principal)); + credentialService.getAccess(databaseId, getId(principal)); } /* parameters */ if (page == null) { @@ -426,7 +457,7 @@ public class SubsetEndpoint extends AbstractEndpoint { log.debug("endpoint persist query, databaseId={}, queryId={}, data.persist={}, principal.name={}", databaseId, queryId, data.getPersist(), principal.getName()); final PrivilegedDatabaseDto database = credentialService.getDatabase(databaseId); - credentialService.getAccess(databaseId, UserUtil.getId(principal)); + credentialService.getAccess(databaseId, getId(principal)); try { subsetService.persist(database, queryId, data.getPersist()); final QueryDto dto = subsetService.findById(database, queryId); diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java index 11720b49063e9ba0c1dbc1b1f844aa215a6b3b4d..f0ec00a035e729157726b65af9f3c85949581c4e 100644 --- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java +++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java @@ -13,7 +13,6 @@ import at.tuwien.api.error.ApiErrorDto; import at.tuwien.exception.*; import at.tuwien.gateway.MetadataServiceGateway; import at.tuwien.service.*; -import at.tuwien.utils.UserUtil; import at.tuwien.validation.EndpointValidator; import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; @@ -277,7 +276,7 @@ public class TableEndpoint extends AbstractEndpoint { log.error("Failed find table data: authentication required"); throw new NotAllowedException("Failed to find table data: authentication required"); } - credentialService.getAccess(databaseId, UserUtil.getId(principal)); + credentialService.getAccess(databaseId, getId(principal)); } try { final HttpHeaders headers = new HttpHeaders(); @@ -335,17 +334,18 @@ public class TableEndpoint extends AbstractEndpoint { public ResponseEntity<Void> insertRawTuple(@NotNull @PathVariable("databaseId") Long databaseId, @NotNull @PathVariable("tableId") Long tableId, @Valid @RequestBody TupleDto data, - @NotNull Principal principal) + @NotNull Principal principal, + @RequestHeader("Authorization") String authorization) throws DatabaseUnavailableException, RemoteUnavailableException, TableNotFoundException, TableMalformedException, QueryMalformedException, NotAllowedException, StorageUnavailableException, StorageNotFoundException, MetadataServiceException { log.debug("endpoint insert raw table data, databaseId={}, tableId={}", databaseId, tableId); final PrivilegedTableDto table = credentialService.getTable(databaseId, tableId); - final DatabaseAccessDto access = credentialService.getAccess(databaseId, UserUtil.getId(principal)); - endpointValidator.validateOnlyWriteOwnOrWriteAllAccess(access.getType(), table.getOwner().getId(), UserUtil.getId(principal)); + final DatabaseAccessDto access = credentialService.getAccess(databaseId, getId(principal)); + endpointValidator.validateOnlyWriteOwnOrWriteAllAccess(access.getType(), table.getOwner().getId(), getId(principal)); try { tableService.createTuple(table, data); - metadataServiceGateway.updateTableStatistics(databaseId, tableId); + metadataServiceGateway.updateTableStatistics(databaseId, tableId, authorization); return ResponseEntity.status(HttpStatus.CREATED) .build(); } catch (SQLException e) { @@ -387,17 +387,18 @@ public class TableEndpoint extends AbstractEndpoint { public ResponseEntity<Void> updateRawTuple(@NotNull @PathVariable("databaseId") Long databaseId, @NotNull @PathVariable("tableId") Long tableId, @Valid @RequestBody TupleUpdateDto data, - @NotNull Principal principal) + @NotNull Principal principal, + @RequestHeader("Authorization") String authorization) throws DatabaseUnavailableException, RemoteUnavailableException, TableNotFoundException, TableMalformedException, QueryMalformedException, NotAllowedException, MetadataServiceException { log.debug("endpoint update raw table data, databaseId={}, tableId={}, data.keys={}", databaseId, tableId, data.getKeys()); final PrivilegedTableDto table = credentialService.getTable(databaseId, tableId); - final DatabaseAccessDto access = credentialService.getAccess(databaseId, UserUtil.getId(principal)); - endpointValidator.validateOnlyWriteOwnOrWriteAllAccess(access.getType(), table.getOwner().getId(), UserUtil.getId(principal)); + final DatabaseAccessDto access = credentialService.getAccess(databaseId, getId(principal)); + endpointValidator.validateOnlyWriteOwnOrWriteAllAccess(access.getType(), table.getOwner().getId(), getId(principal)); try { tableService.updateTuple(table, data); - metadataServiceGateway.updateTableStatistics(databaseId, tableId); + metadataServiceGateway.updateTableStatistics(databaseId, tableId, authorization); return ResponseEntity.status(HttpStatus.ACCEPTED) .build(); } catch (SQLException e) { @@ -439,17 +440,18 @@ public class TableEndpoint extends AbstractEndpoint { public ResponseEntity<Void> deleteRawTuple(@NotNull @PathVariable("databaseId") Long databaseId, @NotNull @PathVariable("tableId") Long tableId, @Valid @RequestBody TupleDeleteDto data, - @NotNull Principal principal) + @NotNull Principal principal, + @RequestHeader("Authorization") String authorization) throws DatabaseUnavailableException, RemoteUnavailableException, TableNotFoundException, TableMalformedException, QueryMalformedException, NotAllowedException, MetadataServiceException { log.debug("endpoint delete raw table data, databaseId={}, tableId={}, data.keys={}", databaseId, tableId, data.getKeys()); final PrivilegedTableDto table = credentialService.getTable(databaseId, tableId); - final DatabaseAccessDto access = credentialService.getAccess(databaseId, UserUtil.getId(principal)); - endpointValidator.validateOnlyWriteOwnOrWriteAllAccess(access.getType(), table.getOwner().getId(), UserUtil.getId(principal)); + final DatabaseAccessDto access = credentialService.getAccess(databaseId, getId(principal)); + endpointValidator.validateOnlyWriteOwnOrWriteAllAccess(access.getType(), table.getOwner().getId(), getId(principal)); try { tableService.deleteTuple(table, data); - metadataServiceGateway.updateTableStatistics(databaseId, tableId); + metadataServiceGateway.updateTableStatistics(databaseId, tableId, authorization); return ResponseEntity.status(HttpStatus.ACCEPTED) .build(); } catch (SQLException e) { @@ -510,7 +512,7 @@ public class TableEndpoint extends AbstractEndpoint { log.error("Failed to find table history: no authentication found"); throw new NotAllowedException("Failed to find table history: no authentication found"); } - credentialService.getAccess(databaseId, UserUtil.getId(principal)); + credentialService.getAccess(databaseId, getId(principal)); } try { final List<TableHistoryDto> dto = tableService.history(table, size); @@ -622,7 +624,7 @@ public class TableEndpoint extends AbstractEndpoint { log.error("Failed to export private table: principal is null"); throw new NotAllowedException("Failed to export private table: principal is null"); } - credentialService.getAccess(databaseId, UserUtil.getId(principal)); + credentialService.getAccess(databaseId, getId(principal)); } final Dataset<Row> dataset = tableService.getData(table.getDatabase(), table.getInternalName(), timestamp, null, null, null, null); @@ -669,14 +671,15 @@ public class TableEndpoint extends AbstractEndpoint { public ResponseEntity<Void> importDataset(@NotNull @PathVariable("databaseId") Long databaseId, @NotNull @PathVariable("tableId") Long tableId, @Valid @RequestBody ImportDto data, - @NotNull Principal principal) + @NotNull Principal principal, + @RequestHeader("Authorization") String authorization) throws RemoteUnavailableException, TableNotFoundException, NotAllowedException, MetadataServiceException, StorageNotFoundException, MalformedException, StorageUnavailableException, QueryMalformedException, DatabaseUnavailableException { log.debug("endpoint insert table data, databaseId={}, tableId={}, data.location={}", databaseId, tableId, data.getLocation()); final PrivilegedTableDto table = credentialService.getTable(databaseId, tableId); - final DatabaseAccessDto access = credentialService.getAccess(databaseId, UserUtil.getId(principal)); - endpointValidator.validateOnlyWriteOwnOrWriteAllAccess(access.getType(), table.getOwner().getId(), UserUtil.getId(principal)); + final DatabaseAccessDto access = credentialService.getAccess(databaseId, getId(principal)); + endpointValidator.validateOnlyWriteOwnOrWriteAllAccess(access.getType(), table.getOwner().getId(), getId(principal)); if (data.getLineTermination() == null) { data.setLineTermination("\\r\\n"); log.debug("line termination not present, default to {}", data.getLineTermination()); @@ -687,7 +690,7 @@ public class TableEndpoint extends AbstractEndpoint { log.error("Failed to establish connection to database: {}", e.getMessage()); throw new DatabaseUnavailableException("Failed to establish connection to database", e); } - metadataServiceGateway.updateTableStatistics(databaseId, tableId); + metadataServiceGateway.updateTableStatistics(databaseId, tableId, authorization); return ResponseEntity.accepted() .build(); } diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java index c8da4239861264539b2140c64fde2b95a8fb8bc4..2b0463483d66429dfb0afd942bbc80ccdfb2fd26 100644 --- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java +++ b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/endpoints/ViewEndpoint.java @@ -9,7 +9,6 @@ import at.tuwien.api.database.internal.PrivilegedViewDto; import at.tuwien.api.error.ApiErrorDto; import at.tuwien.exception.*; import at.tuwien.service.*; -import at.tuwien.utils.UserUtil; import at.tuwien.validation.EndpointValidator; import io.micrometer.observation.annotation.Observed; import io.swagger.v3.oas.annotations.Operation; @@ -275,7 +274,7 @@ public class ViewEndpoint extends AbstractEndpoint { log.error("Failed to get data from view: unauthorized"); throw new NotAllowedException("Failed to get data from view: unauthorized"); } - credentialService.getAccess(databaseId, UserUtil.getId(principal)); + credentialService.getAccess(databaseId, getId(principal)); } try { final HttpHeaders headers = new HttpHeaders(); @@ -351,7 +350,7 @@ public class ViewEndpoint extends AbstractEndpoint { log.error("Failed to export private view: principal is null"); throw new NotAllowedException("Failed to export private view: principal is null"); } - credentialService.getAccess(databaseId, UserUtil.getId(principal)); + credentialService.getAccess(databaseId, getId(principal)); } final Dataset<Row> dataset = tableService.getData(view.getDatabase(), view.getInternalName(), timestamp, null, null, null, null); diff --git a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/utils/UserUtil.java b/dbrepo-data-service/rest-service/src/main/java/at/tuwien/utils/UserUtil.java deleted file mode 100644 index 7a99e839edd3b97758e713260f798ae5357c53c6..0000000000000000000000000000000000000000 --- a/dbrepo-data-service/rest-service/src/main/java/at/tuwien/utils/UserUtil.java +++ /dev/null @@ -1,33 +0,0 @@ -package at.tuwien.utils; - -import at.tuwien.api.user.UserDetailsDto; -import org.springframework.security.core.Authentication; - -import java.security.Principal; -import java.util.UUID; - -public class UserUtil { - - public static boolean hasRole(Principal principal, String role) { - if (principal == null || role == null) { - return false; - } - final Authentication authentication = (Authentication) principal; - return authentication.getAuthorities() - .stream() - .anyMatch(a -> a.getAuthority().equals(role)); - } - - public static UUID getId(Principal principal) { - if (principal == null) { - return null; - } - final Authentication authentication = (Authentication) principal; - final UserDetailsDto user = (UserDetailsDto) authentication.getPrincipal(); - if (user.getId() == null) { - return null; - } - return UUID.fromString(user.getId()); - } - -} diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java index e7de4a04b3b48d089d254d4da2d442ba4a5334f7..1d5e35a41d78b4b812c792b176c447dd00f10da6 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/SubsetEndpointUnitTest.java @@ -23,13 +23,13 @@ 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.http.HttpStatus; -import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.mock.web.MockHttpServletRequest; 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.sql.SQLException; import java.time.Instant; import java.util.List; @@ -82,7 +82,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { .thenReturn(List.of(QUERY_1_DTO, QUERY_2_DTO, QUERY_3_DTO, QUERY_4_DTO, QUERY_5_DTO, QUERY_6_DTO)); /* test */ - final List<QueryDto> response = generic_list(DATABASE_3_ID, DATABASE_3_PRIVILEGED_DTO); + final List<QueryDto> response = generic_list(DATABASE_3_ID, DATABASE_3_PRIVILEGED_DTO, null); assertEquals(6, response.size()); } @@ -92,7 +92,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(DatabaseNotFoundException.class, () -> { - generic_list(null, null); + generic_list(null, null, null); }); } @@ -110,7 +110,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(DatabaseUnavailableException.class, () -> { - generic_list(DATABASE_3_ID, DATABASE_3_PRIVILEGED_DTO); + generic_list(DATABASE_3_ID, DATABASE_3_PRIVILEGED_DTO, null); }); } @@ -119,7 +119,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { public void findById_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException, UserNotFoundException, DatabaseUnavailableException, StorageUnavailableException, QueryMalformedException, QueryNotFoundException, FormatNotAvailableException, TableNotFoundException, MetadataServiceException, SQLException, - ViewMalformedException { + ViewMalformedException, NotAllowedException { /* mock */ when(credentialService.getDatabase(DATABASE_3_ID)) @@ -128,7 +128,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { .thenReturn(QUERY_5_DTO); /* test */ - generic_findById(QUERY_5_ID, MediaType.APPLICATION_JSON, null); + generic_findById(QUERY_5_ID, "application/json", null, null); } @Test @@ -144,7 +144,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(FormatNotAvailableException.class, () -> { - generic_findById(QUERY_5_ID, MediaType.APPLICATION_PDF, null); + generic_findById(QUERY_5_ID, "application/pdf", null, null); }); } @@ -153,7 +153,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { public void findById_acceptCsv_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException, UserNotFoundException, DatabaseUnavailableException, StorageUnavailableException, QueryMalformedException, QueryNotFoundException, FormatNotAvailableException, SQLException, MetadataServiceException, - TableNotFoundException, ViewMalformedException { + TableNotFoundException, ViewMalformedException, NotAllowedException { final Dataset<Row> mock = sparkSession.emptyDataFrame(); /* mock */ @@ -167,7 +167,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { .thenReturn(mock); /* test */ - generic_findById(QUERY_5_ID, MediaType.parseMediaType("text/csv"), null); + generic_findById(QUERY_5_ID,"text/csv", null, null); } @Test @@ -175,7 +175,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { public void findById_timestamp_succeeds() throws DatabaseNotFoundException, RemoteUnavailableException, UserNotFoundException, DatabaseUnavailableException, StorageUnavailableException, QueryMalformedException, QueryNotFoundException, FormatNotAvailableException, SQLException, MetadataServiceException, - TableNotFoundException, ViewMalformedException { + TableNotFoundException, ViewMalformedException, NotAllowedException { final Dataset<Row> mock = sparkSession.emptyDataFrame(); /* mock */ @@ -189,7 +189,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { .thenReturn(EXPORT_RESOURCE_DTO); /* test */ - generic_findById(QUERY_5_ID, MediaType.parseMediaType("text/csv"), Instant.now()); + generic_findById(QUERY_5_ID, "text/csv", Instant.now(), null); } @Test @@ -203,7 +203,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(DatabaseNotFoundException.class, () -> { - generic_findById(QUERY_5_ID, MediaType.APPLICATION_JSON, null); + generic_findById(QUERY_5_ID, "application/json", null, null); }); } @@ -221,7 +221,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(DatabaseUnavailableException.class, () -> { - generic_findById(QUERY_5_ID, MediaType.APPLICATION_JSON, null); + generic_findById(QUERY_5_ID, "application/json", null, null); }); } @@ -244,7 +244,7 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(DatabaseUnavailableException.class, () -> { - generic_findById(QUERY_5_ID, MediaType.parseMediaType("text/csv"), null); + generic_findById(QUERY_5_ID, "text/csv", null, null); }); } @@ -687,9 +687,9 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { }); } - protected List<QueryDto> generic_list(Long databaseId, PrivilegedDatabaseDto database) throws NotAllowedException, - DatabaseUnavailableException, QueryNotFoundException, DatabaseNotFoundException, RemoteUnavailableException, - MetadataServiceException { + protected List<QueryDto> generic_list(Long databaseId, PrivilegedDatabaseDto database, Principal principal) + throws NotAllowedException, DatabaseUnavailableException, QueryNotFoundException, DatabaseNotFoundException, + RemoteUnavailableException, MetadataServiceException { /* mock */ if (database != null) { @@ -702,22 +702,19 @@ public class SubsetEndpointUnitTest extends AbstractUnitTest { } /* test */ - final ResponseEntity<List<QueryDto>> response = subsetEndpoint.list(databaseId, null); + final ResponseEntity<List<QueryDto>> response = subsetEndpoint.list(databaseId, null, principal); assertEquals(HttpStatus.OK, response.getStatusCode()); return response.getBody(); } - protected void generic_findById(Long subsetId, MediaType accept, Instant timestamp) throws UserNotFoundException, - DatabaseUnavailableException, StorageUnavailableException, QueryMalformedException, QueryNotFoundException, - DatabaseNotFoundException, RemoteUnavailableException, FormatNotAvailableException, - MetadataServiceException, TableNotFoundException, ViewMalformedException, SQLException { - - /* mock */ - when(mockHttpServletRequest.getHeader("Accept")) - .thenReturn(accept.toString()); + protected void generic_findById(Long subsetId, String accept, Instant timestamp, Principal principal) + throws UserNotFoundException, DatabaseUnavailableException, StorageUnavailableException, + NotAllowedException, QueryMalformedException, QueryNotFoundException, DatabaseNotFoundException, + RemoteUnavailableException, FormatNotAvailableException, MetadataServiceException, TableNotFoundException, + ViewMalformedException { /* test */ - final ResponseEntity<?> response = subsetEndpoint.findById(DATABASE_3_ID, subsetId, mockHttpServletRequest, timestamp); + final ResponseEntity<?> response = subsetEndpoint.findById(DATABASE_3_ID, subsetId, accept, timestamp, principal); assertEquals(HttpStatus.OK, response.getStatusCode()); assertNotNull(response.getBody()); } diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/TableEndpointUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/TableEndpointUnitTest.java index ed720ac930565c0c1f97b291058d4aa293ba0061..e3171892a0be41f816feb749990a70aafc252f3d 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/TableEndpointUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/endpoint/TableEndpointUnitTest.java @@ -165,9 +165,8 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @Test @WithAnonymousUser - public void statistic_succeeds() throws DatabaseUnavailableException, TableNotFoundException, - TableMalformedException, DatabaseNotFoundException, RemoteUnavailableException, MetadataServiceException, - SQLException { + public void statistic_succeeds() throws DatabaseUnavailableException, TableNotFoundException, SQLException, + TableMalformedException, RemoteUnavailableException, MetadataServiceException { /* mock */ when(credentialService.getTable(DATABASE_3_ID, TABLE_8_ID)) @@ -454,10 +453,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .createTuple(TABLE_8_PRIVILEGED_DTO, request); doNothing() .when(metadataServiceGateway) - .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID); + .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN); /* test */ - final ResponseEntity<Void> response = tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); + final ResponseEntity<Void> response = tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL, TOKEN_ACCESS_TOKEN); assertEquals(HttpStatus.CREATED, response.getStatusCode()); } @@ -473,7 +472,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { - tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN); }); } @@ -495,7 +494,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(TableNotFoundException.class, () -> { - tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN); }); } @@ -518,7 +517,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(NotAllowedException.class, () -> { - tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN); }); } @@ -545,7 +544,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(DatabaseUnavailableException.class, () -> { - tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); + tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL, TOKEN_ACCESS_TOKEN); }); } @@ -568,7 +567,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .thenReturn(DATABASE_3_USER_1_WRITE_OWN_ACCESS_DTO); /* test */ - tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); + tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL, TOKEN_ACCESS_TOKEN); } @Test @@ -590,7 +589,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(NotAllowedException.class, () -> { - tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN); }); } @@ -613,7 +612,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .thenReturn(DATABASE_3_USER_3_WRITE_ALL_ACCESS_DTO); /* test */ - tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.insertRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN); } @Test @@ -641,10 +640,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .updateTuple(TABLE_8_PRIVILEGED_DTO, request); doNothing() .when(metadataServiceGateway) - .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID); + .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN); /* test */ - final ResponseEntity<Void> response = tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); + final ResponseEntity<Void> response = tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL, TOKEN_ACCESS_TOKEN); assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); } @@ -663,7 +662,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { - tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN); }); } @@ -688,7 +687,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(TableNotFoundException.class, () -> { - tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN); }); } @@ -714,7 +713,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(NotAllowedException.class, () -> { - tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN); }); } @@ -743,7 +742,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(DatabaseUnavailableException.class, () -> { - tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN); }); } @@ -772,10 +771,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .updateTuple(TABLE_8_PRIVILEGED_DTO, request); doNothing() .when(metadataServiceGateway) - .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID); + .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN); /* test */ - final ResponseEntity<Void> response = tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); + final ResponseEntity<Void> response = tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL, TOKEN_ACCESS_TOKEN); assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); } @@ -801,7 +800,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(NotAllowedException.class, () -> { - tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN); }); } @@ -830,10 +829,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .updateTuple(TABLE_8_PRIVILEGED_DTO, request); doNothing() .when(metadataServiceGateway) - .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID); + .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN); /* test */ - final ResponseEntity<Void> response = tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + final ResponseEntity<Void> response = tableEndpoint.updateRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN); assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); } @@ -858,10 +857,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .deleteTuple(TABLE_8_PRIVILEGED_DTO, request); doNothing() .when(metadataServiceGateway) - .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID); + .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN); /* test */ - final ResponseEntity<Void> response = tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); + final ResponseEntity<Void> response = tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL, TOKEN_ACCESS_TOKEN); assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); } @@ -876,7 +875,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { - tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN); }); } @@ -897,7 +896,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(TableNotFoundException.class, () -> { - tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN); }); } @@ -919,7 +918,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(NotAllowedException.class, () -> { - tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN); }); } @@ -944,7 +943,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(DatabaseUnavailableException.class, () -> { - tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); + tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL, TOKEN_ACCESS_TOKEN); }); } @@ -969,10 +968,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .deleteTuple(TABLE_8_PRIVILEGED_DTO, request); doNothing() .when(metadataServiceGateway) - .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID); + .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN); /* test */ - final ResponseEntity<Void> response = tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); + final ResponseEntity<Void> response = tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL, TOKEN_ACCESS_TOKEN); assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); } @@ -994,7 +993,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(NotAllowedException.class, () -> { - tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN); }); } @@ -1019,10 +1018,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .deleteTuple(TABLE_8_PRIVILEGED_DTO, request); doNothing() .when(metadataServiceGateway) - .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID); + .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN); /* test */ - final ResponseEntity<Void> response = tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + final ResponseEntity<Void> response = tableEndpoint.deleteRawTuple(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN); assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); } @@ -1269,10 +1268,10 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .importDataset(TABLE_8_PRIVILEGED_DTO, request); doNothing() .when(metadataServiceGateway) - .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID); + .updateTableStatistics(DATABASE_3_ID, TABLE_8_ID, TOKEN_ACCESS_TOKEN); /* test */ - final ResponseEntity<Void> response = tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); + final ResponseEntity<Void> response = tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL, TOKEN_ACCESS_TOKEN); assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); } @@ -1287,7 +1286,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(org.springframework.security.access.AccessDeniedException.class, () -> { - tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_4_PRINCIPAL); + tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_4_PRINCIPAL, TOKEN_ACCESS_TOKEN); }); } @@ -1308,7 +1307,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(TableNotFoundException.class, () -> { - tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN); }); } @@ -1334,7 +1333,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(DatabaseUnavailableException.class, () -> { - tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN); }); } @@ -1360,7 +1359,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(NotAllowedException.class, () -> { - tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN); }); } @@ -1382,7 +1381,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(NotAllowedException.class, () -> { - tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN); }); } @@ -1404,7 +1403,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .thenReturn(DATABASE_3_USER_1_WRITE_OWN_ACCESS_DTO); /* test */ - tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); + tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL, TOKEN_ACCESS_TOKEN); } @Test @@ -1425,7 +1424,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(NotAllowedException.class, () -> { - tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL); + tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_3_PRINCIPAL, TOKEN_ACCESS_TOKEN); }); } @@ -1447,7 +1446,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .thenReturn(DATABASE_3_USER_3_WRITE_ALL_ACCESS_DTO); /* test */ - tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL); + tableEndpoint.importDataset(DATABASE_3_ID, TABLE_8_ID, request, USER_1_PRINCIPAL, TOKEN_ACCESS_TOKEN); } @Test @@ -1468,7 +1467,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .thenReturn(DATABASE_1_USER_2_WRITE_ALL_ACCESS_DTO); /* test */ - tableEndpoint.importDataset(DATABASE_1_ID, TABLE_1_ID, request, USER_2_PRINCIPAL); + tableEndpoint.importDataset(DATABASE_1_ID, TABLE_1_ID, request, USER_2_PRINCIPAL, TOKEN_ACCESS_TOKEN); } @Test @@ -1489,7 +1488,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { .thenReturn(DATABASE_1_USER_2_WRITE_OWN_ACCESS_DTO); /* test */ - tableEndpoint.importDataset(DATABASE_1_ID, TABLE_2_ID, request, USER_2_PRINCIPAL); + tableEndpoint.importDataset(DATABASE_1_ID, TABLE_2_ID, request, USER_2_PRINCIPAL, TOKEN_ACCESS_TOKEN); } @Test @@ -1510,7 +1509,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(NotAllowedException.class, () -> { - tableEndpoint.importDataset(DATABASE_1_ID, TABLE_1_ID, request, USER_2_PRINCIPAL); + tableEndpoint.importDataset(DATABASE_1_ID, TABLE_1_ID, request, USER_2_PRINCIPAL, TOKEN_ACCESS_TOKEN); }); } @@ -1532,7 +1531,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { /* test */ assertThrows(NotAllowedException.class, () -> { - tableEndpoint.importDataset(DATABASE_1_ID, TABLE_2_ID, request, USER_2_PRINCIPAL); + tableEndpoint.importDataset(DATABASE_1_ID, TABLE_2_ID, request, USER_2_PRINCIPAL, TOKEN_ACCESS_TOKEN); }); } diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/gateway/MetadataServiceGatewayUnitTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/gateway/MetadataServiceGatewayUnitTest.java index 720e0652b3ee7f9dc0f30eb9ec014efe76275009..f1d8e0f9fc3e19aea2e4f435c0051e629b1a892a 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/gateway/MetadataServiceGatewayUnitTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/gateway/MetadataServiceGatewayUnitTest.java @@ -817,7 +817,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { .build()); /* test */ - metadataServiceGateway.updateTableStatistics(DATABASE_1_ID, TABLE_1_ID); + metadataServiceGateway.updateTableStatistics(DATABASE_1_ID, TABLE_1_ID, TOKEN_ACCESS_TOKEN); } @Test @@ -830,7 +830,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { /* test */ assertThrows(RemoteUnavailableException.class, () -> { - metadataServiceGateway.updateTableStatistics(DATABASE_1_ID, TABLE_1_ID); + metadataServiceGateway.updateTableStatistics(DATABASE_1_ID, TABLE_1_ID, TOKEN_ACCESS_TOKEN); }); } @@ -844,7 +844,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { /* test */ assertThrows(TableNotFoundException.class, () -> { - metadataServiceGateway.updateTableStatistics(DATABASE_1_ID, TABLE_1_ID); + metadataServiceGateway.updateTableStatistics(DATABASE_1_ID, TABLE_1_ID, TOKEN_ACCESS_TOKEN); }); } @@ -858,7 +858,7 @@ public class MetadataServiceGatewayUnitTest extends AbstractUnitTest { /* test */ assertThrows(MetadataServiceException.class, () -> { - metadataServiceGateway.updateTableStatistics(DATABASE_1_ID, TABLE_1_ID); + metadataServiceGateway.updateTableStatistics(DATABASE_1_ID, TABLE_1_ID, TOKEN_ACCESS_TOKEN); }); } diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java index 01bce16b08f84e7ad3901beb143ef0737d9ad571..e48f0c048da2dd6c83934566e6e5d9956838ba62 100644 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java +++ b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java @@ -27,7 +27,6 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; -import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.web.servlet.MockMvc; @@ -131,7 +130,7 @@ public class PrometheusEndpointMvcTest extends AbstractUnitTest { /* mock */ try { - subsetEndpoint.list(DATABASE_1_ID, null); + subsetEndpoint.list(DATABASE_1_ID, null, USER_1_PRINCIPAL); } catch (Exception e) { /* ignore */ } @@ -151,7 +150,7 @@ public class PrometheusEndpointMvcTest extends AbstractUnitTest { /* ignore */ } try { - subsetEndpoint.findById(DATABASE_1_ID, QUERY_1_ID, new MockHttpServletRequest(), null); + subsetEndpoint.findById(DATABASE_1_ID, QUERY_1_ID, "application/json", null, USER_1_PRINCIPAL); } catch (Exception e) { /* ignore */ } @@ -176,17 +175,17 @@ public class PrometheusEndpointMvcTest extends AbstractUnitTest { /* ignore */ } try { - tableEndpoint.insertRawTuple(DATABASE_1_ID, TABLE_1_ID, TupleDto.builder().build(), USER_1_PRINCIPAL); + tableEndpoint.insertRawTuple(DATABASE_1_ID, TABLE_1_ID, TupleDto.builder().build(), USER_1_PRINCIPAL, TOKEN_ACCESS_TOKEN); } catch (Exception e) { /* ignore */ } try { - tableEndpoint.updateRawTuple(DATABASE_1_ID, TABLE_1_ID, TupleUpdateDto.builder().build(), USER_1_PRINCIPAL); + tableEndpoint.updateRawTuple(DATABASE_1_ID, TABLE_1_ID, TupleUpdateDto.builder().build(), USER_1_PRINCIPAL, TOKEN_ACCESS_TOKEN); } catch (Exception e) { /* ignore */ } try { - tableEndpoint.deleteRawTuple(DATABASE_1_ID, TABLE_1_ID, TupleDeleteDto.builder().build(), USER_1_PRINCIPAL); + tableEndpoint.deleteRawTuple(DATABASE_1_ID, TABLE_1_ID, TupleDeleteDto.builder().build(), USER_1_PRINCIPAL, TOKEN_ACCESS_TOKEN); } catch (Exception e) { /* ignore */ } @@ -201,7 +200,7 @@ public class PrometheusEndpointMvcTest extends AbstractUnitTest { /* ignore */ } try { - tableEndpoint.importDataset(DATABASE_1_ID, TABLE_1_ID, ImportDto.builder().build(), USER_1_PRINCIPAL); + tableEndpoint.importDataset(DATABASE_1_ID, TABLE_1_ID, ImportDto.builder().build(), USER_1_PRINCIPAL, TOKEN_ACCESS_TOKEN); } catch (Exception e) { /* ignore */ } diff --git a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/utils/UserUtilTest.java b/dbrepo-data-service/rest-service/src/test/java/at/tuwien/utils/UserUtilTest.java deleted file mode 100644 index 13ddfce8d3c171b79096d2e0d1d05948848a8c86..0000000000000000000000000000000000000000 --- a/dbrepo-data-service/rest-service/src/test/java/at/tuwien/utils/UserUtilTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package at.tuwien.utils; - -import at.tuwien.test.BaseTest; -import org.junit.jupiter.api.Test; - - -import static org.junit.jupiter.api.Assertions.*; - -public class UserUtilTest extends BaseTest { - - @Test - public void constructor_succeeds() { - - /* test */ - new UserUtil(); - } - - @Test - public void hasRole_succeeds() { - assertTrue(UserUtil.hasRole(USER_1_PRINCIPAL, "find-container")); - } - - @Test - public void hasRole_principalMissing_fails() { - assertFalse(UserUtil.hasRole(null, "find-container")); - } - - @Test - public void hasRole_roleMissing_fails() { - assertFalse(UserUtil.hasRole(USER_1_PRINCIPAL, null)); - } - - @Test - public void getId_succeeds() { - assertEquals(USER_1_ID, UserUtil.getId(USER_1_PRINCIPAL)); - } - - @Test - public void getId_principalMissing_fails() { - assertNull(UserUtil.getId(null)); - } - - @Test - public void getId_roleMissing_fails() { - assertNull(UserUtil.getId(USER_LOCAL_ADMIN_PRINCIPAL)); - } -} diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/MetadataServiceGateway.java b/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/MetadataServiceGateway.java index 8c86d26bf4d7eb66d496713e2f286fade575aee0..e2851071dad176ca44a9f2d0952a0392101ad2eb 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/MetadataServiceGateway.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/MetadataServiceGateway.java @@ -118,11 +118,13 @@ public interface MetadataServiceGateway { /** * Update the table statistics in the metadata service. * - * @param databaseId The database id. - * @param tableId The table id. + * @param databaseId The database id. + * @param tableId The table id. + * @param authorization The authorization header. * @throws RemoteUnavailableException The remote service is not available and invalid data was returned. * @throws TableNotFoundException The table was not found. * @throws MetadataServiceException The remote service returned invalid data. */ - void updateTableStatistics(Long databaseId, Long tableId) throws TableNotFoundException, MetadataServiceException, RemoteUnavailableException; + void updateTableStatistics(Long databaseId, Long tableId, String authorization) throws TableNotFoundException, + MetadataServiceException, RemoteUnavailableException; } diff --git a/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java b/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java index 8e63bbb7b6aac5eee92a87c3d1221ff07b00c7e5..a39b93ef1d8133e9e27d14eeb6c0875d3b1ce280 100644 --- a/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java +++ b/dbrepo-data-service/services/src/main/java/at/tuwien/gateway/impl/MetadataServiceGatewayImpl.java @@ -11,21 +11,20 @@ import at.tuwien.api.database.table.internal.PrivilegedTableDto; import at.tuwien.api.identifier.IdentifierBriefDto; import at.tuwien.api.user.UserDto; import at.tuwien.api.user.internal.PrivilegedUserDto; +import at.tuwien.config.GatewayConfig; import at.tuwien.exception.*; import at.tuwien.gateway.MetadataServiceGateway; import at.tuwien.mapper.MetadataMapper; import jakarta.validation.constraints.NotNull; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; +import org.springframework.http.*; import org.springframework.stereotype.Service; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.HttpServerErrorException; import org.springframework.web.client.ResourceAccessException; import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.DefaultUriBuilderFactory; import java.time.Instant; import java.util.List; @@ -36,11 +35,14 @@ import java.util.UUID; public class MetadataServiceGatewayImpl implements MetadataServiceGateway { private final RestTemplate restTemplate; + private final GatewayConfig gatewayConfig; private final MetadataMapper metadataMapper; @Autowired - public MetadataServiceGatewayImpl(RestTemplate restTemplate, MetadataMapper metadataMapper) { + public MetadataServiceGatewayImpl(RestTemplate restTemplate, GatewayConfig gatewayConfig, + MetadataMapper metadataMapper) { this.restTemplate = restTemplate; + this.gatewayConfig = gatewayConfig; this.metadataMapper = metadataMapper; } @@ -48,8 +50,10 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { public PrivilegedContainerDto getContainerById(Long containerId) throws RemoteUnavailableException, ContainerNotFoundException, MetadataServiceException { final ResponseEntity<ContainerDto> response; + final String url = "/api/container/" + containerId; + log.debug("get privileged container info from metadata service: {}", url); try { - response = restTemplate.exchange("/api/container/" + containerId, HttpMethod.GET, HttpEntity.EMPTY, + response = restTemplate.exchange(url, HttpMethod.GET, HttpEntity.EMPTY, ContainerDto.class); } catch (ResourceAccessException | HttpServerErrorException e) { log.error("Failed to find container with id {}: {}", containerId, e.getMessage()); @@ -85,10 +89,9 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { MetadataServiceException { final ResponseEntity<PrivilegedDatabaseDto> response; final String url = "/api/database/" + id; - log.debug("find privileged database from url: {}", url); + log.debug("get privileged database info from metadata service: {}", url); try { - response = restTemplate.exchange(url, HttpMethod.GET, HttpEntity.EMPTY, - PrivilegedDatabaseDto.class); + response = restTemplate.exchange(url, HttpMethod.GET, HttpEntity.EMPTY, PrivilegedDatabaseDto.class); } catch (ResourceAccessException | HttpServerErrorException e) { log.error("Failed to find database with id {}: {}", id, e.getMessage()); throw new RemoteUnavailableException("Failed to find database: " + e.getMessage(), e); @@ -125,7 +128,7 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { RemoteUnavailableException, MetadataServiceException { final ResponseEntity<TableDto> response; final String url = "/api/database/" + databaseId + "/table/" + id; - log.debug("find privileged table from url: {}", url); + log.debug("get privileged table info from metadata service: {}", url); try { response = restTemplate.exchange(url, HttpMethod.GET, HttpEntity.EMPTY, TableDto.class); } catch (ResourceAccessException | HttpServerErrorException e) { @@ -167,7 +170,7 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { ViewNotFoundException, MetadataServiceException { final ResponseEntity<ViewDto> response; final String url = "/api/database/" + databaseId + "/view/" + id; - log.debug("find privileged view from url: {}", url); + log.debug("get privileged view info from metadata service: {}", url); try { response = restTemplate.exchange(url, HttpMethod.GET, HttpEntity.EMPTY, ViewDto.class); } catch (ResourceAccessException | HttpServerErrorException e) { @@ -208,8 +211,10 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { public UserDto getUserById(UUID userId) throws RemoteUnavailableException, UserNotFoundException, MetadataServiceException { final ResponseEntity<UserDto> response; + final String url = "/api/user/" + userId; + log.debug("get user info from metadata service: {}", url); try { - response = restTemplate.exchange("/api/user/" + userId, HttpMethod.GET, HttpEntity.EMPTY, UserDto.class); + response = restTemplate.exchange(url, HttpMethod.GET, HttpEntity.EMPTY, UserDto.class); } catch (ResourceAccessException | HttpServerErrorException e) { log.error("Failed to find user with id {}: {}", userId, e.getMessage()); throw new RemoteUnavailableException("Failed to find user: " + e.getMessage(), e); @@ -232,8 +237,10 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { public PrivilegedUserDto getPrivilegedUserById(UUID userId) throws RemoteUnavailableException, UserNotFoundException, MetadataServiceException { final ResponseEntity<UserDto> response; + final String url = "/api/user/" + userId; + log.debug("get privileged user info from metadata service: {}", url); try { - response = restTemplate.exchange("/api/user/" + userId, HttpMethod.GET, HttpEntity.EMPTY, UserDto.class); + response = restTemplate.exchange(url, HttpMethod.GET, HttpEntity.EMPTY, UserDto.class); } catch (ResourceAccessException | HttpServerErrorException e) { log.error("Failed to find user with id {}: {}", userId, e.getMessage()); throw new RemoteUnavailableException("Failed to find user: " + e.getMessage(), e); @@ -267,8 +274,10 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { public DatabaseAccessDto getAccess(Long databaseId, UUID userId) throws RemoteUnavailableException, NotAllowedException, MetadataServiceException { final ResponseEntity<DatabaseAccessDto> response; + final String url = "/api/database/" + databaseId + "/access/" + userId; + log.debug("get database access from metadata service: {}", url); try { - response = restTemplate.exchange("/api/database/" + databaseId + "/access/" + userId, HttpMethod.GET, HttpEntity.EMPTY, DatabaseAccessDto.class); + response = restTemplate.exchange(url, HttpMethod.GET, HttpEntity.EMPTY, DatabaseAccessDto.class); } catch (ResourceAccessException | HttpServerErrorException e) { log.error("Failed to find database access for user with id {}: {}", userId, e.getMessage()); throw new RemoteUnavailableException("Failed to find database access: " + e.getMessage(), e); @@ -292,7 +301,7 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { RemoteUnavailableException, DatabaseNotFoundException { final ResponseEntity<IdentifierBriefDto[]> response; final String url = "/api/identifier?dbid=" + databaseId + (subsetId != null ? ("&qid=" + subsetId) : ""); - log.trace("mapped url: {}", url); + log.debug("get identifiers from metadata service: {}", url); try { response = restTemplate.exchange(url, HttpMethod.GET, HttpEntity.EMPTY, IdentifierBriefDto[].class); } catch (ResourceAccessException | HttpServerErrorException e) { @@ -314,13 +323,17 @@ public class MetadataServiceGatewayImpl implements MetadataServiceGateway { } @Override - public void updateTableStatistics(Long databaseId, Long tableId) throws TableNotFoundException, MetadataServiceException, - RemoteUnavailableException { + public void updateTableStatistics(Long databaseId, Long tableId, String authorization) throws TableNotFoundException, + MetadataServiceException, RemoteUnavailableException { final ResponseEntity<Void> response; final String url = "/api/database/" + databaseId + "/table/" + tableId + "/statistic"; - log.trace("mapped url: {}", url); + log.debug("update table statistics in metadata service: {}", url); + final RestTemplate restTemplate = new RestTemplate(); + restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory(gatewayConfig.getMetadataEndpoint())); + final HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization", authorization); try { - response = restTemplate.exchange(url, HttpMethod.PUT, HttpEntity.EMPTY, Void.class); + response = restTemplate.exchange(url, HttpMethod.PUT, new HttpEntity<>(null, headers), Void.class); } catch (ResourceAccessException | HttpServerErrorException e) { log.error("Failed to update table statistic for table with id {}: {}", tableId, e.getMessage()); throw new RemoteUnavailableException("Failed to update table statistic: " + e.getMessage(), e); diff --git a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseBriefDto.java b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseBriefDto.java index e8456c42ef8d0dc6a464a43be64c940faa46c792..2d01129fefc8ca18d9e4d16d9ebbef6b0ad04f59 100644 --- a/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseBriefDto.java +++ b/dbrepo-metadata-service/api/src/main/java/at/tuwien/api/database/DatabaseBriefDto.java @@ -43,6 +43,11 @@ public class DatabaseBriefDto { @Schema(example = "true") private Boolean isPublic; + @NotNull + @JsonProperty("is_schema_public") + @Schema(example = "true") + private Boolean isSchemaPublic; + private List<IdentifierBriefDto> identifiers; @ToString.Exclude diff --git a/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/Database.java b/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/Database.java index a692cce3297525771471d60a28ed83c9df4b804d..aef7ebd7b8e5cfe718814f18c30ea0e5b157e37a 100644 --- a/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/Database.java +++ b/dbrepo-metadata-service/entities/src/main/java/at/tuwien/entities/database/Database.java @@ -33,10 +33,10 @@ import static jakarta.persistence.GenerationType.IDENTITY; }) @NamedQueries({ @NamedQuery(name = "Database.findAllDesc", query = "select distinct d from Database d order by d.id desc"), - @NamedQuery(name = "Database.findAllPublicDesc", query = "select distinct d from Database d where d.isPublic = true order by d.id desc"), - @NamedQuery(name = "Database.findAllPublicOrReadAccessDesc", query = "select distinct d from Database d where d.isPublic = true or exists(select a.hdbid from DatabaseAccess a where a.huserid = ?1 and a.hdbid = d.id) order by d.id desc"), - @NamedQuery(name = "Database.findAllPublicOrReadAccessByInternalNameDesc", query = "select distinct d from Database d where d.isPublic = true and d.internalName = ?2 or exists(select a.hdbid from DatabaseAccess a where a.huserid = ?1 and a.hdbid = d.id) order by d.id desc"), - @NamedQuery(name = "Database.findAllPublicByInternalNameDesc", query = "select distinct d from Database d where d.isPublic = true and d.internalName = ?1 order by d.id desc"), + @NamedQuery(name = "Database.findAllPublicDesc", query = "select distinct d from Database d where d.isPublic = true or d.isSchemaPublic = true order by d.id desc"), + @NamedQuery(name = "Database.findAllPublicOrReadAccessDesc", query = "select distinct d from Database d where d.isPublic = true or d.isSchemaPublic = true or exists(select a.hdbid from DatabaseAccess a where a.huserid = ?1 and a.hdbid = d.id) order by d.id desc"), + @NamedQuery(name = "Database.findAllPublicOrReadAccessByInternalNameDesc", query = "select distinct d from Database d where (d.isPublic = true or d.isSchemaPublic = true) and d.internalName = ?2 or exists(select a.hdbid from DatabaseAccess a where a.huserid = ?1 and a.hdbid = d.id) order by d.id desc"), + @NamedQuery(name = "Database.findAllPublicByInternalNameDesc", query = "select distinct d from Database d where (d.isPublic = true or d.isSchemaPublic = true) and d.internalName = ?1 order by d.id desc"), }) public class Database implements Serializable { 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 c4ad192eec938eb0d115d0bb469dd5fd0a46dff7..d1f40b29bac3fe2c88c017f2635ab0748f72f5ed 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 @@ -70,7 +70,7 @@ public class TableEndpoint extends AbstractEndpoint { @Transactional(readOnly = true) @Observed(name = "dbrepo_tables_findall") @Operation(summary = "List tables", - description = "Lists all tables known to the metadata database.", + description = "Lists all tables known to the metadata database. When a database has a hidden schema (i.e. when `is_schema_public` is `false`), then the user needs to have at least read access and the role `list-tables`.", security = {@SecurityRequirement(name = "bearerAuth"), @SecurityRequirement(name = "basicAuth")}) @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -94,10 +94,11 @@ public class TableEndpoint extends AbstractEndpoint { DatabaseNotFoundException, UserNotFoundException, AccessNotFoundException { log.debug("endpoint list tables, databaseId={}", databaseId); final Database database = databaseService.findById(databaseId); - endpointValidator.validateOnlyPrivateAccess(database, principal); - endpointValidator.validateOnlyPrivateHasRole(database, principal, "list-tables"); + endpointValidator.validateOnlyPrivateSchemaAccess(database, principal); + endpointValidator.validateOnlyPrivateSchemaHasRole(database, principal, "list-tables"); return ResponseEntity.ok(database.getTables() .stream() + .filter(Table::getIsPublic) .map(metadataMapper::tableToTableBriefDto) .collect(Collectors.toList())); } @@ -435,7 +436,7 @@ public class TableEndpoint extends AbstractEndpoint { @Transactional(readOnly = true) @Observed(name = "dbrepo_tables_find") @Operation(summary = "Find table", - description = "Finds a table with id. When the `system` role is present, the endpoint responds with additional connection metadata in the header.", + description = "Finds a table with id. When a table is hidden (i.e. when `is_public` is `false`), then the user needs to have at least read access and the role `find-table`. When the `system` role is present, the endpoint responds with additional connection metadata in the header.", security = {@SecurityRequirement(name = "bearerAuth"), @SecurityRequirement(name = "basicAuth")}) @ApiResponses(value = { @ApiResponse(responseCode = "200", @@ -475,9 +476,12 @@ public class TableEndpoint extends AbstractEndpoint { public ResponseEntity<TableDto> findById(@NotNull @PathVariable("databaseId") Long databaseId, @NotNull @PathVariable("tableId") Long tableId, Principal principal) throws DataServiceException, - DataServiceConnectionException, TableNotFoundException, DatabaseNotFoundException, QueueNotFoundException { + DataServiceConnectionException, TableNotFoundException, DatabaseNotFoundException, QueueNotFoundException, + UserNotFoundException, NotAllowedException, AccessNotFoundException { log.debug("endpoint find table, databaseId={}, tableId={}", databaseId, tableId); final Database database = databaseService.findById(databaseId); + endpointValidator.validateOnlyPrivateDataAccess(database, principal); + endpointValidator.validateOnlyPrivateDataHasRole(database, principal, "find-table"); final Table table = tableService.findById(database, tableId); boolean hasAccess = isSystem(principal); boolean isOwner = false; diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java index a748c0d82dab0032ea0b75f6e649f1d1973469d4..b8716532b022ebc0602cb20dc8119a3ffbcc8504 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/validation/EndpointValidator.java @@ -43,7 +43,7 @@ public class EndpointValidator extends AbstractEndpoint { this.accessService = accessService; } - public void validateOnlyPrivateAccess(Database database, Principal principal, boolean writeAccessOnly) + public void validateOnlyPrivateDataAccess(Database database, Principal principal, boolean writeAccessOnly) throws NotAllowedException, UserNotFoundException, AccessNotFoundException { if (database.getIsPublic()) { log.trace("database with id {} is public: no access needed", database.getId()); @@ -52,9 +52,23 @@ public class EndpointValidator extends AbstractEndpoint { validateOnlyAccess(database, principal, writeAccessOnly); } - public void validateOnlyPrivateAccess(Database database, Principal principal) throws NotAllowedException, + public void validateOnlyPrivateSchemaAccess(Database database, Principal principal, boolean writeAccessOnly) + throws NotAllowedException, UserNotFoundException, AccessNotFoundException { + if (database.getIsSchemaPublic()) { + log.trace("database schema with id {} is public: no access needed", database.getId()); + return; + } + validateOnlyAccess(database, principal, writeAccessOnly); + } + + public void validateOnlyPrivateDataAccess(Database database, Principal principal) throws NotAllowedException, UserNotFoundException, AccessNotFoundException { - validateOnlyPrivateAccess(database, principal, false); + validateOnlyPrivateDataAccess(database, principal, false); + } + + public void validateOnlyPrivateSchemaAccess(Database database, Principal principal) throws NotAllowedException, + UserNotFoundException, AccessNotFoundException { + validateOnlyPrivateSchemaAccess(database, principal, false); } public void validateOnlyAccess(Database database, Principal principal, boolean writeAccessOnly) @@ -221,7 +235,7 @@ public class EndpointValidator extends AbstractEndpoint { throw new NotAllowedException("Access not allowed: insufficient access (neither owner nor write-all access)"); } - public void validateOnlyPrivateHasRole(Database database, Principal principal, String role) + public void validateOnlyPrivateDataHasRole(Database database, Principal principal, String role) throws NotAllowedException { if (database.getIsPublic()) { log.trace("database with id {} is public: no access needed", database.getId()); @@ -240,6 +254,25 @@ public class EndpointValidator extends AbstractEndpoint { log.trace("principal has role '{}': access granted", role); } + public void validateOnlyPrivateSchemaHasRole(Database database, Principal principal, String role) + throws NotAllowedException { + if (database.getIsSchemaPublic()) { + log.trace("database with id {} has public schema: no access needed", database.getId()); + return; + } + log.trace("database with id {} has private schema", database.getId()); + if (principal == null) { + log.error("Access not allowed: no authorization provided"); + throw new NotAllowedException("Access not allowed: no authorization provided"); + } + log.trace("principal: {}", principal.getName()); + if (!hasRole(principal, role)) { + log.error("Access not allowed: role {} missing", role); + throw new NotAllowedException("Access not allowed: role " + role + " missing"); + } + log.trace("principal has role '{}': access granted", role); + } + public void validateDataParams(Long page, Long size) throws PaginationException { log.trace("validate data params, page={}, size={}", page, size); if ((page == null && size != null) || (page != null && size == null)) { 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 dcc10f61a70ef663f0b4427c60f1acefba8f58ed..8f72fe57dc24b2a7f50201df3737f786aad47ee7 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 @@ -513,7 +513,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @WithAnonymousUser public void findById_publicAnonymous_succeeds() throws DataServiceException, DataServiceConnectionException, TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException, QueueNotFoundException, - UserNotFoundException { + UserNotFoundException, NotAllowedException { /* test */ generic_findById(DATABASE_3_ID, DATABASE_3, TABLE_8_ID, TABLE_8, null, null, null); @@ -543,7 +543,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @WithMockUser(username = USER_1_USERNAME, authorities = "find-table") public void findById_publicHasRole_succeeds() throws DataServiceException, DataServiceConnectionException, TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException, QueueNotFoundException, - UserNotFoundException { + UserNotFoundException, NotAllowedException { /* 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); @@ -556,7 +556,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @WithMockUser(username = USER_4_USERNAME) public void findById_publicNoRole_succeeds() throws DataServiceException, DataServiceConnectionException, TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException, QueueNotFoundException, - UserNotFoundException { + UserNotFoundException, NotAllowedException { /* test */ generic_findById(DATABASE_3_ID, DATABASE_3, TABLE_8_ID, TABLE_8, USER_1_PRINCIPAL, USER_1, null); @@ -900,7 +900,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @WithAnonymousUser public void findById_privateAnonymous_succeeds() throws DataServiceException, DataServiceConnectionException, TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException, QueueNotFoundException, - UserNotFoundException { + UserNotFoundException, NotAllowedException { /* test */ generic_findById(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, null, null, null); @@ -930,7 +930,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @WithMockUser(username = USER_1_USERNAME, authorities = "find-table") public void findById_privateHasRole_succeeds() throws DataServiceException, DataServiceConnectionException, TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException, QueueNotFoundException, - UserNotFoundException { + UserNotFoundException, NotAllowedException { /* 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); @@ -943,7 +943,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { @WithMockUser(username = USER_4_USERNAME) public void findById_privateNoRole_succeeds() throws DataServiceException, DataServiceConnectionException, TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException, QueueNotFoundException, - UserNotFoundException { + UserNotFoundException, NotAllowedException { /* test */ generic_findById(DATABASE_1_ID, DATABASE_1, TABLE_1_ID, TABLE_1, USER_4_PRINCIPAL, USER_4, null); @@ -1160,7 +1160,7 @@ public class TableEndpointUnitTest extends AbstractUnitTest { Table table, Principal principal, User user, DatabaseAccess access) throws DataServiceException, DataServiceConnectionException, TableNotFoundException, DatabaseNotFoundException, AccessNotFoundException, - QueueNotFoundException, UserNotFoundException { + QueueNotFoundException, UserNotFoundException, NotAllowedException { /* mock */ if (database != null) { 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 29718b0962c76b8b1ad01ef003de092459d92714..342a9e328ecc8e69abe7bfedf1827bdc7777fff3 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 @@ -324,20 +324,38 @@ public class EndpointValidatorUnitTest extends AbstractUnitTest { } @Test - public void validateOnlyPrivateHasRole_privatePrincipalMissing_fails() { + public void validateOnlyPrivateDataHasRole_privatePrincipalMissing_fails() { /* test */ assertThrows(NotAllowedException.class, () -> { - endpointValidator.validateOnlyPrivateHasRole(DATABASE_1, null, "list-tables"); + endpointValidator.validateOnlyPrivateDataHasRole(DATABASE_1, null, "list-tables"); }); } @Test - public void validateOnlyPrivateHasRole_privateRoleMissing_fails() { + public void validateOnlyPrivateDataHasRole_privateRoleMissing_fails() { /* test */ assertThrows(NotAllowedException.class, () -> { - endpointValidator.validateOnlyPrivateHasRole(DATABASE_1, USER_4_PRINCIPAL, "list-tables"); + endpointValidator.validateOnlyPrivateDataHasRole(DATABASE_1, USER_4_PRINCIPAL, "list-tables"); + }); + } + + @Test + public void validateOnlyPrivateSchemaHasRole_privatePrincipalMissing_fails() { + + /* test */ + assertThrows(NotAllowedException.class, () -> { + endpointValidator.validateOnlyPrivateSchemaHasRole(DATABASE_1, null, "list-tables"); + }); + } + + @Test + public void validateOnlyPrivateSchemaHasRole_privateRoleMissing_fails() { + + /* test */ + assertThrows(NotAllowedException.class, () -> { + endpointValidator.validateOnlyPrivateSchemaHasRole(DATABASE_1, USER_4_PRINCIPAL, "list-tables"); }); } 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 5e5b653852782dd6fab81dd39d1d7e5762396bfa..2859a30a7134109b7db625d491b353a39ff9e5e2 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 @@ -283,9 +283,12 @@ public abstract class BaseTest { .type(AccessTypeDto.WRITE_ALL) .build(); + public final static String TOKEN_ACCESS_TOKEN = "ey.yee.skrr"; + public final static String TOKEN_ACCESS_SCOPE = "openid"; + public final static TokenDto TOKEN_DTO = TokenDto.builder() - .accessToken("ey.yee.skrr") - .scope("openid") + .accessToken(TOKEN_ACCESS_TOKEN) + .scope(TOKEN_ACCESS_SCOPE) .build(); public final static RefreshTokenRequestDto REFRESH_TOKEN_REQUEST_DTO = RefreshTokenRequestDto.builder() diff --git a/dbrepo-ui/components/JumboBox.vue b/dbrepo-ui/components/JumboBox.vue index 10b1a8a6697d44bda97e402ef056dc1ba093a8ff..d2b804f819f22782ba4895c601c8b2ea118ce4e3 100644 --- a/dbrepo-ui/components/JumboBox.vue +++ b/dbrepo-ui/components/JumboBox.vue @@ -1,11 +1,12 @@ <template> <div> <v-row> - <v-col - offset-md="2" - md="8"> + <v-col> <v-card + elevation="0" + variant="flat" class="mx-auto" + max-width="600" :title="title" :subtitle="subtitle" :text="text"> diff --git a/dbrepo-ui/components/ResourceStatus.vue b/dbrepo-ui/components/ResourceStatus.vue new file mode 100644 index 0000000000000000000000000000000000000000..50c70899991a0f6c3e9210939a41065f14f6f239 --- /dev/null +++ b/dbrepo-ui/components/ResourceStatus.vue @@ -0,0 +1,62 @@ +<template> + <span + v-if="mode"> + <v-chip + v-if="!inline" + size="small" + :color="color" + variant="outlined"> + {{ status }} + </v-chip> + <span + v-else> + {{ status }} + </span> + </span> +</template> +<script> +export default { + props: { + resource: { + default: () => { + return null + } + }, + inline: { + default: () => { + return false + } + } + }, + computed: { + mode () { + if (!this.resource) { + return null + } + if (!this.resource.is_public) { + if (!this.resource.is_schema_public) { + return 'draft' + } + return 'private' + } + return 'public' + }, + status () { + if (!this.resource) { + return null + } + return this.$t(`pages.database.status.${this.mode}`) + }, + color () { + switch (this.mode) { + case 'private': + return 'secondary' + case 'draft': + return 'warning' + case 'public': + return 'success' + } + } + } +} +</script> diff --git a/dbrepo-ui/components/database/DatabaseCard.vue b/dbrepo-ui/components/database/DatabaseCard.vue index fba3853a31cf25649f915d4c0a3e422f5ec879bf..c31d288c6fde7ed711ab8d1cab5e5da22a38876e 100644 --- a/dbrepo-ui/components/database/DatabaseCard.vue +++ b/dbrepo-ui/components/database/DatabaseCard.vue @@ -27,21 +27,8 @@ {{ identifierDescription(database) }} </div> <div class="mt-2 db-tags"> - <v-chip - v-if="database.is_public" - size="small" - color="success" - variant="outlined"> - {{ $t('toolbars.database.public') }} - </v-chip> - <v-chip - v-if="!database.is_public" - size="small" - :color="colorVariant" - variant="outlined" - flat> - {{ $t('toolbars.database.private') }} - </v-chip> + <ResourceStatus + :resource="database" /> <v-chip v-if="identifierYear(database)" size="small" @@ -86,8 +73,12 @@ <script> import { formatLanguage } from '@/utils' +import ResourceStatus from '@/components/ResourceStatus.vue' export default { + components: { + ResourceStatus + }, data() { return { loading: false @@ -175,7 +166,7 @@ export default { return null } return this.identifiers[0] - }, + } } } </script> diff --git a/dbrepo-ui/components/database/DatabaseCreate.vue b/dbrepo-ui/components/database/DatabaseCreate.vue index d0f386b08899b2c3f911958297e88d7d67bba4f8..1797dbdad333a5c16ff58f2cf673f2d615b12b4d 100644 --- a/dbrepo-ui/components/database/DatabaseCreate.vue +++ b/dbrepo-ui/components/database/DatabaseCreate.vue @@ -55,33 +55,12 @@ </v-row> <v-row> <v-col> - <v-select - v-model="mode" - name="mode" - :label="$t('pages.database.subpages.create.visibility.label')" - :hint="$t('pages.database.subpages.create.visibility.hint')" - persistent-hint - :variant="inputVariant" - :items="visibilityOptions" - item-title="name" - item-value="value" - :rules="[v => !!v || $t('validation.required')]" - return-object - required> - <template - v-slot:append-inner> - <v-tooltip - location="bottom"> - <template - v-slot:activator="{ props }"> - <v-icon - v-bind="props" - icon="mdi-help-circle-outline" /> - </template> - {{ mode.hint }} - </v-tooltip> - </template> - </v-select> + <v-checkbox + v-model="draft" + name="draft" + :label="$t('pages.database.subpages.create.draft.label')" + :hint="$t('pages.database.subpages.create.draft.hint')" + persistent-hint /> </v-col> </v-row> </v-card-text> @@ -130,7 +109,7 @@ export default { value: false } ], - mode: true, + draft: true, payload: { name: null, is_public: true, diff --git a/dbrepo-ui/components/database/DatabaseToolbar.vue b/dbrepo-ui/components/database/DatabaseToolbar.vue index 65363c3467067aa4dd3eca9e2d5595a913723684..3f7412d5cb538972f5c21af2b155259738a74d7b 100644 --- a/dbrepo-ui/components/database/DatabaseToolbar.vue +++ b/dbrepo-ui/components/database/DatabaseToolbar.vue @@ -11,21 +11,9 @@ v-if="database && $vuetify.display.lgAndUp"> {{ database.name }} </span> - <v-chip - v-if="database && database.is_public" - size="small" + <ResourceStatus class="ml-2" - color="success" - :text="$t('toolbars.database.public')" - variant="outlined" /> - <v-chip - v-if="database && !database.is_public" - size="small" - class="ml-2" - :color="colorVariant" - variant="outlined" - :text="$t('toolbars.database.private')" - flat /> + :resource="database" /> </v-toolbar-title> <v-spacer /> <v-btn @@ -95,8 +83,12 @@ <script> import { useCacheStore } from '@/stores/cache' import { useUserStore } from '@/stores/user' +import ResourceStatus from '@/components/ResourceStatus.vue' export default { + components: { + ResourceStatus + }, data () { return { tab: null, diff --git a/dbrepo-ui/components/dialogs/UpdateTable.vue b/dbrepo-ui/components/dialogs/UpdateTable.vue index 6e4e07182c12c480b1f4c5d4da3756d3e111164a..8defb2669e68edd7c36af6b528cec0d252e4d12f 100644 --- a/dbrepo-ui/components/dialogs/UpdateTable.vue +++ b/dbrepo-ui/components/dialogs/UpdateTable.vue @@ -31,29 +31,29 @@ md="6"> <v-select v-model="modify.is_public" - :items="visibilities" + :items="dataOptions" persistent-hint :variant="inputVariant" required :rules="[ v => v !== null || $t('validation.required') ]" - :label="$t('pages.database.subpages.create.data.label')" - :hint="$t('pages.database.subpages.create.data.hint')" /> + :label="$t('pages.database.resource.data.label')" + :hint="$t('pages.database.resource.data.hint')" /> </v-col> <v-col md="6"> <v-select v-model="modify.is_schema_public" - :items="visibilities" + :items="schemaOptions" persistent-hint :variant="inputVariant" required :rules="[ v => v !== null || $t('validation.required') ]" - :label="$t('pages.database.subpages.create.schema.label')" - :hint="$t('pages.database.subpages.create.schema.hint')" /> + :label="$t('pages.database.resource.schema.label')" + :hint="$t('pages.database.resource.schema.hint')" /> </v-col> </v-row> </v-card-text> @@ -98,9 +98,13 @@ export default { return { valid: false, loading: false, - visibilities: [ - { title: this.$t('toolbars.database.public'), value: true }, - { title: this.$t('toolbars.database.private'), value: false }, + dataOptions: [ + { title: this.$t('pages.database.resource.data.enabled'), value: true }, + { title: this.$t('pages.database.resource.data.disabled'), value: false }, + ], + schemaOptions: [ + { title: this.$t('pages.database.resource.schema.enabled'), value: true }, + { title: this.$t('pages.database.resource.schema.disabled'), value: false }, ], modify: { description: this.table.description, @@ -153,7 +157,7 @@ export default { this.$emit('close', { success: true }) this.cacheStore.reloadTable() }) - .catch(({code}) => { + .catch(({ code }) => { this.loading = false const toast = useToastInstance() toast.error(this.$t(code)) diff --git a/dbrepo-ui/components/dialogs/ViewVisibility.vue b/dbrepo-ui/components/dialogs/ViewVisibility.vue index 226772edd922a9d71e40a312d461c666c50e8893..d8cc01790eb82e52e5fb320baf3800afdbd031d0 100644 --- a/dbrepo-ui/components/dialogs/ViewVisibility.vue +++ b/dbrepo-ui/components/dialogs/ViewVisibility.vue @@ -14,29 +14,29 @@ md="6"> <v-select v-model="modify.is_public" - :items="visibilities" + :items="dataOptions" persistent-hint :variant="inputVariant" required :rules="[ v => v !== null || $t('validation.required') ]" - :label="$t('pages.database.subpages.create.data.label')" - :hint="$t('pages.database.subpages.create.data.hint')" /> + :label="$t('pages.database.resource.data.label')" + :hint="$t('pages.database.resource.data.hint')" /> </v-col> <v-col md="6"> <v-select v-model="modify.is_schema_public" - :items="visibilities" + :items="schemaOptions" persistent-hint :variant="inputVariant" required :rules="[ v => v !== null || $t('validation.required') ]" - :label="$t('pages.database.subpages.create.schema.label')" - :hint="$t('pages.database.subpages.create.schema.hint')" /> + :label="$t('pages.database.resource.schema.label')" + :hint="$t('pages.database.resource.schema.hint')" /> </v-col> </v-row> </v-card-text> @@ -82,9 +82,13 @@ export default { loadingUsers: false, users: [], error: false, - visibilities: [ - { title: this.$t('toolbars.database.public'), value: true }, - { title: this.$t('toolbars.database.private'), value: false }, + dataOptions: [ + { title: this.$t('pages.database.resource.data.enabled'), value: true }, + { title: this.$t('pages.database.resource.data.disabled'), value: false }, + ], + schemaOptions: [ + { title: this.$t('pages.database.resource.schema.enabled'), value: true }, + { title: this.$t('pages.database.resource.schema.disabled'), value: false }, ], modify: { is_public: this.view.is_public, diff --git a/dbrepo-ui/components/subset/Builder.vue b/dbrepo-ui/components/subset/Builder.vue index 2a14b8f474a8f7a3cc0e38302e3ebad0e0df5a7d..9b85162457aa8e095caff187b6dbeed08490a65a 100644 --- a/dbrepo-ui/components/subset/Builder.vue +++ b/dbrepo-ui/components/subset/Builder.vue @@ -68,7 +68,7 @@ md="4"> <v-select v-model="view.is_public" - :items="visibilities" + :items="dataOptions" persistent-hint :variant="inputVariant" required @@ -76,14 +76,14 @@ :rules="[ v => !!v || $t('validation.required') ]" - :label="$t('pages.database.subpages.create.data.label')" - :hint="$t('pages.database.subpages.create.data.hint')" /> + :label="$t('pages.database.resource.data.label')" + :hint="$t('pages.database.resource.data.hint')" /> </v-col> <v-col md="4"> <v-select v-model="view.is_schema_public" - :items="visibilities" + :items="schemaOptions" persistent-hint :variant="inputVariant" required @@ -91,8 +91,8 @@ :rules="[ v => !!v || $t('validation.required') ]" - :label="$t('pages.database.subpages.create.schema.label')" - :hint="$t('pages.database.subpages.create.schema.hint')" /> + :label="$t('pages.database.resource.schema.label')" + :hint="$t('pages.database.resource.schema.hint')" /> </v-col> </v-row> <v-window @@ -332,9 +332,13 @@ export default { columns: [], timestamp: null, executeDifferentTimestamp: false, - visibilities: [ - { title: this.$t('toolbars.database.public'), value: true }, - { title: this.$t('toolbars.database.private'), value: false }, + dataOptions: [ + { title: this.$t('pages.database.resource.data.enabled'), value: true }, + { title: this.$t('pages.database.resource.data.disabled'), value: false }, + ], + schemaOptions: [ + { title: this.$t('pages.database.resource.schema.enabled'), value: true }, + { title: this.$t('pages.database.resource.schema.disabled'), value: false }, ], tableDetails: null, resultId: null, diff --git a/dbrepo-ui/components/subset/SubsetList.vue b/dbrepo-ui/components/subset/SubsetList.vue index f57dc68a88fd77c06b858b423565b4ccac7d46f0..ae8f2bf448753d4d7b6b4454f0b7e1671d7eb25d 100644 --- a/dbrepo-ui/components/subset/SubsetList.vue +++ b/dbrepo-ui/components/subset/SubsetList.vue @@ -26,21 +26,8 @@ :to="link(item)" :href="link(item)"> <template v-slot:append> - <v-chip - v-if="database.is_public" - size="small" - class="ml-2" - color="success" - :text="$t('toolbars.database.public')" - variant="outlined" /> - <v-chip - v-if="!database.is_public" - size="small" - class="ml-2" - :color="colorVariant" - variant="outlined" - :text="$t('toolbars.database.private')" - flat /> + <ResourceStatus + :resource="item" /> <v-tooltip v-if="hasPublishedIdentifier(item)" :text="$t('pages.identifier.pid.title')" diff --git a/dbrepo-ui/components/table/TableList.vue b/dbrepo-ui/components/table/TableList.vue index 8bb77b2b5a8971e3a0d8dbe557e9d5d849f492c1..2cb42a1633e47ca9d117e4449318582666975d35 100644 --- a/dbrepo-ui/components/table/TableList.vue +++ b/dbrepo-ui/components/table/TableList.vue @@ -19,21 +19,8 @@ :subtitle="table.description ? table.description : ''" :to="`/database/${$route.params.database_id}/table/${table.id}/info`"> <template v-slot:append> - <v-chip - v-if="table && table.is_public" - size="small" - class="ml-2" - color="success" - :text="$t('toolbars.database.public')" - variant="outlined" /> - <v-chip - v-if="table && !table.is_public" - size="small" - class="ml-2" - :color="colorVariant" - variant="outlined" - :text="$t('toolbars.database.private')" - flat /> + <ResourceStatus + :resource="table" /> <v-tooltip v-if="hasPublishedIdentifier(table)" :text="$t('pages.identifier.pid.title')" diff --git a/dbrepo-ui/components/table/TableToolbar.vue b/dbrepo-ui/components/table/TableToolbar.vue index b7d358d2492bd01aa8eddc479405c660784f66c6..2840e2e4c43e545a98385bb42b993650940dc4a2 100644 --- a/dbrepo-ui/components/table/TableToolbar.vue +++ b/dbrepo-ui/components/table/TableToolbar.vue @@ -16,21 +16,9 @@ v-if="table && $vuetify.display.lgAndUp"> {{ table.name }} </span> - <v-chip - v-if="table && table.is_public" - size="small" + <ResourceStatus class="ml-2" - color="success" - :text="$t('toolbars.database.public')" - variant="outlined" /> - <v-chip - v-if="table && !table.is_public" - size="small" - class="ml-2" - :color="colorVariant" - variant="outlined" - :text="$t('toolbars.database.private')" - flat /> + :resource="table" /> </v-toolbar-title> <v-spacer /> <v-btn diff --git a/dbrepo-ui/components/view/ViewList.vue b/dbrepo-ui/components/view/ViewList.vue index 543a8746affd8cbbed495647fc963c8db1534072..1b278a555c1154492882e92773b0f9203057c799 100644 --- a/dbrepo-ui/components/view/ViewList.vue +++ b/dbrepo-ui/components/view/ViewList.vue @@ -14,21 +14,8 @@ :class="clazz(view)" :to="`/database/${$route.params.database_id}/view/${view.id}/info`"> <template v-slot:append> - <v-chip - v-if="view && view.is_public" - size="small" - class="ml-2" - color="success" - :text="$t('toolbars.database.public')" - variant="outlined" /> - <v-chip - v-if="view && !view.is_public" - size="small" - class="ml-2" - :color="colorVariant" - variant="outlined" - :text="$t('toolbars.database.private')" - flat /> + <ResourceStatus + :resource="view" /> <v-tooltip v-if="hasPublishedIdentifier(view)" :text="$t('pages.identifier.pid.title')" diff --git a/dbrepo-ui/components/view/ViewToolbar.vue b/dbrepo-ui/components/view/ViewToolbar.vue index 64ea3f1029407e3143bc6dd9b5cdfcb634aa9b94..9e980e7a3b502109db67f7a369197af1c5441cd4 100644 --- a/dbrepo-ui/components/view/ViewToolbar.vue +++ b/dbrepo-ui/components/view/ViewToolbar.vue @@ -6,26 +6,14 @@ icon="mdi-arrow-left" :to="`/database/${$route.params.database_id}/view`" /> <v-toolbar-title - v-if="cachedView"> + v-if="view"> <span v-if="$vuetify.display.lgAndUp"> {{ title }} </span> - <v-chip - v-if="cachedView.is_public" - size="small" + <ResourceStatus class="ml-2" - color="success" - :text="$t('toolbars.database.public')" - variant="outlined" /> - <v-chip - v-if="!cachedView.is_public" - size="small" - class="ml-2" - :color="colorVariant" - variant="outlined" - :text="$t('toolbars.database.private')" - flat /> + :resource="view" /> </v-toolbar-title> <v-spacer /> <v-btn @@ -58,7 +46,7 @@ persistent max-width="640"> <ViewVisibility - :view="cachedView" + :view="view" @close="close" /> </v-dialog> <template v-slot:extension> @@ -69,11 +57,11 @@ :text="$t('navigation.info')" :to="`/database/${$route.params.database_id}/view/${$route.params.view_id}/info`" /> <v-tab - v-if="canReadData" + v-if="canViewData" :text="$t('navigation.data')" :to="`/database/${$route.params.database_id}/view/${$route.params.view_id}/data`" /> <v-tab - v-if="canReadData" + v-if="canViewSchema" :text="$t('navigation.schema')" :to="`/database/${$route.params.database_id}/view/${$route.params.view_id}/schema`" /> </v-tabs> @@ -113,59 +101,47 @@ export default { const runtimeConfig = useRuntimeConfig() return this.$vuetify.theme.global.name.toLowerCase().endsWith('contrast') ? runtimeConfig.public.variant.button.contrast : runtimeConfig.public.variant.button.normal }, - cachedView () { - if (!this.database) { - return null - } - return this.database.views.filter(v => v.id === Number(this.$route.params.view_id))[0] + view () { + return this.cacheStore.getView }, canViewData () { - if (!this.cachedView) { + if (!this.view) { return false } - if (this.cachedView.is_public) { + if (this.view.is_public) { return true } if (!this.user) { return false } - return this.hasReadAccess || this.cachedView.owned_by === this.user.id || this.database.owner.id === this.user.id + return this.hasReadAccess || this.view.owner.id === this.user.id || this.database.owner.id === this.user.id }, canViewSchema () { - if (!this.cachedView) { + if (!this.view) { return false } - if (this.cachedView.is_schema_public) { + if (this.view.is_schema_public) { return true } if (!this.user) { return false } - return this.hasReadAccess || this.cachedView.owned_by === this.user.id || this.database.owner.id === this.user.id + return this.hasReadAccess || this.view.owner.id === this.user.id || this.database.owner.id === this.user.id }, canDeleteView () { - if (!this.roles || !this.user || !this.cachedView) { + if (!this.roles || !this.user || !this.view) { return false } - return this.roles.includes('delete-database-view') && this.cachedView.owned_by === this.user.id + return this.roles.includes('delete-database-view') && this.view.owner.id === this.user.id }, canUpdateVisibility () { - if (!this.roles || !this.user || !this.cachedView) { + if (!this.roles || !this.user || !this.view) { return false } - return this.roles.includes('modify-view-visibility') && this.cachedView.owned_by === this.user.id - }, - isContrastTheme () { - return this.$vuetify.theme.global.name.toLowerCase().endsWith('contrast') - }, - isDarkTheme () { - return this.$vuetify.theme.global.name.toLowerCase().startsWith('dark') - }, - colorVariant () { - return this.isContrastTheme ? '' : (this.isDarkTheme ? 'tertiary' : 'secondary') + return this.roles.includes('modify-view-visibility') && this.view.owner.id === this.user.id }, canCreatePid () { - if (!this.roles || !this.user || !this.cachedView) { + if (!this.roles || !this.user || !this.view) { return false } const userService = useUserService() @@ -186,23 +162,11 @@ export default { } return this.access.type === 'read' || this.access.type === 'write_own' || this.access.type === 'write_all' }, - canReadData () { - if (!this.cachedView) { - return false - } - if (this.cachedView.is_public) { - return true - } - if (!this.user) { - return false - } - return this.cachedView.owner.id === this.user.id || this.hasReadAccess - }, identifiers () { - if (!this.cachedView) { + if (!this.view) { return [] } - return this.cachedView.identifiers.filter(s => s.view_id === Number(this.$route.params.view_id)) + return this.view.identifiers.filter(s => s.view_id === Number(this.$route.params.view_id)) }, identifier () { /* mount pid */ @@ -217,10 +181,10 @@ export default { return this.identifiers[0] }, title () { - if (!this.cachedView) { + if (!this.view) { return null } - return this.cachedView.name + return this.view.name } }, methods: { diff --git a/dbrepo-ui/composables/identifier-service.ts b/dbrepo-ui/composables/identifier-service.ts index f85c48dc21f1428ea0656a00861eb260e48c0cf5..3853d9df751aef86b62e2cb30394cf0be1896efb 100644 --- a/dbrepo-ui/composables/identifier-service.ts +++ b/dbrepo-ui/composables/identifier-service.ts @@ -328,6 +328,9 @@ export const useIdentifierService = (): any => { } function databaseToServerHead(database: DatabaseDto) { + if (!database) { + return + } const config = useRuntimeConfig() /* Google Rich Results */ const json: any = { diff --git a/dbrepo-ui/composables/view-service.ts b/dbrepo-ui/composables/view-service.ts index 4c948a57f10b5637cb441baa83e42bb9c3850a46..417f5a645e978cc8f93e7fc524df22756c5b093e 100644 --- a/dbrepo-ui/composables/view-service.ts +++ b/dbrepo-ui/composables/view-service.ts @@ -24,7 +24,7 @@ export const useViewService = (): any => { return new Promise<ViewDto>((resolve, reject) => { axios.get<ViewDto>(`/api/database/${databaseId}/view/${viewId}`) .then((response) => { - console.info('Deleted view with id', viewId, 'in database with id', databaseId) + console.info('Found view with id', viewId, 'in database with id', databaseId) resolve(response.data) }) .catch((error) => { diff --git a/dbrepo-ui/layouts/default.vue b/dbrepo-ui/layouts/default.vue index 8816ab5c9190662331e8ff86b85984cb92eec37f..952b66a00a3f6924c4cd3a338e67e6de62f33688 100644 --- a/dbrepo-ui/layouts/default.vue +++ b/dbrepo-ui/layouts/default.vue @@ -251,11 +251,14 @@ export default { if (this.user) { this.userStore.setRouteAccess(newObj.database_id) } - if (!newObj.table_id) { - return - } /* load table */ - this.cacheStore.setRouteTable(newObj.database_id, newObj.table_id) + if (newObj.table_id) { + this.cacheStore.setRouteTable(newObj.database_id, newObj.table_id) + } + /* load view */ + if (newObj.view_id) { + this.cacheStore.setRouteView(newObj.database_id, newObj.view_id) + } }, deep: true, immediate: true diff --git a/dbrepo-ui/locales/en-US.json b/dbrepo-ui/locales/en-US.json index 102ba3fd0a01f24cde64c070ec6fb51ee5efc68f..53fec12c18ffac36b71817dae7d5f404323453d1 100644 --- a/dbrepo-ui/locales/en-US.json +++ b/dbrepo-ui/locales/en-US.json @@ -293,10 +293,6 @@ }, "visibility": { "title": "Visibility", - "open": "Open", - "data": "Data is public", - "schema": "Schema is public", - "closed": "Closed" }, "description": { "title": "Description", @@ -599,16 +595,29 @@ "internal-name": { "title": "Internal Name" }, - "visibility": { - "title": "Visibility", - "open": "Open", - "data": "Data is public", - "schema": "Schema is public", - "closed": "Closed" - }, "size": { "title": "Size" }, + "status": { + "title": "Status", + "public": "Public", + "private": "Private", + "draft": "Draft" + }, + "resource": { + "data": { + "label": "Transparency", + "hint": "Required, e.g. can hide the resource so it is hidden.", + "enabled": "Visible", + "disabled": "Hidden" + }, + "schema": { + "label": "Insights", + "hint": "Required, e.g. can show metadata on resources.", + "enabled": "Visible", + "disabled": "Hidden" + } + }, "owner": { "title": "Owner" }, @@ -649,22 +658,14 @@ "hint": "Required. The internal database name will be lowercase alphanumeric, others will be replaced with _", "placeholder": "e.g. my_database, air_quality" }, + "draft": { + "label": "Draft", + "hint": "Hides the database, only users with access can see it" + }, "engine": { "label": "Engine", "hint": "Required" }, - "visibility": { - "label": "Visibility", - "hint": "Required", - "public": { - "label": "Public", - "hint": "Everything is visible to the public, e.g. to publish data used in a open-access paper. You can later update the visibility of data." - }, - "private": { - "label": "Private", - "hint": "Nothing is visible to anyone without dedicated read-access, e.g. to work on a dataset. You can later update the visibility of data." - } - }, "submit": { "text": "Create" }, @@ -727,11 +728,6 @@ "hint": "Required", "help": "Public = visible to anyone, Private = visible only to designated users" }, - "schema": { - "label": "Schema Visibility", - "hint": "Required", - "help": "Public = visible to anyone, Private = visible only to designated users" - }, "submit": { "text": "Modify" } @@ -1357,8 +1353,6 @@ "database": { "recent": "Recent Databases", "links": "Important Links", - "public": "Public", - "private": "Private", "current": "Current Data", "history": "Historic Data", "create": { diff --git a/dbrepo-ui/pages/database/[database_id]/info.vue b/dbrepo-ui/pages/database/[database_id]/info.vue index f5c68e328f603d06dffb0a5a8d8a06999297c77a..20ba2dbe16d064493ab0b36f79048cf440a90bc6 100644 --- a/dbrepo-ui/pages/database/[database_id]/info.vue +++ b/dbrepo-ui/pages/database/[database_id]/info.vue @@ -1,9 +1,7 @@ <template> <div> - <DatabaseToolbar - v-if="!isError" /> + <DatabaseToolbar /> <v-window - v-if="!isError" v-model="tab"> <v-window-item value="1"> <Summary @@ -59,11 +57,6 @@ {{ database.internal_name }} </div> </v-list-item> - <v-list-item - :title="$t('pages.database.visibility.title')" - density="compact"> - {{ databaseVisibility }} - </v-list-item> <v-list-item :title="$t('pages.database.size.title')" density="compact"> @@ -102,13 +95,6 @@ </span> </div> </v-list-item> - <v-list-item - v-if="access" - :title="$t('pages.database.connection.title')" - density="compact"> - <pre - class="pb-1">{{ jdbcString }}</pre> - </v-list-item> <v-list-item v-if="database.contact" :title="$t('pages.database.contact.title')" @@ -169,16 +155,12 @@ </v-card> </v-window-item> </v-window> - <JumboBox - v-if="isError" - :title="errorTitle" - :subtitle="errorSubtitle" - :text="errorText" /> <v-breadcrumbs :items="items" class="pa-0 mt-2" /> </div> </template> + <script> import DatabaseToolbar from '@/components/database/DatabaseToolbar.vue' import Summary from '@/components/identifier/Summary.vue' @@ -199,10 +181,15 @@ export default { }, setup () { const config = useRuntimeConfig() + const userStore = useUserStore() const { database_id } = useRoute().params const { error, data } = useFetch(`${config.public.api.server}/api/database/${database_id}`, { immediate: true, - timeout: 90_000 + timeout: 90_000, + headers: { + Accept: 'application/json', + Authorization: userStore.getToken ? `Bearer ${userStore.getToken}` : null + } }) if (data.value) { const identifierService = useIdentifierService() @@ -260,6 +247,9 @@ export default { user () { return this.userStore.getUser }, + database () { + return this.cacheStore.getDatabase + }, roles () { return this.userStore.getRoles }, @@ -349,13 +339,6 @@ export default { return { text: null, class: null } } }, - jdbcString () { - if (!this.database || !this.user) { - return - } - const flags = this.database.container.ui_additional_flags ? this.database.container.ui_additional_flags : '' - return `jdbc:${this.database.container.image.jdbc_method}://${this.database.container.ui_host}:${this.database.container.ui_port}/${this.database.internal_name}${flags} (${this.$t('pages.database.connection.username')}=${this.user.username}, ${this.$t('pages.database.connection.password')}=yourpassword)` - }, databaseExtraInfo () { return this.$config.public.database.extra }, @@ -367,50 +350,11 @@ export default { this.database.tables.forEach((t) => { sum += t.data_length }) return sizeToHumanLabel(sum) }, - databaseVisibility () { - if (!this.database) { - return null - } - if (this.database.is_public && this.database.is_schema_public) { - return this.$t('pages.database.visibility.open') - } - if (!this.database.is_public && !this.database.is_schema_public) { - return this.$t('pages.database.visibility.closed') - } - return this.database.is_public ? this.$t('pages.database.visibility.data') : this.$t('pages.database.visibility.schema') - }, previewImage () { if (!this.database) { return null } return this.database.preview_image - }, - isError () { - return this.error - }, - errorTitle () { - switch (this.error.statusCode) { - case 404: - return this.$t('error.missing.title') - default: - return this.$t('error.permission.title') - } - }, - errorSubtitle () { - switch (this.error.statusCode) { - case 404: - return 'ERROR_NOT_FOUND' - default: - return 'ERROR_NOT_AUTHORIZED' - } - }, - errorText () { - switch (this.error.statusCode) { - case 404: - return this.$t('error.missing.text') - default: - return this.$t('error.permission.text') - } } } } diff --git a/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/data.vue b/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/data.vue index 3ac8f40d645b44449f5607b9f3b031d06fa463d6..1346f7d4e8988acf01330f996a654bcb40744a76 100644 --- a/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/data.vue +++ b/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/data.vue @@ -51,12 +51,30 @@ import QueryResults from '@/components/subset/Results.vue' import SubsetToolbar from '@/components/subset/SubsetToolbar.vue' import { formatTimestampUTCLabel } from '@/utils' import { useCacheStore } from '@/stores/cache' +import {useUserStore} from "~/stores/user.js"; export default { components: { SubsetToolbar, QueryResults }, + setup () { + const config = useRuntimeConfig() + const userStore = useUserStore() + const { database_id, subset_id } = useRoute().params + const { error, data } = useFetch(`${config.public.api.server}/api/database/${database_id}/subset/${subset_id}`, { + immediate: true, + timeout: 90_000, + headers: { + Accept: 'application/json', + Authorization: userStore.getToken ? `Bearer ${userStore.getToken}` : null + } + }) + return { + subset: data, + error + } + }, data () { return { loadingSubset: false, @@ -84,9 +102,6 @@ export default { disabled: true } ], - subset: { - id: this.$route.params.subset_id - }, cacheStore: useCacheStore() } }, @@ -113,11 +128,11 @@ export default { if (this.database.is_public) { return true } - return this.subset.creator.username === this.username + return this.subset.owner.username === this.username }, }, mounted () { - this.loadSubset() + this.loadResult() }, methods: { loadSubset () { diff --git a/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/info.vue b/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/info.vue index 764d1b55ffa6b134c07e86290325525caa8919b2..ece7cde1357f4801abd3b7ccd70b6f2067a2b90d 100644 --- a/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/info.vue +++ b/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/info.vue @@ -37,7 +37,9 @@ v-if="database" :title="$t('pages.subset.visibility.title')" density="compact"> - {{ database.is_public ? $t('toolbars.database.public') : $t('toolbars.database.private') }} + <ResourceStatus + :inline="true" + :resource="subset" /> </v-list-item> <v-list-item v-if="subset.creator" diff --git a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/info.vue b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/info.vue index 5f322fa498b4b05607db6422cc7964d98515cb74..9b1e053abe4a0bc07f016b69d1e200bcf67da511 100644 --- a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/info.vue +++ b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/info.vue @@ -1,5 +1,6 @@ <template> - <div> + <div + v-if="table"> <TableToolbar :selection="selection" /> <v-card @@ -21,43 +22,32 @@ rounded="0" :title="$t('pages.table.title')"> <v-card-text> - <v-skeleton-loader - v-if="!cachedTable" - type="list-item-three-line" - width="50%" /> <v-list - v-if="cachedTable" dense> <v-list-item :title="$t('pages.table.id.title')"> - {{ cachedTable.id }} + {{ table.id }} </v-list-item> <v-list-item :title="$t('pages.table.name.title')"> - {{ cachedTable.internal_name }} + {{ table.internal_name }} </v-list-item> <v-list-item - :title="$t('pages.table.visibility.title')"> - {{ databaseVisibility }} - </v-list-item> - <v-list-item - v-if="table" :title="$t('pages.table.size.title')"> {{ sizeToHumanLabel(table.data_length) }} </v-list-item> <v-list-item - v-if="table" + v-if="canRead && table.num_rows" :title="$t('pages.table.rows.title')"> {{ table.num_rows }} </v-list-item> <v-list-item :title="$t('pages.table.description.title')"> - {{ hasDescription ? cachedTable.description : $t('pages.table.description.empty') }} + {{ hasDescription ? table.description : $t('pages.table.description.empty') }} </v-list-item> <v-list-item :title="$t('pages.table.owner.title')"> <UserBadge - v-if="table" :user="table.owner" :other-user="user" /> </v-list-item> @@ -128,49 +118,15 @@ </v-list> </v-card-text> </v-card> - <v-divider /> - <v-card - :title="$t('pages.database.title')" - variant="flat"> - <v-card-text> - <v-list dense> - <v-list-item - v-if="database" - :title="$t('pages.database.visibility.title')"> - {{ database.is_public ? $t('toolbars.database.public') : $t('toolbars.database.private') }} - </v-list-item> - <v-list-item - v-if="database" - :title="$t('pages.database.name.title')"> - <NuxtLink - class="text-primary" - :to="`/database/${database.id}`"> - {{ database.internal_name }} - </NuxtLink> - </v-list-item> - </v-list> - </v-card-text> - </v-card> <v-breadcrumbs :items="items" class="pa-0 mt-2" /> </div> </template> -<script setup> -const config = useRuntimeConfig() -const { database_id, table_id } = useRoute().params -const { data } = await useFetch(`${config.public.api.server}/api/database/${database_id}/table/${table_id}`) -if (data.value) { - const identifierService = useIdentifierService() - useServerHead(identifierService.tableToServerHead(data.value)) - useServerSeoMeta(identifierService.tableToServerSeoMeta(data.value)) -} -</script> <script> import TableToolbar from '@/components/table/TableToolbar.vue' import Select from '@/components/identifier/Select.vue' import Summary from '@/components/identifier/Summary.vue' import UserBadge from '@/components/user/UserBadge.vue' -import { formatTimestampUTCLabel, sizeToHumanLabel } from '@/utils' import { useUserStore } from '@/stores/user' import { useCacheStore } from '@/stores/cache' @@ -181,11 +137,32 @@ export default { TableToolbar, UserBadge }, + setup () { + const config = useRuntimeConfig() + const userStore = useUserStore() + const { database_id, table_id } = useRoute().params + const { error, data } = useFetch(`${config.public.api.server}/api/database/${database_id}/table/${table_id}`, { + immediate: true, + timeout: 90_000, + headers: { + Accept: 'application/json', + Authorization: userStore.getToken ? `Bearer ${userStore.getToken}` : null + } + }) + if (data.value) { + const identifierService = useIdentifierService() + useServerHead(identifierService.databaseToServerHead(data.value)) + useServerSeoMeta(identifierService.databaseToServerSeoMeta(data.value)) + } + return { + table: data, + error + } + }, data () { return { selection: [], consumers: [], - table: null, items: [ { title: this.$t('navigation.databases'), @@ -228,9 +205,6 @@ export default { database () { return this.cacheStore.getDatabase }, - cachedTable () { - return this.cacheStore.getTable - }, roles () { return this.userStore.getRoles }, @@ -247,13 +221,13 @@ export default { if (!this.table || !this.user || !this.access) { return false } - return (this.access.type === 'write_own' && this.cachedTable.owned_by === this.user.id) || this.access.type === 'write_all' + return (this.access.type === 'write_own' && this.table.owned_by === this.user.id) || this.access.type === 'write_all' }, access () { return this.userStore.getAccess }, hasDescription () { - return this.table && this.cachedTable.description + return this.table && this.table.description }, canWriteQueues () { if (!this.roles) { @@ -317,40 +291,6 @@ export default { } else if (this.canRead) { return this.$t('pages.table.connection.permissions.read') } - }, - databaseVisibility () { - if (!this.database) { - return null - } - if (this.database.is_public && this.cachedTable.is_schema_public) { - return this.$t('pages.table.visibility.open') - } - if (!this.database.is_public && !this.cachedTable.is_schema_public) { - return this.$t('pages.table.visibility.closed') - } - return this.database.is_public ? this.$t('pages.database.visibility.data') : this.$t('pages.database.visibility.schema') - } - }, - mounted () { - this.fetchTable() - }, - methods: { - fetchTable () { - this.loading = true - const tableService = useTableService() - tableService.findOne(this.$route.params.database_id, this.$route.params.table_id) - .then((table) => { - this.loading = false - this.table = table - }) - .catch(({code}) => { - this.loading = false - const toast = useToastInstance() - toast.error(this.$t(code)) - }) - .finally(() => { - this.loading = false - }) } } } diff --git a/dbrepo-ui/pages/database/[database_id]/table/create/dataset.vue b/dbrepo-ui/pages/database/[database_id]/table/create/dataset.vue index 29773c24d98801f1c710a87e2cf513a842f562eb..774f11907ef9fa31bc7ea4ecf440f46ae4d7c904 100644 --- a/dbrepo-ui/pages/database/[database_id]/table/create/dataset.vue +++ b/dbrepo-ui/pages/database/[database_id]/table/create/dataset.vue @@ -106,12 +106,12 @@ <v-select v-model="tableCreate.is_public" name="public" - :label="$t('pages.database.subpages.create.data.label')" - :hint="$t('pages.database.subpages.create.data.hint')" + :label="$t('pages.database.resource.data.label')" + :hint="$t('pages.database.resource.data.hint')" persistent-hint :variant="inputVariant" - :items="visibilityOptions" - item-title="name" + :items="dataOptions" + item-title="title" item-value="value" :rules="[v => v !== null || $t('validation.required')]" required> @@ -122,12 +122,12 @@ <v-select v-model="tableCreate.is_schema_public" name="schema-public" - :label="$t('pages.database.subpages.create.schema.label')" - :hint="$t('pages.database.subpages.create.schema.hint')" + :label="$t('pages.database.resource.schema.label')" + :hint="$t('pages.database.resource.schema.hint')" persistent-hint :variant="inputVariant" - :items="visibilityOptions" - item-title="name" + :items="schemaOptions" + item-title="title" item-value="value" :rules="[v => v !== null || $t('validation.required')]" required> @@ -235,15 +235,13 @@ export default { loadingImport: false, fileModel: null, rowCount: null, - visibilityOptions: [ - { - name: this.$t('toolbars.database.public'), - value: true - }, - { - name: this.$t('toolbars.database.private'), - value: false - } + dataOptions: [ + { title: this.$t('pages.database.resource.data.enabled'), value: true }, + { title: this.$t('pages.database.resource.data.disabled'), value: false }, + ], + schemaOptions: [ + { title: this.$t('pages.database.resource.schema.enabled'), value: true }, + { title: this.$t('pages.database.resource.schema.disabled'), value: false }, ], file: { filename: null, diff --git a/dbrepo-ui/pages/database/[database_id]/view/[view_id]/data.vue b/dbrepo-ui/pages/database/[database_id]/view/[view_id]/data.vue index 9751fce5b7843b27043ea9e103003a01a3dcfbb4..935e26314f91054712ebcb1a9375428a077b842f 100644 --- a/dbrepo-ui/pages/database/[database_id]/view/[view_id]/data.vue +++ b/dbrepo-ui/pages/database/[database_id]/view/[view_id]/data.vue @@ -2,7 +2,7 @@ <div v-if="canReadData"> <ViewToolbar - v-if="cachedView" /> + v-if="view" /> <v-toolbar color="secondary" :title="$t('toolbars.database.current')" @@ -80,11 +80,8 @@ export default { database () { return this.cacheStore.getDatabase }, - cachedView () { - if (!this.database) { - return null - } - return this.database.views.filter(v => v.id === Number(this.$route.params.view_id))[0] + view () { + return this.cacheStore.getView }, access () { return this.userStore.getAccess @@ -96,10 +93,10 @@ export default { return this.access.type === 'read' || this.access.type === 'write_own' || this.access.type === 'write_all' }, canReadData () { - if (!this.cachedView) { + if (!this.view) { return false } - if (this.cachedView.is_public) { + if (this.view.is_public) { return true } if (!this.user) { diff --git a/dbrepo-ui/pages/database/[database_id]/view/[view_id]/info.vue b/dbrepo-ui/pages/database/[database_id]/view/[view_id]/info.vue index 364516f45f29209c56748add8bff0da310de7d75..f7b025e98298fd37181d551822e9a4b18b849e16 100644 --- a/dbrepo-ui/pages/database/[database_id]/view/[view_id]/info.vue +++ b/dbrepo-ui/pages/database/[database_id]/view/[view_id]/info.vue @@ -4,7 +4,7 @@ <v-window v-model="tab"> <v-window-item - v-if="cachedView"> + v-if="view"> <v-card variant="flat"> <Summary v-if="hasIdentifier" @@ -23,15 +23,15 @@ variant="flat"> <v-card-text> <v-list - v-if="cachedView" + v-if="view" dense> <v-list-item :title="$t('pages.view.name.title')"> - {{ cachedView.internal_name }} + {{ view.internal_name }} </v-list-item> <v-list-item :title="$t('pages.view.query.title')"> - <pre>{{ cachedView.query }}</pre> + <pre>{{ view.query }}</pre> </v-list-item> <v-list-item :title="$t('pages.view.owner.title')"> @@ -45,34 +45,9 @@ width="200" /> </v-list-item> <v-list-item - v-if="cachedView.created" + v-if="view.created" :title="$t('pages.view.creation.title')"> - {{ formatUTC(cachedView.created) }} - </v-list-item> - <v-list-item - :title="$t('pages.view.visibility.title')"> - {{ viewVisibility }} - </v-list-item> - </v-list> - </v-card-text> - </v-card> - <v-divider /> - <v-card - :title="$t('pages.database.title')" - variant="flat"> - <v-card-text> - <v-list dense> - <v-list-item - :title="$t('pages.database.visibility.title')"> - {{ database.is_public ? $t('toolbars.database.public') : $t('toolbars.database.private') }} - </v-list-item> - <v-list-item - :title="$t('pages.database.name.title')"> - <NuxtLink - class="text-primary" - :to="`/database/${database.id}`"> - {{ database.internal_name }} - </NuxtLink> + {{ formatUTC(view.created) }} </v-list-item> </v-list> </v-card-text> @@ -83,16 +58,6 @@ </div> </template> -<script setup> -const config = useRuntimeConfig() -const { database_id, view_id } = useRoute().params -const { data } = await useFetch(`${config.public.api.server}/api/database/${database_id}/view/${view_id}`) -if (data.value) { - const identifierService = useIdentifierService() - useServerHead(identifierService.viewToServerHead(data.value)) - useServerSeoMeta(identifierService.viewToServerSeoMeta(data.value)) -} -</script> <script> import ViewToolbar from '@/components/view/ViewToolbar.vue' import Summary from '@/components/identifier/Summary.vue' @@ -109,11 +74,32 @@ export default { ViewToolbar, UserBadge }, + setup () { + const config = useRuntimeConfig() + const userStore = useUserStore() + const { database_id, view_id } = useRoute().params + const { error, data } = useFetch(`${config.public.api.server}/api/database/${database_id}/view/${view_id}`, { + immediate: true, + timeout: 90_000, + headers: { + Accept: 'application/json', + Authorization: userStore.getToken ? `Bearer ${userStore.getToken}` : null + } + }) + if (data.value) { + const identifierService = useIdentifierService() + useServerHead(identifierService.viewToServerHead(data.value)) + useServerSeoMeta(identifierService.viewToServerSeoMeta(data.value)) + } + return { + view: data, + error + } + }, data () { return { tab: 0, loadingView: false, - view: null, items: [ { title: this.$t('navigation.databases'), @@ -152,15 +138,12 @@ export default { database () { return this.cacheStore.getDatabase }, - cachedView () { - if (!this.database) { - return null - } - return this.database.views.filter(v => v.id === Number(this.$route.params.view_id))[0] - }, access () { return this.userStore.getAccess }, + view () { + return this.cacheStore.getView + }, identifiers () { if (!this.view) { return [] @@ -203,43 +186,11 @@ export default { } const userService = useUserService() return userService.userToFullName(this.view.creator) - }, - viewVisibility () { - if (!this.cachedView) { - return null - } - if (this.cachedView.is_public && this.cachedView.is_schema_public) { - return this.$t('pages.database.visibility.open') - } - if (!this.cachedView.is_public && !this.cachedView.is_schema_public) { - return this.$t('pages.database.visibility.closed') - } - return this.cachedView.is_public ? this.$t('pages.database.visibility.data') : this.$t('pages.database.visibility.schema') } }, - mounted () { - this.fetchView() - }, methods: { formatUTC (timestamp) { return formatTimestampUTCLabel(timestamp) - }, - fetchView () { - this.loadingView = true - const viewService = useViewService() - viewService.findOne(this.$route.params.database_id, this.$route.params.view_id) - .then((view) => { - this.view = view - this.loadingView = false - }) - .catch(({code}) => { - this.loadingView = false - const toast = useToastInstance() - toast.error(this.$t(code)) - }) - .finally(() => { - this.loadingView = false - }) } } } diff --git a/dbrepo-ui/pages/database/[database_id]/view/[view_id]/schema.vue b/dbrepo-ui/pages/database/[database_id]/view/[view_id]/schema.vue index b3352010b26653c7b3cb766734b6eb0a2e64dc44..7426d50468ee42e4b8459a123286a83c27f91a57 100644 --- a/dbrepo-ui/pages/database/[database_id]/view/[view_id]/schema.vue +++ b/dbrepo-ui/pages/database/[database_id]/view/[view_id]/schema.vue @@ -119,23 +119,20 @@ export default { } return this.access.type === 'read' || this.access.type === 'write_all' || this.access.type === 'write_own' }, - cachedView () { - if (!this.database) { - return null - } - return this.database.views.filter(v => v.id === Number(this.$route.params.view_id))[0] + view () { + return this.cacheStore.getView }, canViewSchema () { - if (!this.cachedView) { + if (!this.view) { return false } - if (this.cachedView.is_schema_public) { + if (this.view.is_schema_public) { return true } if (!this.user) { return false } - return this.hasReadAccess || this.cachedView.owned_by === this.user.id || this.database.owner.id === this.user.id + return this.hasReadAccess || this.view.owner.id === this.user.id || this.database.owner.id === this.user.id }, roles () { return this.userStore.getRoles diff --git a/dbrepo-ui/pages/index.vue b/dbrepo-ui/pages/index.vue index 037f5b9410f316cce4e24ec2a9d45c6d6615a741..6e71854c0dc72ce8f68f75772befc72fcbbf3279 100644 --- a/dbrepo-ui/pages/index.vue +++ b/dbrepo-ui/pages/index.vue @@ -7,7 +7,7 @@ <v-spacer /> <v-btn v-if="canCreateDatabase" - class="mr-4" + class="mr-2" prepend-icon="mdi-plus" variant="flat" :text="$t('toolbars.database.create.text')" diff --git a/dbrepo-ui/pages/search.vue b/dbrepo-ui/pages/search.vue index ebe16ecec1c159de639a6c735b8f806dda1da21f..c437dff5564557a1e29f1c05289e7e1a7d42ad0b 100644 --- a/dbrepo-ui/pages/search.vue +++ b/dbrepo-ui/pages/search.vue @@ -8,7 +8,7 @@ <v-spacer /> <v-btn v-if="canCreateDatabase" - class="mr-4" + class="mr-2" prepend-icon="mdi-plus" color="secondary" variant="flat" diff --git a/dbrepo-ui/stores/cache.js b/dbrepo-ui/stores/cache.js index bbe4f966c2577724e6c9a8f08f5a1143794b7497..b19658d08d2bc4531e329562d48e2c894e8a6061 100644 --- a/dbrepo-ui/stores/cache.js +++ b/dbrepo-ui/stores/cache.js @@ -6,6 +6,7 @@ export const useCacheStore = defineStore('cache', { return { database: null, table: null, + view: null, ontologies: [], messages: [], uploadProgress: null @@ -14,6 +15,7 @@ export const useCacheStore = defineStore('cache', { getters: { getDatabase: (state) => state.database, getTable: (state) => state.table, + getView: (state) => state.view, getOntologies: (state) => state.ontologies, getMessages: (state) => state.messages, getUploadProgress: (state) => state.uploadProgress, @@ -25,6 +27,9 @@ export const useCacheStore = defineStore('cache', { setTable (table) { this.table = table }, + setView (view) { + this.view = view + }, setOntologies (ontologies) { this.ontologies = ontologies }, @@ -88,6 +93,19 @@ export const useCacheStore = defineStore('cache', { .catch((error) => { console.error('Failed to set route table', error) }) + }, + setRouteView (databaseId, view_id) { + if (!databaseId || !view_id) { + this.view = null + console.error('Cannot set route view: missing view id', databaseId, 'or view id', view_id) + return + } + const viewService = useViewService() + viewService.findOne(databaseId, view_id) + .then(view => this.view = view) + .catch((error) => { + console.error('Failed to set route view', error) + }) } }, }) diff --git a/dbrepo-ui/utils/index.ts b/dbrepo-ui/utils/index.ts index 4f30d8953405445c152dee0b6afd4dc6b44cba3a..3946a709387901f2767d054b2d60c3a8c44340f5 100644 --- a/dbrepo-ui/utils/index.ts +++ b/dbrepo-ui/utils/index.ts @@ -1,4 +1,4 @@ -import { format } from 'date-fns' +import {format} from 'date-fns' import moment from 'moment' import type {AxiosError} from 'axios'