From b58828a7557c24aac3cfa88101f4b12ed8d474ff Mon Sep 17 00:00:00 2001 From: Balduin Landolt <33053745+BalduinLandolt@users.noreply.github.com> Date: Tue, 3 Jan 2023 15:57:43 +0100 Subject: [PATCH] fix!: return external representation of ontology IRIs in admin routes (#2330) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: irinaschubert Co-authored-by: Christian Kleinbölting Co-authored-by: Marcin Procyk --- Makefile | 2 +- docs/03-endpoints/api-admin/projects.md | 881 +++++++++++++++--- .../design/api-admin/administration.md | 3 + .../webapi/e2e/admin/GroupsADME2ESpec.scala | 8 +- .../webapi/e2e/admin/UsersADME2ESpec.scala | 36 +- .../admin/PermissionsResponderADMSpec.scala | 727 ++++++++------- .../admin/ProjectsResponderADMSpec.scala | 18 +- .../SharedOntologyTestDataADM.scala | 4 + .../sharedtestdata/SharedTestDataADM.scala | 93 +- .../ProjectsMessagesADM.scala | 8 - .../admin/PermissionsResponderADM.scala | 187 ++-- .../admin/ProjectsResponderADM.scala | 138 +-- .../knora/webapi/routing/RouteUtilADM.scala | 67 +- 13 files changed, 1438 insertions(+), 734 deletions(-) diff --git a/Makefile b/Makefile index 28e977bf00..3cf2353fcb 100644 --- a/Makefile +++ b/Makefile @@ -93,7 +93,7 @@ stack-up: docker-build env-file ## starts the dsp-stack: fuseki, sipi, api and a $(CURRENT_DIR)/webapi/scripts/wait-for-api.sh .PHONY: stack-up-fast -stack-up-fast: docker-build-knora-api-image env-file ## starts the dsp-stack by skipping rebuilding most of the images (only api image is rebuilt). +stack-up-fast: docker-build-dsp-api-image env-file ## starts the dsp-stack by skipping rebuilding most of the images (only api image is rebuilt). docker-compose -f docker-compose.yml up -d .PHONY: stack-up-ci diff --git a/docs/03-endpoints/api-admin/projects.md b/docs/03-endpoints/api-admin/projects.md index 2d84649725..ee523b7d50 100644 --- a/docs/03-endpoints/api-admin/projects.md +++ b/docs/03-endpoints/api-admin/projects.md @@ -5,95 +5,162 @@ # Projects Endpoint -## Endpoint Overview +| Scope | Route | Operations | Explanation | +| --------------- | -------------------------------------------------------------- | ---------- | ----------------------------------------------------------------------- | +| projects | `/admin/projects` | `GET` | [get all projects](#get-all-projects) | +| projects | `/admin/projects` | `POST` | [create a project](#create-a-new-project) | +| projects | `/admin/projects/shortname/{shortname}` | `GET` | [get a single project](#get-project-by-id) | +| projects | `/admin/projects/shortcode/{shortcode}` | `GET` | [get a single project](#get-project-by-id) | +| projects | `/admin/projects/iri/{iri}` | `GET` | [get a single project](#get-project-by-id) | +| projects | `/admin/projects/iri/{iri}` | `PUT` | [update a project](#update-project-information) | +| projects | `/admin/projects/iri/{iri}` | `DELETE` | [delete a project](#delete-a-project) | +| projects | `/admin/projects/iri/{iri}/AllData` | `GET` | [get all data of a project](#get-all-data-of-a-project) | +| project members | `/admin/projects/shortname/{shortname}/members` | `GET` | [get all project members](#get-project-members-by-id) | +| project members | `/admin/projects/shortcode/{shortcode}/members` | `GET` | [get all project members](#get-project-members-by-id) | +| project members | `/admin/projects/iri/{iri}/members` | `GET` | [get all project members](#get-project-members-by-id) | +| project members | `/admin/projects/shortname/{shortname}/admin-members` | `GET` | [get all project admins](#get-project-admins-by-id) | +| project members | `/admin/projects/shortcode/{shortcode}/admin-members` | `GET` | [get all project admins](#get-project-admins-by-id) | +| project members | `/admin/projects/iri/{iri}/admin-members` | `GET` | [get all project admins](#get-project-admins-by-id) | +| keywords | `/admin/projects/Keywords` | `GET` | [get all project keywords](#get-all-keywords) | +| keywords | `/admin/projects/iri/{iri}/Keywords` | `GET` | [get project keywords of a single project](#get-keywords-of-a-project) | +| view settings | `/admin/projects/shortname/{shortname}/RestrictedViewSettings` | `GET` | [get restricted view settings for a project](#restricted-view-settings) | +| view settings | `/admin/projects/shortcode/{shortcode}/RestrictedViewSettings` | `GET` | [get restricted view settings for a project](#restricted-view-settings) | +| view settings | `/admin/projects/iri/{iri}/RestrictedViewSettings` | `GET` | [get restricted view settings for a project](#restricted-view-settings) | -**Project Operations:** -- `GET: /admin/projects` : return all projects - -- `POST: /admin/projects` : create a new project - -- `GET: /admin/projects/[iri | shortname | shortcode]/` : returns a single project identified either through iri, shortname or shortcode - -- `PUT: /admin/projects/iri/` : update a project identified by iri - -- `DELETE: /admin/projects/iri/` : update project status to false - -- `GET: /admin/projects/iri//AllData` : returns a TriG file containing the project's data - -**Project Member Operations:** - -- `GET: /admin/projects/[iri | shortname | shortcode]//members` : returns all members part of a project identified through iri, shortname or shortcode - -**Project Admin Member Operations:** +## Project Operations -- `GET: /admin/projects/[iri | shortname | shortcode]//admin-members` : returns all admin members part of a project identified through iri, shortname or shortcode +### Get All Projects -**Project Keyword Operations:** +Permissions: No permissions required -- `GET: /admin/projects/Keywords` : returns all unique keywords for all projects as a list +Request definition: `GET /admin/projects` -- `GET: /admin/projects/iri//Keywords` : returns all keywords for a single project +Description: Returns a list of all projects. -**Project Restricted View Settings Operations:** +Example request: -- `GET: /admin/projects/iri//RestrictedViewSettings` : returns the project's restricted view settings +```bash +curl --request GET --url http://localhost:3333/admin/projects +``` -## Project Operations +Example response: -### Create a new project: - - - Required permission: SystemAdmin - - Required information: - - shortcode (unique, 4-digits) - - shortname (unique; it should be in the form of a - [xsd:NCNAME](https://www.w3.org/TR/xmlschema11-2/#NCName) and it should be URL safe.) - - description (collection of descriptions as strings with language tag.) - - keywords (collection of keywords) - - status (true, if project is active. false, if project is inactive) - - selfjoin - - Optional information: longname, logo - - Returns information about the newly created project - - Remark: There are two distinct use cases / payload combination: - - (1) change ontology and data graph: ontologygraph, datagraph, - - (2) basic project information: shortcode, shortname, longname, description, - keywords, logo, institution, status, selfjoin - - - POST: `/admin/projects/` - - BODY: - -```json +```jsonc +{ + "projects": [ { - "shortname": "newproject", - "shortcode": "3333", - "longname": "project longname", - "description": [{"value": "project description", "language": "en"}], - "keywords": ["test project"], - "logo": "/fu/bar/baz.jpg", - "status": true, - "selfjoin": false + "description": [ + { + "value": "A demo project of a collection of images", + "language": "en" + } + ], + "id": "http://rdfh.ch/projects/00FF", + "keywords": [ + "collection", + "images" + ], + "logo": null, + "longname": "Image Collection Demo", + "ontologies": [ + "http://0.0.0.0:3333/ontology/00FF/images/v2" + ], + "selfjoin": false, + "shortcode": "00FF", + "shortname": "images", + "status": true + }, + { + // ... } + ] +} ``` -Additionally, each project can have an optional custom IRI (of [Knora IRI](../api-v2/knora-iris.md#iris-for-data) form) -specified by the `id` in the request body as below: - -```json - { - "id": "http://rdfh.ch/projects/9TaSVMUuiRhQsuWHDPr8rw", - "shortname": "newprojectWithIri", - "shortcode": "3333", - "longname": "new project with a custom IRI", - "description": [{"value": "a project created with a custom IRI", "language": "en"}], - "keywords": ["projectWithIRI"], +### Create a New Project + +Permissions: SystemAdmin + +Request definition: `POST /admin/projects` + +Description: Create a new project. + +Required payload: + +- `shortcode` (unique, 4-digits) +- `shortname` (unique, it should be in the form of a [xsd:NCNAME](https://www.w3.org/TR/xmlschema11-2/#NCName) and it + should be URL safe) +- `description` (collection of descriptions as strings with language tag) +- `keywords` (collection of keywords) +- `status` (true, if project is active. false, if project is inactive) +- `selfjoin` + +Optional payload: + +- `id` (unique, custom DSP IRI, e.g. used for migrating a project from one server to another) +- `longname` +- `logo` + + +Example request: + +```bash +curl --request POST \ + --url http://localhost:3333/admin/projects \ + --header 'Authorization: Basic cm9vdEBleGFtcGxlLmNvbTp0ZXN0' \ + --header 'Content-Type: application/json' \ + --data '{ + "shortname": "newproject", + "shortcode": "3333", + "longname": "project longname", + "description": [ + { + "value": "project description", + "language": "en" + } + ], + "keywords": [ + "test project" + ], + "logo": "/fu/bar/baz.jpg", + "status": true, + "selfjoin": false +}' +``` + +Example response: + +```jsonc +{ + "project": { + "description": [ + { + "value": "project description", + "language": "en" + } + ], + "id": "http://rdfh.ch/projects/3333", + "keywords": [ + "test project" + ], "logo": "/fu/bar/baz.jpg", - "status": true, - "selfjoin": false - } + "longname": "project longname", + "ontologies": [], + "selfjoin": false, + "shortcode": "3333", + "shortname": "newproject", + "status": true + } +} ``` +Errors: + +- `400 Bad Request` if the project already exists or any of the provided properties is invalid. +- `401 Unauthorized` if authorization failed. + + #### Default set of permissions for a new project: When a new project is created, following default permissions are added to its admins and members: @@ -114,91 +181,639 @@ administrative permission is retrievable through its IRI: permissions) of any entity that belongs to the project. This default object access permission is retrievable through its IRI: `http://rdfh.ch/permissions/[projectShortcode]/defaultDoapForMember` -### Update project information: - - Required permission: SystemAdmin / ProjectAdmin - - Changeable information: shortname, longname, description, - keywords, logo, status, selfjoin. The payload must at least contain a new value for one of these properties. - - TypeScript Docs: projectFormats - ChangeProjectApiRequestV1 - - PUT: `/admin/projects/iri/` - - BODY: +### Get Project by ID -```json - { - "shortname": "newproject", - "longname": "project longname", - "description": [{"value": "a new description", "language": "en"}], - "keywords": ["a new key"], - "logo": "/fu/bar/baz.jpg", - "status": true, - "selfjoin": false - } +The ID can be shortcode, shortname or IRI. + +Permissions: No permissions required + +Request definition: + +- `GET /admin/projects/shortcode/{shortcode}` +- `GET /admin/projects/shortname/{shortname}` +- `GET /admin/projects/iri/{iri}` + +Description: Returns a single project identified by shortcode, shortname or IRI. + +Example request: + +```bash +curl --request GET --url http://localhost:3333/admin/projects/shortcode/0001 +``` + +```bash +curl --request GET --url http://localhost:3333/admin/projects/shortname/anything +``` + +```bash +curl --request GET --url \ + http://localhost:3333/admin/projects/iri/http%3A%2F%2Frdfh.ch%2Fprojects%2F0001 +``` + +Example response: + +```jsonc +{ + "project": { + "description": [ + { + "value": "Anything Project" + } + ], + "id": "http://rdfh.ch/projects/0001", + "keywords": [ + "arbitrary test data", + "things" + ], + "logo": null, + "longname": "Anything Project", + "ontologies": [ + "http://0.0.0.0:3333/ontology/0001/something/v2", + "http://0.0.0.0:3333/ontology/0001/sequences/v2", + "http://0.0.0.0:3333/ontology/0001/freetest/v2", + "http://0.0.0.0:3333/ontology/0001/minimal/v2", + "http://0.0.0.0:3333/ontology/0001/anything/v2" + ], + "selfjoin": false, + "shortcode": "0001", + "shortname": "anything", + "status": true + } +} ``` -### Delete project (update project status): +Errors: + +- `400 Bad Request` if the provided ID is not valid. +- `404 Not Found` if no project with the provided ID is found. + +NB: + +- IRI must be URL-encoded. + + +### Update Project Information + +Permissions: SystemAdmin / ProjectAdmin + +Request definition: `PUT /admin/projects/iri/{iri}` + +Description: Update a project identified by its IRI. + +Payload: The following properties can be changed: + +- `longname` +- `description` +- `keywords` +- `logo` +- `status` +- `selfjoin` + +Example request: + +```bash +curl --request PUT \ + --url http://localhost:3333/admin/projects/iri/http%3A%2F%2Frdfh.ch%2Fprojects%2F0001 \ + --header 'Authorization: Basic cm9vdEBleGFtcGxlLmNvbTp0ZXN0' \ + --header 'Content-Type: application/json' \ + --data '{ + "longname": "other longname" +}' +``` + +Example response: + +```jsonc +{ + "project": { + "description": [ + { + "value": "Anything Project" + } + ], + "id": "http://rdfh.ch/projects/0001", + "keywords": [ + "arbitrary test data", + "things" + ], + "logo": null, + "longname": "other longname", + "ontologies": [ + "http://api.knora.org/ontology/0001/something/v2", + "http://api.knora.org/ontology/0001/sequences/v2", + "http://api.knora.org/ontology/0001/freetest/v2", + "http://api.knora.org/ontology/0001/minimal/v2", + "http://api.knora.org/ontology/0001/anything/v2" + ], + "selfjoin": false, + "shortcode": "0001", + "shortname": "anything", + "status": true + } +} +``` + +Errors: + +- `400 Bad Request` + - if the provided IRI is not valid. + - if the provided payload is not valid. +- `404 Not Found` if no project with the provided IRI is found. + + +### Delete a Project + +Permissions: SystemAdmin / ProjectAdmin + +Request definition: `DELETE /admin/projects/iri/{iri}` + +Description: Mark a project as deleted (by setting the `status` flag to `false`). + +```bash +curl --request DELETE \ + --url http://localhost:3333/admin/projects/iri/http%3A%2F%2Frdfh.ch%2Fprojects%2F0001 \ + --header 'Authorization: Basic cm9vdEBleGFtcGxlLmNvbTp0ZXN0' \ + --header 'Content-Type: application/json' +``` + +Example response: + +```jsonc +{ + "project": { + "description": [ + { + "value": "Anything Project" + } + ], + "id": "http://rdfh.ch/projects/0001", + "keywords": [ + "arbitrary test data", + "things" + ], + "logo": null, + "longname": "other longname", + "ontologies": [ + "http://api.knora.org/ontology/0001/something/v2", + "http://api.knora.org/ontology/0001/sequences/v2", + "http://api.knora.org/ontology/0001/freetest/v2", + "http://api.knora.org/ontology/0001/minimal/v2", + "http://api.knora.org/ontology/0001/anything/v2" + ], + "selfjoin": false, + "shortcode": "0001", + "shortname": "anything", + "status": false + } +} +``` + +Errors: + +- `400 Bad Request` if the provided IRI is not valid. +- `404 Not Found` if no project with the provided IRI is found. - - Required permission: SystemAdmin / ProjectAdmin - - Remark: The same as updating a project and changing `status` to - `false`. To un-delete, set `status` to `true`. - - DELETE: `/admin/projects/iri/` - - BODY: empty -### Dump project data: +### Get all Data of a Project -Returns a [TriG](https://www.w3.org/TR/trig/) file containing the project's -ontologies, resource data, admin data, and permissions. +Permissions: ProjectAdmin / SystemAdmin - - Required permission: SystemAdmin / ProjectAdmin - - Required information: project IRI - - `GET: /admin/projects/iri//AllData` +Request definition: `POST /admin/projects/iri/{iri}/AllData` + +Description: Gets all data of a project as a TriG file (ontologies, resource data, admin data, and permissions). + +Example request: + +```bash +curl --request GET \ + --url http://localhost:3333/admin/projects/iri/http%3A%2F%2Frdfh.ch%2Fprojects%2F00FF/AllData \ + --header 'Authorization: Basic cm9vdEBleGFtcGxlLmNvbTp0ZXN0' +``` + +Example response: + +```trig +@prefix images: . +@prefix knora-admin: . +@prefix knora-base: . +@prefix owl: . +@prefix rdf: . +@prefix rdfs: . +@prefix salsah-gui: . +@prefix standoff: . +@prefix xml: . +@prefix xsd: . + + { + + rdf:type owl:Ontology ; + rdfs:label "The images demo ontology" ; + knora-base:attachedToProject ; + knora-base:lastModificationDate "2012-12-12T12:12:12.12Z"^^xsd:dateTime . + images:lastname rdf:type owl:ObjectProperty ; + rdfs:comment "Nachname einer Person"@de ; + rdfs:comment "Last name of a person"@en ; + rdfs:label "Name"@de ; + rdfs:subPropertyOf knora-base:hasValue ; + knora-base:objectClassConstraint knora-base:TextValue ; + knora-base:subjectClassConstraint images:person ; + salsah-gui:guiAttribute "size=32" ; + salsah-gui:guiAttribute "maxlength=32" ; + salsah-gui:guiElement salsah-gui:SimpleText . + # ... +} + + { + + rdf:type images:bild ; + rdfs:label "1 Alpinismus" ; + images:bearbeiter ; + images:titel ; + images:urheber ; + images:urheberValue ; + knora-base:attachedToProject ; + knora-base:attachedToUser ; + knora-base:creationDate "2016-03-02T15:05:57Z"^^xsd:dateTime ; + knora-base:hasPermissions "CR knora-admin:ProjectMember,knora-admin:Creator|V knora-admin:KnownUser|RV knora-admin:UnknownUser" ; + knora-base:hasStillImageFileValue ; + knora-base:isDeleted false . + # ... +} + + { + # ... +} + + { + # ... +} +``` ## Project Member Operations -### Get project members: +### Get Project Members by ID - - Required permission: SystemAdmin / ProjectAdmin - - Required information: project identifier - - GET: `/admin/projects/[iri | shortname | shortcode]//members` +Permissions: SystemAdmin / ProjectAdmin +Request definition: -## Project Admin Member Operations +- `GET /admin/projects/shortcode/{shortcode}/members` +- `GET /admin/projects/shortname/{shortname}/members` +- `GET /admin/projects/iri/{iri}/members` -### Get project members: +Description: returns all members part of a project identified through iri, shortname or shortcode - - Required permission: SystemAdmin / ProjectAdmin - - Required information: project identifier - - GET: `/admin/projects/[iri | shortname | shortcode]//admin-members` +Example request: +```bash +curl --request GET 'http://0.0.0.0:3333/admin/projects/shortcode/0001/members' \ +--header 'Authorization: Basic cm9vdEBleGFtcGxlLmNvbTp0ZXN0' +``` -### Restricted View Settings Operations +```bash +curl --request GET 'http://0.0.0.0:3333/admin/projects/shortname/anything/members' \ +--header 'Authorization: Basic cm9vdEBleGFtcGxlLmNvbTp0ZXN0' +``` -Operates on the following properties: - - `knora-admin:projectRestrictedViewSize` - takes the IIIF size value - - `knora-admin:projectRestrictedViewWatermark` - takes the path to the watermark image. **Currently not used.** +```bash +curl --request GET 'http://0.0.0.0:3333/admin/projects/iri/http%3A%2F%2Frdfh.ch%2Fprojects%2F0001/members' +--header 'Authorization: Basic cm9vdEBleGFtcGxlLmNvbTp0ZXN0' +``` + +Example response: + +```jsonc +{ + "members": [ + { + "email": "anything.user01@example.org", + "familyName": "UserFamilyName", + "givenName": "UserGivenName", + "groups": [], + "id": "http://rdfh.ch/users/BhkfBc3hTeS_IDo-JgXRbQ", + "lang": "de", + "password": null, + "permissions": { + "administrativePermissionsPerProject": { + "http://rdfh.ch/projects/0001": [ + { + "additionalInformation": null, + "name": "ProjectResourceCreateAllPermission", + "permissionCode": null + } + ] + }, + "groupsPerProject": { + "http://rdfh.ch/projects/0001": [ + "http://www.knora.org/ontology/knora-admin#ProjectMember" + ] + } + }, + "projects": [ + { + "description": [ + { + "value": "Anything Project" + } + ], + "id": "http://rdfh.ch/projects/0001", + "keywords": [ + "arbitrary test data", + "things" + ], + "logo": null, + "longname": "Anything Project", + "ontologies": [ + "http://0.0.0.0:3333/ontology/0001/something/v2", + "http://0.0.0.0:3333/ontology/0001/anything/v2" + ], + "selfjoin": false, + "shortcode": "0001", + "shortname": "anything", + "status": true + } + ], + "sessionId": null, + "status": true, + "token": null, + "username": "anything.user01" + } + ] +} +``` + +Errors: + +- `400 Bad Request` if the provided ID is not valid. +- `404 Not Found` if no project with the provided ID is found. + +NB: + +- IRI must be URL-encoded. + +### Get Project Admins by ID + +Permissions: SystemAdmin / ProjectAdmin + +Request definition: +- `GET /admin/projects/shortcode/{shortcode}/admin-members` +- `GET /admin/projects/shortname/{shortname}/admin-members` +- `GET /admin/projects/iri/{iri}/admin-members` + +Description: returns all admin members part of a project identified through iri, shortname or shortcode + +Example request: + +```bash +curl --request GET 'http://0.0.0.0:3333/admin/projects/shortcode/0001/admin-members' \ +--header 'Authorization: Basic cm9vdEBleGFtcGxlLmNvbTp0ZXN0' +``` + +```bash +curl --request GET 'http://0.0.0.0:3333/admin/projects/shortname/anything/admin-members' \ +--header 'Authorization: Basic cm9vdEBleGFtcGxlLmNvbTp0ZXN0' +``` + +```bash +curl --request GET 'http://0.0.0.0:3333/admin/projects/iri/http%3A%2F%2Frdfh.ch%2Fprojects%2F0001/admin-members' +--header 'Authorization: Basic cm9vdEBleGFtcGxlLmNvbTp0ZXN0' +``` + +Example response: + +```jsonc +{ + "members": [ + { + "email": "anything.admin@example.org", + "familyName": "Admin", + "givenName": "Anything", + "groups": [], + "id": "http://rdfh.ch/users/AnythingAdminUser", + "lang": "de", + "password": null, + "permissions": { + "administrativePermissionsPerProject": { + "http://rdfh.ch/projects/0001": [ + { + "additionalInformation": null, + "name": "ProjectResourceCreateAllPermission", + "permissionCode": null + }, + { + "additionalInformation": null, + "name": "ProjectAdminAllPermission", + "permissionCode": null + } + ] + }, + "groupsPerProject": { + "http://rdfh.ch/projects/0001": [ + "http://www.knora.org/ontology/knora-admin#ProjectMember", + "http://www.knora.org/ontology/knora-admin#ProjectAdmin" + ] + } + }, + "projects": [ + { + "description": [ + { + "value": "Anything Project" + } + ], + "id": "http://rdfh.ch/projects/0001", + "keywords": [ + "arbitrary test data", + "things" + ], + "logo": null, + "longname": "Anything Project", + "ontologies": [ + "http://0.0.0.0:3333/ontology/0001/something/v2", + "http://0.0.0.0:3333/ontology/0001/sequences/v2", + "http://0.0.0.0:3333/ontology/0001/freetest/v2", + "http://0.0.0.0:3333/ontology/0001/minimal/v2", + "http://0.0.0.0:3333/ontology/0001/anything/v2" + ], + "selfjoin": false, + "shortcode": "0001", + "shortname": "anything", + "status": true + } + ], + "sessionId": null, + "status": true, + "token": null, + "username": "anything.admin" + } + ] +} + +``` + +Errors: + +- `400 Bad Request` if the provided ID is not valid. +- `404 Not Found` if no project with the provided ID is found. + +NB: -#### Get the restricted view settings: +- IRI must be URL-encoded. - - Required permission: ProjectAdmin - - Required information: `identifier`. The `identifier` can be the project's IRI, shortname or shortcode. - - GET: `/admin/projects/[iri | shortname | shortcode]//RestrictedViewSettings` +## Other Project Operations -## Example Data +### Get all Keywords -The following is an example for project information stored in the `admin` named graph: +Permissions: +Request definition: `GET /admin/projects/Keywords` + +Description: returns keywords of all projects as a list + +Example request: + +```bash +curl --request GET 'http://0.0.0.0:3333/admin/projects/Keywords' ``` - - rdf:type knora-admin:knoraProject ; - knora-admin:projectShortname "images"^^xsd:string ; - knora-admin:projectShortcode "00FF"^^xsd:string ; - knora-admin:projectLongname "Image Collection Demo"^^xsd:string ; - knora-admin:projectDescription "A demo project of a collection of images"@en ; - knora-admin:projectKeyword "images"^^xsd:string, - "collection"^^xsd:string ; - knora-admin:projectRestrictedViewSize "!512,512"^^xsd:string ; - knora-admin:projectRestrictedViewWatermark "path_to_image"^^xsd:string ; - knora-admin:belongsToInstitution ; - knora-admin:status "true"^^xsd:boolean ; - knora-admin:hasSelfJoinEnabled "false"^^xsd:boolean . + +Example response: + +```jsonc +{ + "keywords": [ + "Annotation", + "Arabe", + "Arabic", + "Arabisch", + "Audio", + "Basel", + "Basler Frühdrucke", + "Bilder", + "Bilderfolgen", + "Contectualisation of images", + "Cyrillic", + "Cyrillique", + "Data and Service Center for the Humanities (DaSCH)", + "Grec", + "Greek", + "Griechisch", + "Hebrew", + "Hebräisch", + "Hieroglyphen", + "Hébreu", + "Inkunabel", + "Japanese", + "Japanisch", + "Japonais", + "Keilschrift", + "Kunsthistorisches Seminar Universität Basel", + "Kyrillisch", + "Late Middle Ages", + "Letterpress Printing", + "Markup", + "Narrenschiff", + "Objekte", + "Sebastian Brant", + "Sonderzeichen", + "Texteigenschaften", + "Textquellen", + "Wiegendrucke", + "XML", + "arbitrary test data", + "asdf", + "audio", + "caractères spéciaux", + "collection", + "cuneiform", + "cunéiforme", + "early print", + "hieroglyphs", + "hiéroglyphes", + "images", + "incunabula", + "objects", + "objets", + "propriétés de texte", + "ship of fools", + "sources", + "special characters", + "textual properties", + "textual sources", + "things" + ] +} ``` + +### Get Keywords of a Project + +Permissions: + +Request definition: +- `GET /admin/projects/iri/{iri}/Keywords` + +Description: returns the keywords of a single project + +Example request: + +```bash +curl --request GET 'http://0.0.0.0:3333/admin/projects/iri/http%3A%2F%2Frdfh.ch%2Fprojects%2F0001/Keywords' +--header 'Authorization: Basic cm9vdEBleGFtcGxlLmNvbTp0ZXN0' +``` + +Example response: + +```jsonc +{ + "keywords": [ + "arbitrary test data", + "things" + ] +} + +``` + +### Restricted View Settings + +Permissions: ProjectAdmin + +Request definition: +- `GET /admin/projects/shortcode/{shortcode}/RestrictedViewSettings` +- `GET /admin/projects/shortname/{shortname}/RestrictedViewSettings` +- `GET /admin/projects/iri/{iri}/RestrictedViewSettings` + +Description: returns the project's restricted view settings + +Example request: + +```bash +curl --request GET 'http://0.0.0.0:3333/admin/projects/shortcode/0001/RestrictedViewSettings' \ +--header 'Authorization: Basic cm9vdEBleGFtcGxlLmNvbTp0ZXN0' +``` + +```bash +curl --request GET 'http://0.0.0.0:3333/admin/projects/shortname/anything/RestrictedViewSettings' \ +--header 'Authorization: Basic cm9vdEBleGFtcGxlLmNvbTp0ZXN0' +``` + +```bash +curl --request GET 'http://0.0.0.0:3333/admin/projects/iri/http%3A%2F%2Frdfh.ch%2Fprojects%2F0001/RestrictedViewSettings' +--header 'Authorization: Basic cm9vdEBleGFtcGxlLmNvbTp0ZXN0' +``` + +Example response: + +```jsonc +{ + "settings": { + "size": "!512,512", + "watermark": "path_to_image" + } +} +``` + +Operates on the following properties: + - `knora-admin:projectRestrictedViewSize`: the IIIF size value + - `knora-admin:projectRestrictedViewWatermark`: the path to the watermark image. **Currently not used!** + diff --git a/docs/05-internals/design/api-admin/administration.md b/docs/05-internals/design/api-admin/administration.md index 9690aa7e25..e784a53f26 100644 --- a/docs/05-internals/design/api-admin/administration.md +++ b/docs/05-internals/design/api-admin/administration.md @@ -74,6 +74,9 @@ the built-in group to `knora-admin`, e.g., `knora-admin:KnownUser` where The permissions API endpoint is described [here](../../../03-endpoints/api-admin/permissions.md). +The default permissions when a project is created are described +[here](../../../03-endpoints/api-admin/projects.md#default-set-of-permissions-for-a-new-project). + Up until know, we have mentioned two groups of permissions. The first called *object access permissions*, which contains permissions that point from explicit **objects** (resources/values) to groups. The second diff --git a/webapi/src/it/scala/org/knora/webapi/e2e/admin/GroupsADME2ESpec.scala b/webapi/src/it/scala/org/knora/webapi/e2e/admin/GroupsADME2ESpec.scala index 14eeca1c7e..e03b4c23a2 100644 --- a/webapi/src/it/scala/org/knora/webapi/e2e/admin/GroupsADME2ESpec.scala +++ b/webapi/src/it/scala/org/knora/webapi/e2e/admin/GroupsADME2ESpec.scala @@ -190,7 +190,7 @@ class GroupsADME2ESpec extends E2ESpec with GroupsADMJsonProtocol with SessionJs groupInfo.name should be("NewGroup") groupInfo.descriptions should be(Seq(StringLiteralV2("NewGroupDescription", Some("en")))) - groupInfo.project should be(SharedTestDataADM.imagesProject) + groupInfo.project should be(SharedTestDataADM.imagesProjectExternal) groupInfo.status should be(true) groupInfo.selfjoin should be(false) @@ -240,7 +240,7 @@ class GroupsADME2ESpec extends E2ESpec with GroupsADMJsonProtocol with SessionJs groupInfo.name should be("UpdatedGroupName") groupInfo.descriptions should be(Seq(StringLiteralV2("UpdatedGroupDescription", Some("en")))) - groupInfo.project should be(SharedTestDataADM.imagesProject) + groupInfo.project should be(SharedTestDataADM.imagesProjectExternal) groupInfo.status should be(true) groupInfo.selfjoin should be(false) @@ -269,7 +269,7 @@ class GroupsADME2ESpec extends E2ESpec with GroupsADMJsonProtocol with SessionJs groupInfo.name should be("UpdatedGroupName") groupInfo.descriptions should be(Seq(StringLiteralV2("UpdatedGroupDescription", Some("en")))) - groupInfo.project should be(SharedTestDataADM.imagesProject) + groupInfo.project should be(SharedTestDataADM.imagesProjectExternal) groupInfo.status should be(false) groupInfo.selfjoin should be(false) @@ -316,7 +316,7 @@ class GroupsADME2ESpec extends E2ESpec with GroupsADMJsonProtocol with SessionJs groupInfo.name should be("UpdatedGroupName") groupInfo.descriptions should be(Seq(StringLiteralV2("UpdatedGroupDescription", Some("en")))) - groupInfo.project should be(SharedTestDataADM.imagesProject) + groupInfo.project should be(SharedTestDataADM.imagesProjectExternal) groupInfo.status should be(true) groupInfo.selfjoin should be(false) diff --git a/webapi/src/it/scala/org/knora/webapi/e2e/admin/UsersADME2ESpec.scala b/webapi/src/it/scala/org/knora/webapi/e2e/admin/UsersADME2ESpec.scala index 5c22a92dd1..e9c964fde6 100644 --- a/webapi/src/it/scala/org/knora/webapi/e2e/admin/UsersADME2ESpec.scala +++ b/webapi/src/it/scala/org/knora/webapi/e2e/admin/UsersADME2ESpec.scala @@ -323,7 +323,7 @@ class UsersADME2ESpec } "given a custom Iri" should { - "create a user with the provided custom IRI " in { + "create a user with the provided custom IRI" in { val createUserWithCustomIriRequest: String = s"""{ | "id": "$customUserIri", @@ -469,7 +469,7 @@ class UsersADME2ESpec } "used to create a user" should { - "create the user if the supplied email and username are unique " in { + "create the user if the supplied email and username are unique" in { val createUserRequest: String = s"""{ | "username": "donald.duck", @@ -519,7 +519,7 @@ class UsersADME2ESpec ) } - "return a 'BadRequest' if the supplied username is not unique " in { + "return a 'BadRequest' if the supplied username is not unique" in { val createUserRequest: String = s"""{ | "username": "donald.duck", @@ -559,7 +559,7 @@ class UsersADME2ESpec ) } - "return a 'BadRequest' if the supplied email is not unique " in { + "return a 'BadRequest' if the supplied email is not unique" in { val createUserRequest: String = s"""{ | "username": "new.donald.duck", @@ -1095,9 +1095,9 @@ class UsersADME2ESpec val projects: Seq[ProjectADM] = AkkaHttpUtils.httpResponseToJson(response).fields("projects").convertTo[List[ProjectADM]] projects should contain allElementsOf Seq( - SharedTestDataADM.imagesProject, - SharedTestDataADM.incunabulaProject, - SharedTestDataADM.anythingProject + SharedTestDataADM.imagesProjectExternal, + SharedTestDataADM.incunabulaProjectExternal, + SharedTestDataADM.anythingProjectExternal ) // testing getUserProjectMemberships method, which should return the same result @@ -1128,7 +1128,7 @@ class UsersADME2ESpec assert(response.status === StatusCodes.OK) val membershipsAfterUpdate = getUserProjectMemberships(normalUserIri, rootCreds) - membershipsAfterUpdate should equal(Seq(SharedTestDataADM.imagesProject)) + membershipsAfterUpdate should equal(Seq(SharedTestDataADM.imagesProjectExternal)) clientTestDataCollector.addFile( TestDataFileContent( @@ -1170,7 +1170,7 @@ class UsersADME2ESpec "remove user from project" in { val membershipsBeforeUpdate = getUserProjectMemberships(normalUserCreds.userIri, rootCreds) - membershipsBeforeUpdate should equal(Seq(SharedTestDataADM.imagesProject)) + membershipsBeforeUpdate should equal(Seq(SharedTestDataADM.imagesProjectExternal)) val request = Delete( baseApiUrl + s"/admin/users/iri/${normalUserCreds.urlEncodedIri}/project-memberships/$imagesProjectIriEnc" @@ -1207,9 +1207,9 @@ class UsersADME2ESpec val projects: Seq[ProjectADM] = AkkaHttpUtils.httpResponseToJson(response).fields("projects").convertTo[Seq[ProjectADM]] projects should contain allElementsOf Seq( - SharedTestDataADM.imagesProject, - SharedTestDataADM.incunabulaProject, - SharedTestDataADM.anythingProject + SharedTestDataADM.imagesProjectExternal, + SharedTestDataADM.incunabulaProjectExternal, + SharedTestDataADM.anythingProjectExternal ) // explicitly testing 'getUserProjectsAdminMemberships' method, which should return the same result @@ -1260,7 +1260,7 @@ class UsersADME2ESpec // verify that user has been added as project admin to images project val membershipsAfterUpdate = getUserProjectAdminMemberships(normalUserCreds.userIri, rootCreds) - membershipsAfterUpdate should equal(Seq(SharedTestDataADM.imagesProject)) + membershipsAfterUpdate should equal(Seq(SharedTestDataADM.imagesProjectExternal)) clientTestDataCollector.addFile( TestDataFileContent( @@ -1277,7 +1277,7 @@ class UsersADME2ESpec "remove user from project admin group" in { val membershipsBeforeUpdate = getUserProjectAdminMemberships(normalUserCreds.userIri, rootCreds) - membershipsBeforeUpdate should equal(Seq(SharedTestDataADM.imagesProject)) + membershipsBeforeUpdate should equal(Seq(SharedTestDataADM.imagesProjectExternal)) val request = Delete( baseApiUrl + s"/admin/users/iri/${normalUserCreds.urlEncodedIri}/project-admin-memberships/$imagesProjectIriEnc" @@ -1313,7 +1313,7 @@ class UsersADME2ESpec // verify that user has been added as project admin to images project val membershipsBeforeUpdate = getUserProjectAdminMemberships(normalUserCreds.userIri, rootCreds) - membershipsBeforeUpdate should equal(Seq(SharedTestDataADM.imagesProject)) + membershipsBeforeUpdate should equal(Seq(SharedTestDataADM.imagesProjectExternal)) // remove user as project member from images project val request = Delete( @@ -1342,7 +1342,7 @@ class UsersADME2ESpec val groups: Seq[GroupADM] = AkkaHttpUtils.httpResponseToJson(response).fields("groups").convertTo[List[GroupADM]] - groups should contain allElementsOf Seq(SharedTestDataADM.imagesReviewerGroup) + groups should contain allElementsOf Seq(SharedTestDataADM.imagesReviewerGroupExternal) // testing getUserGroupMemberships method, which should return the same result groups should contain allElementsOf getUserGroupMemberships(multiUserIri, rootCreds) @@ -1374,7 +1374,7 @@ class UsersADME2ESpec assert(response.status === StatusCodes.OK) val membershipsAfterUpdate = getUserGroupMemberships(normalUserIri, rootCreds) - membershipsAfterUpdate should equal(Seq(SharedTestDataADM.imagesReviewerGroup)) + membershipsAfterUpdate should equal(Seq(SharedTestDataADM.imagesReviewerGroupExternal)) clientTestDataCollector.addFile( TestDataFileContent( @@ -1390,7 +1390,7 @@ class UsersADME2ESpec "remove user from group" in { val membershipsBeforeUpdate = getUserGroupMemberships(normalUserCreds.userIri, rootCreds) - membershipsBeforeUpdate should equal(Seq(SharedTestDataADM.imagesReviewerGroup)) + membershipsBeforeUpdate should equal(Seq(SharedTestDataADM.imagesReviewerGroupExternal)) val request = Delete( baseApiUrl + s"/admin/users/iri/${normalUserCreds.urlEncodedIri}/group-memberships/$imagesReviewerGroupIriEnc" diff --git a/webapi/src/it/scala/org/knora/webapi/responders/admin/PermissionsResponderADMSpec.scala b/webapi/src/it/scala/org/knora/webapi/responders/admin/PermissionsResponderADMSpec.scala index 692ddae2af..a5c2f6fb67 100644 --- a/webapi/src/it/scala/org/knora/webapi/responders/admin/PermissionsResponderADMSpec.scala +++ b/webapi/src/it/scala/org/knora/webapi/responders/admin/PermissionsResponderADMSpec.scala @@ -7,7 +7,6 @@ package org.knora.webapi.responders.admin import akka.actor.Status.Failure import akka.testkit.ImplicitSender -import org.scalatest.PrivateMethodTester import java.util.UUID import scala.collection.Map @@ -36,10 +35,15 @@ import org.knora.webapi.util.cache.CacheUtil /** * This spec is used to test the [[PermissionsResponderADM]] actor. */ -class PermissionsResponderADMSpec extends CoreSpec with ImplicitSender with PrivateMethodTester { +class PermissionsResponderADMSpec extends CoreSpec with ImplicitSender { private val rootUser = SharedTestDataADM.rootUser private val multiuserUser = SharedTestDataADM.multiuserUser + private val knownUser = OntologyConstants.KnoraAdmin.KnownUser + private val unknownUser = OntologyConstants.KnoraAdmin.UnknownUser + private val projectAdmin = OntologyConstants.KnoraAdmin.ProjectAdmin + private val projectMember = OntologyConstants.KnoraAdmin.ProjectMember + private val creator = OntologyConstants.KnoraAdmin.Creator override lazy val rdfDataObjects = List( RdfDataObject( @@ -158,7 +162,7 @@ class PermissionsResponderADMSpec extends CoreSpec with ImplicitSender with Priv "ask about administrative permissions " should { - "return all AdministrativePermissions for project " in { + "return all AdministrativePermissions for project" in { appActor ! AdministrativePermissionsForProjectGetRequestADM( projectIri = SharedTestDataADM.imagesProjectIri, requestingUser = rootUser, @@ -180,7 +184,7 @@ class PermissionsResponderADMSpec extends CoreSpec with ImplicitSender with Priv expectMsg(AdministrativePermissionGetResponseADM(perm002_a1.p)) } - "return AdministrativePermission for IRI " in { + "return AdministrativePermission for IRI" in { appActor ! AdministrativePermissionForIriGetRequestADM( administrativePermissionIri = perm002_a1.iri, requestingUser = rootUser, @@ -188,7 +192,7 @@ class PermissionsResponderADMSpec extends CoreSpec with ImplicitSender with Priv ) expectMsg(AdministrativePermissionGetResponseADM(perm002_a1.p)) } - "throw ForbiddenException for AdministrativePermissionForIriGetRequestADM if requesting user is not system or project admin " in { + "throw ForbiddenException for AdministrativePermissionForIriGetRequestADM if requesting user is not system or project admin" in { val permissionIri = perm002_a1.iri appActor ! AdministrativePermissionForIriGetRequestADM( administrativePermissionIri = permissionIri, @@ -276,8 +280,8 @@ class PermissionsResponderADMSpec extends CoreSpec with ImplicitSender with Priv val received: AdministrativePermissionCreateResponseADM = expectMsgType[AdministrativePermissionCreateResponseADM] assert(received.administrativePermission.iri == customIri) + assert(received.administrativePermission.forGroup == knownUser) assert(received.administrativePermission.forProject == SharedTestDataADM.anythingProjectIri) - assert(received.administrativePermission.forGroup == OntologyConstants.KnoraAdmin.KnownUser) assert(received.administrativePermission.hasPermissions.equals(expectedHasPermissions)) } } @@ -471,11 +475,11 @@ class PermissionsResponderADMSpec extends CoreSpec with ImplicitSender with Priv val received: DefaultObjectAccessPermissionCreateResponseADM = expectMsgType[DefaultObjectAccessPermissionCreateResponseADM] assert(received.defaultObjectAccessPermission.iri == customIri) + assert(received.defaultObjectAccessPermission.forGroup.contains(unknownUser)) assert(received.defaultObjectAccessPermission.forProject == SharedTestDataADM.anythingProjectIri) - assert(received.defaultObjectAccessPermission.forGroup.contains(OntologyConstants.KnoraAdmin.UnknownUser)) assert( received.defaultObjectAccessPermission.hasPermissions - .contains(PermissionADM.restrictedViewPermission(OntologyConstants.KnoraAdmin.UnknownUser)) + .contains(PermissionADM.restrictedViewPermission(unknownUser)) ) } @@ -498,7 +502,7 @@ class PermissionsResponderADMSpec extends CoreSpec with ImplicitSender with Priv ) assert( received.defaultObjectAccessPermission.hasPermissions - .contains(PermissionADM.modifyPermission(OntologyConstants.KnoraAdmin.KnownUser)) + .contains(PermissionADM.modifyPermission(knownUser)) ) } @@ -521,7 +525,7 @@ class PermissionsResponderADMSpec extends CoreSpec with ImplicitSender with Priv ) assert( received.defaultObjectAccessPermission.hasPermissions - .contains(PermissionADM.changeRightsPermission(OntologyConstants.KnoraAdmin.Creator)) + .contains(PermissionADM.changeRightsPermission(creator)) ) } @@ -646,11 +650,11 @@ class PermissionsResponderADMSpec extends CoreSpec with ImplicitSender with Priv ) val received: DefaultObjectAccessPermissionCreateResponseADM = expectMsgType[DefaultObjectAccessPermissionCreateResponseADM] + assert(received.defaultObjectAccessPermission.forGroup == Some(unknownUser)) assert(received.defaultObjectAccessPermission.forProject == SharedTestDataADM.imagesProjectIri) - assert(received.defaultObjectAccessPermission.forGroup == Some(OntologyConstants.KnoraAdmin.UnknownUser)) assert( received.defaultObjectAccessPermission.hasPermissions - .contains(PermissionADM.restrictedViewPermission(OntologyConstants.KnoraAdmin.UnknownUser)) + .contains(PermissionADM.restrictedViewPermission(unknownUser)) ) } @@ -665,7 +669,7 @@ class PermissionsResponderADMSpec extends CoreSpec with ImplicitSender with Priv val expectedPermissions = Set( PermissionADM( name = OntologyConstants.KnoraBase.DeletePermission, - additionalInformation = Some(OntologyConstants.KnoraAdmin.ProjectAdmin), + additionalInformation = Some(projectAdmin), permissionCode = Some(7) ) ) @@ -681,341 +685,362 @@ class PermissionsResponderADMSpec extends CoreSpec with ImplicitSender with Priv val received: DefaultObjectAccessPermissionCreateResponseADM = expectMsgType[DefaultObjectAccessPermissionCreateResponseADM] assert(received.defaultObjectAccessPermission.forProject == SharedTestDataADM.imagesProjectIri) - assert(received.defaultObjectAccessPermission.forGroup == Some(OntologyConstants.KnoraAdmin.ProjectAdmin)) + assert(received.defaultObjectAccessPermission.forGroup == Some(projectAdmin)) assert(received.defaultObjectAccessPermission.hasPermissions.equals(expectedPermissions)) } } -// -// "ask to get all permissions" should { -// -// "return all permissions for 'image' project " in { -// appActor ! PermissionsForProjectGetRequestADM( -// projectIri = SharedTestDataADM.imagesProjectIri, -// requestingUser = rootUser, -// apiRequestID = UUID.randomUUID() -// ) -// val received: PermissionsForProjectGetResponseADM = expectMsgType[PermissionsForProjectGetResponseADM] -// received.allPermissions.size should be(8) -// } -// -// "return all permissions for 'incunabula' project " in { -// appActor ! PermissionsForProjectGetRequestADM( -// projectIri = SharedTestDataADM.incunabulaProjectIri, -// requestingUser = rootUser, -// apiRequestID = UUID.randomUUID() -// ) -// expectMsg( -// PermissionsForProjectGetResponseADM(allPermissions = Set( -// PermissionInfoADM(perm003_a1.iri, OntologyConstants.KnoraAdmin.AdministrativePermission), -// PermissionInfoADM(perm003_a2.iri, OntologyConstants.KnoraAdmin.AdministrativePermission), -// PermissionInfoADM(perm003_d1.iri, OntologyConstants.KnoraAdmin.DefaultObjectAccessPermission), -// PermissionInfoADM(perm003_d2.iri, OntologyConstants.KnoraAdmin.DefaultObjectAccessPermission), -// PermissionInfoADM(perm003_d3.iri, OntologyConstants.KnoraAdmin.DefaultObjectAccessPermission), -// PermissionInfoADM(perm003_d4.iri, OntologyConstants.KnoraAdmin.DefaultObjectAccessPermission), -// PermissionInfoADM(perm003_d5.iri, OntologyConstants.KnoraAdmin.DefaultObjectAccessPermission) -// ))) -// } -// } -// -// "ask for default object access permissions 'string'" should { -// -// "return the default object access permissions 'string' for the 'knora-base:LinkObj' resource class (system resource class)" in { -// appActor ! DefaultObjectAccessPermissionsStringForResourceClassGetADM( -// projectIri = SharedTestDataADM.incunabulaProjectIri, -// resourceClassIri = OntologyConstants.KnoraBase.LinkObj, -// targetUser = SharedTestDataADM.incunabulaProjectAdminUser, -// requestingUser = KnoraSystemInstances.Users.SystemUser -// ) -// expectMsg( -// DefaultObjectAccessPermissionsStringResponseADM( -// "M knora-admin:ProjectMember|V knora-admin:KnownUser,knora-admin:UnknownUser")) -// } -// -// "return the default object access permissions 'string' for the 'knora-base:hasStillImageFileValue' property (system property)" in { -// appActor ! DefaultObjectAccessPermissionsStringForPropertyGetADM( -// projectIri = SharedTestDataADM.incunabulaProjectIri, -// resourceClassIri = OntologyConstants.KnoraBase.StillImageRepresentation, -// propertyIri = OntologyConstants.KnoraBase.HasStillImageFileValue, -// targetUser = SharedTestDataADM.incunabulaProjectAdminUser, -// requestingUser = KnoraSystemInstances.Users.SystemUser -// ) -// expectMsg( -// DefaultObjectAccessPermissionsStringResponseADM( -// "M knora-admin:Creator,knora-admin:ProjectMember|V knora-admin:KnownUser,knora-admin:UnknownUser")) -// } -// -// "return the default object access permissions 'string' for the 'incunabula:book' resource class (project resource class)" in { -// appActor ! DefaultObjectAccessPermissionsStringForResourceClassGetADM( -// projectIri = SharedTestDataADM.incunabulaProjectIri, -// resourceClassIri = SharedOntologyTestDataADM.INCUNABULA_BOOK_RESOURCE_CLASS, -// targetUser = SharedTestDataADM.incunabulaProjectAdminUser, -// requestingUser = KnoraSystemInstances.Users.SystemUser -// ) -// expectMsg( -// DefaultObjectAccessPermissionsStringResponseADM( -// "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser")) -// } -// -// "return the default object access permissions 'string' for the 'incunabula:page' resource class (project resource class)" in { -// appActor ! DefaultObjectAccessPermissionsStringForResourceClassGetADM( -// projectIri = SharedTestDataADM.incunabulaProjectIri, -// resourceClassIri = SharedOntologyTestDataADM.INCUNABULA_PAGE_RESOURCE_CLASS, -// targetUser = SharedTestDataADM.incunabulaProjectAdminUser, -// requestingUser = KnoraSystemInstances.Users.SystemUser -// ) -// expectMsg( -// DefaultObjectAccessPermissionsStringResponseADM( -// "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser")) -// } -// -// "return the default object access permissions 'string' for the 'images:jahreszeit' property" in { -// appActor ! DefaultObjectAccessPermissionsStringForPropertyGetADM( -// projectIri = SharedTestDataADM.imagesProjectIri, -// resourceClassIri = s"${SharedOntologyTestDataADM.IMAGES_ONTOLOGY_IRI}#bild", -// propertyIri = s"${SharedOntologyTestDataADM.IMAGES_ONTOLOGY_IRI}#jahreszeit", -// targetUser = SharedTestDataADM.imagesUser01, -// requestingUser = KnoraSystemInstances.Users.SystemUser -// ) -// expectMsg( -// DefaultObjectAccessPermissionsStringResponseADM( -// "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser")) -// } -// -// "return the default object access permissions 'string' for the 'anything:hasInterval' property" in { -// appActor ! DefaultObjectAccessPermissionsStringForPropertyGetADM( -// projectIri = SharedTestDataADM.anythingProjectIri, -// resourceClassIri = "http://www.knora.org/ontology/0001/anything#Thing", -// propertyIri = "http://www.knora.org/ontology/0001/anything#hasInterval", -// targetUser = SharedTestDataADM.anythingUser2, -// requestingUser = KnoraSystemInstances.Users.SystemUser -// ) -// expectMsg( -// DefaultObjectAccessPermissionsStringResponseADM( -// "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser")) -// } -// -// "return the default object access permissions 'string' for the 'anything:Thing' class" in { -// appActor ! DefaultObjectAccessPermissionsStringForResourceClassGetADM( -// projectIri = SharedTestDataADM.anythingProjectIri, -// resourceClassIri = "http://www.knora.org/ontology/0001/anything#Thing", -// targetUser = SharedTestDataADM.anythingUser2, -// requestingUser = KnoraSystemInstances.Users.SystemUser -// ) -// expectMsg( -// DefaultObjectAccessPermissionsStringResponseADM( -// "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser")) -// } -// -// "return the default object access permissions 'string' for the 'anything:Thing' class and 'anything:hasText' property" in { -// appActor ! DefaultObjectAccessPermissionsStringForPropertyGetADM( -// projectIri = SharedTestDataADM.anythingProjectIri, -// resourceClassIri = "http://www.knora.org/ontology/0001/anything#Thing", -// propertyIri = "http://www.knora.org/ontology/0001/anything#hasText", -// targetUser = SharedTestDataADM.anythingUser1, -// requestingUser = KnoraSystemInstances.Users.SystemUser -// ) -// expectMsg(DefaultObjectAccessPermissionsStringResponseADM("CR knora-admin:Creator")) -// } -// -// "return the default object access permissions 'string' for the 'images:Bild' class and 'anything:hasText' property" in { -// appActor ! DefaultObjectAccessPermissionsStringForPropertyGetADM( -// projectIri = SharedTestDataADM.anythingProjectIri, -// resourceClassIri = s"${SharedOntologyTestDataADM.IMAGES_ONTOLOGY_IRI}#bild", -// propertyIri = "http://www.knora.org/ontology/0001/anything#hasText", -// targetUser = SharedTestDataADM.anythingUser2, -// requestingUser = KnoraSystemInstances.Users.SystemUser -// ) -// expectMsg( -// DefaultObjectAccessPermissionsStringResponseADM( -// "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser")) -// } -// -// "return the default object access permissions 'string' for the 'anything:Thing' resource class for the root user (system admin and not member of project)" in { -// appActor ! DefaultObjectAccessPermissionsStringForResourceClassGetADM( -// projectIri = SharedTestDataADM.anythingProjectIri, -// resourceClassIri = "http://www.knora.org/ontology/0001/anything#Thing", -// targetUser = SharedTestDataADM.rootUser, -// requestingUser = KnoraSystemInstances.Users.SystemUser -// ) -// expectMsg( -// DefaultObjectAccessPermissionsStringResponseADM( -// "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser")) -// } -// -// "return a combined and max set of permissions (default object access permissions) defined on the supplied groups (helper method used in queries before)" in { -// val groups = List("http://rdfh.ch/groups/images-reviewer", -// s"${OntologyConstants.KnoraAdmin.ProjectMember}", -// s"${OntologyConstants.KnoraAdmin.ProjectAdmin}") -// val expected = Set( -// PermissionADM.changeRightsPermission(OntologyConstants.KnoraAdmin.Creator), -// PermissionADM.viewPermission(OntologyConstants.KnoraAdmin.KnownUser), -// PermissionADM.modifyPermission(OntologyConstants.KnoraAdmin.ProjectMember) -// ) -// val f -// : Future[Set[PermissionADM]] = responderUnderTest invokePrivate defaultObjectAccessPermissionsForGroupsGetADM( -// SharedTestDataADM.imagesProjectIri, -// groups) -// val result: Set[PermissionADM] = Await.result(f, 1.seconds) -// result should equal(expected) -// } -// } -// -// "ask to get the permission by IRI" should { -// "not return the permission if requesting user does not have permission to see it" in { -// val permissionIri = perm002_a1.iri -// appActor ! PermissionByIriGetRequestADM( -// permissionIri = perm002_a1.iri, -// requestingUser = SharedTestDataADM.imagesUser02 -// ) -// expectMsg( -// Failure(ForbiddenException( -// s"Permission $permissionIri can only be queried/updated/deleted by system or project admin."))) -// } -// -// "return an administrative permission" in { -// appActor ! PermissionByIriGetRequestADM( -// permissionIri = perm002_a1.iri, -// requestingUser = rootUser -// ) -// expectMsg(AdministrativePermissionGetResponseADM(perm002_a1.p)) -// } -// -// "return a default object access permission" in { -// appActor ! PermissionByIriGetRequestADM( -// permissionIri = perm002_d1.iri, -// requestingUser = rootUser -// ) -// expectMsg(DefaultObjectAccessPermissionGetResponseADM(perm002_d1.p)) -// } -// } -// -// "ask to update group of a permission" should { -// "update group of an administrative permission" in { -// val permissionIri = "http://rdfh.ch/permissions/00FF/a2" -// val newGroupIri = "http://rdfh.ch/groups/00FF/images-reviewer" -// appActor ! PermissionChangeGroupRequestADM( -// permissionIri = permissionIri, -// changePermissionGroupRequest = ChangePermissionGroupApiRequestADM( -// forGroup = newGroupIri -// ), -// requestingUser = rootUser, -// apiRequestID = UUID.randomUUID() -// ) -// val received: AdministrativePermissionGetResponseADM = expectMsgType[AdministrativePermissionGetResponseADM] -// val ap = received.administrativePermission -// assert(ap.iri == permissionIri) -// assert(ap.forGroup == newGroupIri) -// } -// -// "throw ForbiddenException for PermissionChangeGroupRequestADM if requesting user is not system or project Admin" in { -// val permissionIri = "http://rdfh.ch/permissions/00FF/a2" -// val newGroupIri = "http://rdfh.ch/groups/00FF/images-reviewer" -// appActor ! PermissionChangeGroupRequestADM( -// permissionIri = permissionIri, -// changePermissionGroupRequest = ChangePermissionGroupApiRequestADM( -// forGroup = newGroupIri -// ), -// requestingUser = SharedTestDataADM.imagesUser02, -// apiRequestID = UUID.randomUUID() -// ) -// expectMsg( -// Failure(ForbiddenException( -// s"Permission $permissionIri can only be queried/updated/deleted by system or project admin."))) -// } -// -// "update group of a default object access permission" in { -// val permissionIri = "http://rdfh.ch/permissions/00FF/d1" -// val newGroupIri = "http://rdfh.ch/groups/00FF/images-reviewer" -// appActor ! PermissionChangeGroupRequestADM( -// permissionIri = permissionIri, -// changePermissionGroupRequest = ChangePermissionGroupApiRequestADM( -// forGroup = newGroupIri -// ), -// requestingUser = rootUser, -// apiRequestID = UUID.randomUUID() -// ) -// val received: DefaultObjectAccessPermissionGetResponseADM = -// expectMsgType[DefaultObjectAccessPermissionGetResponseADM] -// val doap = received.defaultObjectAccessPermission -// assert(doap.iri == permissionIri) -// assert(doap.forGroup.get == newGroupIri) -// } -// -// "update group of a default object access permission, resource class must be deleted" in { -// val permissionIri = "http://rdfh.ch/permissions/0803/003-d2" -// val newGroupIri = "http://www.knora.org/ontology/knora-admin#ProjectMember" -// appActor ! PermissionChangeGroupRequestADM( -// permissionIri = permissionIri, -// changePermissionGroupRequest = ChangePermissionGroupApiRequestADM( -// forGroup = newGroupIri -// ), -// requestingUser = rootUser, -// apiRequestID = UUID.randomUUID() -// ) -// val received: DefaultObjectAccessPermissionGetResponseADM = -// expectMsgType[DefaultObjectAccessPermissionGetResponseADM] -// val doap = received.defaultObjectAccessPermission -// assert(doap.iri == permissionIri) -// assert(doap.forGroup.get == newGroupIri) -// assert(doap.forResourceClass.isEmpty) -// } -// -// "update group of a default object access permission, property must be deleted" in { -// val permissionIri = "http://rdfh.ch/permissions/0000/001-d3" -// val newGroupIri = "http://www.knora.org/ontology/knora-admin#ProjectMember" -// appActor ! PermissionChangeGroupRequestADM( -// permissionIri = permissionIri, -// changePermissionGroupRequest = ChangePermissionGroupApiRequestADM( -// forGroup = newGroupIri -// ), -// requestingUser = rootUser, -// apiRequestID = UUID.randomUUID() -// ) -// val received: DefaultObjectAccessPermissionGetResponseADM = -// expectMsgType[DefaultObjectAccessPermissionGetResponseADM] -// val doap = received.defaultObjectAccessPermission -// assert(doap.iri == permissionIri) -// assert(doap.forGroup.get == newGroupIri) -// assert(doap.forProperty.isEmpty) -// } -// } + + "ask to get all permissions" should { + + "return all permissions for 'image' project" in { + appActor ! PermissionsForProjectGetRequestADM( + projectIri = SharedTestDataADM.imagesProjectIri, + requestingUser = rootUser, + apiRequestID = UUID.randomUUID() + ) + val received: PermissionsForProjectGetResponseADM = expectMsgType[PermissionsForProjectGetResponseADM] + received.allPermissions.size should be(10) + } + + "return all permissions for 'incunabula' project" in { + appActor ! PermissionsForProjectGetRequestADM( + projectIri = SharedTestDataADM.incunabulaProjectIri, + requestingUser = rootUser, + apiRequestID = UUID.randomUUID() + ) + expectMsg( + PermissionsForProjectGetResponseADM(allPermissions = + Set( + PermissionInfoADM( + perm003_a1.iri, + OntologyConstants.KnoraAdmin.AdministrativePermission + ), + PermissionInfoADM( + perm003_a2.iri, + OntologyConstants.KnoraAdmin.AdministrativePermission + ), + PermissionInfoADM( + perm003_d1.iri, + OntologyConstants.KnoraAdmin.DefaultObjectAccessPermission + ), + PermissionInfoADM( + perm003_d2.iri, + OntologyConstants.KnoraAdmin.DefaultObjectAccessPermission + ), + PermissionInfoADM( + perm003_d3.iri, + OntologyConstants.KnoraAdmin.DefaultObjectAccessPermission + ), + PermissionInfoADM( + perm003_d4.iri, + OntologyConstants.KnoraAdmin.DefaultObjectAccessPermission + ), + PermissionInfoADM( + perm003_d5.iri, + OntologyConstants.KnoraAdmin.DefaultObjectAccessPermission + ) + ) + ) + ) + } + } + + "ask for default object access permissions 'string'" should { + + "return the default object access permissions 'string' for the 'knora-base:LinkObj' resource class (system resource class)" in { + appActor ! DefaultObjectAccessPermissionsStringForResourceClassGetADM( + projectIri = SharedTestDataADM.incunabulaProjectIri, + resourceClassIri = OntologyConstants.KnoraBase.LinkObj, + targetUser = SharedTestDataADM.incunabulaProjectAdminUser, + requestingUser = KnoraSystemInstances.Users.SystemUser + ) + expectMsg( + DefaultObjectAccessPermissionsStringResponseADM( + "M knora-admin:ProjectMember|V knora-admin:KnownUser,knora-admin:UnknownUser" + ) + ) + } + + "return the default object access permissions 'string' for the 'knora-base:hasStillImageFileValue' property (system property)" in { + appActor ! DefaultObjectAccessPermissionsStringForPropertyGetADM( + projectIri = SharedTestDataADM.incunabulaProjectIri, + resourceClassIri = OntologyConstants.KnoraBase.StillImageRepresentation, + propertyIri = OntologyConstants.KnoraBase.HasStillImageFileValue, + targetUser = SharedTestDataADM.incunabulaProjectAdminUser, + requestingUser = KnoraSystemInstances.Users.SystemUser + ) + expectMsg( + DefaultObjectAccessPermissionsStringResponseADM( + "M knora-admin:Creator,knora-admin:ProjectMember|V knora-admin:KnownUser,knora-admin:UnknownUser" + ) + ) + } + + "return the default object access permissions 'string' for the 'incunabula:book' resource class (project resource class)" in { + appActor ! DefaultObjectAccessPermissionsStringForResourceClassGetADM( + projectIri = SharedTestDataADM.incunabulaProjectIri, + resourceClassIri = SharedOntologyTestDataADM.INCUNABULA_BOOK_RESOURCE_CLASS, + targetUser = SharedTestDataADM.incunabulaProjectAdminUser, + requestingUser = KnoraSystemInstances.Users.SystemUser + ) + expectMsg( + DefaultObjectAccessPermissionsStringResponseADM( + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser" + ) + ) + } + + "return the default object access permissions 'string' for the 'incunabula:page' resource class (project resource class)" in { + appActor ! DefaultObjectAccessPermissionsStringForResourceClassGetADM( + projectIri = SharedTestDataADM.incunabulaProjectIri, + resourceClassIri = SharedOntologyTestDataADM.INCUNABULA_PAGE_RESOURCE_CLASS, + targetUser = SharedTestDataADM.incunabulaProjectAdminUser, + requestingUser = KnoraSystemInstances.Users.SystemUser + ) + expectMsg( + DefaultObjectAccessPermissionsStringResponseADM( + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser" + ) + ) + } + + "return the default object access permissions 'string' for the 'anything:hasInterval' property" in { + appActor ! DefaultObjectAccessPermissionsStringForPropertyGetADM( + projectIri = SharedTestDataADM.anythingProjectIri, + resourceClassIri = "http://www.knora.org/ontology/0001/anything#Thing", + propertyIri = "http://www.knora.org/ontology/0001/anything#hasInterval", + targetUser = SharedTestDataADM.anythingUser2, + requestingUser = KnoraSystemInstances.Users.SystemUser + ) + expectMsg( + DefaultObjectAccessPermissionsStringResponseADM( + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser" + ) + ) + } + + "return the default object access permissions 'string' for the 'anything:Thing' class" in { + appActor ! DefaultObjectAccessPermissionsStringForResourceClassGetADM( + projectIri = SharedTestDataADM.anythingProjectIri, + resourceClassIri = "http://www.knora.org/ontology/0001/anything#Thing", + targetUser = SharedTestDataADM.anythingUser2, + requestingUser = KnoraSystemInstances.Users.SystemUser + ) + expectMsg( + DefaultObjectAccessPermissionsStringResponseADM( + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser" + ) + ) + } + + "return the default object access permissions 'string' for the 'anything:Thing' class and 'anything:hasText' property" in { + appActor ! DefaultObjectAccessPermissionsStringForPropertyGetADM( + projectIri = SharedTestDataADM.anythingProjectIri, + resourceClassIri = "http://www.knora.org/ontology/0001/anything#Thing", + propertyIri = "http://www.knora.org/ontology/0001/anything#hasText", + targetUser = SharedTestDataADM.anythingUser1, + requestingUser = KnoraSystemInstances.Users.SystemUser + ) + expectMsg(DefaultObjectAccessPermissionsStringResponseADM("CR knora-admin:Creator")) + } + + "return the default object access permissions 'string' for the 'images:Bild' class and 'anything:hasText' property" in { + appActor ! DefaultObjectAccessPermissionsStringForPropertyGetADM( + projectIri = SharedTestDataADM.anythingProjectIri, + resourceClassIri = s"${SharedOntologyTestDataADM.IMAGES_ONTOLOGY_IRI}#bild", + propertyIri = "http://www.knora.org/ontology/0001/anything#hasText", + targetUser = SharedTestDataADM.anythingUser2, + requestingUser = KnoraSystemInstances.Users.SystemUser + ) + expectMsg( + DefaultObjectAccessPermissionsStringResponseADM( + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser" + ) + ) + } + + "return the default object access permissions 'string' for the 'anything:Thing' resource class for the root user (system admin and not member of project)" in { + appActor ! DefaultObjectAccessPermissionsStringForResourceClassGetADM( + projectIri = SharedTestDataADM.anythingProjectIri, + resourceClassIri = "http://www.knora.org/ontology/0001/anything#Thing", + targetUser = SharedTestDataADM.rootUser, + requestingUser = KnoraSystemInstances.Users.SystemUser + ) + expectMsg( + DefaultObjectAccessPermissionsStringResponseADM( + "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser" + ) + ) + } + + } + + "ask to get the permission by IRI" should { + "not return the permission if requesting user does not have permission to see it" in { + val permissionIri = perm002_a1.iri + appActor ! PermissionByIriGetRequestADM( + permissionIri = perm002_a1.iri, + requestingUser = SharedTestDataADM.imagesUser02 + ) + expectMsg( + Failure( + ForbiddenException( + s"Permission $permissionIri can only be queried/updated/deleted by system or project admin." + ) + ) + ) + } + + "return an administrative permission" in { + appActor ! PermissionByIriGetRequestADM( + permissionIri = perm002_a1.iri, + requestingUser = rootUser + ) + expectMsg(AdministrativePermissionGetResponseADM(perm002_a1.p)) + } + + "return a default object access permission" in { + appActor ! PermissionByIriGetRequestADM( + permissionIri = perm002_d1.iri, + requestingUser = rootUser + ) + expectMsg(DefaultObjectAccessPermissionGetResponseADM(perm002_d1.p)) + } + } + + "ask to update group of a permission" should { + "update group of an administrative permission" in { + val permissionIri = "http://rdfh.ch/permissions/00FF/a2" + val newGroupIri = "http://rdfh.ch/groups/00FF/images-reviewer" + appActor ! PermissionChangeGroupRequestADM( + permissionIri = permissionIri, + changePermissionGroupRequest = ChangePermissionGroupApiRequestADM( + forGroup = newGroupIri + ), + requestingUser = rootUser, + apiRequestID = UUID.randomUUID() + ) + val received: AdministrativePermissionGetResponseADM = expectMsgType[AdministrativePermissionGetResponseADM] + val ap = received.administrativePermission + assert(ap.iri == permissionIri) + assert(ap.forGroup == newGroupIri) + } + + "throw ForbiddenException for PermissionChangeGroupRequestADM if requesting user is not system or project Admin" in { + val permissionIri = "http://rdfh.ch/permissions/00FF/a2" + val newGroupIri = "http://rdfh.ch/groups/00FF/images-reviewer" + appActor ! PermissionChangeGroupRequestADM( + permissionIri = permissionIri, + changePermissionGroupRequest = ChangePermissionGroupApiRequestADM( + forGroup = newGroupIri + ), + requestingUser = SharedTestDataADM.imagesUser02, + apiRequestID = UUID.randomUUID() + ) + expectMsg( + Failure( + ForbiddenException( + s"Permission $permissionIri can only be queried/updated/deleted by system or project admin." + ) + ) + ) + } + + "update group of a default object access permission" in { + val permissionIri = "http://rdfh.ch/permissions/00FF/d1" + val newGroupIri = "http://rdfh.ch/groups/00FF/images-reviewer" + appActor ! PermissionChangeGroupRequestADM( + permissionIri = permissionIri, + changePermissionGroupRequest = ChangePermissionGroupApiRequestADM( + forGroup = newGroupIri + ), + requestingUser = rootUser, + apiRequestID = UUID.randomUUID() + ) + val received: DefaultObjectAccessPermissionGetResponseADM = + expectMsgType[DefaultObjectAccessPermissionGetResponseADM] + val doap = received.defaultObjectAccessPermission + assert(doap.iri == permissionIri) + assert(doap.forGroup == Some(newGroupIri)) + } + + "update group of a default object access permission, resource class must be deleted" in { + val permissionIri = "http://rdfh.ch/permissions/0803/003-d2" + appActor ! PermissionChangeGroupRequestADM( + permissionIri = permissionIri, + changePermissionGroupRequest = ChangePermissionGroupApiRequestADM( + forGroup = projectMember + ), + requestingUser = rootUser, + apiRequestID = UUID.randomUUID() + ) + val received: DefaultObjectAccessPermissionGetResponseADM = + expectMsgType[DefaultObjectAccessPermissionGetResponseADM] + val doap = received.defaultObjectAccessPermission + assert(doap.iri == permissionIri) + assert(doap.forGroup == Some(projectMember)) + assert(doap.forResourceClass.isEmpty) + } + + "update group of a default object access permission, property must be deleted" in { + val permissionIri = "http://rdfh.ch/permissions/0000/001-d3" + appActor ! PermissionChangeGroupRequestADM( + permissionIri = permissionIri, + changePermissionGroupRequest = ChangePermissionGroupApiRequestADM( + forGroup = projectMember + ), + requestingUser = rootUser, + apiRequestID = UUID.randomUUID() + ) + val received: DefaultObjectAccessPermissionGetResponseADM = + expectMsgType[DefaultObjectAccessPermissionGetResponseADM] + val doap = received.defaultObjectAccessPermission + assert(doap.iri == permissionIri) + assert(doap.forGroup == Some(projectMember)) + assert(doap.forProperty.isEmpty) + } + } "ask to update hasPermissions of a permission" should { -// "throw ForbiddenException for PermissionChangeHasPermissionsRequestADM if requesting user is not system or project Admin" in { -// val permissionIri = "http://rdfh.ch/permissions/00FF/a2" -// val hasPermissions = Set(PermissionADM.ProjectResourceCreateAllPermission) -// -// appActor ! PermissionChangeHasPermissionsRequestADM( -// permissionIri = permissionIri, -// changePermissionHasPermissionsRequest = ChangePermissionHasPermissionsApiRequestADM( -// hasPermissions = hasPermissions -// ), -// requestingUser = SharedTestDataADM.imagesUser02, -// apiRequestID = UUID.randomUUID() -// ) -// expectMsg( -// Failure(ForbiddenException( -// s"Permission $permissionIri can only be queried/updated/deleted by system or project admin."))) -// } -// -// "update hasPermissions of an administrative permission" in { -// val permissionIri = "http://rdfh.ch/permissions/00FF/a2" -// val hasPermissions = Set(PermissionADM.ProjectResourceCreateAllPermission) -// -// appActor ! PermissionChangeHasPermissionsRequestADM( -// permissionIri = permissionIri, -// changePermissionHasPermissionsRequest = ChangePermissionHasPermissionsApiRequestADM( -// hasPermissions = hasPermissions -// ), -// requestingUser = rootUser, -// apiRequestID = UUID.randomUUID() -// ) -// val received: AdministrativePermissionGetResponseADM = expectMsgType[AdministrativePermissionGetResponseADM] -// val ap = received.administrativePermission -// assert(ap.iri == permissionIri) -// ap.hasPermissions.size should be(1) -// assert(ap.hasPermissions.equals(hasPermissions)) -// } + "throw ForbiddenException for PermissionChangeHasPermissionsRequestADM if requesting user is not system or project Admin" in { + val permissionIri = "http://rdfh.ch/permissions/00FF/a2" + val hasPermissions = Set(PermissionADM.ProjectResourceCreateAllPermission) + + appActor ! PermissionChangeHasPermissionsRequestADM( + permissionIri = permissionIri, + changePermissionHasPermissionsRequest = ChangePermissionHasPermissionsApiRequestADM( + hasPermissions = hasPermissions + ), + requestingUser = SharedTestDataADM.imagesUser02, + apiRequestID = UUID.randomUUID() + ) + expectMsg( + Failure( + ForbiddenException( + s"Permission $permissionIri can only be queried/updated/deleted by system or project admin." + ) + ) + ) + } + + "update hasPermissions of an administrative permission" in { + val permissionIri = "http://rdfh.ch/permissions/00FF/a2" + val hasPermissions = Set(PermissionADM.ProjectResourceCreateAllPermission) + + appActor ! PermissionChangeHasPermissionsRequestADM( + permissionIri = permissionIri, + changePermissionHasPermissionsRequest = ChangePermissionHasPermissionsApiRequestADM( + hasPermissions = hasPermissions + ), + requestingUser = rootUser, + apiRequestID = UUID.randomUUID() + ) + val received: AdministrativePermissionGetResponseADM = expectMsgType[AdministrativePermissionGetResponseADM] + val ap = received.administrativePermission + assert(ap.iri == permissionIri) + ap.hasPermissions.size should be(1) + assert(ap.hasPermissions.equals(hasPermissions)) + } "ignore irrelevant parameters given in ChangePermissionHasPermissionsApiRequestADM for an administrative permission" in { val permissionIri = "http://rdfh.ch/permissions/00FF/a2" @@ -1045,8 +1070,8 @@ class PermissionsResponderADMSpec extends CoreSpec with ImplicitSender with Priv "update hasPermissions of a default object access permission" in { val permissionIri = "http://rdfh.ch/permissions/0803/003-d1" val hasPermissions = Set( - PermissionADM.changeRightsPermission(OntologyConstants.KnoraAdmin.Creator), - PermissionADM.modifyPermission(OntologyConstants.KnoraAdmin.ProjectMember) + PermissionADM.changeRightsPermission(creator), + PermissionADM.modifyPermission(projectMember) ) appActor ! PermissionChangeHasPermissionsRequestADM( @@ -1078,7 +1103,7 @@ class PermissionsResponderADMSpec extends CoreSpec with ImplicitSender with Priv val expectedHasPermissions = Set( PermissionADM( name = OntologyConstants.KnoraBase.ChangeRightsPermission, - additionalInformation = Some(OntologyConstants.KnoraAdmin.Creator), + additionalInformation = Some(creator), permissionCode = Some(8) ) ) @@ -1111,7 +1136,7 @@ class PermissionsResponderADMSpec extends CoreSpec with ImplicitSender with Priv val expectedHasPermissions = Set( PermissionADM( name = OntologyConstants.KnoraBase.DeletePermission, - additionalInformation = Some(OntologyConstants.KnoraAdmin.ProjectAdmin), + additionalInformation = Some(projectAdmin), permissionCode = Some(7) ) ) @@ -1282,7 +1307,7 @@ class PermissionsResponderADMSpec extends CoreSpec with ImplicitSender with Priv expectMsgType[DefaultObjectAccessPermissionGetResponseADM] val doap = received.defaultObjectAccessPermission assert(doap.iri == permissionIri) - assert(doap.forResourceClass.get == resourceClassIri) + assert(doap.forResourceClass == Some(resourceClassIri)) } "update resource class of a default object access permission, and delete group" in { @@ -1301,7 +1326,7 @@ class PermissionsResponderADMSpec extends CoreSpec with ImplicitSender with Priv expectMsgType[DefaultObjectAccessPermissionGetResponseADM] val doap = received.defaultObjectAccessPermission assert(doap.iri == permissionIri) - assert(doap.forResourceClass.get == resourceClassIri) + assert(doap.forResourceClass == Some(resourceClassIri)) assert(doap.forGroup.isEmpty) } @@ -1385,7 +1410,7 @@ class PermissionsResponderADMSpec extends CoreSpec with ImplicitSender with Priv expectMsgType[DefaultObjectAccessPermissionGetResponseADM] val doap = received.defaultObjectAccessPermission assert(doap.iri == permissionIri) - assert(doap.forProperty.get == propertyIri) + assert(doap.forProperty == Some(propertyIri)) } "update property of a default object access permission, delete group" in { @@ -1404,7 +1429,7 @@ class PermissionsResponderADMSpec extends CoreSpec with ImplicitSender with Priv expectMsgType[DefaultObjectAccessPermissionGetResponseADM] val doap = received.defaultObjectAccessPermission assert(doap.iri == permissionIri) - assert(doap.forProperty.get == propertyIri) + assert(doap.forProperty == Some(propertyIri)) assert(doap.forGroup.isEmpty) } } diff --git a/webapi/src/it/scala/org/knora/webapi/responders/admin/ProjectsResponderADMSpec.scala b/webapi/src/it/scala/org/knora/webapi/responders/admin/ProjectsResponderADMSpec.scala index a9699ae9bb..c73468f51b 100644 --- a/webapi/src/it/scala/org/knora/webapi/responders/admin/ProjectsResponderADMSpec.scala +++ b/webapi/src/it/scala/org/knora/webapi/responders/admin/ProjectsResponderADMSpec.scala @@ -84,18 +84,18 @@ class ProjectsResponderADMSpec extends CoreSpec with ImplicitSender { } - "return 'NotFoundException' when the project shortname is unknown " in { - appActor ! ProjectGetRequestADM(identifier = - ShortnameIdentifier + "return 'NotFoundException' when the project shortname is unknown" in { + appActor ! ProjectGetRequestADM( + identifier = ShortnameIdentifier .fromString("wrongshortname") .getOrElseWith(e => throw BadRequestException(e.head.getMessage)) ) expectMsg(Failure(NotFoundException(s"Project 'wrongshortname' not found"))) } - "return 'NotFoundException' when the project shortcode is unknown " in { - appActor ! ProjectGetRequestADM(identifier = - ShortcodeIdentifier + "return 'NotFoundException' when the project shortcode is unknown" in { + appActor ! ProjectGetRequestADM( + identifier = ShortcodeIdentifier .fromString("9999") .getOrElseWith(e => throw BadRequestException(e.head.getMessage)) ) @@ -209,10 +209,12 @@ class ProjectsResponderADMSpec extends CoreSpec with ImplicitSender { // Check Administrative Permission of ProjectAdmin val receivedApAdmin: AdministrativePermissionsForProjectGetResponseADM = expectMsgType[AdministrativePermissionsForProjectGetResponseADM] + val hasAPForProjectAdmin = receivedApAdmin.administrativePermissions.filter { ap: AdministrativePermissionADM => ap.forProject == received.project.id && ap.forGroup == OntologyConstants.KnoraAdmin.ProjectAdmin && - ap.hasPermissions - .equals(Set(PermissionADM.ProjectAdminAllPermission, PermissionADM.ProjectResourceCreateAllPermission)) + ap.hasPermissions.equals( + Set(PermissionADM.ProjectAdminAllPermission, PermissionADM.ProjectResourceCreateAllPermission) + ) } hasAPForProjectAdmin.size shouldBe 1 diff --git a/webapi/src/it/scala/org/knora/webapi/sharedtestdata/SharedOntologyTestDataADM.scala b/webapi/src/it/scala/org/knora/webapi/sharedtestdata/SharedOntologyTestDataADM.scala index 8600004334..b6f22aa5ce 100644 --- a/webapi/src/it/scala/org/knora/webapi/sharedtestdata/SharedOntologyTestDataADM.scala +++ b/webapi/src/it/scala/org/knora/webapi/sharedtestdata/SharedOntologyTestDataADM.scala @@ -19,6 +19,10 @@ object SharedOntologyTestDataADM { val ANYTHING_HasListItem_PROPERTY_LocalHost: IRI = ANYTHING_ONTOLOGY_IRI_LocalHost + "#" + "hasListItem" val ANYTHING_HasDate_PROPERTY_LocalHost: IRI = ANYTHING_ONTOLOGY_IRI_LocalHost + "#" + "hasDate" + // something + val SomethingOntologyIri: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0001/something" + val SomethingOntologyIriLocalhost: IRI = LocalHost_Ontology + "/0001/something/v2" + // freetest val FREETEST_ONTOLOGY_IRI: IRI = OntologyConstants.KnoraInternal.InternalOntologyStart + "/0001/freetest" val FREETEST_ONTOLOGY_IRI_LocalHost: IRI = LocalHost_Ontology + "/0001/freetest/v2" diff --git a/webapi/src/it/scala/org/knora/webapi/sharedtestdata/SharedTestDataADM.scala b/webapi/src/it/scala/org/knora/webapi/sharedtestdata/SharedTestDataADM.scala index e562436550..b417a5e266 100644 --- a/webapi/src/it/scala/org/knora/webapi/sharedtestdata/SharedTestDataADM.scala +++ b/webapi/src/it/scala/org/knora/webapi/sharedtestdata/SharedTestDataADM.scala @@ -159,7 +159,7 @@ object SharedTestDataADM { ) ) - /* represents the full project info of the Knora System project */ + /* represents the full ProjectADM of the Knora System project */ def systemProject: ProjectADM = ProjectADM( id = OntologyConstants.KnoraAdmin.SystemProject, shortname = "SystemProject", @@ -178,7 +178,7 @@ object SharedTestDataADM { selfjoin = false ) - /* represents the full project info of the default shared ontologies project */ + /* represents the full ProjectADM of the default shared ontologies project */ def defaultSharedOntologiesProject: ProjectADM = ProjectADM( id = OntologyConstants.KnoraAdmin.DefaultSharedOntologiesProject, shortname = "DefaultSharedOntologiesProject", @@ -294,7 +294,7 @@ object SharedTestDataADM { ) ) - /* represents the full project info of the images project */ + /* represents the full ProjectADM of the images project */ def imagesProject: ProjectADM = ProjectADM( id = imagesProjectIri, shortname = "images", @@ -308,7 +308,21 @@ object SharedTestDataADM { selfjoin = false ) - /* represents the full GroupInfoV1 of the images ProjectAdmin group */ + /* represents the full ProjectADM of the images project in the external format */ + def imagesProjectExternal: ProjectADM = ProjectADM( + id = imagesProjectIri, + shortname = "images", + shortcode = "00FF", + longname = Some("Image Collection Demo"), + description = Seq(StringLiteralV2(value = "A demo project of a collection of images", language = Some("en"))), + keywords = Seq("images", "collection").sorted, + logo = None, + ontologies = Seq(SharedOntologyTestDataADM.IMAGES_ONTOLOGY_IRI_LocalHost), + status = true, + selfjoin = false + ) + + /* represents the full GroupADM of the images ProjectAdmin group */ def imagesProjectAdminGroup: GroupADM = GroupADM( id = "-", name = "ProjectAdmin", @@ -318,7 +332,7 @@ object SharedTestDataADM { selfjoin = false ) - /* represents the full GroupInfoV1 of the images ProjectMember group */ + /* represents the full GroupADM of the images ProjectMember group */ def imagesProjectMemberGroup: GroupADM = GroupADM( id = "-", name = "ProjectMember", @@ -328,7 +342,7 @@ object SharedTestDataADM { selfjoin = false ) - /* represents the full GroupInfoV1 of the images project reviewer group */ + /* represents the full GroupADM of the images project reviewer group */ def imagesReviewerGroup: GroupADM = GroupADM( id = "http://rdfh.ch/groups/00FF/images-reviewer", name = "Image reviewer", @@ -338,6 +352,16 @@ object SharedTestDataADM { selfjoin = false ) + /* represents the full GroupADM of the images project reviewer group in the external format*/ + def imagesReviewerGroupExternal: GroupADM = GroupADM( + id = "http://rdfh.ch/groups/00FF/images-reviewer", + name = "Image reviewer", + descriptions = Seq(StringLiteralV2(value = "A group for image reviewers.", language = Some("en"))), + project = imagesProjectExternal, + status = true, + selfjoin = false + ) + /** * ********************************** */ @@ -432,7 +456,7 @@ object SharedTestDataADM { ) ) - /* represents the ProjectInfoV1 of the incunabula project */ + /* represents the ProjectADM of the incunabula project */ def incunabulaProject: ProjectADM = ProjectADM( id = incunabulaProjectIri, shortname = "incunabula", @@ -467,6 +491,41 @@ object SharedTestDataADM { selfjoin = false ) + /* represents the ProjectADM of the incunabula project in the external format*/ + def incunabulaProjectExternal: ProjectADM = ProjectADM( + id = incunabulaProjectIri, + shortname = "incunabula", + shortcode = "0803", + longname = Some("Bilderfolgen Basler Frühdrucke"), + description = Seq( + StringLiteralV2( + value = + "

Das interdisziplinäre Forschungsprojekt \"Die Bilderfolgen der Basler Frühdrucke: Spätmittelalterliche Didaxe als Bild-Text-Lektüre\" verbindet eine umfassende kunstwissenschaftliche Analyse der Bezüge zwischen den Bildern und Texten in den illustrierten Basler Inkunabeln mit der Digitalisierung der Bestände der Universitätsbibliothek und der Entwicklung einer elektronischen Edition in der Form einer neuartigen Web-0.2-Applikation.\n

\n

Das Projekt wird durchgeführt vom Kunsthistorischen Seminar der Universität Basel (Prof. B. Schellewald) und dem Digital Humanities Lab der Universität Basel (PD Dr. L. Rosenthaler).\n

\n

\nDas Kernstück der digitalen Edition besteht aus rund zwanzig reich bebilderten Frühdrucken aus vier verschiedenen Basler Offizinen. Viele davon sind bereits vor 1500 in mehreren Ausgaben erschienen, einige fast gleichzeitig auf Deutsch und Lateinisch. Es handelt sich um eine ausserordentlich vielfältige Produktion; neben dem Heilsspiegel finden sich ein Roman, die Melusine, die Reisebeschreibungen des Jean de Mandeville, einige Gebets- und Erbauungsbüchlein, theologische Schriften, Fastenpredigten, die Leben der Heiligen Fridolin und Meinrad, das berühmte Narrenschiff sowie die Exempelsammlung des Ritters vom Thurn.\n

\nDie Internetpublikation macht das digitalisierte Korpus dieser Frühdrucke durch die Möglichkeiten nichtlinearer Verknüpfung und Kommentierung der Bilder und Texte, für die wissenschaftliche Edition sowie für die Erforschung der Bilder und Texte nutzbar machen. Auch können bereits bestehende und entstehende Online-Editionen damit verknüpft werden , wodurch die Nutzung von Datenbanken anderer Institutionen im Hinblick auf unser Corpus optimiert wird.\n

", + language = None + ) + ), + keywords = Seq( + "Basler Frühdrucke", + "Inkunabel", + "Narrenschiff", + "Wiegendrucke", + "Sebastian Brant", + "Bilderfolgen", + "early print", + "incunabula", + "ship of fools", + "Kunsthistorisches Seminar Universität Basel", + "Late Middle Ages", + "Letterpress Printing", + "Basel", + "Contectualisation of images" + ).sorted, + logo = Some("incunabula_logo.png"), + ontologies = Seq(SharedOntologyTestDataADM.INCUNABULA_ONTOLOGY_IRI_LocalHost), + status = true, + selfjoin = false + ) + /** * ********************************* */ @@ -580,12 +639,28 @@ object SharedTestDataADM { description = Seq(StringLiteralV2(value = "Anything Project", language = None)), keywords = Seq("things", "arbitrary test data").sorted, logo = None, - ontologies = Seq("http://www.knora.org/ontology/0001/anything", "http://www.knora.org/ontology/0001/something"), + ontologies = Seq(SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI, SharedOntologyTestDataADM.SomethingOntologyIri), + status = true, + selfjoin = false + ) + + def anythingProjectExternal: ProjectADM = ProjectADM( + id = anythingProjectIri, + shortname = "anything", + shortcode = "0001", + longname = Some("Anything Project"), + description = Seq(StringLiteralV2(value = "Anything Project", language = None)), + keywords = Seq("things", "arbitrary test data").sorted, + logo = None, + ontologies = Seq( + SharedOntologyTestDataADM.ANYTHING_ONTOLOGY_IRI_LocalHost, + SharedOntologyTestDataADM.SomethingOntologyIriLocalhost + ), status = true, selfjoin = false ) - /* represents the full GroupInfoV1 of the Thing searcher group */ + /* represents the full GroupADM of the Thing searcher group */ def thingSearcherGroup: GroupADM = GroupADM( id = "http://rdfh.ch/groups/0001/thing-searcher", name = "Thing searcher", diff --git a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectsMessagesADM.scala b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectsMessagesADM.scala index 457e1b35d2..c134505085 100644 --- a/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectsMessagesADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/messages/admin/responder/projectsmessages/ProjectsMessagesADM.scala @@ -171,14 +171,6 @@ sealed trait ProjectsResponderRequestADM extends KnoraRequestADM */ case class ProjectsGetRequestADM(requestingUser: UserADM) extends ProjectsResponderRequestADM -/** - * Get all information about all projects in form of a sequence of [[ProjectADM]]. Returns an empty sequence if - * no projects are found. Administration permission checking is skipped. - * - * @param requestingUser the user making the request. - */ -case class ProjectsGetADM(requestingUser: UserADM) extends ProjectsResponderRequestADM - /** * Get info about a single project identified either through its IRI, shortname or shortcode. The response is in form * of [[ProjectGetResponseADM]]. External use. diff --git a/webapi/src/main/scala/org/knora/webapi/responders/admin/PermissionsResponderADM.scala b/webapi/src/main/scala/org/knora/webapi/responders/admin/PermissionsResponderADM.scala index f16e432d70..c66100ed57 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/admin/PermissionsResponderADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/admin/PermissionsResponderADM.scala @@ -18,6 +18,7 @@ import org.knora.webapi._ import org.knora.webapi.messages.IriConversions._ import org.knora.webapi.messages.OntologyConstants import org.knora.webapi.messages.SmartIri +import org.knora.webapi.messages.StringFormatter import org.knora.webapi.messages.admin.responder.groupsmessages.GroupADM import org.knora.webapi.messages.admin.responder.groupsmessages.GroupGetADM import org.knora.webapi.messages.admin.responder.permissionsmessages @@ -204,7 +205,6 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re requestingUser: UserADM ): Future[PermissionsDataADM] = { // find out which project each group belongs to - // _ = log.debug("getPermissionsProfileV1 - find out to which project each group belongs to") val groupFutures: Seq[Future[(IRI, IRI)]] = if (groupIris.nonEmpty) { groupIris.map { groupIri => @@ -233,7 +233,6 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re for { groups: Seq[(IRI, IRI)] <- groupsFuture - // _ = log.debug(s"permissionsProfileGetV1 - groups: {}", MessageUtil.toSource(groups)) /* materialize implicit membership in 'http://www.knora.org/ontology/knora-base#ProjectMember' group for each project */ projectMembers: Seq[(IRI, IRI)] = @@ -245,7 +244,6 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re } else { Seq.empty[(IRI, IRI)] } - // _ = log.debug(s"permissionsProfileGetV1 - projectMembers: {}", MessageUtil.toSource(projectMembers)) /* materialize implicit membership in 'http://www.knora.org/ontology/knora-base#ProjectAdmin' group for each project */ projectAdmins: Seq[(IRI, IRI)] = @@ -257,7 +255,6 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re } else { Seq.empty[(IRI, IRI)] } - // _ = log.debug("permissionsProfileGetV1 - projectAdmins: {}", MessageUtil.toSource(projectAdmins)) /* materialize implicit membership in 'http://www.knora.org/ontology/knora-base#SystemAdmin' group */ systemAdmin: Seq[(IRI, IRI)] = @@ -266,14 +263,11 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re } else { Seq.empty[(IRI, IRI)] } - // _ = log.debug(s"permissionsProfileGetV1 - systemAdmin: {}", MessageUtil.toSource(systemAdmin)) /* combine explicit groups with materialized implicit groups */ /* here we don't add the KnownUser group, as this would inflate the whole thing. */ - /* we instead inject the relevant information in defaultObjectAccessPermissionsStringForEntityGetV1 */ allGroups = groups ++ projectMembers ++ projectAdmins ++ systemAdmin groupsPerProject = allGroups.groupBy(_._1).map { case (k, v) => (k, v.map(_._2)) } - // _ = log.debug(s"permissionsProfileGetV1 - groupsPerProject: {}", MessageUtil.toSource(groupsPerProject)) /* retrieve the administrative permissions for each group per project the user is member of */ administrativePermissionsPerProjectFuture: Future[Map[IRI, Set[PermissionADM]]] = @@ -289,7 +283,6 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re groupsPerProject = groupsPerProject, administrativePermissionsPerProject = administrativePermissionsPerProject ) - // _ = log.debug(s"permissionsDataGetV1 - resulting permissionData: {}", result) } yield result } @@ -326,7 +319,6 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re permissionsListBuffer += (("ProjectAdmin", administrativePermissionsOnProjectAdminGroup)) } } - // _ = log.debug(s"userAdministrativePermissionsGetV1 - project: $projectIri, administrativePermissionsOnProjectAdminGroup: $administrativePermissionsOnProjectAdminGroup") /* Get administrative permissions for custom groups (all groups other than the built-in groups) */ administrativePermissionsOnCustomGroups: Set[PermissionADM] <- { @@ -346,7 +338,6 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re permissionsListBuffer += (("CustomGroups", administrativePermissionsOnCustomGroups)) } } - // _ = log.debug(s"userAdministrativePermissionsGetV1 - project: $projectIri, administrativePermissionsOnCustomGroups: $administrativePermissionsOnCustomGroups") /* Get administrative permissions for the knora-base:ProjectMember group */ administrativePermissionsOnProjectMemberGroup: Set[PermissionADM] <- @@ -361,7 +352,6 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re } } } - // _ = log.debug(s"userAdministrativePermissionsGetV1 - project: $projectIri, administrativePermissionsOnProjectMemberGroup: $administrativePermissionsOnProjectMemberGroup") /* Get administrative permissions for the knora-base:KnownUser group */ administrativePermissionsOnKnownUserGroup: Set[PermissionADM] <- administrativePermissionForGroupsGetADM( @@ -375,13 +365,9 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re } } } - // _ = log.debug(s"userAdministrativePermissionsGetV1 - project: $projectIri, administrativePermissionsOnKnownUserGroup: $administrativePermissionsOnKnownUserGroup") projectAdministrativePermissions: (IRI, Set[PermissionADM]) = permissionsListBuffer.length match { case 1 => - log.debug( - s"userAdministrativePermissionsGetV1 - project: $projectIri, precedence: ${permissionsListBuffer.head._1}, administrativePermissions: ${permissionsListBuffer.head._2}" - ) (projectIri, permissionsListBuffer.head._2) case 0 => (projectIri, Set.empty[PermissionADM]) @@ -406,7 +392,6 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re val result: Future[Map[IRI, Set[PermissionADM]]] = Future.sequence(permissionsPerProject).map(_.toMap) - log.debug(s"userAdministrativePermissionsGetV1 - result: $result") result } @@ -418,7 +403,7 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re * ********************************************************************** */ /** - * Convenience method returning a set with combined administrative permission. Used in userAdministrativePermissionsGetV1. + * Convenience method returning a set with combined administrative permission. * * @param projectIri the IRI of the project. * @param groups the list of groups for which administrative permissions are retrieved and combined. @@ -449,7 +434,7 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re val result: Future[Set[PermissionADM]] = for { allPermissions: Seq[Seq[PermissionADM]] <- allPermissionsFuture - // remove instances with empty PermissionV1 sets + // remove instances with empty PermissionADM sets cleanedAllPermissions: Seq[Seq[PermissionADM]] = allPermissions.filter(_.nonEmpty) /* Combine permission sequences */ @@ -488,7 +473,6 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re // _ = log.debug(s"administrativePermissionsForProjectGetRequestADM - query: $sparqlQueryString") permissionsQueryResponse <- appActor.ask(SparqlSelectRequest(sparqlQueryString)).mapTo[SparqlSelectResult] - // _ = log.debug(s"getProjectAdministrativePermissionsV1 - result: ${MessageUtil.toSource(permissionsQueryResponse)}") /* extract response rows */ permissionsQueryResponseRows: Seq[VariableResultsRow] = permissionsQueryResponse.results.bindings @@ -550,8 +534,7 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re for { administrativePermission <- permissionGetADM(administrativePermissionIri, requestingUser) result = administrativePermission match { - case ap: AdministrativePermissionADM => - AdministrativePermissionGetResponseADM(ap) + case ap: AdministrativePermissionADM => AdministrativePermissionGetResponseADM(ap) case _ => throw BadRequestException(s"$administrativePermissionIri is not an administrative permission.") } @@ -794,8 +777,7 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re private def objectAccessPermissionsForResourceGetADM( resourceIri: IRI, requestingUser: UserADM - ): Future[Option[ObjectAccessPermissionADM]] = { - log.debug(s"objectAccessPermissionsForResourceGetV1 - resourceIRI: $resourceIri") + ): Future[Option[ObjectAccessPermissionADM]] = for { projectIri <- getProjectOfEntity(resourceIri) // Check user's permission for the operation @@ -814,10 +796,8 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re ) .toString() ) - // _ = log.debug(s"objectAccessPermissionsForResourceGetV1 - query: $sparqlQueryString") permissionQueryResponse <- appActor.ask(SparqlSelectRequest(sparqlQueryString)).mapTo[SparqlSelectResult] - // _ = log.debug(s"objectAccessPermissionsForResourceGetV1 - result: ${MessageUtil.toSource(permissionQueryResponse)}") permissionQueryResponseRows: Seq[VariableResultsRow] = permissionQueryResponse.results.bindings @@ -833,14 +813,16 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re PermissionType.OAP ) Some( - ObjectAccessPermissionADM(forResource = Some(resourceIri), forValue = None, hasPermissions = hasPermissions) + ObjectAccessPermissionADM( + forResource = Some(resourceIri), + forValue = None, + hasPermissions = hasPermissions + ) ) } else { None } - _ = log.debug(s"objectAccessPermissionsForResourceGetV1 - permission: $permission") } yield permission - } /** * Gets all permissions attached to the value. @@ -852,8 +834,7 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re private def objectAccessPermissionsForValueGetADM( valueIri: IRI, requestingUser: UserADM - ): Future[Option[ObjectAccessPermissionADM]] = { - log.debug(s"objectAccessPermissionsForValueGetV1 - valueIRI: $valueIri") + ): Future[Option[ObjectAccessPermissionADM]] = for { projectIri <- getProjectOfEntity(valueIri) // Check user's permission for the operation @@ -872,10 +853,8 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re ) .toString() ) - // _ = log.debug(s"objectAccessPermissionsForValueGetV1 - query: $sparqlQueryString") permissionQueryResponse <- appActor.ask(SparqlSelectRequest(sparqlQueryString)).mapTo[SparqlSelectResult] - // _ = log.debug(s"objectAccessPermissionsForValueGetV1 - result: ${MessageUtil.toSource(permissionQueryResponse)}") permissionQueryResponseRows: Seq[VariableResultsRow] = permissionQueryResponse.results.bindings @@ -891,14 +870,16 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re PermissionType.OAP ) Some( - ObjectAccessPermissionADM(forResource = None, forValue = Some(valueIri), hasPermissions = hasPermissions) + ObjectAccessPermissionADM( + forResource = None, + forValue = Some(valueIri), + hasPermissions = hasPermissions + ) ) } else { None } - _ = log.debug(s"objectAccessPermissionsForValueGetV1 - permission: $permission") } yield permission - } /////////////////////////////////////////////////////////////////////////// // DEFAULT OBJECT ACCESS PERMISSIONS @@ -925,10 +906,8 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re ) .toString() ) - // _ = log.debug(s"defaultObjectAccessPermissionsForProjectGetRequestADM - query: $sparqlQueryString") permissionsQueryResponse <- appActor.ask(SparqlSelectRequest(sparqlQueryString)).mapTo[SparqlSelectResult] - // _ = log.debug(s"defaultObjectAccessPermissionsForProjectGetRequestADM - result: ${MessageUtil.toSource(permissionsQueryResponse)}") /* extract response rows */ permissionsQueryResponseRows: Seq[VariableResultsRow] = permissionsQueryResponse.results.bindings @@ -939,39 +918,37 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re .map { case (permissionIri: String, rows: Seq[VariableResultsRow]) => (permissionIri, rows.map(row => (row.rowMap("p"), row.rowMap("o"))).toMap) } - // _ = log.debug(s"defaultObjectAccessPermissionsForProjectGetRequestADM - permissionsWithProperties: $permissionsWithProperties") - - permissions: Seq[DefaultObjectAccessPermissionADM] = permissionsWithProperties.map { - case (permissionIri: IRI, propsMap: Map[String, String]) => - /* parse permissions */ - val hasPermissions: Set[PermissionADM] = - PermissionUtilADM.parsePermissionsWithType( - propsMap.get( - OntologyConstants.KnoraBase.HasPermissions - ), - PermissionType.OAP - ) - - /* construct permission object */ - DefaultObjectAccessPermissionADM( - iri = permissionIri, - forProject = propsMap.getOrElse( - OntologyConstants.KnoraAdmin.ForProject, - throw InconsistentRepositoryDataException( - s"Permission $permissionIri has no project." - ) - ), - forGroup = - propsMap.get(OntologyConstants.KnoraAdmin.ForGroup), - forResourceClass = propsMap.get( - OntologyConstants.KnoraAdmin.ForResourceClass - ), - forProperty = propsMap.get( - OntologyConstants.KnoraAdmin.ForProperty - ), - hasPermissions = hasPermissions - ) - }.toSeq + + permissions: Seq[DefaultObjectAccessPermissionADM] = + permissionsWithProperties.map { case (permissionIri: IRI, propsMap: Map[String, String]) => + /* parse permissions */ + val hasPermissions: Set[PermissionADM] = + PermissionUtilADM.parsePermissionsWithType( + propsMap.get( + OntologyConstants.KnoraBase.HasPermissions + ), + PermissionType.OAP + ) + + /* construct permission object */ + DefaultObjectAccessPermissionADM( + iri = permissionIri, + forProject = propsMap.getOrElse( + OntologyConstants.KnoraAdmin.ForProject, + throw InconsistentRepositoryDataException( + s"Permission $permissionIri has no project." + ) + ), + forGroup = propsMap.get(OntologyConstants.KnoraAdmin.ForGroup), + forResourceClass = propsMap.get( + OntologyConstants.KnoraAdmin.ForResourceClass + ), + forProperty = propsMap.get( + OntologyConstants.KnoraAdmin.ForProperty + ), + hasPermissions = hasPermissions + ) + }.toSeq /* construct response object */ response = DefaultObjectAccessPermissionsForProjectGetResponseADM(permissions) @@ -1132,8 +1109,9 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re resourceClassIri: Option[IRI], propertyIri: Option[IRI], requestingUser: UserADM - ): Future[DefaultObjectAccessPermissionGetResponseADM] = - defaultObjectAccessPermissionGetADM(projectIri, groupIri, resourceClassIri, propertyIri) + ): Future[DefaultObjectAccessPermissionGetResponseADM] = { + val projectIriInternal = projectIri.toSmartIri.toOntologySchema(InternalSchema).toString + defaultObjectAccessPermissionGetADM(projectIriInternal, groupIri, resourceClassIri, propertyIri) .mapTo[Option[DefaultObjectAccessPermissionADM]] .flatMap { case Some(doap) => Future(DefaultObjectAccessPermissionGetResponseADM(doap)) @@ -1146,15 +1124,16 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re case Some(systemDoap) => DefaultObjectAccessPermissionGetResponseADM(systemDoap) case None => throw NotFoundException( - s"No Default Object Access Permission found for project: $projectIri, group: $groupIri, resourceClassIri: $resourceClassIri, propertyIri: $propertyIri combination" + s"No Default Object Access Permission found for project: $projectIriInternal, group: $groupIri, resourceClassIri: $resourceClassIri, propertyIri: $propertyIri combination" ) } } else { throw NotFoundException( - s"No Default Object Access Permission found for project: $projectIri, group: $groupIri, resourceClassIri: $resourceClassIri, propertyIri: $propertyIri combination" + s"No Default Object Access Permission found for project: $projectIriInternal, group: $groupIri, resourceClassIri: $resourceClassIri, propertyIri: $propertyIri combination" ) } } + } /** * Convenience method returning a set with combined max default object access permissions. @@ -1171,7 +1150,6 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re /* Get default object access permissions for each group and combine them */ val gpf: Seq[Future[Seq[PermissionADM]]] = for { groupIri <- groups - // _ = log.debug(s"userDefaultObjectAccessPermissionsGetV1 - projectIri: $projectIri, groupIri: $groupIri") groupPermissions: Future[Seq[PermissionADM]] = defaultObjectAccessPermissionGetADM( projectIri = projectIri, @@ -1192,7 +1170,7 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re val result: Future[Set[PermissionADM]] = for { allPermissions: Seq[Seq[PermissionADM]] <- allPermissionsFuture - // remove instances with empty PermissionV1 sets + // remove instances with empty PermissionADM sets cleanedAllPermissions: Seq[Seq[PermissionADM]] = allPermissions.filter(_.nonEmpty) /* Combine permission sequences */ @@ -1321,8 +1299,6 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re OntologyConstants.KnoraAdmin.KnownUser :: userGroups.toList } - // _ = log.debug("defaultObjectAccessPermissionsStringForEntityGetV1 - extendedUserGroups: {}", extendedUserGroups) - /* List buffer holding default object access permissions tagged with the precedence level: 0. ProjectAdmin > 1. ProjectEntity > 2. SystemEntity > 3. CustomGroups > 4. ProjectMember > 5. KnownUser Permissions are added following the precedence level from the highest to the lowest. As soon as one set @@ -1344,7 +1320,6 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re ) ) { permissionsListBuffer += (("ProjectAdmin", defaultPermissionsOnProjectAdminGroup)) - // log.debug(s"defaultObjectAccessPermissionsStringForEntityGetV1 - defaultPermissionsOnProjectAdminGroup: $defaultPermissionsOnProjectAdminGroup") } } @@ -1370,7 +1345,6 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re defaultPermissionsOnProjectResourceClassProperty ) ) - // log.debug(s"defaultObjectAccessPermissionsStringForEntityGetV1 - defaultPermissionsOnProjectResourceClassProperty: {}", defaultPermissionsOnProjectResourceClassProperty) } /* system resource class / property combination */ @@ -1388,7 +1362,6 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re } _ = if (defaultPermissionsOnSystemResourceClassProperty.nonEmpty) { permissionsListBuffer += (("SystemResourceClassProperty", defaultPermissionsOnSystemResourceClassProperty)) - // log.debug(s"defaultObjectAccessPermissionsStringForEntityGetV1 - defaultPermissionsOnSystemResourceClassProperty: {}", defaultPermissionsOnSystemResourceClassProperty) } /////////////////////// @@ -1407,7 +1380,6 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re } _ = if (defaultPermissionsOnProjectResourceClass.nonEmpty) { permissionsListBuffer += (("ProjectResourceClass", defaultPermissionsOnProjectResourceClass)) - // log.debug(s"defaultObjectAccessPermissionsStringForEntityGetV1 - defaultPermissionsOnProjectResourceClass: {}", defaultPermissionsOnProjectResourceClass) } /* Get the default object access permissions defined on the resource class inside the SystemProject */ @@ -1424,7 +1396,6 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re } _ = if (defaultPermissionsOnSystemResourceClass.nonEmpty) { permissionsListBuffer += (("SystemResourceClass", defaultPermissionsOnSystemResourceClass)) - // log.debug(s"defaultObjectAccessPermissionsStringForEntityGetV1 - defaultPermissionsOnSystemResourceClass: {}", defaultPermissionsOnSystemResourceClass) } /////////////////////// @@ -1443,7 +1414,6 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re } _ = if (defaultPermissionsOnProjectProperty.nonEmpty) { permissionsListBuffer += (("ProjectProperty", defaultPermissionsOnProjectProperty)) - // logger.debug(s"defaultObjectAccessPermissionsStringForEntityGetV1 - defaultPermissionsOnProjectProperty: {}", defaultPermissionsOnProjectProperty) } /* system property */ @@ -1460,7 +1430,6 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re } _ = if (defaultPermissionsOnSystemProperty.nonEmpty) { permissionsListBuffer += (("SystemProperty", defaultPermissionsOnSystemProperty)) - // logger.debug(s"defaultObjectAccessPermissionsStringForEntityGetV1 - defaultPermissionsOnSystemProperty: {}", defaultPermissionsOnSystemProperty) } /////////////////////// @@ -1487,7 +1456,6 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re } _ = if (defaultPermissionsOnCustomGroups.nonEmpty) { permissionsListBuffer += (("CustomGroups", defaultPermissionsOnCustomGroups)) - // logger.debug(s"defaultObjectAccessPermissionsStringForEntityGetV1 - defaultPermissionsOnCustomGroups: $defaultPermissionsOnCustomGroups") } /////////////////////// @@ -1509,7 +1477,6 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re ) { permissionsListBuffer += (("ProjectMember", defaultPermissionsOnProjectMemberGroup)) } - // logger.debug(s"defaultObjectAccessPermissionsStringForEntityGetV1 - defaultPermissionsOnProjectMemberGroup: $defaultPermissionsOnProjectMemberGroup") } /////////////////////// @@ -1526,7 +1493,6 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re _ = if (defaultPermissionsOnKnownUserGroup.nonEmpty) { if (extendedUserGroups.contains(OntologyConstants.KnoraAdmin.KnownUser)) { permissionsListBuffer += (("KnownUser", defaultPermissionsOnKnownUserGroup)) - // logger.debug(s"defaultObjectAccessPermissionsStringForEntityGetV1 - defaultPermissionsOnKnownUserGroup: $defaultPermissionsOnKnownUserGroup") } } @@ -1540,7 +1506,6 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re PermissionADM.changeRightsPermission(OntologyConstants.KnoraAdmin.Creator) ) permissionsListBuffer += (("Fallback", defaultFallbackPermission)) - // logger.debug(s"defaultObjectAccessPermissionsStringForEntityGetV1 - defaultFallbackPermission: $defaultFallbackPermission") } else { FastFuture.successful(Set.empty[PermissionADM]) } @@ -1888,23 +1853,23 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re requestingUser: UserADM, apiRequestID: UUID ): Future[PermissionGetResponseADM] = { - + val permissionIriInternal = permissionIri.toSmartIri.toOntologySchema(InternalSchema).toString /*Verify that hasPermissions is updated successfully*/ def verifyUpdateOfHasPermissions(expectedPermissions: Set[PermissionADM]): Future[PermissionItemADM] = for { - updatedPermission <- permissionGetADM(permissionIri, requestingUser) + updatedPermission <- permissionGetADM(permissionIriInternal, requestingUser) /*Verify that update was successful*/ _ = updatedPermission match { case ap: AdministrativePermissionADM => if (!ap.hasPermissions.equals(expectedPermissions)) throw UpdateNotPerformedException( - s"The hasPermissions set of permission $permissionIri was not updated. Please report this as a bug." + s"The hasPermissions set of permission $permissionIriInternal was not updated. Please report this as a bug." ) case doap: DefaultObjectAccessPermissionADM => if (!doap.hasPermissions.equals(expectedPermissions)) { throw UpdateNotPerformedException( - s"The hasPermissions set of permission $permissionIri was not updated. Please report this as a bug." + s"The hasPermissions set of permission $permissionIriInternal was not updated. Please report this as a bug." ) } case _ => None @@ -1918,10 +1883,11 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re permissionIri: IRI, changeHasPermissionsRequest: ChangePermissionHasPermissionsApiRequestADM, requestingUser: UserADM - ): Future[PermissionGetResponseADM] = + ): Future[PermissionGetResponseADM] = { + val permissionIriInternal = permissionIri.toSmartIri.toOntologySchema(InternalSchema).toString for { // get permission - permission <- permissionGetADM(permissionIri, requestingUser) + permission <- permissionGetADM(permissionIriInternal, requestingUser) response <- permission match { // Is permission an administrative permission? case ap: AdministrativePermissionADM => @@ -1962,6 +1928,7 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re ) } } yield response + } for { // run list info update with an local IRI lock @@ -1990,11 +1957,11 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re requestingUser: UserADM, apiRequestID: UUID ): Future[PermissionGetResponseADM] = { - + val permissionIriInternal = permissionIri.toSmartIri.toOntologySchema(InternalSchema).toString /*Verify that resource class of doap is updated successfully*/ def verifyUpdateOfResourceClass: Future[PermissionItemADM] = for { - updatedPermission <- permissionGetADM(permissionIri, requestingUser) + updatedPermission <- permissionGetADM(permissionIriInternal, requestingUser) /*Verify that update was successful*/ _ = updatedPermission match { @@ -2006,12 +1973,12 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re if (doap.forGroup.isDefined) throw UpdateNotPerformedException( - s"The $permissionIri is not correctly updated. Please report this as a bug." + s"The $permissionIriInternal is not correctly updated. Please report this as a bug." ) case _ => throw UpdateNotPerformedException( - s"Incorrect permission type returned for $permissionIri. Please report this as a bug." + s"Incorrect permission type returned for $permissionIriInternal. Please report this as a bug." ) } } yield updatedPermission @@ -2080,11 +2047,11 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re requestingUser: UserADM, apiRequestID: UUID ): Future[PermissionGetResponseADM] = { - + val permissionIriInternal = permissionIri.toSmartIri.toOntologySchema(InternalSchema).toString /*Verify that property of doap is updated successfully*/ def verifyUpdateOfProperty: Future[PermissionItemADM] = for { - updatedPermission <- permissionGetADM(permissionIri, requestingUser) + updatedPermission <- permissionGetADM(permissionIriInternal, requestingUser) /*Verify that update was successful*/ _ = updatedPermission match { @@ -2096,12 +2063,12 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re if (doap.forGroup.isDefined) throw UpdateNotPerformedException( - s"The $permissionIri is not correctly updated. Please report this as a bug." + s"The $permissionIriInternal is not correctly updated. Please report this as a bug." ) case _ => throw UpdateNotPerformedException( - s"Incorrect permission type returned for $permissionIri. Please report this as a bug." + s"Incorrect permission type returned for $permissionIriInternal. Please report this as a bug." ) } } yield updatedPermission @@ -2168,25 +2135,27 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re requestingUser: UserADM, apiRequestID: UUID ): Future[PermissionDeleteResponseADM] = { - + val permissionIriInternal = permissionIri.toSmartIri.toOntologySchema(InternalSchema).toString def permissionDeleteTask(): Future[PermissionDeleteResponseADM] = for { // check that there is a permission with a given IRI - _ <- permissionGetADM(permissionIri, requestingUser) + _ <- permissionGetADM(permissionIriInternal, requestingUser) // Is permission in use? _ <- isPermissionUsed( - permissionIri = permissionIri, + permissionIri = permissionIriInternal, errorFun = throw UpdateNotPerformedException( s"Permission $permissionIri cannot be deleted, because it is in use." ) ) - _ <- deletePermission(permissionIri) + _ <- deletePermission(permissionIriInternal) + sf = StringFormatter.getGeneralInstance + iriExternal = sf.toSmartIri(permissionIri).toOntologySchema(ApiV2Complex).toString - } yield PermissionDeleteResponseADM(permissionIri, true) + } yield PermissionDeleteResponseADM(iriExternal, true) for { - // run list info update with an local IRI lock + // run list info update with a local IRI lock taskResult <- IriLocker.runWithIriLock( apiRequestID, permissionIri, diff --git a/webapi/src/main/scala/org/knora/webapi/responders/admin/ProjectsResponderADM.scala b/webapi/src/main/scala/org/knora/webapi/responders/admin/ProjectsResponderADM.scala index c242c4eaa6..c0fa6e490d 100644 --- a/webapi/src/main/scala/org/knora/webapi/responders/admin/ProjectsResponderADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/responders/admin/ProjectsResponderADM.scala @@ -39,6 +39,7 @@ import org.knora.webapi.messages.store.triplestoremessages._ import org.knora.webapi.messages.util.KnoraSystemInstances import org.knora.webapi.messages.util.rdf._ import org.knora.webapi.messages.v2.responder.ontologymessages.OntologyMetadataGetByProjectRequestV2 +import org.knora.webapi.messages.v2.responder.ontologymessages.OntologyMetadataV2 import org.knora.webapi.messages.v2.responder.ontologymessages.ReadOntologyMetadataV2 import org.knora.webapi.responders.ActorDeps import org.knora.webapi.responders.IriLocker @@ -59,10 +60,9 @@ final case class ProjectsResponderADM(actorDeps: ActorDeps, cacheServiceSettings private val PERMISSIONS_DATA_GRAPH = "http://www.knora.org/data/permissions" /** - * Receives a message extending [[ProjectsResponderRequestV1]], and returns an appropriate response message. + * Receives a message extending [[ProjectsResponderRequestADM]], and returns an appropriate response message. */ def receive(msg: ProjectsResponderRequestADM) = msg match { - case ProjectsGetADM(requestingUser) => projectsGetADM(requestingUser) case ProjectsGetRequestADM(requestingUser) => projectsGetRequestADM(requestingUser) case ProjectGetADM(identifier) => getSingleProjectADM(identifier) case ProjectGetRequestADM(identifier) => getSingleProjectADMRequest(identifier) @@ -107,44 +107,31 @@ final case class ProjectsResponderADM(actorDeps: ActorDeps, cacheServiceSettings requestingUser: UserADM ): Future[Seq[ProjectADM]] = for { - sparqlQueryString <- Future( - org.knora.webapi.messages.twirl.queries.sparql.admin.txt - .getProjects( - maybeIri = None, - maybeShortname = None, - maybeShortcode = None - ) - .toString() - ) - // _ = log.debug(s"getProjectsResponseV1 - query: $sparqlQueryString") - - projectsResponse <- appActor - .ask( - SparqlExtendedConstructRequest( - sparql = sparqlQueryString - ) - ) - .mapTo[SparqlExtendedConstructResponse] - // _ = log.debug(s"projectsGetADM - projectsResponse: $projectsResponse") - - statements: List[(SubjectV2, Map[SmartIri, Seq[LiteralV2]])] = projectsResponse.statements.toList - // _ = log.debug(s"projectsGetADM - statements: $statements") - - projectIris = statements.map { case (projectIri: SubjectV2, _) => - projectIri.toString - }.toSet + sparqlQueryString <- + Future( + org.knora.webapi.messages.twirl.queries.sparql.admin.txt + .getProjects( + maybeIri = None, + maybeShortname = None, + maybeShortcode = None + ) + .toString() + ) + request = SparqlExtendedConstructRequest(sparql = sparqlQueryString) + projectsResponse <- appActor.ask(request).mapTo[SparqlExtendedConstructResponse] + projectIris = projectsResponse.statements.keySet.map(_.toString) ontologiesForProjects: Map[IRI, Seq[IRI]] <- getOntologiesForProjects(projectIris, requestingUser) - - projects: Seq[ProjectADM] = statements.map { - case (projectIriSubject: SubjectV2, propsMap: Map[SmartIri, Seq[LiteralV2]]) => - val projectOntologies = - ontologiesForProjects.getOrElse(projectIriSubject.toString, Seq.empty[IRI]) - statements2ProjectADM( - statements = (projectIriSubject, propsMap), - ontologies = projectOntologies - ) - } + projects = + projectsResponse.statements.toList.map { + case (projectIriSubject: SubjectV2, propsMap: Map[SmartIri, Seq[LiteralV2]]) => + val projectOntologies = + ontologiesForProjects.getOrElse(projectIriSubject.toString, Seq.empty[IRI]) + statements2ProjectADM( + statements = (projectIriSubject, propsMap), + ontologies = projectOntologies + ) + } } yield projects.sorted @@ -155,55 +142,39 @@ final case class ProjectsResponderADM(actorDeps: ActorDeps, cacheServiceSettings * @param requestingUser the requesting user. * @return a map of project IRIs to sequences of ontology IRIs. */ - private def getOntologiesForProjects(projectIris: Set[IRI], requestingUser: UserADM): Future[Map[IRI, Seq[IRI]]] = + private def getOntologiesForProjects(projectIris: Set[IRI], requestingUser: UserADM): Future[Map[IRI, Seq[IRI]]] = { + def getIriPair(ontology: OntologyMetadataV2) = + ontology.projectIri.fold( + throw InconsistentRepositoryDataException(s"Ontology ${ontology.ontologyIri} has no project") + )(project => (project.toString, ontology.ontologyIri.toString)) + + val request = OntologyMetadataGetByProjectRequestV2( + projectIris = projectIris.map(_.toSmartIri), + requestingUser = requestingUser + ) + for { - ontologyMetadataResponse: ReadOntologyMetadataV2 <- appActor - .ask( - OntologyMetadataGetByProjectRequestV2( - projectIris = projectIris.map(_.toSmartIri), - requestingUser = requestingUser - ) - ) - .mapTo[ReadOntologyMetadataV2] - } yield ontologyMetadataResponse.ontologies.map { ontology => - val ontologyIri: IRI = ontology.ontologyIri.toString - val projectIri: IRI = ontology.projectIri - .getOrElse(throw InconsistentRepositoryDataException(s"Ontology $ontologyIri has no project")) - .toString - projectIri -> ontologyIri - } - .groupBy(_._1) - .map { case (projectIri, projectIriAndOntologies: Set[(IRI, IRI)]) => - projectIri -> projectIriAndOntologies.map(_._2).toSeq - } + ontologyMetadataResponse <- appActor.ask(request).mapTo[ReadOntologyMetadataV2] + ontologies = ontologyMetadataResponse.ontologies.toList + iriPairs = ontologies.map(getIriPair(_)) + projectToOntologyMap = iriPairs.groupMap { case (project, _) => project } { case (_, onto) => onto } + } yield projectToOntologyMap + } /** - * Gets all the projects and returns them as a [[ProjectsResponseV1]]. + * Gets all the projects and returns them as a [[ProjectADM]]. * * @param requestingUser the user that is making the request. - * @return all the projects as a [[ProjectsResponseV1]]. + * @return all the projects as a [[ProjectADM]]. * @throws NotFoundException if no projects are found. */ private def projectsGetRequestADM( requestingUser: UserADM ): Future[ProjectsGetResponseADM] = - // log.debug("projectsGetRequestADM") - - // ToDo: What permissions should be required, if any? for { - projects <- projectsGetADM( - requestingUser = requestingUser - ) - - result = - if (projects.nonEmpty) { - ProjectsGetResponseADM( - projects = projects - ) - } else { - throw NotFoundException(s"No projects found") - } - + projects <- projectsGetADM(requestingUser = requestingUser) + result = if (projects.nonEmpty) { ProjectsGetResponseADM(projects = projects) } + else { throw NotFoundException(s"No projects found") } } yield result /** @@ -270,7 +241,6 @@ final case class ProjectsResponderADM(actorDeps: ActorDeps, cacheServiceSettings identifier: ProjectIdentifierADM, requestingUser: UserADM ): Future[ProjectMembersGetResponseADM] = - // log.debug("projectMembersGetRequestADM - maybeIri: {}, maybeShortname: {}, maybeShortcode: {}", maybeIri, maybeShortname, maybeShortcode) for { /* Get project and verify permissions. */ @@ -300,7 +270,6 @@ final case class ProjectsResponderADM(actorDeps: ActorDeps, cacheServiceSettings ) .toString() ) - // _ = log.debug(s"projectMembersGetRequestADM - query: $sparqlQueryString") projectMembersResponse <- appActor .ask( @@ -312,8 +281,6 @@ final case class ProjectsResponderADM(actorDeps: ActorDeps, cacheServiceSettings statements = projectMembersResponse.statements.toList - // _ = log.debug(s"projectMembersGetRequestADM - statements: {}", MessageUtil.toSource(statements)) - // get project member IRI from results rows userIris: Seq[IRI] = if (statements.nonEmpty) { @@ -337,8 +304,6 @@ final case class ProjectsResponderADM(actorDeps: ActorDeps, cacheServiceSettings maybeUsers: Seq[Option[UserADM]] <- Future.sequence(maybeUserFutures) users: Seq[UserADM] = maybeUsers.flatten - // _ = log.debug(s"projectMembersGetRequestADM - users: {}", users) - } yield ProjectMembersGetResponseADM(members = users) /** @@ -353,7 +318,6 @@ final case class ProjectsResponderADM(actorDeps: ActorDeps, cacheServiceSettings identifier: ProjectIdentifierADM, requestingUser: UserADM ): Future[ProjectAdminMembersGetResponseADM] = - // log.debug("projectAdminMembersGetRequestADM - maybeIri: {}, maybeShortname: {}, maybeShortcode: {}", maybeIri, maybeShortname, maybeShortcode) for { /* Get project and verify permissions. */ project <- getSingleProjectADM( @@ -378,7 +342,6 @@ final case class ProjectsResponderADM(actorDeps: ActorDeps, cacheServiceSettings ) .toString() ) - // _ = log.debug(s"projectAdminMembersByIRIGetRequestV1 - query: $sparqlQueryString") projectAdminMembersResponse <- appActor .ask( @@ -387,7 +350,6 @@ final case class ProjectsResponderADM(actorDeps: ActorDeps, cacheServiceSettings ) ) .mapTo[SparqlExtendedConstructResponse] - // _ = log.debug(s"projectAdminMembersByIRIGetRequestV1 - result: ${MessageUtil.toSource(projectMembersResponse)}") statements = projectAdminMembersResponse.statements.toList @@ -414,8 +376,6 @@ final case class ProjectsResponderADM(actorDeps: ActorDeps, cacheServiceSettings maybeUsers: Seq[Option[UserADM]] <- Future.sequence(maybeUserFutures) users: Seq[UserADM] = maybeUsers.flatten - // _ = log.debug(s"projectMembersGetRequestADM - users: $users") - } yield ProjectAdminMembersGetResponseADM(members = users) /** @@ -752,8 +712,6 @@ final case class ProjectsResponderADM(actorDeps: ActorDeps, cacheServiceSettings apiRequestID: UUID ): Future[ProjectOperationResponseADM] = { - // log.debug(s"changeBasicInformationRequestV1: changeProjectRequest: {}", changeProjectRequest) - /** * The actual change project task run with an IRI lock. */ @@ -1234,7 +1192,7 @@ final case class ProjectsResponderADM(actorDeps: ActorDeps, cacheServiceSettings } yield maybeProjectADM /** - * Helper method that turns SPARQL result rows into a [[ProjectInfoV1]]. + * Helper method that turns SPARQL result rows into a [[ProjectADM]]. * * @param statements results from the SPARQL query representing information about the project. * @param ontologies the ontologies in the project. diff --git a/webapi/src/main/scala/org/knora/webapi/routing/RouteUtilADM.scala b/webapi/src/main/scala/org/knora/webapi/routing/RouteUtilADM.scala index 3fbf0c16cc..3fcd9cd859 100644 --- a/webapi/src/main/scala/org/knora/webapi/routing/RouteUtilADM.scala +++ b/webapi/src/main/scala/org/knora/webapi/routing/RouteUtilADM.scala @@ -17,20 +17,80 @@ import scala.concurrent.ExecutionContext import scala.concurrent.Future import dsp.errors.UnexpectedMessageException +import org.knora.webapi.ApiV2Complex import org.knora.webapi.messages.ResponderRequest.KnoraRequestADM +import org.knora.webapi.messages.StringFormatter import org.knora.webapi.messages.admin.responder.KnoraResponseADM +import org.knora.webapi.messages.admin.responder.groupsmessages._ +import org.knora.webapi.messages.admin.responder.projectsmessages._ +import org.knora.webapi.messages.admin.responder.usersmessages._ /** * Convenience methods for Knora Admin routes. */ object RouteUtilADM { + /** + * Transforms all ontology IRIs referenced inside a KnoraResponseADM into their external format. + * + * @param response the response that should be transformed + * @return the transformed [[KnoraResponseADM]] + */ + def transformResponseIntoExternalFormat( + response: KnoraResponseADM + ): KnoraResponseADM = { + val sf = StringFormatter.getGeneralInstance + + def projectAsExternalRepresentation(project: ProjectADM): ProjectADM = { + val ontologiesExternal = project.ontologies.map(sf.toSmartIri(_)).map(_.toOntologySchema(ApiV2Complex).toString) + project.copy(ontologies = ontologiesExternal) + } + + def groupAsExternalRepresentation(group: GroupADM): GroupADM = { + val projectExternal = projectAsExternalRepresentation(group.project) + group.copy(project = projectExternal) + } + + def userAsExternalRepresentation(user: UserADM): UserADM = { + val groupsExternal = user.groups.map { g: GroupADM => groupAsExternalRepresentation(g) } + val projectsExternal = user.projects.map { p: ProjectADM => projectAsExternalRepresentation(p) } + user.copy(groups = groupsExternal, projects = projectsExternal) + } + + response match { + case ProjectsGetResponseADM(projects) => ProjectsGetResponseADM(projects.map(projectAsExternalRepresentation(_))) + case ProjectGetResponseADM(project) => ProjectGetResponseADM(projectAsExternalRepresentation(project)) + case ProjectMembersGetResponseADM(members) => + ProjectMembersGetResponseADM(members.map(userAsExternalRepresentation(_))) + case ProjectAdminMembersGetResponseADM(members) => + ProjectAdminMembersGetResponseADM(members.map(userAsExternalRepresentation(_))) + case ProjectOperationResponseADM(project) => ProjectOperationResponseADM(projectAsExternalRepresentation(project)) + + case GroupsGetResponseADM(groups) => GroupsGetResponseADM(groups.map(groupAsExternalRepresentation(_))) + case GroupGetResponseADM(group) => GroupGetResponseADM(groupAsExternalRepresentation(group)) + case GroupMembersGetResponseADM(members) => + GroupMembersGetResponseADM(members.map(userAsExternalRepresentation(_))) + case GroupOperationResponseADM(group) => GroupOperationResponseADM(groupAsExternalRepresentation(group)) + + case UsersGetResponseADM(users) => UsersGetResponseADM(users.map(userAsExternalRepresentation(_))) + case UserResponseADM(user) => UserResponseADM(userAsExternalRepresentation(user)) + case UserProjectMembershipsGetResponseADM(projects) => + UserProjectMembershipsGetResponseADM(projects.map(projectAsExternalRepresentation(_))) + case UserProjectAdminMembershipsGetResponseADM(projects) => + UserProjectAdminMembershipsGetResponseADM(projects.map(projectAsExternalRepresentation(_))) + case UserGroupMembershipsGetResponseADM(groups) => + UserGroupMembershipsGetResponseADM(groups.map(groupAsExternalRepresentation(_))) + case UserOperationResponseADM(user) => UserOperationResponseADM(userAsExternalRepresentation(user)) + + case _ => response + } + } + /** * Sends a message to a responder and completes the HTTP request by returning the response as JSON. * * @param requestMessageF a future containing a [[KnoraRequestADM]] message that should be sent to the responder manager. * @param requestContext the akka-http [[RequestContext]]. - * * @param appActor a reference to the application actor. * @param log a logging adapter. * @param timeout a timeout for `ask` messages. @@ -48,7 +108,7 @@ object RouteUtilADM { requestMessage <- requestMessageF - // Make sure the responder sent a reply of type KnoraResponseV2. + // Make sure the responder sent a reply of type KnoraResponseADM. knoraResponse <- (appActor.ask(requestMessage)).map { case replyMessage: KnoraResponseADM => replyMessage @@ -60,7 +120,8 @@ object RouteUtilADM { ) } - jsonResponse = knoraResponse.toJsValue.asJsObject + knoraResponseExternal = transformResponseIntoExternalFormat(knoraResponse) + jsonResponse = knoraResponseExternal.toJsValue.asJsObject } yield HttpResponse( status = StatusCodes.OK, entity = HttpEntity(