MscmpSystPerms (mscmp_syst_perms v0.1.0)

Provides a generalized foundation for user permission system implementations.

The principle idea of this component is to organization permissions in a way that higher level components can introduce the concept of user and establish contexts of applicability while keeping a cohesion in permissioning capability. To this end this component provides the core concepts for use in any permissioning system using this ecosystem of components.

Concepts

Below are the concepts used in formulating the functional design of this component. While these concepts are generally applicable it's worth keeping in mind that any specific implementation of these concepts in an application will have its own nuance and details that will have to be understood to properly use the permissions of the system.

Rights

A Right is defined as the general ability to perform an action in the system. There are a small handful of Rights:

  • "View Right" - the ability of a user to see data. For users lacking this Right for a certain data element, the element will either be presented as an empty field or hidden altogether. The View Right must always be at least equal to or more expansive than the "Maintenance Right" defined below.

  • "Maintenance Right" - the ability to perform data maintenance specifically maintaining (editing) pre-existing data. This Right does not include the idea of being able to create new records or deleting existing records; just changing existing records.

  • "Administrative Right" - the ability to perform data administration which is the ability to create or destroy data. While it is typical for Administrative Right holders to also be granted similar Maintenance Rights, it is not implicitly so and there are some circumstances where it can be desirable for these two rights to not be equal.

  • "Operations Right" - whereas the previous Rights all concern being able to view or manipulate data, the Operations Right expresses the ability to perform system operations or processes. Such an operation might be logging into the system, scheduling a job, starting some expensive process, or launching an integration job to a third party platform. The operation or process will naturally likely be working with system data, but the user is not directly doing so and as such data related rights are not necessarily required to exercise a granted Operations Right.

Scopes

Scopes define the extent to which a granted Right applies. For example, a user being given View Rights to a document such as a Sales Order may be allowed to view all Sales Orders in the system or they may be limited to those Sales Orders for which they are designated as the "owner". Scope is the mechanism which allows the View to be fine tuned in that way.

Similar to Rights, a small number of Scopes are available for use:

  • "Deny" - the Right is not actually granted to the user but instead is explicitly denied to the user. The use of the Deny Scope will become clearer when we discuss Permissions and Permission Role Grants.

  • "Same User" - the Right is limited to the current user exercising the Right. This is the case from the section summary example can only view their own Sales Orders. The Right does not extend to data owned by other users.

  • "Same Group" - the Right is limited to data belonging to a group or organizational designation to which the user is also assigned. The details of group membership/designation will be specific to the implementation but the concept of a group based limitation of Rights is supported by this Scope.

  • "All" - the Right grant permits access to all applicable data or operations without additional limitation. From the summary example this is the case where the user can see all Sales Orders.

  • "Unused" - is a special scope which indicates that a Right is not applicable to any grants in question. The use of the Unused Scope will become clearer when we discuss Permissions and Permission Role Grants.

Permission Functional Types

Permission Functional Types provide a grouping mechanism for Permissions and Permission Roles (discussed below). The idea here is that an application may define different applicability contexts for the permissions of the system. Consider an application which supports business operations and inventory management across multiple warehouses. A permission such as one that grants an employee the right to log into the system might have a "global" reach (there's only one system) but having permission to process inventory transactions may be specific to each warehouse, a "warehouse" reach. The application could accommodate this by implementing a "Global" Permission Functional Type to include all those permissions which are global in nature and a "Warehouse" Permission Functional Type to include all permissions which are granted warehouse by warehouse.

Permission Functional Types are deeply tied to the implementation of application functionality and as such they are for the most part not end user configurable. The exception to this are the user interface representations such as the display name and description fields.

This concept is conveyed via the Msdata.SystPermFunctionalTypes struct.

Permissions

A Permission defines the specific data point, application document, or operation for which Rights may be granted. For all Rights a Permission will define the available Scopes which may be granted; this means that any given Permission may offer all Scopes or some subset of them for a given Right.

If a Permission's functional use doesn't include a specific Right, the only available Scope for the irrelevant Right will be the special "Unused" Scope. For example a Permission for logging into the system doesn't include the concept of viewing data, logging in is only an "Operation"; so the login Permission will make the "Unused" Scope the only available scope for its View Right.

Because a Permission's functional use may include multiple Rights and it may only be appropriate to grant some of the of those Rights to the user, a Permission may include the Scope of "Deny" as an available Scope. An example of this would be viewing certain documents; it might be appropriate to grant a user read only access to all Purchase Orders in the system. In this case a Purchase Order Document Permission grant to the user would use the "All" Scope for the View Right, but would set the Maintenance and Administration Rights of that permission to "Deny". Since the Purchase Order Document Permission is for all rights pertaining to Purchase Order Documents, we need that ability to selectively deny permission.

This concept is conveyed via the Msdata.SystPermFuncs struct.

Permission Roles

Permission Roles allow for the grouping of specific Permissions and the specific grants of Rights. These Permission Roles are then the assigned to system users, giving these users the collective Rights of their assigned Permission Roles. Note that Permission Roles are intended to be additive; this means that if a two Permission Roles are granted to a user, and one Role gives the user view only access to Sales Orders but the other Role gives the user both Maintenance and Administrative Rights to Sales Orders, the Role which gives Maintenance and Administrative Rights will be the effective Role for that Permission. Its the greatest of grants from each granted Permission Roles which wins.

Permission Roles may be system defined, which means they cannot be changed except for their user interface presentation elements. This component also supports arbitrarily defined user Permission Roles as user security policy dictates as appropriate.

A Permission Role is defined for a specific Permission Functional Type which becomes important to the Permission Role's child Permission Role Grants. In essence this means that a single Permission Role may not span the application defined Permission Functional Type boundaries when granting Permissions to users.

Permission Role Grants

A child of the Permission Role, a single Permission Role Grant grants a single Permission and defines the specific Scope for each of the Rights from the available Scopes defined by the Permission itself.

Only Permissions which share the same Permission Functional Type as the parent Permission Role may be granted using the Permission Role Grants. This ensures that each Permission Role functions which a cohesive application context.

Permission Role Grants assume the system defined status of their parent Permission Role. If the parent Permission Role is system defined then the Permission Role Grant may not be changed or deleted nor may new Permission Role Grant records be created for that Permission Role. User defined Permission Role parents allow editing Scope assignments to Rights and the addition and removal of Permission Role Grant records.

Naturally, only a single Permission Role Grant record for exist for any combination of Permission Role and Permission records.

A Basis for Permissions

As has been stated before, this component provides the basic concepts of what it means to have Permissions in an application and the general aspects of implementing these concepts. Notably absent is what constitutes a "user", how contexts of applicability are established, or implementations of permission checks.

The thinking in this regard is that higher level components or applications themselves will use MscmpSystPerms as a library providing basic Permissions data management while providing implementations for users, Permission Role assignment to those users in their correct contexts, and necessarily building on those next steps the actual evaluation of whether or not a user has a permission to do something in the system.

One recommendation for implementing applications is that the Permission Role assignment to users should be the exclusive mechanism for granting Permission Rights; what we mean by this is that Permission specific, one-off grants to users are to be avoided. From a user security perspective it becomes difficult to consistently manage two paths for granting authority in the system, especially if one of those paths is genuinely exceptional. One-off revocations of Permissions Rights from specific individual users is acceptable, however, because the its immediately apparent to the user when user maintenance has overlooked the removal of a one-off revocations (the user can't dont something they expect to be able to do).

Summary

Permissions

Provides the effective Permissions/Rights/Scopes for the user context identified by the selector as calculated from all effective grants and revocations.

Grants a Permission Role to the given selector.

List all explicit denials of Permissions from the identified user context.

Lists all of the Permission Role records granted to the user context identified by the selector, including the Rights/Scopes of the grants.

Revokes a previously granted Permission Role from the given selector.

Data Management

Compares two Scope values and returns a value indicating the relative expansiveness of Scope.

Creates a new user defined Permission record.

Creates a new user defined Permission Role record.

Creates a new Permission Role Grant to a user defined Permission Role.

Deletes a user defined Permission record.

Deletes user defined Permission Role records.

Deletes the Permission Role Grant records of user defined Permission Roles.

Retrieves the Permission Role record ID as found by its functional type name and Internal Name.

Updates an existing Permission record.

Updates Permission Functional Type user maintainable data.

Updates Permission Role records.

Updates the Permission Role Grant records of user defined Permission Roles.

Permissions

Link to this function

get_effective_perm_grants(selector, opts \\ [])

@spec get_effective_perm_grants(
  struct(),
  Keyword.t()
) :: {:ok, MscmpSystPerms.Types.perm_grants()} | {:error, MscmpSystError.t()}

Provides the effective Permissions/Rights/Scopes for the user context identified by the selector as calculated from all effective grants and revocations.

This function answers the question, "what rights does this user really have?"

On successful execution, a success tuple is returned including a map of the selected Permissions and the Rights/Scopes granted. Errors will result in the return of an error tuple.

Parameters

  • selector - this value is a struct which determines the specific implementation of this function to call and which contains the keys/values to use in selecting which Permission and Permission Role Grant records to retrieve. Specific details about what records are involved and how the selection return values are determine are implementation specific and will be documented on a case-by-case basis.

  • opts - a Keyword List of optional parameters which may be provided. The only general option is listed below, each specific implementation of this function may extend the available options as appropriate to the implementation.

    • permissions - a list of specific Permission names to lookup. This is usually supplied as a limiting filter; without this list the typical behavior is to return all of the permissions for a given Permission Functional Type filtered only by the selector data. Again, the details of the filtering or inclusion using this option will be implementation specific and documented for each individual implementation.
Link to this function

grant_perm_role(selector, perm_role_id)

@spec grant_perm_role(
  struct(),
  MscmpSystPerms.Types.perm_role_id()
) :: :ok | {:error, MscmpSystError.t()}

Grants a Permission Role to the given selector.

On successful execution of the grant, the function will return a simple :ok. On error, an error tuple is returned.

Parameters

  • selector - this value is a struct which determines the specific implementation of this function to call and which contains the keys/values to use as the unique identifier of the user context to which you are granting Permission Roles.

  • perm_role_id - the record ID value of the Permission Role record which you are granting to the user context identified by the selector.

Link to this function

list_perm_denials(selector, opts \\ [])

@spec list_perm_denials(
  struct(),
  Keyword.t()
) :: {:ok, [Msdata.SystPerms.t()] | []} | {:error, MscmpSystError.t()}

List all explicit denials of Permissions from the identified user context.

An assumption made by this module is that Permission Roles are granted to users as whole roles, but individual Permissions may be explicitly denied from users on a Permission by Permission basis. This function is intended to list Permission denials for a user context so that the denials may be managed.

Some user contexts may not offer explicit Permission denials. In these cases this function should simply return a success tuple containing an empty list as the value.

Parameters

  • selector - this value is a struct which determines the specific implementation of this function to call and which contains the keys/values to use in selecting which Permission denial records to retrieve. Specific details about what records are involved and how the selection return values are determine are implementation specific and will be documented on a case- by-case basis.

  • opts - a Keyword List of optional parameters which may be provided. The only general option is listed below, each specific implementation of this function may extend the available options as appropriate to the implementation.

Link to this function

list_perm_grants(selector, opts \\ [])

@spec list_perm_grants(
  struct(),
  Keyword.t()
) :: {:ok, [Msdata.SystPermRoles.t()]} | {:error, MscmpSystError.t()}

Lists all of the Permission Role records granted to the user context identified by the selector, including the Rights/Scopes of the grants.

This function facilitates understanding what roles have been granted to user and what Permissions/Rights/Scopes those roles grant to the user. This list is intended to be descriptive and not directly indicating the effective grants applied to the user. Typical uses of this function are to populate lists of Permission Role Grants for the purposes of managing user access.

Parameters

  • selector - this value is a struct which determines the specific implementation of this function to call and which contains the keys/values to use in selecting which Permission and Permission Role Grant records to retrieve. Specific details about what records are involved and how the selection return values are determine are implementation specific and will be documented on a case-by-case basis.

  • opts - a Keyword List of optional parameters which may be provided. The only general option is listed below, each specific implementation of this function may extend the available options as appropriate to the implementation.

    • include_perms - a boolean option which, when set true, will preload the Msdata.SystPermRoleGrants perm data. The default value for this option is false.
Link to this function

revoke_perm_role(selector, perm_role_id)

@spec revoke_perm_role(
  struct(),
  MscmpSystPerms.Types.perm_role_id()
) :: {:ok, :deleted | :not_found} | {:error, MscmpSystError.t()}

Revokes a previously granted Permission Role from the given selector.

On successful execution a success tuple is returned. If the grant was actually deleted this tuple will take the form {:ok, :deleted}. If the grant was not found for the user context identified by the selector then the {:ok, :not_found} tuple will be returned. Any other outcome is an error resulting in an error tuple being returned.

Parameters

  • selector - this value is a struct which determines the specific implementation of this function to call and which contains the keys/values to use as the unique identifier of the user context from which you are revoking Permission Roles.

  • perm_role_id - the record ID value of the Permission Role record which you are revoking from the user context identified by the selector.

Data Management

Link to this function

compare_scopes(test_scope, standard_scope)

@spec compare_scopes(
  MscmpSystPerms.Types.rights_scope() | String.t(),
  MscmpSystPerms.Types.rights_scope() | String.t()
) :: :eq | :gt | :lt

Compares two Scope values and returns a value indicating the relative expansiveness of Scope.

Scopes restrict, to varying degrees, how much data a user might access for a given Right. We can compare Scopes relative to how much more or less data a Scope grants to a user and that's what this function does. Scopes granting more expansive access to data are considered greater than Scopes granting data on more restrictive terms. Of course any two scopes may be equal as well.

The return value is an atom indicating whether the Scope in the first parameter position is greater than, less than, or equal to the expansiveness of Scope in the second parameter position. These return values are:

  • :eq - both the first and second Scopes are equal in terms of the expansiveness and are considered 'equal' to each other.

  • :gt - the first Scope parameter confers a greater expansiveness than the second Scope parameter and is considered 'greater than' the Scope of the second parameter.

  • :lt - the first Scope parameter confers a lesser expansiveness than the second Scope parameter and is considered 'less than' the Scope of the second parameter.

Link to this function

create_perm(perm_params)

@spec create_perm(MscmpSystPerms.Types.perm_params()) ::
  {:ok, Msdata.SystPerms.t()} | {:error, MscmpSystError.t()}

Creates a new user defined Permission record.

Upon creation this function returns a success tuple in the form {:ok, %Msdata.SystPerms{}} where the struct is the data of the newly created record. On error an error tuple is returned ({:error, %MscmpSystError{}}).

Parameters

Link to this function

create_perm_role(perm_role_params)

@spec create_perm_role(MscmpSystPerms.Types.perm_role_params()) ::
  {:ok, Msdata.SystPermRoles.t()} | {:error, MscmpSystError.t()}

Creates a new user defined Permission Role record.

On successful record creation this function returns a success tuple in the form {:ok, %Msdata.SystPermRoles{}} where the struct is the data of the created record. On error an error tuple is returned ({:error, %MscmpSystError{}}).

Parameters

Link to this function

create_perm_role_grant(perm_role_grant_params)

@spec create_perm_role_grant(MscmpSystPerms.Types.perm_role_grant_params()) ::
  {:ok, Msdata.SystPermRoleGrants.t()} | {:error, MscmpSystError.t()}

Creates a new Permission Role Grant to a user defined Permission Role.

On successful record creation this function returns a success tuple in the form {:ok, %Msdata.SystPermRoleGrants{}} where the struct is the data of the created record. On error an error tuple is returned ({:error, %MscmpSystError{}}).

New Permission Role Grant records can only be created for parent Permission Role records that are not set as being system defined.

Parameters

Link to this function

delete_perm(perm)

@spec delete_perm(Msdata.SystPerms.t() | MscmpSystPerms.Types.perm_id()) ::
  {:ok, :deleted | :not_found} | {:error, MscmpSystError.t()}

Deletes a user defined Permission record.

On successful deletion of the record this function returns a success tuple in the form {:ok, :deleted}. If the requested record is not found a success tuple in the form {:ok, :not_found} is returned. On error an error tuple is returned ({:error, %MscmpSystError{}}).

Parameters

  • perm - either a populated Msdata.SystPerms struct or the record ID value of the Permission record to delete.
Link to this function

delete_perm_role(perm_role)

@spec delete_perm_role(Msdata.SystPermRoles.t() | MscmpSystPerms.Types.perm_role_id()) ::
  {:ok, :deleted | :not_found} | {:error, MscmpSystError.t()}

Deletes user defined Permission Role records.

On successful deletion of the record this function returns a success tuple in the form {:ok, :deleted}. If the requested record is not found a success tuple in the form {:ok, :not_found} is returned. On error an error tuple is returned ({:error, %MscmpSystError{}}).

System defined Permission Role records may not be deleted using this API.

Parameters

  • perm_role - either the record ID value of the Permission Role record to delete or a populated Msdata.SystPermRoles struct representing the Permission Role record.
Link to this function

delete_perm_role_grant(perm_role_grant)

@spec delete_perm_role_grant(
  Msdata.SystPermRoleGrants.t()
  | MscmpSystPerms.Types.perm_role_grant_id()
) ::
  {:ok, :deleted | :not_found} | {:error, MscmpSystError.t()}

Deletes the Permission Role Grant records of user defined Permission Roles.

On successful deletion of the record this function returns a success tuple in the form {:ok, :deleted}. If the requested record is not found a success tuple in the form {:ok, :not_found} is returned. On error an error tuple is returned ({:error, %MscmpSystError{}}).

Permission Role Grant records belonging to System defined Permission Roles may not be deleted using this API.

Parameters

  • perm_role_grant - either the record ID value of the Permission Role Grant record to delete or a populated Msdata.SystPermRoleGrants struct representing the Permission Role Grant record.
Link to this function

get_perm_role_id_by_name(perm_func_type_name, perm_role_name)

Retrieves the Permission Role record ID as found by its functional type name and Internal Name.

The function will either return the record ID of the requested Permission Role or nil of that role was not found. If an error occurs an error tuple is returned.

Parameters

  • perm_func_type_name - the Internal Name of the Permission Functional Type to which the search should be restricted. While the Permission Role name itself is unique, specifying the Permission Functional Type serves as a check that request context is correct.

  • perm_role_name - the Internal Name value of the Permission Role to search for.

Examples

Retrieve the record ID of a Permission Role record.

iex> _perm_role_id =
...>   MscmpSystPerms.get_perm_role_id_by_name("func_type_1", "perm_role_1")

Searching for a non-existent record returns nil.

iex> MscmpSystPerms.get_perm_role_id_by_name("func_type_1", "nonexistent_role")
nil
Link to this function

update_perm(perm, perm_params)

Updates an existing Permission record.

Upon update this function returns a success tuple in the form {:ok, %Msdata.SystPerms{}} where the struct is the data of the updated record. On error an error tuple is returned ({:error, %MscmpSystError{}}).

System defined Permission records only allow the user interface display fields to be updated (display_name, user_description). User defined permissions allow a broader range of changes.

Parameters

  • perm - either a fully populated Msdata.SystPerms struct representing the before-update state of the Permission record or the record ID of the Permission record to update.

  • perm_params - a map of values which describe the desired update fields and their new values. For details see MscmpSystPerms.Types.perm_params/0.

Link to this function

update_perm_functional_type(perm_functional_type, perm_functional_type_params)

Updates Permission Functional Type user maintainable data.

This record type is usually maintained via database migrations and most changes are not possible at the application server level. However the user facing display_name and user_description fields are available for update.

On successful update, a success tuple is returned including a copy of the updated Permission Functional Type record ({:ok, <record>}). On failure an error tuple is returned indicating the reason for failure ({:error, reason}).

Parameters

Link to this function

update_perm_role(perm_role, perm_role_params)

Updates Permission Role records.

Upon update this function returns a success tuple in the form {:ok, %Msdata.SystPermRoles{}} where the struct is the data of the updated record. On error an error tuple is returned ({:error, %MscmpSystError{}}).

System defined Permission Role records only allow the user interface display fields to be updated (display_name, user_description). User defined Permission Roles allow a broader range of changes.

Parameters

  • perm_role - either the record ID of the Permission Role record to update or a populated Msdata.SystPermRoles struct representing the pre-update version of the record's data.

  • perm_role_params - a map describing the new Permission Role record's values. For more about see MscmpSystPerms.Types.perm_role_params/0.

Link to this function

update_perm_role_grant(perm_role_grant, perm_role_grant_params)

Updates the Permission Role Grant records of user defined Permission Roles.

Upon update this function returns a success tuple in the form {:ok, %Msdata.SystPermRoleGrants{}} where the struct is the data of the updated record. On error an error tuple is returned ({:error, %MscmpSystError{}}).

Permission Role Grant records which are children of system defined Permission Roles may not be updated using this API.

Parameters