Versions Compared

Key

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

User permissions migration allows migrating all existing user-permission relations from mod-permissionsfor users with authentication information in Keycloak.

Info

This migration is idempotent and can be repeated multiple times providing the same results

...

Expand
titlePlantUML script
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>


start
group PermissionMigrationService.migratePermissions {
  :log that migration started;<<task>>

  group UserPermissionsLoader.loadUserPermissions {
    :load all users from Keycloak

    //users loaded in batches, size is controlled by configuration properties//;<<task>>

    :extract user ids from loaded keycloak users and store it as ""userIds""

    //Folio user id is stored as ""user_id"" attribute in KeycloakUser entity//;<<input>>

    :create an empty list with user permissions
    as ""List<UserPermission> userPermissionsData"";

    while (""userIds"" has next element) is (yes)
      :get next user id from ""users"";

      :load user permissions from ""mod-permissions"" by ""userId"" as ""userPermissions""

      //""/users/{userId}/permissions"" endpoint is used//;<<input>>

      :extract permission names from ""userPermissions"" as ""permissionNames"";<<task>>
      if (""permissionNames"" is empty) then (yes)
        :log that user has no permissions;<<output>>
      else (no)
        :remove duplicates from ""permissionNames"" and store it as ""uniquePermissionNames"";<<task>>
        :sort alphabetically ""uniquePermissionNames"" as ""sortedUniquePermissionNames"";<<task>>
        :concatenate ""sortedUniquePermissionNames"" using delimiter ""'|'"" as ""concatenatedPermissionString"";<<task>>
        :generate ""roleName"" as sha1 hash from ""concatenatedPermissionString"";<<task>>
        :create ""userPermissionsValue"" from ""userId"", ""roleName"", ""uniquePermissionNames"";<<task>>
        :add ""userPermissionsValue"" to ""userPermissionsData"" list;<<output>>
      end if

    end while (no)

    :return ""userPermissionsData"";<<output>>
  }

  group MigrationRoleCreator.createRoles {
    :extract unique role names from ""userPermissionsData"" as ""uniqueRoleNames"";<<input>>
    :create roles using ""RoleService"" batch API as ""createdRoles"";<<task>>
    if (""createdRoles"" size is equal to ""uniqueRoleNames"" size) then (yes)
      :return ""createdRoles"";<<output>>
    else (no)
      :retrieve roles by ""uniqueRoleNames""
      from ""RoleService"" as ""foundRoles"";<<output>>
      :return ""foundRoles"" as ""createdRoles"";<<output>>
    end if
  }

  group validateAndGetUserPermissionsWithRoles {
    :group ""createdRoles"" by ""roleName"" as ""createdRolesByName"";<<task>>
    :for each value in ""userPermissionsData"" retrieve role object by ""roleName"" from ""createdRolesByName"";<<task>>
    if (all roles are not found by all ""roleNames"") then (yes)
      :thrown MigrationException;<<output>>
      end
    else (no)
      :return updated ""userPermissionsData"";<<output>>
    end if
  }

  group MigrationRoleCreator.assignUsers {
    :group users by ""roleName""
    as map where key = role identifier and value is a collection of user ids

    //""userId"" and generated ""roleName"" are extracted from ""userPermissionsData"";
    :create a user-role relation for each ""Pair(userId, roleId)"" in ""UserRoleService"";

    if (errors occurred during user-role relation assignment) then (yes)
      :thrown MigrationException;<<output>>
      end
    else (no)
      :log the number of created user-role relations;<<output>>
    end if
  }

  group RolePermissionAssignor.assignPermissions {
    :define a set with visited role identifiers as ""visitedRoleIds"";<<task>>
    while (""userPermissionsData"" has next value) is (yes)
      :get next ""userPermissions"" from ""userPermissionsData"";<<input>>
      :get role id as ""roleId"" from ""userPermissions"";<<input>>
      if (""visitedRoleIds"" contains ""roleId"") then (no)
        :get list of permission names as ""permissions"" from ""userPermissions"";<<input>>
        :find capabilities as ""foundCapabilities"" by ""permissions"" using ""CapabilityService"";<<task>>
        :extract ""capabilityPermissionNames"" from ""foundCapabilities"";<<task>>
        :define ""notFoundPermissions"" as difference between ""permissions"" and ""capabilityPermissionNames"";<<input>>
        if (""foundCapabilities"" collection is not empty) then (yes)
          :assign ""foundCapabilities"" to a role with ""roleId"";<<task>>
        end if

        :find capability sets as ""foundCapabilitySets"" by ""permissions"" using ""CapabilitySetService"";<<task>>
        :extract ""capabilitySetPermissionNames"" from ""foundCapabilitySets"";<<task>>
        :define ""notFoundPermissionsInSets"" as difference between ""notFoundPermissions"" and ""capabilitySetPermissionNames"";<<input>>

        if (""foundCapabilitySets"" collection is not empty) then (yes)
          :assign ""foundCapabilitySets"" to a role with ""roleId"";<<task>>
        end if

        :log assigned permissions for role with ""roleId"";<<output>>
        if (""notFoundPermissionsInSets"" collection is not empty) then (yes)
          :[warn] log ""notFoundPermissionsInSets"" were not assigned for role with ""roleId"";<<output>>
        end if
      else (yes)
      end if
    end while
  }

  :log that migration finished;<<task>>
}

end

@enduml