Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
feat(projects): add default set of permissions when creating new proj…
…ect (DSP-1347) (#1822)

* feature (projects) After creating a new project, add permissions to project Admin and members for operations on entities that belong to this project.

* feat(project): give default permissions custom IRIs and add test

* docs(project): add documentation about the default permissions added for a new project

* test (projects): more tests for creation of default permissions

* fix(projects) fix the failing test

* docs(projects): add more documentation

* refactor (permissions) throw a more helpful error message when failing to create a permission

* test (permissions): some more tests

* refactor (projects) some minor cleanup

* refactor (projects): fix typos
  • Loading branch information
SepidehAlassi committed Feb 17, 2021
1 parent d92bb01 commit b7c71ca
Show file tree
Hide file tree
Showing 10 changed files with 355 additions and 18 deletions.
15 changes: 14 additions & 1 deletion docs/03-apis/api-admin/permissions.md
Expand Up @@ -79,6 +79,10 @@ As a response, the created administrative permission and its IRI are returned as
}
}
```
Note that during the creation of a new project, a default set of administrative permissions are added to its ProjectAdmin and
ProjectMember groups (See [Default set of permissions for a new project](./projects.md#default-set-of-permissions-for-a-new-project)).
Therefore, it is not possible to create new administrative permissions for the ProjectAdmin and ProjectMember groups of
a project. However, the default permissions set for these groups can be modified (See [update permission](./permissions.md#updating-a-permissions-scope)).

- `POST: /admin/permissions/doap` : create a new default object access permission.
A single instance of `knora-admin:DefaultObjectAccessPermission` must
Expand Down Expand Up @@ -133,6 +137,10 @@ The response contains the newly created permission and its IRI, as:
}
}
```
Note that during the creation of a new project, a set of default object access permissions are created for its
ProjectAdmin and ProjectMember groups (See [Default set of permissions for a new project](./projects.md#default-set-of-permissions-for-a-new-project)).
Therefore, it is not possible to create new default object access permissions for the ProjectAdmin and ProjectMember
groups of a project. However, the default permissions set for these groups can be modified; see below for more information.

### Updating a Permission's Group:
- `PUT: /admin/permissions/<permissionIri>/group` to change the group for which an administrative or a default object
Expand Down Expand Up @@ -183,4 +191,9 @@ updating a default object acceess permission. The IRI of the new property must b
```
Note that if the default object access permission was originally defined for a group, with this operation, the permission
will be defined for the given property instead of the group. That means the value of the `forGroup` will
be deleted.
be deleted.

### Deleting a permission:
- `DELETE: /admin/permissions/<permissionIri>` to delete an administrative, or a default object access permission. The
IRI of the permission must be given in encoded form.

18 changes: 18 additions & 0 deletions docs/03-apis/api-admin/projects.md
Expand Up @@ -100,6 +100,24 @@ Additionally, each project can have an optional custom IRI (of [Knora IRI](../ap
"selfjoin": false
}
```
#### Default set of permissions for a new project:
When a new project is created, following default permissions are added to its admins and members:
- ProjectAdmin group receives an administrative permission to do all project level operations and to create resources
within the new project. This administrative permission is retrievable through its IRI:
`http://rdfh.ch/permissions/[projectShortcode]/defaultApForAdmin`

ProjectAdmin group also gets a default object access permission to change rights, delete, modify, view,
and restricted view of any entity that belongs to the project. This default object access permission is retrievable
through its IRI:
`http://rdfh.ch/permissions/[projectShortcode]/defaultDoapForAdmin`

- ProjectMember group receives an administrative permission to create resources within the new project. This
administrative permission is retrievable through its IRI:
`http://rdfh.ch/permissions/[projectShortcode]/defaultApForMember`

ProjectMember group also gets a default object access permission to modify, view, and restricted view 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:

Expand Down
24 changes: 23 additions & 1 deletion test_data/all_data/permissions-data.ttl
Expand Up @@ -132,7 +132,7 @@
knora-base:hasPermissions "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser"^^xsd:string .


### Default Object Access Permissions on ProjectMember Group
### Default Object Access Permissions on KnownUser Group
<http://rdfh.ch/permissions/00FF/d2>

rdf:type knora-admin:DefaultObjectAccessPermission ;
Expand Down Expand Up @@ -210,6 +210,28 @@

knora-base:hasPermissions "CR knora-admin:Creator|M knora-admin:ProjectMember|V knora-admin:KnownUser|RV knora-admin:UnknownUser"^^xsd:string .

### Default Object Access Permissions on incunabula:partOfValue property
<http://rdfh.ch/permissions/0803/003-d4>

rdf:type knora-admin:DefaultObjectAccessPermission ;

knora-admin:forProject <http://rdfh.ch/projects/0803> ;

knora-admin:forProperty incunabula:partOfValue ;

knora-base:hasPermissions "V knora-admin:KnownUser|RV knora-admin:UnknownUser"^^xsd:string .


### Default Object Access Permissions on incunabula:partOfValue property
<http://rdfh.ch/permissions/0803/003-d5>

rdf:type knora-admin:DefaultObjectAccessPermission ;

knora-admin:forProject <http://rdfh.ch/projects/0803> ;
knora-admin:forResourceClass incunabula:page ;
knora-admin:forProperty incunabula:partOfValue ;

knora-base:hasPermissions "M knora-admin:ProjectMember"^^xsd:string .

##########################################################
#
Expand Down
Expand Up @@ -613,16 +613,19 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re
for {

// does the permission already exist
checkResult <- administrativePermissionForProjectGroupGetADM(
checkResult: Option[AdministrativePermissionADM] <- administrativePermissionForProjectGroupGetADM(
createRequest.forProject,
createRequest.forGroup,
requestingUser = KnoraSystemInstances.Users.SystemUser
)

_ = checkResult match {
case Some(ap) =>
case Some(ap: AdministrativePermissionADM) =>
throw DuplicateValueException(
s"Permission for project: '${createRequest.forProject}' and group: '${createRequest.forGroup}' combination already exists.")
s"An administrative permission for project: '${createRequest.forProject}' and group: '${createRequest.forGroup}' combination already exists. " +
s"This permission currently has the scope '${PermissionUtilADM
.formatPermissionADMs(ap.hasPermissions, PermissionType.AP)}'. " +
s"Use its IRI ${ap.iri} to modify it, if necessary.")
case None => ()
}

Expand Down Expand Up @@ -653,6 +656,7 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re
throw NotFoundException(s"Group '${createRequest.forGroup}' not found. Aborting request."))
} yield group.id
}

customPermissionIri: Option[SmartIri] = createRequest.id.map(iri => iri.toSmartIri)
newPermissionIri: IRI <- checkOrCreateEntityIri(customPermissionIri,
stringFormatter.makeRandomPermissionIri(project.shortcode))
Expand Down Expand Up @@ -1445,8 +1449,25 @@ class PermissionsResponderADM(responderData: ResponderData) extends Responder(re
createRequest.forProperty)

_ = checkResult match {
case Some(doap) => throw DuplicateValueException(s"Default object access permission already exists.")
case None => ()
case Some(doap: DefaultObjectAccessPermissionADM) =>
val errorMessage = if (doap.forGroup.nonEmpty) {
s"and group: '${doap.forGroup.get}' "
} else {
val resourceClassExists = if (doap.forResourceClass.nonEmpty) {
s"and resourceClass: '${doap.forResourceClass.get}' "
} else ""
val propExists = if (doap.forProperty.nonEmpty) {
s"and property: '${doap.forProperty.get}' "
} else ""
resourceClassExists + propExists
}
throw DuplicateValueException(
s"A default object access permission for project: '${createRequest.forProject}' " +
errorMessage + "combination already exists. " +
s"This permission currently has the scope '${PermissionUtilADM
.formatPermissionADMs(doap.hasPermissions, PermissionType.OAP)}'. " +
s"Use its IRI ${doap.iri} to modify it, if necessary.")
case None => ()
}

// get project
Expand Down
Expand Up @@ -31,13 +31,16 @@ import org.knora.webapi.exceptions._
import org.knora.webapi.feature.FeatureFactoryConfig
import org.knora.webapi.instrumentation.InstrumentationSupport
import org.knora.webapi.messages.IriConversions._
import org.knora.webapi.messages.StringFormatter.IriDomain
import org.knora.webapi.messages.admin.responder.projectsmessages._
import org.knora.webapi.messages.admin.responder.usersmessages.{
UserADM,
UserGetADM,
UserIdentifierADM,
UserInformationTypeADM
}
import org.knora.webapi.messages.admin.responder.permissionsmessages._

import org.knora.webapi.messages.store.cacheservicemessages.{
CacheServiceGetProjectADM,
CacheServicePutProjectADM,
Expand Down Expand Up @@ -968,6 +971,84 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo
requestingUser: UserADM,
apiRequestID: UUID): Future[ProjectOperationResponseADM] = {

/**
* Creates following permissions for the new project
* 1. Permissions for project admins to do all operations on project level and to create, modify, delete, change rights,
* view, and restricted view of all new resources and values that belong to this project.
* 2. Permissions for project members to create, modify, view and restricted view of all new resources and values that belong to this project.
*
* @param projectIri the IRI of the new project.
* @throws BadRequestException if a permission is not created.
*/
def createPermissionsForAdminsAndMembersOfNewProject(projectIri: IRI, projectShortCode: String): Future[Unit] =
for {
baseIri: String <- Future.successful(s"http://$IriDomain/permissions/$projectShortCode/")
// Give the admins of the new project rights for any operation in project level, and rights to create resources.
apPermissionForProjectAdmin: AdministrativePermissionCreateResponseADM <- (responderManager ? AdministrativePermissionCreateRequestADM(
createRequest = CreateAdministrativePermissionAPIRequestADM(
id = Some(baseIri + "defaultApForAdmin"),
forProject = projectIri,
forGroup = OntologyConstants.KnoraAdmin.ProjectAdmin,
hasPermissions =
Set(PermissionADM.ProjectAdminAllPermission, PermissionADM.ProjectResourceCreateAllPermission)
),
featureFactoryConfig = featureFactoryConfig,
requestingUser = requestingUser,
apiRequestID = UUID.randomUUID()
)).mapTo[AdministrativePermissionCreateResponseADM]

// Give the members of the new project rights to create resources.
apPermissionForProjectMember: AdministrativePermissionCreateResponseADM <- (responderManager ? AdministrativePermissionCreateRequestADM(
createRequest = CreateAdministrativePermissionAPIRequestADM(
id = Some(baseIri + "defaultApForMember"),
forProject = projectIri,
forGroup = OntologyConstants.KnoraAdmin.ProjectMember,
hasPermissions = Set(PermissionADM.ProjectResourceCreateAllPermission)
),
featureFactoryConfig = featureFactoryConfig,
requestingUser = requestingUser,
apiRequestID = UUID.randomUUID()
)).mapTo[AdministrativePermissionCreateResponseADM]

// Give the admins of the new project rights to change rights, modify, delete, view,
// and restricted view of all resources and values that belong to the project.
doapForProjectAdmin <- (responderManager ? DefaultObjectAccessPermissionCreateRequestADM(
createRequest = CreateDefaultObjectAccessPermissionAPIRequestADM(
id = Some(baseIri + "defaultDoapForAdmin"),
forProject = projectIri,
forGroup = Some(OntologyConstants.KnoraAdmin.ProjectAdmin),
hasPermissions = Set(
PermissionADM.changeRightsPermission(OntologyConstants.KnoraAdmin.ProjectAdmin),
PermissionADM.deletePermission(OntologyConstants.KnoraAdmin.ProjectAdmin),
PermissionADM.modifyPermission(OntologyConstants.KnoraAdmin.ProjectAdmin),
PermissionADM.viewPermission(OntologyConstants.KnoraAdmin.ProjectAdmin),
PermissionADM.restrictedViewPermission(OntologyConstants.KnoraAdmin.ProjectAdmin)
)
),
featureFactoryConfig = featureFactoryConfig,
requestingUser = requestingUser,
apiRequestID = UUID.randomUUID()
)).mapTo[DefaultObjectAccessPermissionCreateResponseADM]

// Give the members of the new project rights to modify, view, and restricted view of all resources and values
// that belong to the project.
doapForProjectMember <- (responderManager ? DefaultObjectAccessPermissionCreateRequestADM(
createRequest = CreateDefaultObjectAccessPermissionAPIRequestADM(
id = Some(baseIri + "defaultDoapForMember"),
forProject = projectIri,
forGroup = Some(OntologyConstants.KnoraAdmin.ProjectMember),
hasPermissions = Set(
PermissionADM.modifyPermission(OntologyConstants.KnoraAdmin.ProjectMember),
PermissionADM.viewPermission(OntologyConstants.KnoraAdmin.ProjectMember),
PermissionADM.restrictedViewPermission(OntologyConstants.KnoraAdmin.ProjectMember)
)
),
featureFactoryConfig = featureFactoryConfig,
requestingUser = requestingUser,
apiRequestID = UUID.randomUUID()
)).mapTo[DefaultObjectAccessPermissionCreateResponseADM]
} yield ()

def projectCreateTask(createProjectRequest: CreateProjectApiRequestADM,
requestingUser: UserADM): Future[ProjectOperationResponseADM] =
for {
Expand Down Expand Up @@ -1044,6 +1125,8 @@ class ProjectsResponderADM(responderData: ResponderData) extends Responder(respo
throw UpdateNotPerformedException(
s"Project $newProjectIRI was not created. Please report this as a possible bug.")
)
// create permissions for admins and members of the new group
_ <- createPermissionsForAdminsAndMembersOfNewProject(newProjectIRI, newProjectADM.shortcode)

} yield ProjectOperationResponseADM(project = newProjectADM)

Expand Down
Expand Up @@ -288,9 +288,9 @@ class PermissionsADME2ESpec extends E2ESpec(PermissionsADME2ESpec.config) with T

val permissionPayload =
s"""{
| "forGroup":"http://www.knora.org/ontology/knora-admin#ProjectMember",
| "forGroup":"http://www.knora.org/ontology/knora-admin#KnownUser",
| "forProject":"$projectIri",
| "hasPermissions":[{"additionalInformation":null,"name":"ProjectAdminGroupAllPermission","permissionCode":null}]
| "hasPermissions":[{"additionalInformation":null,"name":"ProjectResourceCreateAllPermission","permissionCode":null}]
|}""".stripMargin

val permissionRequest = Post(baseApiUrl + s"/admin/permissions/ap",
Expand Down

0 comments on commit b7c71ca

Please sign in to comment.