Skip to end of banner
Go to start of banner

FOLIO permission model

Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

Version 1 Next »

Why FOLIO needs a complex permission model?

FOLIO is a highly modular platform with the server-side modules heavily decoupled from the UI and with strong isolation from other modules. It is thus required to control authorization across modules and between modules and the UI in a highly granular and transparent way. FOLIO's middleware, Okapi, must be able to provide enforcement guarantees, so permissions must be exposed to Okapi during module registration. FOLIO clients/UI want to make decisions and choices based existence of particular permissions, before the backend perform permission enforcement. This makes permissions an integral part of the module interface, which means things like backward compatibility, API evolution and versioning must be taken into account.

On the other hand, FOLIO needs to be very flexible with respect to how permissions are grouped (which is based e.g on how the tenant's organization has been modelled into FOLIO) and associated with users. In cerain case the permission groups or "roles" must be modelled to closely match a hierarchy from an external authorization system. This is likely a converse problem/requirement to the one above.

How are FOLIO permissions structured?

FOLIO permission model is based on a recursive tree-like structure with the following elements (frequently referred to simply as permissions):

  • atomic permissions are very granular, matching API endpoints granularity or going below it, and defined in the form of simple flags like "users.item.get" or "users.item.delete".  Atomic permissions can be one of two kinds: required, which are enforced by Okapi and the authentication module in tandem and desired which are enforced by the module themselves. Okapi can provide guaranteed enforcement of required permissions so those are preferable. In some cases, however --. e.g to protect resources or operations with more granularity than the API granularity – desired permissions are the only possible solution. In practice it is possible to avoid desired permissions with careful API design. Atomic permissions are defined in the Module Descriptor for a particular module. They are leaf nodes of the permission tree.
  • immutable permission sets (IPS): are groupings of atomic permissions that include a user-friendly name. They may also recursively include other IPSes when a more complex hierarchy is needed. The main role for IPSes is to model logical permissions that make sense to the FOLIO administrator. E.g  "Can edit users" is an IPS that includes "users.item.get", "users.item.delete", "users.collection.get", "login.user.item.read", and many other atomic permissions for accessing endpoints and sub-resource related to managing users. IPS are defined in the Module Descriptor for a particular module and can include permissions defined in the same module and permissions defined in the dependent modules. IPSes are loaded to the FOLIO permission module automatically when a module is enabled for a particular tenant, they can be further grouped or directly assigned to users but cannot be modified. In the FOLIO UI IPSes are shown simply as "permissions". IPS are immediate parents of leave nodes in the permission tree.
  • mutable permission sets (MPS): are groups of permissions (IPSes) created by the admin for a particular tenant. Just like IPSes they can be recursive and include other MPSes. They are specific to a particular FOLIO tenant and can be used to model tenant's organizational structure. Some examples are "Catalogers",  "System librarians", "Student helpers", etc. They are the root of the permission tree.

Besides the ability to protect access to particular resources, atomic permissions are used to to conditionally render elements of the FOLIO UI. E.g in the Users app the "Create new user" button is shown only if the current user has the "users.item.post" permission. Since UI modules also come with Module Descriptors they can define their own specific UI-only permissions but this is done very seldom when no matching backend permission can be found or defined.

IPSes can be defined on any level in the module structure (system module, business-logic module or UI modules) but depending what operations they refer to a particular level can be a better place than the other. Currently most IPSes are defined within the BL modules.

How are FOLIO permissions enforced?

When a user logs in his/her credentials are validated and if the validation is successful and access token is generated. With the access token in hand, the client/UI requests current user permissions: IPSes or MPSes associated with his/her login credentials are expanded all the way down to atomic permissions and returned to the client/UI. This allows the client/UI to make decisions about whether or not present access to certain operations. Every request from the client/UI, made on behalf of the current user, is rerouted by Okapi to  the "authtoken" module.  The "authtoken" module check validity of the user's access token, extracts his credentials from the token and performs a lookup for associated permissions, also expanded down to the atomic permissions. With permissions retrieved "authtoken" makes an immediate decision if the user request should be allowed to be delivered down to the handler module or rejected, based on the required permissions. If the request is allowed, desired permissions are encoded into a header and attached to the request. The handler module can then perform additional authorization based on the desired permissions.

  • No labels