
The system of separation of access rights in a web application
In this article we will go through a full cycle with you from the idea, designing a database, writing a PHP code, and final optimization. I will try to talk about everything as simple as possible. I will use PHP and Mysql for examples. At the same time I will train newcomers :).
In this article I will touch upon the following questions:
1. The idea of ACL
2. Database design
3. Database normalization
4. Code refactoring
5. Optimization of the working code The
article is a response to the Binary distribution of access rights in CMS . While the author is writing the practical part, I want to provide my version, which I have been using for quite some time.
What I will tell you now looks like an ACL .
Access rights belong to all objects to which they must be applied.
If we consider an example of a simple news page (which we will write here), then the access rights should have:
1) The main news page - global access rights, meaning "create new news", "moderate news", "view the page itself."
2) Each news - the ability to "edit the author of the news" or "do not leave comments."
The access rights system consists of:
{Group} + {Actions} or {Group} - {Actions}
Group is a set of names that are:
1) The rights of a specific user (for example, 'User1', 'User2' ...). For example, it is used for private messages to which this user has access or to allow editing only his messages on the site.
2) Groups of private pages (or user groups) to which you need to give rights to certain actions. (for example, administrators, super moderators, etc.)
3) Additional properties. (For example, a flag is a mode switch)
Action is a set of actions that users with an existing {Group} can do. In our news system you can use:
N - add a new topic
D - delete a topic
E - edit a topic
V - see a topic
C - leave a comment
B - delete comment
± means to give the user with such rights or not to give (priority) access to the action. For example: Users + VC, Users-C = Users + V.
Now let's look at an example of access rights for a simple news site:
MainNewsPage object:
Users + VC, Moderator + NEDB, Admin + NEDB NewsMessage
object:
User1 + ED (in principle, it’s not necessary if only moderators can add it)
Users-C (you can use if you don’t want to leave comments)
NewsComment:
User2 + B object (and here it is necessary, since any user can leave a comment, but not everyone can delete them)
To begin with, we define Databases, for work with the rights of objects.
Since we get a list of several rights, we can start with such a database:
RightsID - identifier of the list of rights.
Group - the name of the group.
Sign - a group sign.
Action - the name of the action.
Example1 (permissions for MainNewsPage):
Example2 (rights to NewsMessage):
Now, if we ask for SELECT * FROM `rights_action` WHERE` RightsID` = 100, we get all the rights that belong to the object we need.
The user who will view our page must have the rights that he owns. Based on them, we can know whether the user has the right to action.
For example: User2, Users, Moderator.
To do this, define the rights table:
RightsID - identifier of the list of user rights.
Group - the name of the group the user is a member of.
Example:
Now we bring both of our tables to normal form . ( wiki )
As a result, the ID keys are eliminated, and we get 3 tables:
rights_action - rights of the
RightsID object : integer (pk) - identifier of the list of rights.
GroupID: integer (pk) - the name of the group.
Sign: tinyint (1) - the sign of the group.
Action: enum (pk) - the name of the action.
rights_group - user rights
RightsID: integer (pk) - identifier of the list of user rights.
GroupID: integer (pk) - identifier of the group the user is a member of.
rights_names - GroupID names of groups
: integer (pk)- group identifier.
name - the name of the group.
We replaced the Primary key 'ID' with other keys, which in some cases consist of several fields in the table.
The sign of the group is now 0 (+) or 1 (-), because this way it will be easier for us to access them.
The GroupID identifier directly points to the name in rights_names.
In fact, the rights_names table is in our case an appendix that will not be used to identify rights to the necessary action. This table now serves only to “humanize” the results.
An example of what we got:
It has become less obvious - for a person. A computer that operates with numbers has become much easier to handle tables.
Now we can add rights to any object in the table for any action. Actions are now written to the table in the form of ENUM (field 'action'), which simplifies the understanding and development of projects. The action itself is like a string and can be called as you like.
`rights_group` should be tied to users and speaks about those rights which the user possesses.
`rights_action` should be bound to objects and says with what rights, what actions the user can perform.
For example (for our news site):
And now we will see what is necessary for these tables to be put together and we need to select the results we need.
The algorithm of our actions when checking the possibility of action:
1) We take from the database a selection of rights for the necessary object (s). (100: Users + VC, Moderator + NEDB, Admin + NEDB)
2) Select the actions we need (action). (V: Users + V)
3) Compare the access rights of the user and our selection. (Users, User1 <=> Users +)
4) If there are no results, then return false.
5) If the results are, but consist of minuses, return false. Otherwise, return true.
Another point worth noting: permissions from parent (news page) go to child (in this case, to the message). That is, if you specify on the page + 'message_view', then all messages will automatically be with such rights (read). We will use and verify this circumstance in paragraph 1 of our algorithm.
Let's move on to the implementation:
Actually, we don’t need PHP in order to make the right selection. We will do everything on mysql.
Paragraph 1.
In our case, it is necessary to read the `rightsID` data from several objects and then select them from our table.
Several objects, because we will have rights to the page and rights to individual posts on the page. The rights of both will complement each other. (child padding parent)
For example, the rights of the message on the page:
SELECT * FROM `rights_action` WHERE` RightsID` = 100 or `RightsID` = 101, where
100 is the ID of the rights of the page
101 - the ID of the rights of the message on the page
In order to make it easier to communicate in PHP, a little optimizing the syntax:
SELECT * FROM `rights_action` WHERE` RightsID` IN (100, 101)
Item 2. Choosing the
desired action is also simple:
SELECT * FROM `rights_action` WHERE` action` = 'message_view'
Item 3.
A here it is necessary to combine several SELECTs. First, select the user's access rights, and then compare them with the ones we need. If you combine these actions into one, you get:
SELECT * FROM `rights_action` WHERE` GroupID` IN (SELECT `GroupID` FROM` rights_group` WHERE `RightsID` = 1)
Clause 1-3.
Now all together in one complex query:
Item 1-5.
Paste this into the PHP implementation and at the same time add a check:
What do we need?
We will develop an example of working with our class.
1) First, you must specify the rights of the user whose rights we need to work with. And the class itself needs to be bound to a specific user.
2) Adding objects to the class of child rights with the addition of properties.
3) Check access for various actions (action).
Remarks and thoughts:
User rights can be specified when creating a class in __construct.
When adding new properties so that properties in the class are not lost, it will be necessary to make a new class (clone the old one with the addition of properties).
Let's try to implement all this:
Consider adding the right object to verify:
Now we rewrite our check function , introducing it into the class, and see what happened:
Now this class can be used like this:
$ MainPage - The page structure the user is viewing.
$ MainPage-> Messages - an array of messages that are displayed on the page.
Structures, previously, were read from the database.
We are pleased with the quality and functionality of the library, but the question of productivity arises.
The first thing that catches your eye is that with each check with a new 'action', a non-feeble SQL query occurs. Let's try to fix it.
First, let's see what does not change from request to request, and we optimize this.
Firstly, each time a SELECT query `GroupID` FROM` rights_group` WHERE `RightsID` = = .... Let's get started with it.
When declaring UserRights, we will run this query once, and the result will already be inserted into our SQL query.
It has already become easier, but still there is a search throughout the database for each request. How can we get rid of this? Most likely, create a preliminary result that depends only on action - because the selection of `RightsID` and` GroupID` remains unchanged.
When a group of objects is added, we read all the results from the database into an array, which will depend only on the values of 'action'.
Next, already sorting through each 'action' in the array, we are looking for the necessary element. At the same time, there are no more queries in the database - until the next object with new rights.
As a result of optimization, our class will look like this:
Yes you can.
1) If you consider, for example, empty groups of rights for messages (child), which will not change our already used temporary table. In this case, we can use it without creating anew. And for verification, we need to add only one more SELECT count (*) FROM `action_rights` WHERE` GroupID` = ..., which will go through the index and return the result.
2) Correctly arrange the indexes in the tables `action_rights` and` group_rights`.
I'm not sure here. Experts hope they correct me. Personally made PK - 'rightsID', 'action', 'groupID', INDEX - 'groupID', 'rightsID'
3) After creating the Temporary Table, add an index to it by 'action': ALTER TABLE `{$ this-> temptable } `ADD INDEX (` action`). True i'm not sure that this method is also effective. Experts, please devote.
:)
4) Use the cache. But that's another story :)
I think that is enough code for today. Here's how it works: a
working example - I apologize for the lack of clarity.
test.php (working example) - my libraries that work with SQL databases are used here, do not be surprised. I am sure that you will understand.
rights.php is our library.
Any new actions that you will use in your project are added to the 'action' ENUM.
If you do not want to be attached to specific actions and add them in real time, then you should replace the 'action' ENUM with an integer and create another actionID correspondence table with action_name. (as we did with the names of the Groups)
Update: There is a continuation: Optimization of the system of separation of access rights in a web application
In this article I will touch upon the following questions:
1. The idea of ACL
2. Database design
3. Database normalization
4. Code refactoring
5. Optimization of the working code The
article is a response to the Binary distribution of access rights in CMS . While the author is writing the practical part, I want to provide my version, which I have been using for quite some time.
What I will tell you now looks like an ACL .
Simplified description of the idea
Access rights belong to all objects to which they must be applied.
If we consider an example of a simple news page (which we will write here), then the access rights should have:
1) The main news page - global access rights, meaning "create new news", "moderate news", "view the page itself."
2) Each news - the ability to "edit the author of the news" or "do not leave comments."
The access rights system consists of:
{Group} + {Actions} or {Group} - {Actions}
Group is a set of names that are:
1) The rights of a specific user (for example, 'User1', 'User2' ...). For example, it is used for private messages to which this user has access or to allow editing only his messages on the site.
2) Groups of private pages (or user groups) to which you need to give rights to certain actions. (for example, administrators, super moderators, etc.)
3) Additional properties. (For example, a flag is a mode switch)
Action is a set of actions that users with an existing {Group} can do. In our news system you can use:
N - add a new topic
D - delete a topic
E - edit a topic
V - see a topic
C - leave a comment
B - delete comment
± means to give the user with such rights or not to give (priority) access to the action. For example: Users + VC, Users-C = Users + V.
Now let's look at an example of access rights for a simple news site:
MainNewsPage object:
Users + VC, Moderator + NEDB, Admin + NEDB NewsMessage
object:
User1 + ED (in principle, it’s not necessary if only moderators can add it)
Users-C (you can use if you don’t want to leave comments)
NewsComment:
User2 + B object (and here it is necessary, since any user can leave a comment, but not everyone can delete them)
Simplify the system for understanding the idea of a computer
To begin with, we define Databases, for work with the rights of objects.
Since we get a list of several rights, we can start with such a database:
RightsID - identifier of the list of rights.
Group - the name of the group.
Sign - a group sign.
Action - the name of the action.
Example1 (permissions for MainNewsPage):
ID | Rightsid | Group | Sign | Action |
---|---|---|---|---|
1 | 100 | Users | + | V |
2 | 100 | Users | + | C |
3 | 100 | Moderator | + | N |
4 | 100 | Moderator | + | E |
5 | 100 | Moderator | + | D |
6 | 100 | Moderator | + | B |
7 | 100 | Admin | + | N |
8 | 100 | Admin | + | E |
9 | 100 | Admin | + | D |
10 | 100 | Admin | + | B |
Example2 (rights to NewsMessage):
ID | Rightsid | Group | Sign | Action |
---|---|---|---|---|
eleven | 101 | User1 | + | D |
12 | 101 | User1 | + | E |
thirteen | 101 | Users | - | C |
Now, if we ask for SELECT * FROM `rights_action` WHERE` RightsID` = 100, we get all the rights that belong to the object we need.
Normalization of tables. Add user rights.
The user who will view our page must have the rights that he owns. Based on them, we can know whether the user has the right to action.
For example: User2, Users, Moderator.
To do this, define the rights table:
RightsID - identifier of the list of user rights.
Group - the name of the group the user is a member of.
Example:
ID | Rightsid | Group |
---|---|---|
1 | 10 | User1 |
2 | 10 | Users |
3 | 10 | Moderator |
As a result, the ID keys are eliminated, and we get 3 tables:
rights_action - rights of the
RightsID object : integer (pk) - identifier of the list of rights.
GroupID: integer (pk) - the name of the group.
Sign: tinyint (1) - the sign of the group.
Action: enum (pk) - the name of the action.
rights_group - user rights
RightsID: integer (pk) - identifier of the list of user rights.
GroupID: integer (pk) - identifier of the group the user is a member of.
rights_names - GroupID names of groups
: integer (pk)- group identifier.
name - the name of the group.
We replaced the Primary key 'ID' with other keys, which in some cases consist of several fields in the table.
The sign of the group is now 0 (+) or 1 (-), because this way it will be easier for us to access them.
The GroupID identifier directly points to the name in rights_names.
In fact, the rights_names table is in our case an appendix that will not be used to identify rights to the necessary action. This table now serves only to “humanize” the results.
An example of what we got:
rights_name | |||
---|---|---|---|
Groupid | name | ||
10 | Users | ||
eleven | Moderator | ||
12 | Admin | ||
1001 | User1 | ||
1002 | User2 | ||
1003 | User3 | ||
rights_group | |||
Rightsid | Groupid | ||
1 | 1001 | ||
1 | 10 | ||
1 | eleven | ||
rights_action | |||
Rightsid | Groupid | Sign | Action |
100 | 10 | 0 | message_view |
100 | 10 | 0 | comment_create |
100 | eleven | 0 | message_create |
100 | eleven | 0 | message_edit |
100 | eleven | 0 | message_delete |
100 | eleven | 0 | comment_delete |
100 | 12 | 0 | message_create |
100 | 12 | 0 | message_edit |
100 | 12 | 0 | message_delete |
100 | 12 | 0 | comment_delete |
101 | 1001 | 0 | message_edit |
101 | 1001 | 0 | message_delete |
101 | 10 | 1 | comment_create |
Now we can add rights to any object in the table for any action. Actions are now written to the table in the form of ENUM (field 'action'), which simplifies the understanding and development of projects. The action itself is like a string and can be called as you like.
`rights_group` should be tied to users and speaks about those rights which the user possesses.
`rights_action` should be bound to objects and says with what rights, what actions the user can perform.
For example (for our news site):
news_page (parameters of the main news page) | ||||
---|---|---|---|---|
PageID | Rightsid | Name | ||
1 | 100 | News Page | ||
news_message (posts on the news page) | ||||
Msgid | PageID | Rightsid | Header | Message |
1 | 1 | 101 | Hooray, we are on the main !!! | But this is only the beginning, then, when we get closer to the administration of the Habr ... |
2 | 1 | 101 | Last week news | Despite our orderly march along the main, it seems that plans have broken off ... |
Development of access rights library
And now we will see what is necessary for these tables to be put together and we need to select the results we need.
The algorithm of our actions when checking the possibility of action:
1) We take from the database a selection of rights for the necessary object (s). (100: Users + VC, Moderator + NEDB, Admin + NEDB)
2) Select the actions we need (action). (V: Users + V)
3) Compare the access rights of the user and our selection. (Users, User1 <=> Users +)
4) If there are no results, then return false.
5) If the results are, but consist of minuses, return false. Otherwise, return true.
Another point worth noting: permissions from parent (news page) go to child (in this case, to the message). That is, if you specify on the page + 'message_view', then all messages will automatically be with such rights (read). We will use and verify this circumstance in paragraph 1 of our algorithm.
Let's move on to the implementation:
Actually, we don’t need PHP in order to make the right selection. We will do everything on mysql.
Paragraph 1.
In our case, it is necessary to read the `rightsID` data from several objects and then select them from our table.
Several objects, because we will have rights to the page and rights to individual posts on the page. The rights of both will complement each other. (child padding parent)
For example, the rights of the message on the page:
SELECT * FROM `rights_action` WHERE` RightsID` = 100 or `RightsID` = 101, where
100 is the ID of the rights of the page
101 - the ID of the rights of the message on the page
In order to make it easier to communicate in PHP, a little optimizing the syntax:
SELECT * FROM `rights_action` WHERE` RightsID` IN (100, 101)
Item 2. Choosing the
desired action is also simple:
SELECT * FROM `rights_action` WHERE` action` = 'message_view'
Item 3.
A here it is necessary to combine several SELECTs. First, select the user's access rights, and then compare them with the ones we need. If you combine these actions into one, you get:
SELECT * FROM `rights_action` WHERE` GroupID` IN (SELECT `GroupID` FROM` rights_group` WHERE `RightsID` = 1)
Clause 1-3.
Now all together in one complex query:
SELECT * FROM `rights_action` WHERE` RightsID` IN (100, 101) AND `action` = 'message_view' AND` GroupID` IN (SELECT `GroupID` FROM` rights_group` WHERE `RightsID` = 1)This example takes user rights # 1, rights of objects # 100 and # 101, the action 'view messages' (message_view) and produces the result (signs + and -).
Item 1-5.
Paste this into the PHP implementation and at the same time add a check:
function check(/*array(int,int,...)*/ $obj_rights, /*integer*/ $user_rightsID, /*string*/ $action){
$result = mysql_query("SELECT * FROM `rights_action` WHERE `RightsID` IN (". implode(",",$obj_rights) .") AND `action`= '$action' AND `GroupID` IN (SELECT `GroupID` FROM `rights_group` WHERE `RightsID` = $user_rightsID)");
if (!$result)
return false;
$tmp=array();
while ($t = mysql_fetch_assoc($result)){
//В каждую из найденных групп (Users, User1, Moderator) пользователей
//заполняем + (0) или - (1) (с приоритетом).
if (!isset($tmp[$t['groupID']]))
$tmp[$t['groupID']] = $t['sign'];
else
$tmp[$t['groupID']] |= $t['sign'];
}
mysql_free_result($result);
if ($tmp)
//Если нашли + в любой из групп, возвращаем true. Иначе false.
return (array_search(0, $tmp) !== FALSE);
//Не нашли ни одного результата $tmp == false
return false;
}
* This source code was highlighted with Source Code Highlighter.
This is only the beginning, the first step.Create an access rights class
What do we need?
We will develop an example of working with our class.
1) First, you must specify the rights of the user whose rights we need to work with. And the class itself needs to be bound to a specific user.
2) Adding objects to the class of child rights with the addition of properties.
3) Check access for various actions (action).
Remarks and thoughts:
User rights can be specified when creating a class in __construct.
When adding new properties so that properties in the class are not lost, it will be necessary to make a new class (clone the old one with the addition of properties).
Let's try to implement all this:
class Rights{
private $usrID;//User rights ID
function __construct($user_rightsID){
$this->usrID=$user_rightsID;
}
}
Now you can use the construct: $UserRights = new Rights($CurrentUser->rightsID);
and use $ UserRights in a further program. Consider adding the right object to verify:
class Rights{
private $group=array();//Сначала нет никаких объектов сравнения
function include_right($grp){
$clone=clone $this;//Чтобы не запортить объект, клонируем его
$clone->group[]=$grp;//Добавляем права клону
return $clone;
}
//... constructor
}
Let me remind you that we do not need to spoil the object, because we will add rights from different messages (child) to it (parent). Now we rewrite our check function , introducing it into the class, and see what happened:
class Rights{
private $usrID;//User rights ID
private $group=array();//Сначала нет никаких объектов сравнения
function __construct($user_rightsID){
$this->usrID=$user_rightsID;
}
function include_right($grp){
$clone=clone $this;//Чтобы не запортить объект, клонируем его
$clone->group[]=$grp;//Добавляем права клону
return $clone;
}
function check($action){
$result = mysql_query("SELECT * FROM `rights_action` WHERE `RightsID` IN (". implode(",",$this->group) .") AND `action`= '$action' AND `GroupID` IN (SELECT `GroupID` FROM `rights_group` WHERE `RightsID` = ". $this->usrID .")");
if (!$result)
return false;
$tmp=array();
while ($t = mysql_fetch_assoc($result)){
//В каждую из найденных групп (Users, User1, Moderator) пользователей
//заполняем + (0) или - (1) (с приоритетом).
if (!isset($tmp[$t['groupID']]))
$tmp[$t['groupID']] = $t['sign'];
else
$tmp[$t['groupID']] |= $t['sign'];
}
mysql_free_result($result);
if ($tmp)
//Если нашли + в любой из групп, возвращаем true. Иначе false.
return (array_search(0, $tmp) !== FALSE);
//Не нашли ни одного результата $tmp == false
return false;
}
}
* This source code was highlighted with Source Code Highlighter.
What we have done now (redid the function into a class, making it more convenient to use and more universal) is called Refactoring . ( wiki ) Now this class can be used like this:
//Создаём права пользователя
$UserRights = new Rights($CurrentUser->rightsID);
//Добавляем права объекта, с которыми должен иметь дело пользователь.
$PageRights = $UserRights->include_right($MainPage->rightsID);
//Проверяем, может ли пользователь просматривать страницу?
if ($PageRights->check('messages_view')){
//Да, может. Но что делать с сообщениями?
//Пройдемся по каждому из них
foreach($MainPage->Messages as $msg){
//Добавляем к правам страницы (parent), личные права сообщения (child)
$MsgRights = $PageRights->include_right($msg->rightsID);
//И проверяем на читаемость
if ($MsgRights->check('messages_view')){
//И если оно читается, проверяем можем ли мы редактировать сообщения?
if ($MsgRights->check('messages_edit'))
$msg->editable_flag = 1;
//А удалять сообщения?
if ($MsgRights->check('messages_delete'))
$msg->delete_flag = 1;
DrawMessage($msg);
}
}
}
where $ CurrentUser is the structure of the user who is viewing the page. $ MainPage - The page structure the user is viewing.
$ MainPage-> Messages - an array of messages that are displayed on the page.
Structures, previously, were read from the database.
Optimization
We are pleased with the quality and functionality of the library, but the question of productivity arises.
The first thing that catches your eye is that with each check with a new 'action', a non-feeble SQL query occurs. Let's try to fix it.
First, let's see what does not change from request to request, and we optimize this.
SELECT * FROM `rights_action` WHERE` RightsID` IN (.implode (",", $ this-> group).) AND `action` = '$ action' AND` GroupID` IN (SELECT `GroupID` FROM` rights_group` WHERE `RightsID` =. $ This-> usrID.)
Firstly, each time a SELECT query `GroupID` FROM` rights_group` WHERE `RightsID` = = .... Let's get started with it.
When declaring UserRights, we will run this query once, and the result will already be inserted into our SQL query.
function __construct($grp){
$result=mysql_query("SELECT `group_rights`.groupID FROM `group_rights` WHERE `group_rights`.rightsID=$grp");
$this->usrID=array();
while ($t=mysql_fetch_assoc($result)){
$this->usrID[]=$t['groupID'];
}
mysql_free_result($result);
$this->usrID=implode(",",$this->usrID);
}
Now in $ this-> usrID there is a ready-made string that can be directly inserted into the query instead of the whole SELECTa. It has already become easier, but still there is a search throughout the database for each request. How can we get rid of this? Most likely, create a preliminary result that depends only on action - because the selection of `RightsID` and` GroupID` remains unchanged.
When a group of objects is added, we read all the results from the database into an array, which will depend only on the values of 'action'.
SELECT * FROM `rights_action` WHERE `RightsID` IN (...) AND `GroupID` IN (...)
Next, already sorting through each 'action' in the array, we are looking for the necessary element. At the same time, there are no more queries in the database - until the next object with new rights.
As a result of optimization, our class will look like this:
class Rights{
private $group="";
private $usrID=array();
private $temptable="";
function include_right($grp){
$clone=clone $this;
$clone->group[]=$grp;
$result=mysql_query("SELECT * FROM `action_rights` WHERE `action_rights`.groupID IN ({$this->usrID}) AND `action_rights`.rightsID IN (".implode(",",$clone->group).")");
$tmp=array();
while ($t=mysql_fetch_assoc($result)){
$tmp[]=$t;
}
mysql_free_result($result);
$clone->temptable=$tmp;
return $clone;
}
function check($action){
$tmp=array();
foreach ($this->temptable as $t){
if ($t['action']==$action){
if (!isset($tmp[$t['groupID']]))
$tmp[$t['groupID']]=$t['sign'];
else
$tmp[$t['groupID']]|=$t['sign'];
}
}
if ($tmp){
return (array_search(0,$tmp)!==FALSE);
}
return false;
}
function __construct($grp){
$result=mysql_query("SELECT `group_rights`.groupID FROM `group_rights` WHERE `group_rights`.rightsID=$grp");
$this->usrID=array();
while ($t=mysql_fetch_assoc($result)){
$this->usrID[]=$t['groupID'];
}
mysql_free_result($result);
$this->usrID=implode(",",$this->usrID);
}
}
* This source code was highlighted with Source Code Highlighter.
Is it even faster?
Yes you can.
1) If you consider, for example, empty groups of rights for messages (child), which will not change our already used temporary table. In this case, we can use it without creating anew. And for verification, we need to add only one more SELECT count (*) FROM `action_rights` WHERE` GroupID` = ..., which will go through the index and return the result.
2) Correctly arrange the indexes in the tables `action_rights` and` group_rights`.
I'm not sure here. Experts hope they correct me. Personally made PK - 'rightsID', 'action', 'groupID', INDEX - 'groupID', 'rightsID'
3) After creating the Temporary Table, add an index to it by 'action': ALTER TABLE `{$ this-> temptable } `ADD INDEX (` action`). True i'm not sure that this method is also effective. Experts, please devote.
:)
4) Use the cache. But that's another story :)
Working example
I think that is enough code for today. Here's how it works: a
working example - I apologize for the lack of clarity.
test.php (working example) - my libraries that work with SQL databases are used here, do not be surprised. I am sure that you will understand.
rights.php is our library.
Extensibility
Any new actions that you will use in your project are added to the 'action' ENUM.
If you do not want to be attached to specific actions and add them in real time, then you should replace the 'action' ENUM with an integer and create another actionID correspondence table with action_name. (as we did with the names of the Groups)
Update: There is a continuation: Optimization of the system of separation of access rights in a web application