diff --git a/dbrepo-authentication-service/dbrepo-realm.json b/dbrepo-authentication-service/dbrepo-realm.json index 6947bad026f5481d33856119f687844f2ef8f925..887674f1ab9ed8d1970045ccfa1a37c1ec82d70c 100644 --- a/dbrepo-authentication-service/dbrepo-realm.json +++ b/dbrepo-authentication-service/dbrepo-realm.json @@ -258,7 +258,7 @@ "description" : "${default-data-steward-roles}", "composite" : true, "composites" : { - "realm" : [ "escalated-identifier-handling", "escalated-semantics-handling" ] + "realm" : [ "escalated-identifier-handling", "escalated-semantics-handling", "default-user-handling" ] }, "clientRole" : false, "containerId" : "82c39861-d877-4667-a0f3-4daa2ee230e0", @@ -293,7 +293,7 @@ "description" : "${escalated-semantics-handling}", "composite" : true, "composites" : { - "realm" : [ "create-ontology", "delete-ontology" ] + "realm" : [ "create-ontology", "delete-ontology", "modify-foreign-table-column-semantics" ] }, "clientRole" : false, "containerId" : "82c39861-d877-4667-a0f3-4daa2ee230e0", @@ -365,6 +365,14 @@ "clientRole" : false, "containerId" : "82c39861-d877-4667-a0f3-4daa2ee230e0", "attributes" : { } + }, { + "id" : "d4f29937-3ca0-41e9-9786-2b7b921b6cdd", + "name" : "modify-foreign-table-column-semantics", + "description" : "${modify-foreign-table-column-semantics}", + "composite" : false, + "clientRole" : false, + "containerId" : "82c39861-d877-4667-a0f3-4daa2ee230e0", + "attributes" : { } }, { "id" : "8eda9f5c-938c-4915-bed5-6a81a1de15a8", "name" : "list-database-views", @@ -1030,7 +1038,7 @@ "otpPolicyLookAheadWindow" : 1, "otpPolicyPeriod" : 30, "otpPolicyCodeReusable" : false, - "otpSupportedApplications" : [ "totpAppMicrosoftAuthenticatorName", "totpAppGoogleName", "totpAppFreeOTPName" ], + "otpSupportedApplications" : [ "totpAppGoogleName", "totpAppFreeOTPName", "totpAppMicrosoftAuthenticatorName" ], "webAuthnPolicyRpEntityName" : "keycloak", "webAuthnPolicySignatureAlgorithms" : [ "ES256" ], "webAuthnPolicyRpId" : "", @@ -2053,7 +2061,7 @@ "subType" : "authenticated", "subComponents" : { }, "config" : { - "allowed-protocol-mapper-types" : [ "oidc-full-name-mapper", "saml-user-attribute-mapper", "oidc-usermodel-property-mapper", "oidc-address-mapper", "saml-user-property-mapper", "oidc-usermodel-attribute-mapper", "saml-role-list-mapper", "oidc-sha256-pairwise-sub-mapper" ] + "allowed-protocol-mapper-types" : [ "oidc-usermodel-property-mapper", "oidc-full-name-mapper", "oidc-usermodel-attribute-mapper", "saml-user-property-mapper", "saml-role-list-mapper", "oidc-address-mapper", "oidc-sha256-pairwise-sub-mapper", "saml-user-attribute-mapper" ] } }, { "id" : "3ab11d74-5e76-408a-b85a-26bf8950f979", @@ -2062,7 +2070,7 @@ "subType" : "anonymous", "subComponents" : { }, "config" : { - "allowed-protocol-mapper-types" : [ "saml-role-list-mapper", "saml-user-attribute-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-property-mapper", "oidc-full-name-mapper", "saml-user-property-mapper", "oidc-usermodel-attribute-mapper", "oidc-address-mapper" ] + "allowed-protocol-mapper-types" : [ "oidc-usermodel-attribute-mapper", "saml-role-list-mapper", "oidc-address-mapper", "saml-user-attribute-mapper", "oidc-usermodel-property-mapper", "saml-user-property-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-full-name-mapper" ] } } ], "org.keycloak.keys.KeyProvider" : [ { @@ -2114,7 +2122,7 @@ "internationalizationEnabled" : false, "supportedLocales" : [ ], "authenticationFlows" : [ { - "id" : "78674e87-51ab-40ba-9ea5-b1ae1af14686", + "id" : "730765c7-b933-442f-98a8-6e0d53858c41", "alias" : "Account verification options", "description" : "Method with which to verity the existing account", "providerId" : "basic-flow", @@ -2136,7 +2144,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "4246a8d3-31d4-4dc0-a491-91ad0ebfe616", + "id" : "6719a3b7-a2fd-4f2c-a533-6dcf0ec334c8", "alias" : "Authentication Options", "description" : "Authentication options.", "providerId" : "basic-flow", @@ -2165,7 +2173,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "cb8d0d58-e8a2-4db2-88ac-3fd3ac769e55", + "id" : "2f17558d-ada7-426f-8eb4-5298758dc4af", "alias" : "Browser - Conditional OTP", "description" : "Flow to determine if the OTP is required for the authentication", "providerId" : "basic-flow", @@ -2187,7 +2195,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "d09df934-4ac0-4dd7-b5fa-6ae4b48eaeee", + "id" : "938df868-97e0-4f21-b3b9-e9d33d03b39c", "alias" : "Direct Grant - Conditional OTP", "description" : "Flow to determine if the OTP is required for the authentication", "providerId" : "basic-flow", @@ -2209,7 +2217,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "e26e3a1c-8540-4a1a-a44a-ce1b5da70c19", + "id" : "fe0ecfbc-042c-4007-a507-b7bab4a37f08", "alias" : "First broker login - Conditional OTP", "description" : "Flow to determine if the OTP is required for the authentication", "providerId" : "basic-flow", @@ -2231,7 +2239,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "b5d17194-c21e-438b-bd58-187a70a98a31", + "id" : "822dfb8c-aed6-40d0-8126-7c1bf7fe658a", "alias" : "Handle Existing Account", "description" : "Handle what to do if there is existing account with same email/username like authenticated identity provider", "providerId" : "basic-flow", @@ -2253,7 +2261,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "1dd2d999-87a6-41a7-8fe7-86d3090ca0b2", + "id" : "03a6ffde-ed59-49c5-8d20-836f25690f8d", "alias" : "Reset - Conditional OTP", "description" : "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", "providerId" : "basic-flow", @@ -2275,7 +2283,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "38beafa8-7daf-4273-a18f-213af7111cbe", + "id" : "7efe11ce-09c9-4834-9630-e006f703cc80", "alias" : "User creation or linking", "description" : "Flow for the existing/non-existing user alternatives", "providerId" : "basic-flow", @@ -2298,7 +2306,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "a701a337-4b1b-4856-8399-fdfa22b3f92a", + "id" : "c5dad7ff-453b-4792-91e5-3da229a44e15", "alias" : "Verify Existing Account by Re-authentication", "description" : "Reauthentication of existing account", "providerId" : "basic-flow", @@ -2320,7 +2328,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "68e6d1b3-595c-4a92-9950-96d623ff91e3", + "id" : "0f78443a-a44a-4879-beeb-f58a243cc9ca", "alias" : "browser", "description" : "browser based authentication", "providerId" : "basic-flow", @@ -2356,7 +2364,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "792000cd-85fe-4abc-9837-cf8f6ca4cdfd", + "id" : "bcf09a5f-2ead-4b5e-9768-0f7c774eb3b2", "alias" : "clients", "description" : "Base authentication for clients", "providerId" : "client-flow", @@ -2392,7 +2400,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "89f2c77f-313b-49aa-8162-979c229d58e4", + "id" : "a4b48d58-7337-4053-b717-e62e3c8eba33", "alias" : "direct grant", "description" : "OpenID Connect Resource Owner Grant", "providerId" : "basic-flow", @@ -2421,7 +2429,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "af512ed5-156d-4e4e-96e1-6d648c52e599", + "id" : "74d81399-4eb9-4982-9d38-33c33e45cfe4", "alias" : "docker auth", "description" : "Used by Docker clients to authenticate against the IDP", "providerId" : "basic-flow", @@ -2436,7 +2444,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "a117196d-5479-481f-b704-3cff61c15084", + "id" : "7993e139-d713-45c7-9b92-fb2624f67f4a", "alias" : "first broker login", "description" : "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", "providerId" : "basic-flow", @@ -2459,7 +2467,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "9e2577a4-1b38-421b-afbe-44004cd927a1", + "id" : "252ca365-eb9a-41a7-939f-f03cfb5f6f8c", "alias" : "forms", "description" : "Username, password, otp and other auth forms.", "providerId" : "basic-flow", @@ -2481,7 +2489,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "85b4f758-8c22-4edc-8fbb-a21a36b3afd6", + "id" : "dddf77cf-ddf3-4f24-a600-f00e22244f5b", "alias" : "http challenge", "description" : "An authentication flow based on challenge-response HTTP Authentication Schemes", "providerId" : "basic-flow", @@ -2503,7 +2511,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "3eb3523e-3ae2-4d7d-8778-37c8257bd546", + "id" : "66394e08-43ee-4a1d-9dc1-7fe85b099ca8", "alias" : "registration", "description" : "registration flow", "providerId" : "basic-flow", @@ -2519,7 +2527,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "c479c0db-6acd-41be-8f00-4e5f1e9ba2ca", + "id" : "23873637-627e-4e5f-b4fd-51152614b516", "alias" : "registration form", "description" : "registration form", "providerId" : "form-flow", @@ -2555,7 +2563,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "9c76b8ed-e8e2-4794-ad9e-5dab31f423c4", + "id" : "9741926c-d739-4c24-9a42-57774abc8fd1", "alias" : "reset credentials", "description" : "Reset credentials for a user if they forgot their password or something", "providerId" : "basic-flow", @@ -2591,7 +2599,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "33fe3fb0-b198-41dd-81c2-e97fbe95380f", + "id" : "b7567a38-e720-45e0-9c6a-f4019787eace", "alias" : "saml ecp", "description" : "SAML ECP Profile Authentication Flow", "providerId" : "basic-flow", @@ -2607,13 +2615,13 @@ } ] } ], "authenticatorConfig" : [ { - "id" : "fd3fb013-0708-4af4-9b1e-1104109f8d2a", + "id" : "b5f895e9-cb7f-46b0-aa9b-3c4749d14135", "alias" : "create unique user config", "config" : { "require.password.update.after.registration" : "false" } }, { - "id" : "a8f8b4ca-cfad-4cbb-b233-ad3b0baf116a", + "id" : "4c8c4815-5dc5-4831-acdd-fe231e976d07", "alias" : "review profile config", "config" : { "update.profile.on.first.login" : "missing" diff --git a/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/DbrepoSemanticsServiceApplication.java b/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/DbrepoSemanticsServiceApplication.java index 78b4148a5c95cf6927c807fbf9706344ac91dade..bec2e19c3b2a0755da8c7352d63690bf8c121a9c 100644 --- a/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/DbrepoSemanticsServiceApplication.java +++ b/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/DbrepoSemanticsServiceApplication.java @@ -7,12 +7,10 @@ import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; -import org.springframework.transaction.annotation.EnableTransactionManagement; @EnableJpaAuditing @SpringBootApplication -@EnableTransactionManagement @EntityScan(basePackages = {"at.tuwien.entities"}) @EnableElasticsearchRepositories(basePackages = {"at.tuwien.repository.elastic"}) @EnableJpaRepositories(basePackages = {"at.tuwien.repository.jpa"}) diff --git a/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/OntologyEndpoint.java b/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/OntologyEndpoint.java index 9353601049d8e37f6925e7b17b5bd1955157cb3f..1cbcc7f86f283044814b86854605cae8ec332d23 100644 --- a/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/OntologyEndpoint.java +++ b/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/OntologyEndpoint.java @@ -18,7 +18,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import jakarta.validation.Valid; @@ -43,7 +42,6 @@ public class OntologyEndpoint { } @GetMapping - @Transactional(readOnly = true) @Timed(value = "semantics.ontology.list", description = "Time needed to list ontologies") @Operation(summary = "List all ontologies") @ApiResponses(value = { @@ -64,7 +62,6 @@ public class OntologyEndpoint { } @GetMapping("/{id}") - @Transactional(readOnly = true) @Timed(value = "semantics.ontology.find", description = "Time needed to find a specific ontology") @Operation(summary = "Find one ontology") @ApiResponses(value = { @@ -82,7 +79,6 @@ public class OntologyEndpoint { } @PostMapping - @Transactional @PreAuthorize("hasAuthority('create-ontology')") @Timed(value = "semantics.ontology.create", description = "Time needed to register a new ontology") @Operation(summary = "Register a new ontology", security = @SecurityRequirement(name = "bearerAuth")) @@ -103,7 +99,6 @@ public class OntologyEndpoint { } @DeleteMapping("/{id}") - @Transactional @PreAuthorize("hasAuthority('delete-ontology')") @Timed(value = "semantics.ontology.delete", description = "Time needed to delete an ontology") @Operation(summary = "Delete an ontology", security = @SecurityRequirement(name = "bearerAuth")) diff --git a/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/QueryEndpoint.java b/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/QueryEndpoint.java index ceb1cdc6592457555cd29f9aa8a13f228d3245d5..5828965262702d36937192e32573faaaf6729bd9 100644 --- a/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/QueryEndpoint.java +++ b/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/QueryEndpoint.java @@ -20,7 +20,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -44,7 +43,6 @@ public class QueryEndpoint { } @PostMapping("/ontology/{id}/query") - @Transactional @PreAuthorize("hasAuthority('execute-semantic-query')") @Timed(value = "semantics.sparql.execute", description = "Time needed to execute a sparql query") @Operation(summary = "Register a new ontology", security = @SecurityRequirement(name = "bearerAuth")) diff --git a/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/SemanticsEndpoint.java b/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/SemanticsEndpoint.java index 805c743262c293440a1ac17416a10bf7bc2d713c..0e942b22e5ba264488ca3edf1a355ee9fb50ed96 100644 --- a/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/SemanticsEndpoint.java +++ b/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/SemanticsEndpoint.java @@ -18,7 +18,6 @@ import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; @Log4j2 @@ -37,7 +36,6 @@ public class SemanticsEndpoint { } @PostMapping("/concept") - @Transactional @PreAuthorize("hasAuthority('create-semantic-concept')") @Timed(value = "semantics.concept.save", description = "Time needed to create or update a semantic concept") @Operation(summary = "Create or update a semantic concept") @@ -57,7 +55,6 @@ public class SemanticsEndpoint { } @PostMapping("/unit") - @Transactional @PreAuthorize("hasAuthority('create-semantic-unit')") @Timed(value = "semantics.unit.save", description = "Time needed to create or update a semantic unit") @Operation(summary = "Create or update a semantic unit") diff --git a/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java b/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java index 387c2e420cc64721bc0508314f17274183fbb364..cca4ac13eaee80431ae6fa826c703e13b324cc40 100644 --- a/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java +++ b/dbrepo-semantics-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java @@ -16,7 +16,6 @@ import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import java.util.List; @@ -35,7 +34,6 @@ public class TableEndpoint { } @GetMapping - @Transactional @PreAuthorize("hasAuthority('table-semantic-analyse')") @Timed(value = "semantics.table.analyse", description = "Time needed to analyse table semantics") @Operation(summary = "Suggest table semantics", security = @SecurityRequirement(name = "bearerAuth")) diff --git a/dbrepo-semantics-service/services/src/main/java/at/tuwien/service/impl/SemanticServiceImpl.java b/dbrepo-semantics-service/services/src/main/java/at/tuwien/service/impl/SemanticServiceImpl.java index 1007249b6e7983183b03d6e217eaaf64119de74c..66d9f751315b34f3ba2ef5343ba23b8451af391b 100644 --- a/dbrepo-semantics-service/services/src/main/java/at/tuwien/service/impl/SemanticServiceImpl.java +++ b/dbrepo-semantics-service/services/src/main/java/at/tuwien/service/impl/SemanticServiceImpl.java @@ -11,7 +11,6 @@ import at.tuwien.service.SemanticService; import lombok.extern.log4j.Log4j2; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; @Log4j2 @Service @@ -30,7 +29,6 @@ public class SemanticServiceImpl implements SemanticService { } @Override - @Transactional public TableColumnConcept saveConcept(ConceptSaveDto data) { final TableColumnConcept entity = ontologyMapper.conceptSaveDtoToTableColumnConcept(data); final TableColumnConcept concept = tableColumnConceptRepository.save(entity); @@ -39,7 +37,6 @@ public class SemanticServiceImpl implements SemanticService { } @Override - @Transactional public TableColumnUnit saveUnit(UnitSaveDto data) { final TableColumnUnit entity = ontologyMapper.unitSaveDtoToTableColumnUnit(data); final TableColumnUnit unit = tableColumnUnitRepository.save(entity); diff --git a/dbrepo-table-service/rest-service/src/main/java/at/tuwien/endpoints/TableColumnEndpoint.java b/dbrepo-table-service/rest-service/src/main/java/at/tuwien/endpoints/TableColumnEndpoint.java index 0977e057787e7dc965cad1492bc2b0beac759d3a..82d41b6d8662ff7689136c10e6678eeebc109523 100644 --- a/dbrepo-table-service/rest-service/src/main/java/at/tuwien/endpoints/TableColumnEndpoint.java +++ b/dbrepo-table-service/rest-service/src/main/java/at/tuwien/endpoints/TableColumnEndpoint.java @@ -4,6 +4,7 @@ import at.tuwien.api.database.table.columns.ColumnDto; import at.tuwien.api.database.table.columns.concepts.ColumnSemanticsUpdateDto; import at.tuwien.api.error.ApiErrorDto; import at.tuwien.entities.database.table.columns.TableColumn; +import at.tuwien.entities.user.User; import at.tuwien.exception.*; import at.tuwien.mapper.TableMapper; import at.tuwien.service.TableService; @@ -46,7 +47,7 @@ public class TableColumnEndpoint { @PutMapping @Transactional - @PreAuthorize("hasAuthority('modify-table-column-semantics')") + @PreAuthorize("hasAuthority('modify-table-column-semantics') or hasAuthority('modify-foreign-table-column-semantics')") @Timed(value = "semantics.column_update", description = "Time needed to update a table column semantic mapping") @Operation(summary = "Update a table column semantic mapping", security = @SecurityRequirement(name = "bearerAuth")) @ApiResponses(value = { @@ -88,8 +89,10 @@ public class TableColumnEndpoint { SemanticEntityPersistException { log.debug("endpoint update table, containerId={}, databaseId={}, tableId={}, principal={}", containerId, databaseId, tableId, principal); - endpointValidator.validateOnlyAccess(containerId, databaseId, principal, true); - endpointValidator.validateOnlyOwnerOrWriteAll(containerId, databaseId, tableId, principal); + if (!User.hasRole(principal, "modify-foreign-table-column-semantics")) { + endpointValidator.validateOnlyAccess(containerId, databaseId, principal, true); + endpointValidator.validateOnlyOwnerOrWriteAll(containerId, databaseId, tableId, principal); + } final TableColumn column = tableService.update(containerId, databaseId, tableId, columnId, updateDto, authorization); log.info("Updated table semantics of table with id {} and database with id {}", tableId, databaseId); final ColumnDto dto = tableMapper.tableColumnToColumnDto(column); diff --git a/dbrepo-table-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java b/dbrepo-table-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java index bc5c696fd912b6535ccf5adc778c29af0a738dd5..ab10207e973b73ece4339ab2072fbe77a60029ae 100644 --- a/dbrepo-table-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java +++ b/dbrepo-table-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java @@ -177,11 +177,9 @@ public class TableEndpoint { @NotNull @PathVariable("databaseId") Long databaseId, @NotNull @PathVariable("tableId") Long tableId, Principal principal) - throws TableNotFoundException, DatabaseNotFoundException, ContainerNotFoundException, NotAllowedException { + throws TableNotFoundException, DatabaseNotFoundException, ContainerNotFoundException { log.debug("endpoint find table, containerId={}, databaseId={}, tableId={}, principal={}", containerId, databaseId, tableId, principal); -// endpointValidator.validateOnlyPrivateAccess(containerId, databaseId, principal); -// endpointValidator.validateOnlyPrivateHasRole(containerId, databaseId, principal, "find-table"); final Table table = tableService.findById(containerId, databaseId, tableId); final TableDto dto = tableMapper.tableToTableDto(table); log.trace("find table resulted in table {}", dto); diff --git a/dbrepo-table-service/services/src/main/java/at/tuwien/gateway/SemanticsServiceGateway.java b/dbrepo-table-service/services/src/main/java/at/tuwien/gateway/SemanticsServiceGateway.java deleted file mode 100644 index f4534597104502eddb6f35eea6e7c9eb1185e628..0000000000000000000000000000000000000000 --- a/dbrepo-table-service/services/src/main/java/at/tuwien/gateway/SemanticsServiceGateway.java +++ /dev/null @@ -1,13 +0,0 @@ -package at.tuwien.gateway; - -import at.tuwien.api.database.table.columns.concepts.ConceptDto; -import at.tuwien.api.database.table.columns.concepts.ConceptSaveDto; -import at.tuwien.api.database.table.columns.concepts.UnitDto; -import at.tuwien.api.database.table.columns.concepts.UnitSaveDto; -import at.tuwien.exception.SemanticEntityPersistException; - -public interface SemanticsServiceGateway { - ConceptDto saveConcept(ConceptSaveDto data, String authorization) throws SemanticEntityPersistException; - - UnitDto saveUnit(UnitSaveDto data, String authorization) throws SemanticEntityPersistException; -} diff --git a/dbrepo-table-service/services/src/main/java/at/tuwien/gateway/impl/SemanticsServiceGatewayImpl.java b/dbrepo-table-service/services/src/main/java/at/tuwien/gateway/impl/SemanticsServiceGatewayImpl.java deleted file mode 100644 index 16c8ea3c7a042daa5d0a824ec1301d118db39cdc..0000000000000000000000000000000000000000 --- a/dbrepo-table-service/services/src/main/java/at/tuwien/gateway/impl/SemanticsServiceGatewayImpl.java +++ /dev/null @@ -1,51 +0,0 @@ -package at.tuwien.gateway.impl; - -import at.tuwien.api.database.table.columns.concepts.ConceptDto; -import at.tuwien.api.database.table.columns.concepts.ConceptSaveDto; -import at.tuwien.api.database.table.columns.concepts.UnitDto; -import at.tuwien.api.database.table.columns.concepts.UnitSaveDto; -import at.tuwien.exception.SemanticEntityPersistException; -import at.tuwien.gateway.SemanticsServiceGateway; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.*; -import org.springframework.stereotype.Service; -import org.springframework.web.client.RestTemplate; - -@Slf4j -@Service -public class SemanticsServiceGatewayImpl implements SemanticsServiceGateway { - - private final RestTemplate restTemplate; - - @Autowired - public SemanticsServiceGatewayImpl(RestTemplate restTemplate) { - this.restTemplate = restTemplate; - } - - @Override - public ConceptDto saveConcept(ConceptSaveDto data, String authorization) throws SemanticEntityPersistException { - final HttpHeaders headers = new HttpHeaders(); - headers.set("Authorization", authorization); - final ResponseEntity<ConceptDto> response = restTemplate.exchange("/api/semantic/concept", HttpMethod.POST, - new HttpEntity<>(data, headers), ConceptDto.class); - if (!response.getStatusCode().equals(HttpStatus.ACCEPTED)) { - log.error("Failed to save concept with uri {}", data.getUri()); - throw new SemanticEntityPersistException("Failed to save concept with uri " + data.getUri()); - } - return response.getBody(); - } - - @Override - public UnitDto saveUnit(UnitSaveDto data, String authorization) throws SemanticEntityPersistException { - final HttpHeaders headers = new HttpHeaders(); - headers.set("Authorization", authorization); - final ResponseEntity<UnitDto> response = restTemplate.exchange("/api/semantic/unit", HttpMethod.POST, - new HttpEntity<>(data, headers), UnitDto.class); - if (!response.getStatusCode().equals(HttpStatus.ACCEPTED)) { - log.error("Failed to save unit with uri {}", data.getUri()); - throw new SemanticEntityPersistException("Failed to save unit with uri " + data.getUri()); - } - return response.getBody(); - } -} diff --git a/dbrepo-table-service/services/src/main/java/at/tuwien/mapper/TableMapper.java b/dbrepo-table-service/services/src/main/java/at/tuwien/mapper/TableMapper.java index fae0a696e4cea9c71577c0ada10054c68baf944d..5dd576ce0fb3d26a1590c419a1869643ba1064d9 100644 --- a/dbrepo-table-service/services/src/main/java/at/tuwien/mapper/TableMapper.java +++ b/dbrepo-table-service/services/src/main/java/at/tuwien/mapper/TableMapper.java @@ -87,12 +87,12 @@ public interface TableMapper { @Mappings({ @Mapping(source = "conceptUri", target = "uri") }) - ConceptSaveDto columnSemanticsUpdateDtoToConceptSaveDto(ColumnSemanticsUpdateDto data); + TableColumnConcept columnSemanticsUpdateDtoToTableColumnConcept(ColumnSemanticsUpdateDto data); @Mappings({ @Mapping(source = "unitUri", target = "uri") }) - UnitSaveDto columnSemanticsUpdateDtoToUnitSaveDto(ColumnSemanticsUpdateDto data); + TableColumnUnit columnSemanticsUpdateDtoToTableColumnUnit(ColumnSemanticsUpdateDto data); default TableColumn columnNameToTableColumn(Table table, String name) throws TableMalformedException { String internalName = nameToInternalName(name); diff --git a/dbrepo-table-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java b/dbrepo-table-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java index b8168d4b2476b7c9f900b863816cc3a96aebf14e..9f46855a19dd069a71e28bf25d05f7060df204d9 100644 --- a/dbrepo-table-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java +++ b/dbrepo-table-service/services/src/main/java/at/tuwien/service/impl/TableServiceImpl.java @@ -11,7 +11,6 @@ import at.tuwien.entities.database.table.columns.TableColumnConcept; import at.tuwien.entities.database.table.columns.TableColumnUnit; import at.tuwien.entities.user.User; import at.tuwien.exception.*; -import at.tuwien.gateway.SemanticsServiceGateway; import at.tuwien.mapper.TableMapper; import at.tuwien.repository.elastic.TableColumnIdxRepository; import at.tuwien.repository.elastic.TableIdxRepository; @@ -49,7 +48,6 @@ public class TableServiceImpl extends HibernateConnector implements TableService private final ConceptRepository conceptRepository; private final TableIdxRepository tableIdxRepository; private final TableColumnRepository tableColumnRepository; - private final SemanticsServiceGateway semanticsServiceGateway; private final TableColumnIdxRepository tableColumnIdxRepository; @Autowired @@ -57,7 +55,7 @@ public class TableServiceImpl extends HibernateConnector implements TableService TableRepository tableRepository, DatabaseService databaseService, ContainerService containerService, ConceptRepository conceptRepository, TableIdxRepository tableIdxRepository, TableColumnRepository tableColumnRepository, - SemanticsServiceGateway semanticsServiceGateway, TableColumnIdxRepository tableColumnIdxRepository) { + TableColumnIdxRepository tableColumnIdxRepository) { this.tableMapper = tableMapper; this.userService = userService; this.unitRepository = unitRepository; @@ -67,7 +65,6 @@ public class TableServiceImpl extends HibernateConnector implements TableService this.conceptRepository = conceptRepository; this.tableIdxRepository = tableIdxRepository; this.tableColumnRepository = tableColumnRepository; - this.semanticsServiceGateway = semanticsServiceGateway; this.tableColumnIdxRepository = tableColumnIdxRepository; } @@ -217,12 +214,12 @@ public class TableServiceImpl extends HibernateConnector implements TableService public TableColumn update(Long containerId, Long databaseId, Long tableId, Long columnId, ColumnSemanticsUpdateDto updateDto, String authorization) throws TableNotFoundException, DatabaseNotFoundException, ContainerNotFoundException, - TableMalformedException, UnitNotFoundException, ConceptNotFoundException, SemanticEntityPersistException { + TableMalformedException, UnitNotFoundException, ConceptNotFoundException { final Table table = findById(containerId, databaseId, tableId); final TableColumn column = findColumn(table, columnId); /* assign */ if (updateDto.getUnitUri() != null) { - semanticsServiceGateway.saveUnit(tableMapper.columnSemanticsUpdateDtoToUnitSaveDto(updateDto), authorization); + unitRepository.save(tableMapper.columnSemanticsUpdateDtoToTableColumnUnit(updateDto)); final TableColumnUnit unit = findUnit(updateDto.getUnitUri()); column.setUnit(unit); log.debug("update unit of column, unit={}, column={}", unit, column); @@ -231,7 +228,7 @@ public class TableServiceImpl extends HibernateConnector implements TableService log.debug("remove unit of column, column={}", column); } if (updateDto.getConceptUri() != null) { - semanticsServiceGateway.saveConcept(tableMapper.columnSemanticsUpdateDtoToConceptSaveDto(updateDto), authorization); + conceptRepository.save(tableMapper.columnSemanticsUpdateDtoToTableColumnConcept(updateDto)); final TableColumnConcept concept = findConcept(updateDto.getConceptUri()); column.setConcept(concept); log.debug("update ColumnConcept of column, concept={}, column={}", concept, column); diff --git a/dbrepo-ui/api/database.service.js b/dbrepo-ui/api/database.service.js index c01fc2ceae0933c0df03b49cdeccfc89d786b013..db440028953514abb7b97869dc1c21dac64a7109 100644 --- a/dbrepo-ui/api/database.service.js +++ b/dbrepo-ui/api/database.service.js @@ -107,7 +107,7 @@ class DatabaseService { .catch((error) => { const { code, message, response } = error const { status } = response - if (status !== 403 && status !== 405) { /* ignore no access errors */ + if (status !== 401 && status !== 403 && status !== 405) { /* ignore no access errors */ console.error('Failed to check database access', error) Vue.$toast.error(`[${code}] Failed to check database access: ${message}`) reject(error) diff --git a/dbrepo-ui/components/dialogs/Semantics.vue b/dbrepo-ui/components/dialogs/Semantics.vue index 7a5fb91c041e7e1ded9cdb43bf7e3cb3066395c6..f2f182d216442055ee8fd40d84df0e914851c3ff 100644 --- a/dbrepo-ui/components/dialogs/Semantics.vue +++ b/dbrepo-ui/components/dialogs/Semantics.vue @@ -4,8 +4,11 @@ <v-card-title>Assign Semantic Information</v-card-title> <v-card-subtitle>We recommend the following ontologies</v-card-subtitle> <v-card-text> - <div v-for="(ontology,idx) in ontologies" :key="idx"> - <strong>{{ ontology.prefix }}</strong>: <a :href="ontology.uri" target="_blank">{{ ontology.uri }}</a> + <v-skeleton-loader v-if="loadingOntologies" type="list-item-three-line" /> + <div v-else> + <div v-for="(ontology,idx) in ontologies" :key="idx"> + <strong>{{ ontology.prefix }}</strong>: <a :href="ontology.uri" target="_blank">{{ ontology.uri }}</a> + </div> </div> </v-card-text> <v-card-text> @@ -20,7 +23,6 @@ clearable single-line hide-details - :rules="[v => !!v || $t('Required')]" placeholder="http://www.wikidata.org/entity/Q468777" @click:clear="uri = null" /> </v-toolbar> @@ -84,6 +86,17 @@ export default { computed: { }, watch: { + column () { + if (this.column.unit && this.mode === 'unit') { + this.uri = this.column.unit.uri + return + } + if (this.column.concept && this.mode === 'concept') { + this.uri = this.column.concept.uri + return + } + this.uri = null + } }, mounted () { this.loadOntologies() @@ -107,11 +120,8 @@ export default { action: 'assign' }) }) - .catch(() => { - this.loadingSave = true - }) .finally(() => { - this.loadingSave = true + this.loadingSave = false }) }, loadOntologies () { @@ -121,7 +131,7 @@ export default { this.ontologies = ontologies }) .finally(() => { - this.loadingOntologies = true + this.loadingOntologies = false }) }, submit () { diff --git a/dbrepo-ui/pages/container/_container_id/database/_database_id/table/_table_id/schema.vue b/dbrepo-ui/pages/container/_container_id/database/_database_id/table/_table_id/schema.vue index ebcfc2d930cd965d99ca10a725b2cabe084c92af..d5c3d1b0778113e068d10088f25c87bce469a702 100644 --- a/dbrepo-ui/pages/container/_container_id/database/_database_id/table/_table_id/schema.vue +++ b/dbrepo-ui/pages/container/_container_id/database/_database_id/table/_table_id/schema.vue @@ -28,9 +28,9 @@ <span v-if="item.auto_generated">●</span> {{ item.auto_generated }} </template> <template v-slot:item.column_concept="{ item }"> - <v-btn v-if="canModify && !hasConcept(item)" small @click="pick(item, 'concept')">Assign</v-btn> + <v-btn v-if="canAssignSemanticInformation && !hasConcept(item)" small @click="pick(item, 'concept')">Assign</v-btn> <v-btn - v-if="canModify && hasConcept(item)" + v-if="canAssignSemanticInformation && hasConcept(item)" :title="item.concept.uri" color="secondary" small @@ -38,15 +38,15 @@ <span v-if="item.concept.name" v-text="item.concept.name" /> <span v-else v-text="item.concept.uri" /> </v-btn> - <a v-if="!canModify && hasConcept(item)" :href="item.concept.uri" target="_blank"> + <a v-if="!canAssignSemanticInformation && hasConcept(item)" :href="item.concept.uri" target="_blank"> <span v-if="item.concept.name" v-text="item.concept.name" /> <span v-else v-text="item.concept.uri" /> </a> </template> <template v-slot:item.column_unit="{ item }"> - <v-btn v-if="canModify && !hasUnit(item)" small @click="pick(item, 'unit')">Assign</v-btn> + <v-btn v-if="canAssignSemanticInformation && !hasUnit(item)" small @click="pick(item, 'unit')">Assign</v-btn> <v-btn - v-if="canModify && hasUnit(item)" + v-if="canAssignSemanticInformation && hasUnit(item)" :title="item.unit.uri" color="secondary" small @@ -54,7 +54,7 @@ <span v-if="item.unit.name" v-text="item.unit.name" /> <span v-else v-text="item.unit.uri" /> </v-btn> - <a v-if="!canModify && hasUnit(item)" :href="item.unit.uri" target="_blank"> + <a v-if="!canAssignSemanticInformation && hasUnit(item)" :href="item.unit.uri" target="_blank"> <span v-if="item.unit.name" v-text="item.unit.name" /> <span v-else v-text="item.unit.uri" /> </a> @@ -121,20 +121,6 @@ export default { } }, computed: { - token () { - return this.$store.state.token - }, - config () { - if (this.token === null) { - return { - headers: {}, - progress: false - } - } - return { - headers: { Authorization: `Bearer ${this.token}` } - } - }, user () { return this.$store.state.user }, @@ -147,12 +133,20 @@ export default { access () { return this.$store.state.access }, - canModify () { - if (!this.token || !this.user.username) { - /* not yet loaded */ + roles () { + return this.$store.state.roles + }, + canAssignSemanticInformation () { + if (!this.user) { + return false + } + if (this.roles.includes('modify-foreign-table-column-semantics')) { + return true + } + if (!this.access) { return false } - return this.table.creator.username === this.user.username + return this.roles.includes('modify-table-column-semantics') && (this.access.type === 'write_all' || this.table.owner.username === this.user.username) }, versionColor () { if (this.version === null) {