Storing application permissions in a database - php

I'm developing an application for our company that eventually will have lots of ways of restricting users to particular sections/modules. While the application is still small, I'd like to move to a new method of storing permissions that, as the application grows, will remain easy to maintain and query.
Currently in our MySQL database we have a table called "user" which stores the user's ID, username and password. In a separate table called "user_acl" is the following:
user_acl_id
acl_root
acl_news_read
acl_news_write
acl_news_modify
acl_reports_read
acl_reports_write
acl_reports_modify
acl_users_read
acl_users_write
acl_users_modify
We only have 3 modules at the minute, but over time more will be created and permissions for each will need to be added.
Rather than create a column for each permission, is there any other way or storing this information?

I would do it this way.
table name: permission
columns: id, permission_name
and then I can assign multiple permissions to the user using a many to many relationship table
table name: user_permission
columns: permission_id, user_id
This design will allow me to add as many permission as I want, and assign it to as many user as i want.
While the above design go with your requirement, I have my own method of implementing ACL in my application. I am posting it here.
My method of implementation of ACL goes like this:
User will be assigned a role (Admin, guest, staff, public)
A role will have one or many permissions assigned to them (user_write, user_modify, report_read) etc.
Permission for the User will be inherited from the role to which he/she is
User can be assigned with manual permission apart from the permission inherited from role.
To do this I have come up with the following database design.
role
I store the role name here
+----------+
| Field |
+----------+
| id |
| role_name |
+----------+
permission:
I store the permission name and key here
Permission name is for displaying to user.
Permission key is for determining the permission.
+----------------+
| Field |
+----------------+
| id |
| permission_name |
| permission_key |
+----------------+
role_permission
I assign permission to role here
+---------------+
| Field |
+---------------+
| id |
| role_id |
| permission_id |
+---------------+
user_role
I assign role to the user here
+---------------+
| Field |
+---------------+
| id |
| user_id |
| role_id |
+---------------+
user_permission
I store the manual permission I may allow for the user here
+---------------+
| Field |
+---------------+
| id |
| user_id |
| permission_id |
+---------------+
This gives me more control over the ACL. I can allow superadmins to assign permission by themselves, and so on. As I said this is just to give you the idea.

Like Ibrahim says, create a new table specifically for your permissions. Assign a numerical value to a user which represents their permission level, say 1 = read, 2= write/read, 3 = modify/write/read. Then in your code, check for proper permission level before allowing a user to perform a specific task. If they don't have the required value (3 to modify or >=2 to write) then you block that ability.

In a very famous MySQL performance book High Performance MySQL, the author specifically mentioned ACL as where data type like SET could be used. They use following example to demonstrate such use cases:
CREATE TABLE acl (
permissions SET('CAN_READ', 'CAN_WRITE', 'CAN_DELETE') NOT NULL
);
INSERT INTO acl VALUES ('CAN_READ,CAN_WRITE');
SELECT permissions FROM acl WHERE find_in_set('CAN_READ', permissions);
The problem is that, mentioned in the book as well, you can't modify the permissions set easily (you have to use ALTER TABLE), neither can you declare a column typed SET as an index, which may causes performance issue.
You can also use something like TINYINT to "wrap" the ACL list, the cons is that it's harder to read as well as to code SELECT sentence.

I think you should have five tables:
user
user_x_profile
profile
profile_x_function
function
You set up various "generic" profiles "viewer", "employee", "manager" etc.
You set up a "function" entry for each object you want to control.
Then link the functions to the profiles in profile_x_function.
Then assign one or more profiles to each user.
This cuts the administration effort. Say you want to add another function that only "managers" can use -- you just add a new entry in the function table then add an entry in the "profile_x_function" table which links to the "manager" profile the permission to the manager profile and its available to all managers.
To query access you will need a five table join, but you are only selecting one permision attribute.

Related

Create a lookup table with Doctrine2

I have recently installed Doctrine2 on my web server and all works great. I have setup all my entities but now I'm at the point where I want to create a lookup table and I'm a bit confused how to do that. I want a table which looks like this:
tbl_Role tbl_User
+--------+----------+ +--------+----------+--------------+----------+
| id | Name | | id | Name | Password | Role |
+--------+----------+ +--------+----------+--------------+----------+
| 1 | Admin |
| 2 | User |
| 3 | Free |
+--------+----------+
Between the Role and the User table consists a OneToMany relation (one Role has many Users) and this table does not change in future. I do not want to use Enums because of the update or reorder problem with them (if nevertheless something changed)
My question now is how I can represent this lookup table as a Doctrine entity? Normally if I want to create a new User I have to get the fitting Role from the DB and set it as a reference in the User entity. This seems a bit expensive to me. It would be great if I could have only the Role IDs in static PHP fields so I can set them as reference for the User. Is this possible?
Another problem is how I get the lookup values into the database. Can I implement a method into the Role entity itself which is executed after the Role table is created? This way it would be possible to mark the constructor as private and all values are present after a migration.
First of all, it won't be too expensive, because Doctrine2 has a few cache layers and these queries will be efficiently cached since the Role table won't change.
Anyway, if Role table won't change, I'm not sure if it should be kept in database. You could create a "static" factory service for it to create simple value objects as a part of your domain layer.

Multi-Table User Authentication

I am currently developing a Student Information System that is going to be used by educational group to provide students & teachers a portal.
I am using Laravel4 which has a good authentication driver built in but can use only one table for authentication. I am unable to figure out how to authenticate them because I have users in multiple tables. Example :
SchoolOne_students
SchoolOne_teachers
SchoolTwo_students
SchoolTwo_teachers
and so on....
A significantly better way, rather than multiple users tables, would be to link each user to their school and status (teacher or student). You certainly can twist Laravel into doing what you want, but in order to prevent conflicts between the four different user types, you'll probably end up having to rewrite a large part of the entire authentication package.
What I would do is, in your users table, have an ENUM column, with the options student and teacher. Then, have an integer column, school_id (with a separate table for school data, if needed). This will allow much more flexibility, and when your design requirements change (yes, they will change), you'll be able to take them in stride.
Using this method, you should not have to modify any of Laravel's own code.
if all the tables have identical schemas, you could create a view that does a UNION of all the tables and then use that view as your Laravel auth table. I am not saying this is an ideal solution, but if you have restrictions on altering the existing tables and must use them as is, this could work.
The view would not be update-able (I don't believe so, at least) so adding or updating users would require specific add/update code, but for Auth, this may do the trick.
CREATE VIEW users_combined_view
AS
SELECT id, username, password
FROM schoolOne_students
UNION
SELECT id, username, password
FROM schoolOne_teachers
UNION
...
note - this method would be somewhat useless (ok, totally useless) if username was not unique across the tables
A good database design is scalable.
This is particularly a bad idea:
SchoolOne_students
SchoolOne_teachers
SchoolTwo_students
SchoolTwo_teachers and so on...
There should be only one table for all the users, in your case for students.
There should be another table for Schools where all the school information will be stored.
Then you should join the students table with schools table by adding a school_id field to the schools table where the school_id will be stored for the corresponding students row.
===Students Table===
+-----------------------------------------
+ id | name |school_id | ... | ... |
+----+--------+----------+-----------+----
+ 1 |Student1| 5 | ... | ... |
+----+--------+----------+-----------+----
+ 2 |Student1| 3 | ... | ... |
===Schools Table===
+-----------------------------------------
+ id | name | ... | ... | ... |
+----+--------+----------+-----------+----
+ 3 |School 3| ... | ... | ... |
+----+--------+----------+-----------+----
+ 5 |School 5| ... | ... | ... |
+----+--------+----------+-----------+----
This way you can add as many schools as you may want and students for them
I hope this will help

Creating a MySQL table upon user registration

I am developing a website which will collect and store information for users.
I need to create a table specific to each individual user on registration to store the information they are searching for using the website. The table created will be named after the newly registered user's username.
Then when the user is logged in and runs the search, the data collected will be stored in a database using a MySQL insert query where the table name is a variable containing the logged-in user's username which will also be the table name.
I am an amateur developer and have searched everywhere to try and find a solution but I cannot seem to find anything evenly remotely similar to my problem.
Thank you in advance for any help!
Creating tables on the fly is more trouble than it's worth and very much swimming against the tide with any SQL database.
The reason you haven't found any docs about the approach you mention is because this problem is generally (almost without exception) solved best by having all the data in one or more tables, and including a column to specify which entity (user) the row is associated with. In your case, this might be an email address, or a username, or just a sequential number.
E.g.
| user_id | email | first_name | last_name | fave_color |
- - - - - -
| 1 | "a#b.c" | "anton" | "aardvark" | "red" |
| 2 | "b#c.d" | "miles" | "o'brien" | "infrared" |
| ... | | | | |
First take name from user like:
$fullname="$fname"."_"."$lname";
Then, write a query like this to create a table of that name
$sql="CREATE TABLE $fullname(ALL THE COLLUMNS YOU WANT TO CREATE)";
$result1=mysql_query($sql, $db_link);
this query is from my project. Works fine in wampserver.

User login with groups

I have one login form and three groups of users who can login: client, member, admin. All users share some common attributes, like username, password, active.
Most of the other fields are different for the respective group, with the table member having up to 30 fields.
This is the reason I would rather not have one large user table with all the needed fields, but separate the group related fields to different tables.
I would then have following tables:
------------------
| tbl_user |
|----------------|
| id |
| username |
| password |
| active |
------------------
------------------
| tbl_client |
|----------------|
| id |
| pid |
| company |
| address |
| projects |
| ... |
------------------
... same with tbl_member and tbl_admin.
But after the login, how can I select the additional fields from either tbl_client, tbl_member and tbl_admin?
I could use a group field in tbl_user and use the group table name as the value. But that doesn't seem very professional ;)
Any other ideas?
I don't know why you say "it doesn't seem professional". You're separating the user data role from the user authentication information as you should. There's nothing wrong with what you suggested and I would recommend it...
I would suggest adding another column in tbl_user that states what type of account it is. e.g. admin, member, client, that way you can direct them to whatever you need after they have logged in, and you can pull their information.
You could do an outter join on all three tables.
This will return results of all three tables, two of which will have null results (as they don't exist). This is less efficient as you are joining four tables (user, member, client, admin), then adding an additional field to specificy which group the member is a part of, and querying only that table with some logic.
What you suggested is a perfectly acceptable solution - tbl_user needs to identify the user type in some way, somehow. You could swap out the string for an id, if that may be easier to work with. After you determine the user type, you can then simply join with whichever detail table is applicable, or query the detail table directly.

What is the efficient way to make a permission system?

Currently I'm just using something like this in the DB Table:
access: home,register,login
And then in each page:
if(!Functions::has_rights('content'))
{
Functions::noAccess();
}
is there more efficient way to do it with php & MySQL? I may want to gain access even to several parts a page, for example, user can read a page, but doesn't comments to it, and I don't want to build a separate system to each module.
I believe what you are looking for is Access Control List where you model your problem into two things: objects and roles.
Incomplete list of examples one may use, or be inspired from if writing a custom one from scratch:
Zend provides Zend_Acl
Symphony 3.x has ACL, for Symphony 4.x you can use ACL Bundle and the Voters.
The CakePHP has an ACL plugin
I built one using a "*NIX-type" permission system.
I have different type of permissions for a page (read, modify, delete, comment, vote) and I assign a bit to each of those.
So for instance I have
define ('USER_CANREAD', 1);
define ('USER_CANMODIFY', 2);
define ('USER_CANDELETE', 4);
define ('USER_CANINSERT', 8);
define ('USER_CANCOMMENT', 16);
define ('USER_CANVOTE', 32);
Then if the user can read, comment and vote the permission will be 1+16+32 = 49
To check for permissions I just do a bitwise AND with those values.
For instance user->permissions & USER_CANDELETE to check if the user can delete the page (obviously I have a canDelete function for that)
If you are using some kind of routing it will make sense to make your ACL (Access Control List) depend on the routing that you have defined.
I usually run with a permissions table and a permissions_users table in a HABTM relationship. This way when the routing is matches, a permission can be looked up. If the user doesn't have the permission he is denied access.
This can be improved with checking for the different types of methods GET, POST, PUT and DELETE.
This is because I like the opportunity to edit the permissions and settings from the web interface, and allow for non-it people to do the same (i.e. marketing people).
Here is the layout:
+-----------------------+
| permissions |
+-----------------------+
| id | pattern | method |
+-----------------------+
| 1 | | GET | # => Will hit the root of your application
| 2 | users | GET | # => Will hit users/, usually listing the users
| 3 | users | PUT | # => Will hit anyone trying to insert a new user into the system.
| 4 | users/:id | GET | # => Will hit anyone who tries to view a specific user
| 5 | users/:id | POST | # => Will hit anyone trying to update a user
+-----------------------+
+-------------------------+
| permissions_users |
+-------------------------+
| user_id | permission_id |
+-------------------------+
| 1 | 1 | # => Will allow to view the root of the application
| 1 | 2 | # => Will allow to view the users list
+-------------------------+
So user 1 doesn't have any rights that could alter the records. And since the routing defines where the different request methods go, you cant simply POST to the /users to view the list.

Categories