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
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
Data Management
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.
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
perm_params
- a map of values which describe the Permission record to create. For details seeMscmpSystPerms.Types.perm_params/0
.
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
perm_role_params
- a map describing the new Permission Role record's values. For more about seeMscmpSystPerms.Types.perm_role_params/0
.
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
perm_role_grant_params
- a map describing the new Permission Role Grant record's values. For more about seeMscmpSystPerms.Types.perm_role_grant_params/0
.
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 populatedMsdata.SystPerms
struct or the record ID value of the Permission record to delete.
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 populatedMsdata.SystPermRoles
struct representing the Permission Role record.
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 populatedMsdata.SystPermRoleGrants
struct representing the Permission Role Grant record.
get_perm_role_id_by_name(perm_func_type_name, perm_role_name)
@spec get_perm_role_id_by_name( MscmpSystPerms.Types.perm_functional_type_name(), MscmpSystPerms.Types.perm_role_name() ) :: MscmpSystPerms.Types.perm_role_id() | nil | {:error, MscmpSystError.t()}
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
update_perm(perm, perm_params)
@spec update_perm( MscmpSystPerms.Types.perm_id() | Msdata.SystPerms.t(), MscmpSystPerms.Types.perm_params() ) :: {:ok, Msdata.SystPerms.t()} | {:error, MscmpSystError.t()}
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 populatedMsdata.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 seeMscmpSystPerms.Types.perm_params/0
.
update_perm_functional_type(perm_functional_type, perm_functional_type_params)
@spec update_perm_functional_type( MscmpSystPerms.Types.perm_functional_type_id() | Msdata.SystPermFunctionalTypes.t(), MscmpSystPerms.Types.perm_functional_type_params() ) :: {:ok, Msdata.SystPermFunctionalTypes.t()} | {:error, MscmpSystError.t()}
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
perm_functional_type
- a required value which is either the populatedMsdata.SystPermFunctionalTypes
struct or ID value of the record to be updated.perm_functional_type_params
- a map of values which define the change to be made to the database. For details on which fields may be maintained seeMscmpSystPerms.Types.perm_functional_type_params/0
.
update_perm_role(perm_role, perm_role_params)
@spec update_perm_role( MscmpSystPerms.Types.perm_role_id() | Msdata.SystPermRoles.t(), MscmpSystPerms.Types.perm_role_params() ) :: {:ok, Msdata.SystPermRoles.t()} | {:error, MscmpSystError.t()}
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 populatedMsdata.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 seeMscmpSystPerms.Types.perm_role_params/0
.
update_perm_role_grant(perm_role_grant, perm_role_grant_params)
@spec update_perm_role_grant( MscmpSystPerms.Types.perm_role_grant_id() | Msdata.SystPermRoleGrants.t(), MscmpSystPerms.Types.perm_role_grant_params() ) :: {:ok, Msdata.SystPermRoleGrants.t()} | {:error, MscmpSystError.t()}
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
perm_role_grant
- either the record ID of the Permission Role Grant record to update or a populatedMsdata.SystPermRoleGrants
struct representing the pre-update version of the record's data.perm_role_grant_params
- a map describing the Permission Role Grant record values to update. For more about seeMscmpSystPerms.Types.perm_role_grant_params/0
.