/
MODROLESKC-215 Spike - Ability to rename capabilities

MODROLESKC-215 Spike - Ability to rename capabilities

Spike Overview

ID: MODROLESKC-215

Objective: Spike - How Okapi handles permission replacement and what Eureka should do to honor this properly.

Background

During a module enabling OKAPI is responsible for providing the permissions from ModuleDescriptor and the corresponding moduleId to the _tenantPermissions API provided by mod-permissions module. Determination of the appropriate actions to be taken is the responsibility of this module. Most of this will happen without explicit or special provisioning in the ModuleDescriptor. Adding, removing or updating the permissions in the permissionsSet property of ModuleDescriptor is usually sufficient. One exception to that is renaming or replacing one or more permissions / permission sets with another. This is accomplished via the replaces property on the permission object, e.g.

... "permissionSets": [ ... { "permissionName": "tags.item.manage", "displayName": "Tags - manages tag", "description": "Manages tags", "replaces": [ "tags.item.get", "tags.item.post", "tags.item.put", "tags.item.delete" ] }, ...

Scope

  • Check how permission replacement works in OKAPI

    • multiple permissions replace one old permission

    • one permission replaces multiple old permissions

  • Check how OKAPI handles cases for UI

    • Does _/self endpoint return old permissions

    • Does _/self endpoint return new permissions (without direct assignment)

  • Check how replaces works for permissions sets (permission with sub permissions)

  • Identify changes required to support replacement functionality by Eureka platform.

Research

Case 1 - Replace multiple permissions with a single permission

Inputs
  • tags.item.manage permission replaces four individual permissions:

    • tags.item.get,

    • tags.item.post,

    • tags.item.put,

    • tags.item.delete

"permissionSets": [ { "permissionName": "tags.collection.get", "displayName": "Tags - get tags collection", "description": "Get tags collection" }, { "permissionName": "tags.item.get", "displayName": "Tags - get individual tag from storage", "description": "Get individual tag" }, { "permissionName": "tags.item.post", "displayName": "Tags - create tag", "description": "Create tag" }, { "permissionName": "tags.item.put", "displayName": "Tags - modify tag", "description": "Modify tag" }, { "permissionName": "tags.item.delete", "displayName": "Tags - delete tag", "description": "Delete tag" }, { "permissionName": "tags.all", "displayName": "Tags module - all permissions", "description": "Entire set of permissions needed to use the tags module", "subPermissions": [ "tags.collection.get", "tags.item.get", "tags.item.post", "tags.item.put", "tags.item.delete" ], "visible": false } ]
"permissionSets": [ { "permissionName": "tags.collection.get", "displayName": "Tags - get tags collection", "description": "Get tags collection" }, { "permissionName": "tags.item.manage", "displayName": "Tags - manages tag", "description": "Manages tags", "replaces": [ "tags.item.get", "tags.item.post", "tags.item.put", "tags.item.delete" ] }, { "permissionName": "tags.all", "displayName": "Tags module - all permissions", "description": "Entire set of permissions needed to use the tags module", "subPermissions": [ "tags.collection.get", "tags.item.manage" ], "visible": false } ],

All four initial permissions have been assigned to a user (user id = "bd397d0d-0bfc-4e01-8c05-8ae19d92bbe4"):

"user": { "username": "dt", "id": "bd397d0d-0bfc-4e01-8c05-8ae19d92bbe4", "active": true, "proxyFor": [], "personal": { "lastName": "T", "firstName": "D", "addresses": [] }, ... }, "permissions": { "permissions": [ "notes.domain.all", "tags.item.delete", "tags.item.get", "tags.item.post", "tags.item.put" ] }, "proxiesFor": [] }
{ "id": "80c96bfd-5802-46e1-a361-3cc28396cfdb", "userId": "bd397d0d-0bfc-4e01-8c05-8ae19d92bbe4", "permissions": [ "notes.domain.all", "tags.item.delete", "tags.item.get", "tags.item.post", "tags.item.put" ], ... }

Each of initial permissions has deprecated property set to false, displayName corresponds to the one defined in the ModuleDescriptor and grantedTo refers to permission assignment. The content of tags.item.delete as an example:

{ "permissionName": "tags.item.delete", "displayName": "Tags - delete tag", "id": "f6a41cb7-14ff-478d-8851-b6395689163a", "description": "Delete tag", "tags": [], "subPermissions": [], "childOf": [ "tags.all" ], "grantedTo": [ "80c96bfd-5802-46e1-a361-3cc28396cfdb" ], "mutable": false, "visible": false, "dummy": false, "deprecated": false, "moduleName": "mod-tags", "moduleVersion": "2.2.0" }
Outputs

After the module upgrade procedure the user receives one additional permission to the list of his assigned permissions and nothing is removed

{ "user": { "username": "dt", "id": "bd397d0d-0bfc-4e01-8c05-8ae19d92bbe4", "active": true, "proxyFor": [], "personal": { "lastName": "T", "firstName": "D", "addresses": [] }, ... }, "permissions": { "permissions": [ "notes.domain.all", "tags.item.delete", "tags.item.get", "tags.item.post", "tags.item.put", "tags.item.manage" ] }, "proxiesFor": [] }

So the replacement is not actually substitutes four initial permissions but only marks them as deprecated

{ "permissions": [ { "permissionName": "tags.item.put", "displayName": "(deprecated) Tags - modify tag", "id": "b86403e6-c037-4616-9937-3ec4aad19a3f", "description": "Modify tag", "tags": [], "subPermissions": [], "childOf": [ "tags.all" ], "grantedTo": [ "80c96bfd-5802-46e1-a361-3cc28396cfdb" ], "mutable": false, "visible": false, "dummy": false, "deprecated": true, "moduleName": "mod-tags", "moduleVersion": "2.2.0" }, { "permissionName": "tags.item.delete", "displayName": "(deprecated) Tags - delete tag", "id": "f6a41cb7-14ff-478d-8851-b6395689163a", "description": "Delete tag", "tags": [], "subPermissions": [], "childOf": [ "tags.all" ], "grantedTo": [ "80c96bfd-5802-46e1-a361-3cc28396cfdb" ], "mutable": false, "visible": false, "dummy": false, "deprecated": true, "moduleName": "mod-tags", "moduleVersion": "2.2.0" }, ... { "permissionName": "tags.item.get", "displayName": "(deprecated) Tags - get individual tag from storage", "id": "691de162-d60d-404b-8d38-710ae9cbb726", "description": "Get individual tag", "tags": [], "subPermissions": [], "childOf": [ "tags.all" ], "grantedTo": [ "80c96bfd-5802-46e1-a361-3cc28396cfdb" ], "mutable": false, "visible": false, "dummy": false, "deprecated": true, "moduleName": "mod-tags", "moduleVersion": "2.2.0" }, { "permissionName": "tags.item.post", "displayName": "(deprecated) Tags - create tag", "id": "9292500e-e515-4264-867f-1afc9153720d", "description": "Create tag", "tags": [], "subPermissions": [], "childOf": [ "tags.all" ], "grantedTo": [ "80c96bfd-5802-46e1-a361-3cc28396cfdb" ], "mutable": false, "visible": false, "dummy": false, "deprecated": true, "moduleName": "mod-tags", "moduleVersion": "2.2.0" }, ... ] }

Case 2 - Replace permission set with ordinary permissions

Inputs
  • note.types.allops permission set is replaced by five existing individual permissions:

    • note.types.collection.get,

    • note.types.item.get,

    • note.types.item.post,

    • note.types.item.put,

    • note.types.item.delete

"permissionSets": [ { "permissionName": "notes.collection.get", "displayName": "Notes - get notes collection", "description": "Get notes collection" }, { "permissionName": "notes.item.get", "displayName": "Notes - get individual note from storage", "description": "Get individual note" }, { "permissionName": "notes.item.post", "displayName": "Notes - create note", "description": "Create note" }, { "permissionName": "notes.item.put", "displayName": "Notes - modify note", "description": "Modify note" }, { "permissionName": "notes.item.delete", "displayName": "Notes - delete note", "description": "Delete note" }, { "permissionName": "notes.domain.all", "displayName": "Notes - allow access to all domains", "description": "All domains" }, { "permissionName": "note.types.collection.get", "displayName": "Note types - get note types collection", "description": "Get note types collection" }, { "permissionName": "note.types.item.get", "displayName": "Note types - get individual note type from storage", "description": "Get individual note type" }, { "permissionName": "note.types.item.post", "displayName": "Note types - create note type", "description": "Create note type" }, { "permissionName": "note.types.item.put", "displayName": "Note types - modify note type", "description": "Modify note type" }, { "permissionName": "note.types.item.delete", "displayName": "Note types - delete note type", "description": "Delete note type" }, { "permissionName": "note.links.collection.put", "displayName": "Note links - update note links", "description": "Update note links" }, { "permissionName": "notes.collection.get.by.status", "displayName": "Notes - get notes collection sorted by status", "description": "Get notes collection by status and domain" }, { "permissionName": "notes.allops", "displayName": "Notes module - all CRUD permissions", "description": "Entire set of permissions needed to use the notes modules, but no domain permissions", "subPermissions": [ "notes.collection.get", "notes.item.get", "notes.item.post", "notes.item.put", "notes.item.delete", "note.links.collection.put", "notes.collection.get.by.status" ], "visible": false }, { "permissionName": "note.types.allops", "displayName": "Note types - all CRUD permissions", "description": "Entire set of permissions needed to use the note type for note module", "subPermissions": [ "note.types.item.get", "note.types.collection.get", "note.types.item.post", "note.types.item.put", "note.types.item.delete" ], "visible": false }, { "permissionName": "notes.all", "displayName": "Notes module - all permissions and all domains", "description": "Entire set of permissions needed to use the notes modules on any domain", "subPermissions": [ "notes.allops", "notes.domain.all", "note.types.allops" ], "visible": false } ]
"permissionSets": [ { "permissionName": "notes.collection.get", "displayName": "Notes - get notes collection", "description": "Get notes collection" }, { "permissionName": "notes.item.get", "displayName": "Notes - get individual note from storage", "description": "Get individual note" }, { "permissionName": "notes.item.post", "displayName": "Notes - create note", "description": "Create note" }, { "permissionName": "notes.item.put", "displayName": "Notes - modify note", "description": "Modify note" }, { "permissionName": "notes.item.delete", "displayName": "Notes - delete note", "description": "Delete note" }, { "permissionName": "notes.domain.all", "displayName": "Notes - allow access to all domains", "description": "All domains" }, { "permissionName": "note.types.collection.get", "displayName": "Note types - get note types collection", "description": "Get note types collection", "replaces": [ "note.types.allops" ] }, { "permissionName": "note.types.item.get", "displayName": "Note types - get individual note type from storage", "description": "Get individual note type", "replaces": [ "note.types.allops" ] }, { "permissionName": "note.types.item.post", "displayName": "Note types - create note type", "description": "Create note type", "replaces": [ "note.types.allops" ] }, { "permissionName": "note.types.item.put", "displayName": "Note types - modify note type", "description": "Modify note type", "replaces": [ "note.types.allops" ] }, { "permissionName": "note.types.item.delete", "displayName": "Note types - delete note type", "description": "Delete note type", "replaces": [ "note.types.allops" ] }, { "permissionName": "note.links.collection.put", "displayName": "Note links - update note links", "description": "Update note links" }, { "permissionName": "notes.collection.get.by.status", "displayName": "Notes - get notes collection sorted by status", "description": "Get notes collection by status and domain" }, { "permissionName": "notes.allops", "displayName": "Notes module - all CRUD permissions", "description": "Entire set of permissions needed to use the notes modules, but no domain permissions", "subPermissions": [ "notes.collection.get", "notes.item.get", "notes.item.post", "notes.item.put", "notes.item.delete", "note.links.collection.put", "notes.collection.get.by.status" ], "visible": false }, { "permissionName": "note.types.all", "displayName": "Note types - all CRUD permissions", "description": "Entire set of permissions needed to use the note type for note module", "subPermissions": [ "note.types.item.get", "note.types.collection.get", "note.types.item.post", "note.types.item.put", "note.types.item.delete" ], "visible": false }, { "permissionName": "notes.all", "displayName": "Notes module - all permissions and all domains", "description": "Entire set of permissions needed to use the notes modules on any domain", "subPermissions": [ "notes.allops", "notes.domain.all", "note.types.all" ], "visible": false } ],

Test user has the permission set assigned (user id = "bd397d0d-0bfc-4e01-8c05-8ae19d92bbe4"):

{ "user": { "username": "dt", "id": "bd397d0d-0bfc-4e01-8c05-8ae19d92bbe4", "active": true, "proxyFor": [], "personal": { "lastName": "T", "firstName": "D", "addresses": [] }, ... }, "permissions": { "permissions": [ "notes.domain.all", "note.types.allops", "tags.item.delete", "tags.item.get", "tags.item.post", "tags.item.put", "tags.item.manage" ] }, "proxiesFor": [] }

Each of the existing initial permissions as well as the assigned permission set have deprecated property set to false, displayName corresponds to the one defined in the ModuleDescriptor. grantedTo property of the permission set refers to user’s assignment

{ "permissions": [ ... { "permissionName": "note.types.item.get", "displayName": "Note types - get individual note type from storage", "id": "cad061e1-6f2b-44fc-a8cf-f82731d5da66", "description": "Get individual note type", "tags": [], "subPermissions": [], "childOf": [ "note.types.allops" ], "grantedTo": [], "mutable": false, "visible": false, "dummy": false, "deprecated": false, "moduleName": "mod-notes", "moduleVersion": "5.2.0" }, { "permissionName": "note.types.collection.get", "displayName": "Note types - get note types collection", "id": "767b02ce-bf55-470c-9fa6-d49d1b3c3c49", "description": "Get note types collection", "tags": [], "subPermissions": [], "childOf": [ "note.types.allops" ], "grantedTo": [], "mutable": false, "visible": false, "dummy": false, "deprecated": false, "moduleName": "mod-notes", "moduleVersion": "5.2.0" }, ... { "permissionName": "note.types.item.post", "displayName": "Note types - create note type", "id": "aabcbf9c-0f3b-4eea-92cd-0b055d9ece5c", "description": "Create note type", "tags": [], "subPermissions": [], "childOf": [ "note.types.allops" ], "grantedTo": [], "mutable": false, "visible": false, "dummy": false, "deprecated": false, "moduleName": "mod-notes", "moduleVersion": "5.2.0" }, { "permissionName": "note.types.item.put", "displayName": "Note types - modify note type", "id": "4be08ce2-9d51-4b5a-bd4c-42291e429381", "description": "Modify note type", "tags": [], "subPermissions": [], "childOf": [ "note.types.allops" ], "grantedTo": [], "mutable": false, "visible": false, "dummy": false, "deprecated": false, "moduleName": "mod-notes", "moduleVersion": "5.2.0" }, { "permissionName": "note.types.item.delete", "displayName": "Note types - delete note type", "id": "6ecfac13-374d-4ea2-99b8-44740af75971", "description": "Delete note type", "tags": [], "subPermissions": [], "childOf": [ "note.types.allops" ], "grantedTo": [], "mutable": false, "visible": false, "dummy": false, "deprecated": false, "moduleName": "mod-notes", "moduleVersion": "5.2.0" }, ... { "permissionName": "note.types.allops", "displayName": "Note types - all CRUD permissions", "id": "25feab40-c70c-4756-9520-b83ff76d3580", "description": "Entire set of permissions needed to use the note type for note module", "tags": [], "subPermissions": [ "note.types.item.get", "note.types.collection.get", "note.types.item.post", "note.types.item.put", "note.types.item.delete" ], "childOf": [ "notes.all" ], "grantedTo": [ "80c96bfd-5802-46e1-a361-3cc28396cfdb" ], "mutable": false, "visible": false, "dummy": false, "deprecated": false, "moduleName": "mod-notes", "moduleVersion": "5.2.0" }, ] }
Outputs

After the module upgrade procedure the user receives NO additional permissions to the list of his assignments permissions and nothing is removed. So although permission set has been replaced by ordinary permissions it remains assigned and what is more interesting the permissions have not been granted to the user:

{ "user": { "username": "dt", "id": "bd397d0d-0bfc-4e01-8c05-8ae19d92bbe4", "active": true, "proxyFor": [], "personal": { "lastName": "T", "firstName": "D", "addresses": [] }, ... }, "permissions": { "permissions": [ "notes.domain.all", "note.types.allops", "tags.item.delete", "tags.item.get", "tags.item.post", "tags.item.put", "tags.item.manage" ] }, "proxiesFor": [] }

The only difference in the whole permission set after upgrade is that the replaced permission set marked as deprecated, no changes in the ordinary permissions happened (they haven’t been assigned to the user):

{ "permissions": [ ... { "permissionName": "note.types.item.get", "displayName": "Note types - get individual note type from storage", "id": "cad061e1-6f2b-44fc-a8cf-f82731d5da66", "description": "Get individual note type", "tags": [], "subPermissions": [], "childOf": [ "note.types.allops", "note.types.all" ], "grantedTo": [], "mutable": false, "visible": false, "dummy": false, "deprecated": false, "moduleName": "mod-notes", "moduleVersion": "5.2.0" }, ... { "permissionName": "note.types.item.post", "displayName": "Note types - create note type", "id": "aabcbf9c-0f3b-4eea-92cd-0b055d9ece5c", "description": "Create note type", "tags": [], "subPermissions": [], "childOf": [ "note.types.allops", "note.types.all" ], "grantedTo": [], "mutable": false, "visible": false, "dummy": false, "deprecated": false, "moduleName": "mod-notes", "moduleVersion": "5.2.0" }, ... { "permissionName": "note.types.collection.get", "displayName": "Note types - get note types collection", "id": "767b02ce-bf55-470c-9fa6-d49d1b3c3c49", "description": "Get note types collection", "tags": [], "subPermissions": [], "childOf": [ "note.types.allops", "note.types.all" ], "grantedTo": [], "mutable": false, "visible": false, "dummy": false, "deprecated": false, "moduleName": "mod-notes", "moduleVersion": "5.2.0" }, { "permissionName": "note.types.item.put", "displayName": "Note types - modify note type", "id": "4be08ce2-9d51-4b5a-bd4c-42291e429381", "description": "Modify note type", "tags": [], "subPermissions": [], "childOf": [ "note.types.allops", "note.types.all" ], "grantedTo": [], "mutable": false, "visible": false, "dummy": false, "deprecated": false, "moduleName": "mod-notes", "moduleVersion": "5.2.0" }, ... { "permissionName": "note.types.item.delete", "displayName": "Note types - delete note type", "id": "6ecfac13-374d-4ea2-99b8-44740af75971", "description": "Delete note type", "tags": [], "subPermissions": [], "childOf": [ "note.types.allops", "note.types.all" ], "grantedTo": [], "mutable": false, "visible": false, "dummy": false, "deprecated": false, "moduleName": "mod-notes", "moduleVersion": "5.2.0" }, ... { "permissionName": "note.types.allops", "displayName": "(deprecated) Note types - all CRUD permissions", "id": "25feab40-c70c-4756-9520-b83ff76d3580", "description": "Entire set of permissions needed to use the note type for note module", "tags": [], "subPermissions": [ "note.types.item.get", "note.types.collection.get", "note.types.item.post", "note.types.item.put", "note.types.item.delete" ], "childOf": [ "notes.all" ], "grantedTo": [ "80c96bfd-5802-46e1-a361-3cc28396cfdb" ], "mutable": false, "visible": false, "dummy": false, "deprecated": true, "moduleName": "mod-notes", "moduleVersion": "5.2.0" }, ... ] }

Conclusion

Okapi replacement permission functionality does two things:

  • marks replaced permission / permission set as deprecated

  • adds new permission / permission set to a user if the user has been previously granted with replaced permission / permission set. Note: this doesn’t work in all cases

Okapi replacement permission functionality doesn’t do the following:

  • doesn’t physically delete replaced permission / permission set (performs soft delete), although there is an endpoint to clean-up obsolete permissions: POST /perms/purge-deprecated

  • doesn’t filter out deprecated permission / permission set from user’s /_self response

To support replacement permission functionality Eureka platform should be able to perform permission replacement as well as permission re-assignment, but since Eureka doesn’t support downgrades it makes no sense for now to keep deprecated permissions: so once a permission or a permission set is replaced it won’t appear in any response, neither response from user’s /_self endpoint nor from complete permissions list (GET /perms/permissions). Permission replacement and re-assignment in terms of Eureka means that the respective capabilities (capability sets) will be created / removed and then assigned / revoked from user or role.

Spike Status: Completed IN Progress On hold

Attachments

  • Postman collection to play with Okapi replacement functionality:

  • initial tag’s Module Descriptor:

  • tag’s Module Descriptor with replacements:

  • initial note’s Module Descriptor:

  • note’s Module Descriptor with replacements:

 

 

Related content