I do my CMS (more precisely, what I call CMS). The project has reached the level of user rights verification. Such a system should allow:

- Check user rights to the resource
- Assign rights to the resource group
- Inheritance of rights to a child group from the parent
- Rules on the parent resource apply to all child resources.
- Inheritance of rights (example: the right to change includes the right to read)
I will try to describe my vision of such a system of rights.
Permissions
First, a bit of terminology, which I will adhere to. Permission is a concrete action. Right is the ability of the user to perform any action.
By default, the system has the following predefined list of permissions.
Name | Description |
---|
none | No rights |
read | Reading |
create | Creature |
update | Change |
delete | Deletion |
all | All right |
The permissions in the list are from lowest to highest (that is, each next permission (row) includes all previous permissions). For example, if a user is assigned the right to Create (
create ), then he has a right to read (
read ). Accordingly, permission
all - gives all rights. And the resolution of
none is the prohibition of all rights.
Resources
The resource is specified as a link
/ aaa / bbb / ccc / . At the same time, all rights specified, for example, for
/ aaa / , apply to all child resources:
/ aaa / bbb / ,
/ aaa / bbb / ccc / , etc. For resource
/ aaa / bbb / resource
/ aaa / - parent, resource
/ aaa / bbb / ccc / - child.
Groups

A group is an object in which rights are assigned (setting rules) to resources. There is ONE parent group for all groups. This group has all rights to all resources. Usually such a group is called superadmin, god. But in order to be different from other ACLs - I called such a root group
diablo (devil). So in my system, all rights will be “from the devil.” Hence the name of the article about the devil's acl

.
Each subsidiary group can only REDUCE rights. Those. if the admin group (administrator) has the create right for the / aaa / bbb / ccc / resource, then the user group cannot get the right to delete the aaa / bbb / ccc / resource - in this case the right will increase, so as a permission, delete (delete) is higher than the create (create) permission. Also, the user group cannot acquire the right to delete (delete) resources / aaa / bbb /, / aaa /, / since these are the parent resources for the resource / aaa / bbb / ccc / which means in this case, too, an increase in rights will occur . The principle of diminishing rights is needed in order to be able to give users the ability to lead groups and at the same time not to fear that they will be able to access resources that you do not want to show them, since they will not be able to add themselves the rights that they do not have.
Each group can contain only one rule for one resource (including parent resources). For example, if the group has a rule for the resource / aaa / bbb / ccc /, then there should be no other rules for the resources / aaa / bbb / ccc /, / aaa / bbb /, / aaa /, /
Tables
Tables for data storage.

')
Check function: does the user have permission to the resource
As an example, we will check the user's
create permission on the resource
/aaa/bbb/ccc/index.htmlFirst, a picture of the group tree on which I will describe examples for a better understanding of the algorithm (and myself as well). Group number 1 - a group of diablo (Devil), which has all the rights to everything. In groups 12, 13, 15, 10, rules are set for the resource we need for permissions, which are <given - which leads to a ban on the resource. In groups 3, 4, 22, rules are set for the resource we need for permissions, which are ≥ given - which reduces the level of access, but still falls under our example. Those. if in group 1 per resource is set the all permission, then in group (for example) 3 the permission of delete is set, which is still higher than the create permission specified in the example.

The algorithm works as follows:
- According to the Users table, by user ID (and by ID = 0 - guest ID), we determine the list of groups ( aGroupsUsers ) to which this user belongs. For our example, this will be a list of the following groups.

As you can see, in our example the user is defined both in group 2 and in child groups 23 and 13. Theoretically, the presence of a user in groups 23 and 13 is redundant as if the user is defined in the parent group, then it also applies to ALL child groups. However, this is practically possible (at least due to the fact that there is a user with the identifier 0 (Guest), to which each authorized user belongs). That is why I made such an example. - According to the table Permissions, we determine the list of two groups of permissions identifiers: a) which <given b) which ≥ specified. For example, for creating permission, the following lists will be formed: a) aDeny = [none, read, update] b) aAllow = [create, delete, all].
- We break a resource into components: i.e. besides the resource itself, we define all its parents. For example, for the resource /aaa/bbb/ccc/index.html we get the following list: /aaa/bbb/ccc/index.html, / aaa / bbb / ccc /, / aaa / bbb /, / aaa /, /. And according to the Resources table, we determine the identifiers of the specified list of resources ( aResources ).
- According to the Rules table and the list of identifiers from PP.3, we select the list of groups ( aGroupsRules ), in which the rules for a given resource are defined (+ parent resources). In our case, the result will be the following:

In practice, there may be rules that are specified in the child groups to which the user belongs. And they will fall into the sample. However, in our example, we will assume that there are no such groups - all the same, they have no effect on the result. - We have selected a list of groups to which the user belongs (see clause 1). Now we need to check all these groups for the presence of the required rights on the resource we have specified, taking into account the parental rights. Those. We need to check the following branches of the groups (the group branch is a list of groups from the current to the root (devil)):
- 23, 12, 6, 2, 1
- 13, 6, 2, 1
- 2, 1
- 38, 27, 17, 8, 3, 1
- 18, 9, 4, 1
- 20, 10, 4, 1
- 32, 22, 11, 5, 1
Moreover, if in one of the branches the result of the verification of rights is positive, then the others are not necessarily verified, since we already have the rights. If, when searching, we have moved to the group in which the prohibition rule has been set, then we can no longer check this branch and move on to the next one. Below is an algorithm for determining rights. The variable fAccessGroup at the end of the algorithm will be the result of user access to the resource: True / False.

In our example, the search will end on the branch "2, 1" as it has access to the resource.
I also want to draw your attention to the fact that according to this algorithm you will not be able to change the access rights of the devil - he will always have the rights to everything simply by virtue of his diablo name.
OptimizationAs you can see (if you have read the algorithm of work), we get quite a lot of samples from the database, since when checking the branch of a group, we consistently move from the child group to the parent group and the longer the branch, the more samples from the database need to be done. One of the options to increase the speed of work is to cache the search results by group. Those. if at the next check we determined that group
G has permission
P for resource
R , then we can save it in a separate table

In this case, after step 3, we can select from the list of aGroupsUsers groups and the aResources resource list data from the Cache table. If the selected data has permissions ≥ required, then the user has access to the resource. If not, then perform the checks according to the described algorithm. When checking a group branch, we take into account the information about the ban selected from CASH.
I will not describe this process in detail, since it already refers to a specific implementation, and in the article I wanted to make exactly the general description of the operation algorithm. But if you have ideas for optimization, write them in the comments.
Some notes
- Theoretically, the Permissions table is not really needed, since you can store permissions in a regular array (I plan to do just that). Instead of permission identifiers, you can write the technical name (none, read, create, update, delete, all) permissions in the rules table. However, I still indicated such a table, since it may be easier for someone to store the permissions in the table, and it may also be more understandable.
- By default, the guest group must include a user with the identifier 0 (guest). All new users should be included in the user group.
- Each module will add rules for its resources (although this probably already applies to each specific implementation case and does not apply to access rights checking).
- For those who want to write a comment in the style of “why did you invent it, because in the XXX system this is [almost] exactly implemented as well”: the implementation in the XXX system will not work for me, since I have a samopisnaya system (up to the self-written ORM) and implement I will check the rights on it. So, references to systems in which the system of rights is implemented [almost] are also welcome, but without instructions that I shouldn’t screw this system in vain.
- If someone found a problem that I overlooked - write in the comments. This is the third version of the article, the previous two were rejected because as a result of writing the article errors were found in the algorithm
- Considered the option when the devil has no rights to anything and there is an increase in rights. In this case, there is less chance of giving someone access to content that is prohibited for him. However, with this approach, if you give the user the right to assign rights to groups, then he can assign full access to himself. So in the end, after all, I decided that the devil will be omnipotent
.
List of references on a subject:
ps
If you put cons, then at least write - for what .