Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

This page describes algorithms that are used to assign capabilities to a user and role, capability sets to a user, and role.

Capability Relation Management

Role

Capability assignment process

safeCreate flag is used only internally, for outside requests a user must always assign a set of new capability ids, if not - an update operation allows the creation and delete capabilities in a single request

role-capability-assigment-20241014-152818.pngImage Removed Expand

titleRole-Capability assignment PlantUML
Code Block@startuml !theme mono !pragma useVerticalIf on skinparam conditionStyle inside skinparam Capabilities and capability sets are assigned individually

Example:

There is a capability: foo.item.view [endpoints: GET /foo/item/{id}], foo.item.create [endpoints: POST /foo/item], foo.item.update [endpoints: PUT /foo/item/{id}] and capability set foo.item.manage uniting foo.item.view, foo.item.create and foo.item.update.

Usage Scenarios

Scenario #1 (role-capability and role-capabilitySet management)

  1. User assigned capability set foo.item.manage to a role Foo management role (sampleRoleId). The following resources will be created:

  • Entities in mod-roles-keycloak database:

    • policy: [policyId, 'Policy for role: sampleRoleId']

    • roleCapabilitySet: [policyId, fooItemManageCapabilitySetId]

  • Entities in Keycloak:

    • Policy: [policyId, 'Policy for role: sampleRoleId']

    • Permission: [name: 'GET access for role: sampleRoleId to /foo/item/{id}', scope: 'GET', resource: '/foo/item/{id}', policy: policyId]

    • Permission: [name: 'POST access for role: sampleRoleId to /foo/item', scope: 'POST', resource: '/foo/item', policy: policyId]

    • Permission: [PUT access for role: sampleRoleId to /foo/item/{id}, scope: 'GET', resource: '/foo/item/{id}', policy: policyId]

  1. Then a user assigns explicitly foo.item.view capability to a role with a sampleRoleId. The following resources will be created:

  • Entities in mod-roles-keycloak database:

    • roleCapability: [policyId, fooItemViewCapabilityId]

Info

Note that keycloak entities are not affected, because permission: Permission: [name: 'GET access for role: sampleRoleId to /foo/item/{id}', scope: 'GET', resource: '/foo/item/{id}', policy: policyId] already exists

  1. Then a user removes assignment foo.item.manage from a role with a sampleRoleId. The following resources will be deleted:

  • Entities in mod-roles-keycloak database:

    • roleCapabilitySet: [policyId, fooItemManageCapabilitySetId]

  • Entities in Keycloak:

    • Permission: [name: 'POST access for role: sampleRoleId to /foo/item', scope: 'POST', resource: '/foo/item', policy: policyId]

    • Permission: [PUT access for role: sampleRoleId to /foo/item/{id}, scope: 'GET', resource: '/foo/item/{id}', policy: policyId]

Info

Not that Permission: [name: 'GET access for role: sampleRoleId to /foo/item/{id}', scope: 'GET', resource: '/foo/item/{id}', policy: policyId] is not deleted, because it was explicitly assigned to a role via relation roleCapability: [policyId, fooItemViewCapabilityId]

Info

The same process works for user-capability, user-capabilitySet link management

Capability Relation Management

Role

Capability assignment process

safeCreate flag is used only internally, for outside requests a user must always assign a set of new capability ids, if not - an update operation allows the creation and delete capabilities in a single request

role-capability-assigment-20241014-152818.pngImage Added

Expand
titleRole-Capability assignment PlantUML
Code Block
@startuml

!theme mono
!pragma useVerticalIf on
skinparam conditionStyle inside
skinparam defaultTextAlignment center
skinparam defaultFontSize 12

<style>
activityDiagram {
  activity {
    MaximumWidth 600
    backgroundColor #f0f0f0
    LineColor DimGrey
  }

  diamond {
    HorizontalAlignment center
  }

  group {
    LineColor LightGrey
    FontSize 10
  }
}
</style>

group RoleCapabilityServiceImpl {
  :get role by id;<<input>>
  :get ""roleCapabilities"" by ""roleId"";<<task>>
  :extract existing ""capabilityIds"" from ""roleCapabilities"";<<task>>
  if (not ""safeCreate"" AND ""capabilityIds"" not empty)) then (yes)
    :throw EntityExistsException;<<output>>
    end
  else (no)
    :get difference between new and existing ""capabilityIds"" sets as ""newCapabilityIds"";<<input>>
  end if

  group assignCapabilities {
    :get assigned capabilities through capability sets as ""assignedCapabilityIds"";<<input>>

    :store union of ""assignedCapabilityIds"" and ""newCapabilityIds"" as ""assignedCapabilityIds"";<<input>>

    group #LightSteelBlue CapabilityEndpointService.getByCapabilityIds {
      :get difference with ""newCapabilityIds"" and ""assignedCapabilityIds"" as ""changedIdentifiers"";<<input>>

      :get changed capability endpoints by querying capabilities by ""changedIdentifiers""
      and extracting distinct list of assigned endpoints and store it as ""changedCapabilityEndpoints"";<<task>>

      :get assigned capability endpoints by querying capabilities by ""assignedIds""
      and extracting distinct list of assigned endpoints and store it as ""assignedCapabilityEndpoints"";<<task>>

      :return subtraction ""assignedCapabilityEndpoints"" from ""changedCapabilityEndpoints"";<<output>>
    }

    :rolePermissionsService.createPermissions for ""roleId"" and ""endpoints"";<<task>>

    group #LightSteelBlue CapabilityEndpointService.getByCapabilityIds {
      if (""endpoints"" are empty) then (yes)
        :do nothing;<<output>>
      else (no)
        :get role by ""roleId"";<<input>>
        :generate policy name for ""role"";<<input>>
        :getOrCreate role policy by name
        //Policy name template:// ""Policy for role: {{roleId}}"";<<task>>

        :create permissions in Keycloak with role policy, list of endpoint and using permissionNameGenerator

        //Permission name template:// ""{{httpMethod}} access for role '{{roleId}}' to '{{path}}'"";
      end if
    }

    :generate RoleCapabilityEntity for ""newIds"" and store them as ""entities"";<<task>>
    :upsert entities to table ""role_capability"" in database;<<output>>
    :convert ""RoleCapabilityEntity"" to ""RoleCapability"" and return them as ""PageResult"";<<output>>
  }
}

@enduml

Capability remove process

...

Expand
titleRole-Capability removal process PlantUML
Code Block
@startuml

!theme mono
!pragma useVerticalIf on
skinparam conditionStyle inside
skinparam defaultTextAlignment center
skinparam defaultFontSize 12

<style>
activityDiagram {
  activity {
    MaximumWidth 600
    backgroundColor #f0f0f0
    LineColor DimGrey
  }

  diamond {
    HorizontalAlignment center
  }

  group {
    LineColor LightGrey
    FontSize 10
  }
}
</style>

group RoleCapabilityServiceImpl {
  :get existing ""roleCapabilities"" by ""roleId"";<<task>>
  :extract existing ""capabilityIds"" from ""roleCapabilities"";<<task>>

  if (""capabilityIds"" is empty) then (yes)
    :do nothing;<<output>>
    end
  else (no)
    :get intersection between ""capabilityIds"" and existing ""capabilityIds"" sets as ""deprecatedIds"";<<input>>
    :get subtraction between ""assignedCapabilityIds"" and existing ""deprecatedIds"" sets as ""assignedIds"";<<input>>
  end if

  group removeCapabilities {
    :get assigned capabilities through capability sets as ""assignedCapabilityIds"";<<input>>
    :store union of ""assignedCapabilityIds"" and ""assignedIds"" as ""assignedCapabilityIds"";<<task>>

    group #LightSteelBlue CapabilityEndpointService.getByCapabilityIds {
      :get difference with ""deprecatedIds"" and ""assignedIds"" as ""changedIdentifiers"";<<input>>

      :get changed capability endpoints by querying capabilities by ""changedIdentifiers""
      and extracting distinct list of assigned endpoints and store it as ""changedCapabilityEndpoints"";<<task>>

      :get assigned capability endpoints by querying capabilities by ""assignedIds""
      and extracting distinct list of assigned endpoints and store it as ""assignedCapabilityEndpoints"";<<task>>

      :return subtraction ""assignedCapabilityEndpoints"" from ""changedCapabilityEndpoints"";<<output>>
    }

    :rolePermissionsService.deletePermissions for ""roleId"" and ""endpoints"";<<task>>

    group #LightSteelBlue CapabilityEndpointService.getByCapabilityIds {
      if (""endpoints"" are empty) then (yes)
        :do nothing;<<output>>
      else (no)
        :get role by ""roleId"";<<input>>
        :generate policy name for ""role"";<<input>>
        :get policy by ""name"" and type == ""ROLE"";<<task>>

        :delete permissions in Keycloak with role policy, list of endpoint and using permissionNameGenerator

        //Permission name template:// ""{{httpMethod}} access for role '{{roleId}}' to '{{path}}'"";
      end if
    }


    :delete entities from table ""role_capability"" in database by ""roleId"" and ""deprecatedIds"";<<output>>
  }
}

@enduml

Capability update process

A capability update process combines capability assignment and capability remove processes, where unmodified capabilities and corresponding keycloak permissions remain intact.

CapabilitySet assignment process

...

Expand
titleCapability Set assignment process PlantUML
Code Block
@startuml

!theme mono
!pragma useVerticalIf on
skinparam conditionStyle inside
skinparam defaultTextAlignment center
skinparam defaultFontSize 12

<style>
activityDiagram {
  activity {
    MaximumWidth 600
    backgroundColor #f0f0f0
    LineColor DimGrey
  }

  diamond {
    HorizontalAlignment center
  }

  group {
    LineColor LightGrey
    FontSize 10
  }
}
</style>

group RoleCapabilityServiceImpl {
  :get role by id;<<input>>
  :get ""roleCapabilitySets"" by ""roleId"";<<task>>
  :extract existing ""capabilitySetIds"" from ""roleCapabilitySets"";<<task>>
  if (not ""safeCreate"" AND ""capabilitySetIds"" not empty)) then (yes)
    :throw EntityExistsException;<<output>>
    end
  else (no)
    :get difference between new and existing ""capabilitySetIds"" sets as ""newSetIds"";<<input>>
  end if

  group #LightSteelBlue assignCapabilitySets {
    group getChangedEndpoints {
      :retrieve ""Capability"" entities by ""roleId"" as ""directlyAssignedCapabilities"";<<input>>
      :extract endpoints from ""directlyAssignedCapabilities"" and store them as ""excludedEndpoints"";<<task>>

      group capabilityEndpointService.getByCapabilitySetIds {
        :retrieve ""Capability"" entities by ""newSetIds"" as ""changedCapabilities"";<<input>>
        :retrieve ""Capability"" entities by ""assignedSetIds"" as ""assignedCapabilities"";<<input>>

        :extract endpoints from ""changedCapabilities"" and store them as ""changedCapabilitySetEndpoints"";<<task>>
        :extract endpoints from ""assignedCapabilities"" and store them as ""assignedCapabilitySetEndpoints"";<<task>>

        :return subtraction ""assignedCapabilitySetEndpoints"" and ""excludedEndpoints"" from ""changedCapabilitySetEndpoints"" as ""List<Endpoint"";<<output>>
      }
    }

    :create keycloak permissions in ""RolePermissionsService"" for ""roleId"" and ""endpoints"";<<task>>
    :generate ""RoleCapabilitySetEntity"" for ""newSetIds"" and store them as ""entities"";<<task>>
    :upsert entities to table ""role_capability_set"" in database;<<output>>
    :convert ""RoleCapabilitySetEntity"" to ""RoleCapabilitySet"" and return them as ""PageResult"";<<output>>
  }
}

@enduml

CapabilitySet removal process

image-20241015-153200.pngImage Added

Expand
titleCapability Set removal process PlantUML
Code Block
@startuml

!theme mono
!pragma useVerticalIf on
skinparam conditionStyle inside
skinparam defaultTextAlignment center
skinparam defaultFontSize 12

<style>
activityDiagram {
  activity {

<style>
activityDiagram {
  activity {
    MaximumWidth 600
    backgroundColor #f0f0f0
    LineColor DimGrey
  }

  diamond {
    HorizontalAlignment center
  }

  group {
    LineColor LightGrey
    FontSize 10
  }
}
</style>

group RoleCapabilityServiceImpl {
  :get role by id;<<input>>
  :get ""roleCapabilities"" by ""roleId"";<<task>>
  :extract existing ""capabilityIds"" from ""roleCapabilities"";<<task>>
  if (not ""safeCreate"" AND ""capabilityIds"" not empty)) then (yes)
    :throw EntityExistsException;<<output>>
    end
  else (no)
    :get difference between new and existing ""capabilityIds"" sets as ""newCapabilityIds"";<<input>>
  end if

  group assignCapabilities {
    :get assigned capabilities through capability sets as ""assignedCapabilityIds"";<<input>>

    :store union of ""assignedCapabilityIds"" and ""newCapabilityIds"" as ""assignedCapabilityIds"";<<input>>

    group #LightSteelBlue CapabilityEndpointService.getByCapabilityIds {    MaximumWidth 600
    backgroundColor #f0f0f0
    LineColor DimGrey
  }

  diamond {
    HorizontalAlignment center
  }

  group {
    LineColor LightGrey
    FontSize 10
  }
}
</style>

group RoleCapabilityServiceImpl {
  :get role by id;<<input>>
  :get ""roleCapabilitySets"" by ""roleId"" as ""assignedRoleCapabilitySetEntities"";<<task>>
  :extract existing ""assignedCapabilitySetIds"" from ""assignedRoleCapabilitySetEntities"";<<task>>
  if (""assignedCapabilitySetIds"" is empty) then (yes)
    :do nothing;<<output>>
    end
  else (no)
    :get intersection between ""assignedCapabilitySetIds"" and given ""capabilitySetIds"" sets as ""deprecatedSetIds"";<<input>>
    :get subtraction between ""assignedCapabilityIds"" and existing ""deprecatedSetIds"" sets as ""assignedSetIds"";<<input>>
  end if

  group removeCapabilitySets {

    group #LightSteelBlue getChangedEndpoints {
      :retrieve "Capability" entities by ""roleId"" as ""directlyAssignedCapabilities"";<<input>>
      :extract endpoints from ""directlyAssignedCapabilities"" and store them as ""excludedEndpoints"";<<task>>

      group capabilityEndpointService.getByCapabilitySetIds {
        :retrieve "Capability" entities by ""deprecatedSetIds"" as ""changedCapabilities"";<<input>>
        :extract endpoints from ""changedCapabilities"" and store them as ""changedCapabilitySetEndpoints"";<<task>>

        :getretrieve difference with "Capability"newCapabilityIds"" and entities by ""assignedCapabilityIdsassignedSetIds"" as ""changedIdentifiersassignedCapabilities"";<<input>>
        :getextract changedendpoints capability endpoints by querying capabilities byfrom ""assignedCapabilities"" and store them as ""changedIdentifiersassignedCapabilitySetEndpoints"";<<task>>

      and extracting distinct:return list of assigned endpoints and store it subtraction ""assignedCapabilitySetEndpoints"" and ""excludedEndpoints"" from ""changedCapabilitySetEndpoints"" as ""changedCapabilityEndpointsList<Endpoint"";<<task>><<output>>
      }

      :getremove assignedkeycloak capabilitypermissions endpoints by querying capabilities byusing ""RolePermissionsService"" for ""roleId"" and ""assignedIdsendpoints"";<<task>>
      and:delete extractingentities distinctfrom list of assigned endpoints and store it astable ""user_capability"" by ""roleId"" and ""assignedCapabilityEndpointsdeprecatedIds"";<<task>><<output>>
    }
  }
  :return subtraction ""assignedCapabilityEndpoints"" from ""changedCapabilityEndpoints"";<<output>>
    }

    :rolePermissionsService.createPermissions for ""roleId"" and ""endpoints"";<<task>>

    group #LightSteelBlue CapabilityEndpointService.getByCapabilityIds {
      if (""endpoints"" are empty) then (yes)
        :do nothing;<<output>>
      else (no)
        :get role by ""roleId"";<<input>>
        :generate policy name for ""role"";<<input>>
        :getOrCreate role policy by name
        //Policy name template:// ""Policy for role: {{roleId}}"";<<task>>

        :create permissions in Keycloak with role policy, list of endpoint and using permissionNameGenerator

        //Permission name template:// ""{{httpMethod}} access for role '{{roleId}}' to '{{path}}'"";
      end if
    }

    :generate RoleCapabilityEntity for ""newIds"" and store them as ""entities"";<<task>>
    :upsert entities to table ""role_capability"" in database;<<output>>
    :convert ""RoleCapabilityEntity"" to ""RoleCapability"" and return them as ""PageResult"";<<output>>
  }
}

@enduml

Capability remove process

...

titleRole-Capability removal process PlantUML

...

}

@enduml

User

Capability assignment process

safeCreate flag is used only internally, for outside requests a user must always assign a set of new capability ids, if not - an update operation allows the creation and delete capabilities in a single request

...

Expand
titleUser-Capability assignment PlantUML
Code Block
@startuml

!theme mono
!pragma useVerticalIf on
skinparam conditionStyle inside
skinparam defaultTextAlignment center
skinparam defaultFontSize 12

<style>
activityDiagram {
  activity {
    MaximumWidth 600
    backgroundColor #f0f0f0
    LineColor DimGrey
  }

  diamond {
    HorizontalAlignment center
  }

  group {
    LineColor LightGrey
    FontSize 10
  }
}
</style>

group UserCapabilityServiceImpl {
  :create keycloakUser If not exists by calling ""mod-users-keycloak"" API;<<task>>
  :extract existing ""capabilityIds"" from ""userCapabilities"";<<task>>
  if (not ""safeCreate"" AND ""capabilityIds"" not empty)) then (yes)
    :throw EntityExistsException;<<output>>
    end
  else (no)
    :get difference between new and existing ""capabilityIds"" sets as ""newCapabilityIds"";<<input>>
  end if

  group assignCapabilities {
    :get assigned capabilities through capability sets as ""assignedCapabilityIds"";<<input>>

    :store union of ""assignedCapabilityIds"" and ""newCapabilityIds"" as ""assignedCapabilityIds"";<<input>>

    group #PaleGreen CapabilityEndpointService.getByCapabilityIds {
      :get difference with ""newCapabilityIds"" and ""assignedCapabilityIds"" as ""changedIdentifiers"";<<input>>

      :get changed capability endpoints by querying capabilities by ""changedIdentifiers""
      and extracting distinct list of assigned endpoints and store it as ""changedCapabilityEndpoints"";<<task>>

      :get assigned capability endpoints by querying capabilities by ""assignedIds""
      and extracting distinct list of assigned endpoints and store it as ""assignedCapabilityEndpoints"";<<task>>

      :return subtraction ""assignedCapabilityEndpoints"" from ""changedCapabilityEndpoints"";<<output>>
    }

    :userPermissionsService.createPermissions for ""userId"" and ""endpoints"";<<task>>

    group #PaleGreen CapabilityEndpointService.getByCapabilityIds {
      if (""endpoints"" are empty) then (yes)
        :do nothing;<<output>>

   end   else (no)
    :get intersection between ""capabilityIds"" and existing ""capabilityIds"" sets as :get keycloak user by Folio ""deprecatedIdsuserId"";<<input>>
    :get subtraction between ""assignedCapabilityIds"" and existing ""deprecatedIds"" sets as ""assignedIds :generate policy name for ""user"";<<input>>
  end   if   :getOrCreate groupuser policy removeCapabilitiesby {name
    :get assigned capabilities through capability//Policy sets asname template:// ""assignedCapabilityIdsPolicy for user: {{userId}}"";<<input>><<task>>

   :store union of ""assignedCapabilityIds"" and ""assignedIds"" as ""assignedCapabilityIds"";<<task>>

    group #PaleGreen CapabilityEndpointService.getByCapabilityIds {
 :create permissions in Keycloak with user policy, list of endpoint and using permissionNameGenerator

    :get difference with ""deprecatedIds"" and ""assignedIds"" as ""changedIdentifiers"";<<input>>

      :get changed capability endpoints by querying capabilities by ""changedIdentifiers""
      and extracting distinct list of assigned endpoints //Permission name template:// ""{{httpMethod}} access for user '{{userId}}' to '{{path}}'"";
      end if
    }

    :generate UserCapabilityEntity for ""newIds"" and store itthem as ""changedCapabilityEndpointsentities"";<<task>>
       :get assigned capability endpoints by querying capabilities by ""assignedIds :upsert entities to table ""user_capability"" in database;<<output>>
    and extracting distinct list of assigned endpoints and store it:convert ""UserCapabilityEntity"" to ""UserCapability"" and return them as ""assignedCapabilityEndpointsPageResult"";<<task>>

      :return subtraction ""assignedCapabilityEndpoints"" from ""changedCapabilityEndpoints"";<<output>>
    }

    :userPermissionsService.deletePermissions for ""userId"" and ""endpoints"";<<task>>

    group #PaleGreen CapabilityEndpointService.getByCapabilityIds {
      if (""endpoints"" are empty) then (yes)
        :do nothing;<<output>>
      else (no)
        :get keycloak user by ""userId"";<<input>>
        :generate policy name for ""user"";<<input>>
        :get policy by ""name"" and type == ""USER<<output>>
  }
}

@enduml

Capability removal process

...

Expand
titleCapability removal process from a user process PlantUML
Code Block
@startuml

!theme mono
!pragma useVerticalIf on
skinparam conditionStyle inside
skinparam defaultTextAlignment center
skinparam defaultFontSize 12

<style>
activityDiagram {
  activity {
    MaximumWidth 600
    backgroundColor #f0f0f0
    LineColor DimGrey
  }

  diamond {
    HorizontalAlignment center
  }

  group {
    LineColor LightGrey
    FontSize 10
  }
}
</style>

group UserCapabilityServiceImpl {
  :get existing ""userCapabilities"" by ""userId"";<<task>>
  :extract existing ""capabilityIds"" from ""userCapabilities"";<<task>>

  if (""capabilityIds"" is empty) then (yes)
:delete permissions in Keycloak with user policy, list of endpoint and using permissionNameGenerator

:do nothing;<<output>>
    end
  else (no)
    :get intersection  //Permission name template:// ""{{httpMethod}} access for user '{{userId}}' to '{{path}}'"";between ""capabilityIds"" and existing ""capabilityIds"" sets as ""deprecatedIds"";<<input>>
    :get subtraction end if
    }
between ""assignedCapabilityIds"" and existing ""deprecatedIds"" sets as ""assignedIds"";<<input>>
  end  if
:delete
entities from table ""user_capability"" in database by ""userId"" and ""deprecatedIds"";<<output>>
  }
}

@enduml

Capability update process

A capability update process is a combination of capability assignment and capability remove processes, where unmodified capabilities and corresponding keycloak permissions remain intact.

User

Capability assignment process

safeCreate flag is used only internally, for outside requests a user must always assign a set of new capability ids, if not - an update operation allows the creation and delete capabilities in a single request

...

Expand
titleUser-Capability assignment PlantUML
Code Block
@startuml

!theme mono
!pragma useVerticalIf on
skinparam conditionStyle inside
skinparam defaultTextAlignment center
skinparam defaultFontSize 12

<style>
activityDiagram {
  activity {
    MaximumWidth 600
    backgroundColor #f0f0f0
    LineColor DimGrey
  }

  diamond {
    HorizontalAlignment center
  }

  group {
    LineColor LightGrey
    FontSize 10
  }
}
</style>

group UserCapabilityServiceImpl {
  :create keycloakUser If not exists by calling ""mod-users-keycloak"" API;<<task>>
  :extract existing ""capabilityIds"" from ""userCapabilities"";<<task>>
  if (not ""safeCreate"" AND ""capabilityIds"" not empty)) then (yes)
    :throw EntityExistsException;<<output>>
    end
   group removeCapabilities {
    :get assigned capabilities through capability sets as ""assignedCapabilityIds"";<<input>>
    :store union of ""assignedCapabilityIds"" and ""assignedIds"" as ""assignedCapabilityIds"";<<task>>

    group #PaleGreen CapabilityEndpointService.getByCapabilityIds {
      :get difference with ""deprecatedIds"" and ""assignedIds"" as ""changedIdentifiers"";<<input>>

      :get changed capability endpoints by querying capabilities by ""changedIdentifiers""
      and extracting distinct list of assigned endpoints and store it as ""changedCapabilityEndpoints"";<<task>>

      :get assigned capability endpoints by querying capabilities by ""assignedIds""
      and extracting distinct list of assigned endpoints and store it as ""assignedCapabilityEndpoints"";<<task>>

      :return subtraction ""assignedCapabilityEndpoints"" from ""changedCapabilityEndpoints"";<<output>>
    }

    :userPermissionsService.deletePermissions for ""userId"" and ""endpoints"";<<task>>

    group #PaleGreen CapabilityEndpointService.getByCapabilityIds {
      if (""endpoints"" are empty) then (yes)
        :do nothing;<<output>>
      else (no)
    :get difference between new and:get existing ""capabilityIds"" sets as ""newCapabilityIdskeycloak user by ""userId"";<<input>>
   end if

  group assignCapabilities {     :generate policy name for ""user"";<<input>>
        :get assignedpolicy capabilities through capability sets asby ""name"" and type == ""assignedCapabilityIdsUSER"";<<input>><<task>>

        :storedelete permissions unionin of ""assignedCapabilityIds"" and ""newCapabilityIds"" as ""assignedCapabilityIds"";<<input>>

    group #PaleGreen CapabilityEndpointService.getByCapabilityIds {Keycloak with user policy, list of endpoint and using permissionNameGenerator

        //Permission name template:get difference with ""newCapabilityIds"" and ""assignedCapabilityIds"" as ""changedIdentifiers"";<<input>>// ""{{httpMethod}} access for user '{{userId}}' to '{{path}}'"";
      end :getif
changed capability endpoints by querying}
capabilities
by
""changedIdentifiers""    :delete entities from and extracting distinct list of assigned endpoints and store it as ""changedCapabilityEndpoints"";<<task>>

      :get assigned capability endpoints by querying capabilities by ""assignedIds""
      and extracting distinct list of assigned endpoints and store it as ""assignedCapabilityEndpoints"";<<task>>

      :return subtraction ""assignedCapabilityEndpoints"" from ""changedCapabilityEndpoints"";<<output>>
    }

    :userPermissionsService.createPermissions for ""userId"" and ""endpoints"";<<task>>

    group #PaleGreen CapabilityEndpointService.getByCapabilityIds {
      if (""endpoints"" are empty) then (yes)
        :do nothing;<<output>>
      else (no)
        :get keycloak user by Folio ""userId"";<<input>>
        :generate policy name for ""user"";<<input>>
        :getOrCreate user policy by name
        //Policy name template:// ""Policy for user: {{userId}}"";<<task>>

        :create permissions in Keycloak with user policy, list of endpoint and using permissionNameGenerator

        //Permission name template:// ""{{httpMethod}} access for user '{{userId}}' to '{{path}}'"";
      end if
    }

    :generate UserCapabilityEntity for ""newIdstable ""user_capability"" in database by ""userId"" and ""deprecatedIds"";<<output>>
  }
}

@enduml

Capability update process

A capability update process combines capability assignment and capability remove processes, where unmodified capabilities and corresponding keycloak permissions remain intact.

CapabilitySet assignment process

...

Expand
titleCapability Set assignment process PlantUML
Code Block
@startuml

!theme mono
!pragma useVerticalIf on
skinparam conditionStyle inside
skinparam defaultTextAlignment center
skinparam defaultFontSize 12

<style>
activityDiagram {
  activity {
    MaximumWidth 600
    backgroundColor #f0f0f0
    LineColor DimGrey
  }

  diamond {
    HorizontalAlignment center
  }

  group {
    LineColor LightGrey
    FontSize 10
  }
}
</style>

group UserCapabilityServiceImpl {
  :create keycloakUser If not exists by calling ""mod-users-keycloak"" API;<<task>>
  :get ""userCapabilitySets"" by ""userId"";<<task>>
  :extract existing ""capabilitySetIds"" from ""userCapabilitySets"";<<task>>
  if (not ""safeCreate"" AND ""capabilitySetIds"" not empty)) then (yes)
    :throw EntityExistsException;<<output>>
    end
  else (no)
    :get difference between new and existing ""capabilitySetIds"" sets as ""newSetIds"";<<input>>
  end if

  group #PaleGreen assignCapabilitySets {
    group getChangedEndpoints {
      :retrieve ""Capability"" entities by ""userId"" as ""directlyAssignedCapabilities"";<<input>>
      :extract endpoints from ""directlyAssignedCapabilities"" and store them as ""entitiesexcludedEndpoints"";<<task>>

      group capabilityEndpointService.getByCapabilitySetIds {
     :upsert entities to table:retrieve ""user_capabilityCapability"" inentities database;<<output>>
    :convert by ""UserCapabilityEntitynewSetIds"" toas ""UserCapabilitychangedCapabilities"" and return them as;<<input>>
        :retrieve ""PageResultCapability"";<<output>> entities by }
}

@enduml

Capability removal process

...

Expand
titleCapability removal process from a user process PlantUML
Code Block
@startuml

!theme mono
!pragma useVerticalIf on
skinparam conditionStyle inside
skinparam defaultTextAlignment center
skinparam defaultFontSize 12

<style>
activityDiagram {
  activity {
    MaximumWidth 600
    backgroundColor #f0f0f0
    LineColor DimGrey
  }

  diamond {
    HorizontalAlignment center""assignedSetIds"" as ""assignedCapabilities"";<<input>>

        :extract endpoints from ""changedCapabilities"" and store them as ""changedCapabilitySetEndpoints"";<<task>>
        :extract endpoints from ""assignedCapabilities"" and store them as ""assignedCapabilitySetEndpoints"";<<task>>

        :return subtraction ""assignedCapabilitySetEndpoints"" and ""excludedEndpoints"" from ""changedCapabilitySetEndpoints"" as ""List<Endpoint"";<<output>>
      }
   group {}

   LineColor LightGrey:create keycloak permissions in ""UserPermissionsService"" FontSize 10
  }
}
</style>

group UserCapabilityServiceImpl {for ""userId"" and ""endpoints"";<<task>>
    :get existinggenerate ""UserCapabilitySetEntity"" for ""userCapabilitiesnewSetIds"" byand store them as ""userIdentities"";<<task>>
  :extract existing  :upsert entities to table ""capabilityIdsuser_capability_set"" from ""userCapabilities"";<<task>>in database;<<output>>
    if (not:convert ""UserCapabilitySetEntity"" to ""safeCreateUserCapabilitySet"" ANDand return them as ""capabilityIdsPageResult"";<<output>>
not empty)) then}
(yes)
    :do nothing;<<output>>
    end
  else (no)
    :get intersection between ""capabilityIds"" and existing ""capabilityIds"" sets as ""deprecatedIds"";<<input>>
    :get subtraction between ""assignedCapabilityIds"" and existing ""deprecatedIds"" sets as ""assignedIds"";<<input>>
  end if

  group removeCapabilities}

@enduml

CapabilitySet removal process

image-20241015-153016.pngImage Added

Expand
titleCapabilitySet removal process PlantUML
Code Block
@startuml

!theme mono
!pragma useVerticalIf on
skinparam conditionStyle inside
skinparam defaultTextAlignment center
skinparam defaultFontSize 12

<style>
activityDiagram {
  activity {
    MaximumWidth 600
    backgroundColor #f0f0f0
    LineColor DimGrey
  }

  diamond {
    

...

HorizontalAlignment center
  }

  group {
    LineColor 

...

LightGrey

...

 

...

 

...

 

...

 

...

FontSize 10
  }
}
</style>

group 

...

UserCapabilityServiceImpl {
  :get keycloak user by Folio ""userId"";<<input>>
  :get 

...

""

...

userCapabilitySets"" 

...

by ""

...

userId"" as ""

...

assignedUserCapabilitySetEntities"";

...

<<task>>
  :extract existing ""assignedCapabilitySetIds"" from ""assignedUserCapabilitySetEntities"";<<task>>
  if (""assignedCapabilitySetIds"" is empty) then (yes)
    :do nothing;<<output>>
    end
  else (no)
    :get 

...

intersection 

...

between ""assignedCapabilitySetIds"" and given ""

...

capabilitySetIds"" sets as ""deprecatedSetIds"";<<input>>
   

...

 

...

:get 

...

subtraction 

...

between ""assignedCapabilityIds"" and existing ""deprecatedSetIds"" sets as ""

...

assignedSetIds"";

...

<<input>>
  end if

  group removeCapabilitySets {

 

...

 

...

 

...

 

...

group #PaleGreen getChangedEndpoints {
  

...

    :retrieve 

...

"Capability" entities by ""userId"" 

...

as ""

...

directlyAssignedCapabilities"";

...

<<input>>
     

...

 

...

:extract endpoints from ""directlyAssignedCapabilities"" and store them as ""excludedEndpoints"";<<task>>

      

...

group capabilityEndpointService.getByCapabilitySetIds {
        :

...

retrieve 

...

"Capability" 

...

entities by ""deprecatedSetIds"" as ""

...

changedCapabilities"";<<input>>
        :

...

extract endpoints from ""changedCapabilities"" and store them as ""

...

changedCapabilitySetEndpoints"";

...

<<task>>

        :

...

retrieve "Capability" entities by ""

...

assignedSetIds"" 

...

as ""

...

assignedCapabilities"";

...

<<input>>
        :extract 

...

endpoints 

...

from ""assignedCapabilities"" and store them as ""assignedCapabilitySetEndpoints"";<<task>>

        :return subtraction ""assignedCapabilitySetEndpoints"" and 

...

""excludedEndpoints"" from ""changedCapabilitySetEndpoints"" as ""List<Endpoint"";<<output>>
      }

     

...

 

...

:remove keycloak permissions using 

...

""UserPermissionsService"" for ""userId"" and ""endpoints"";<<task>>
      :delete entities from table ""user_capability""

...

 

...

by ""userId"" and ""deprecatedIds"";<<output>>
    }
  }
}

@enduml

...

Capability update process

A capability update process is a combination of capability assignment and capability remove processes, where unmodified capabilities and corresponding keycloak permissions remain intact.