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.
Related
I am currently creating a Filesharing System for learning reasons in Laravel. Currently it is possible to upload Files and then to Download them later. Now I would like to extend it in a way that one File can be downloaded by multiple (but not all) persons.
Currently I have a User Table and the user id is referenced in a File Table. That way I know which File belongs to which User.
The file Table looks kinda like this:
id | filename | user_id |
1 | doc.php | 2 |
2 | fly.php | 4 |
3 | dog.jpg | 3 |
4 | cat.gif | 2 |
And so forth... That way I can just check if the user_id is the same as the Authenticated and logged in User.
The problem is I couldnt quite figure out how to make a file accessible to multiple users (10-20).
Do I just create a new Table which will look like this:?
user_id | file_id_1 | file_id_2 |
1 | 3 | 6 |
And everytime a additional file is assigned to a User a new Column is created?
To me this seems like very bad Data Schematic.
K
As #Greg has already commented you are best to use a pivot table to link the files and users to each other.
This will allow you to use hasManyThrough relationship to get all the files that are accessable by a user and all the users that have access to a file
https://laravel.com/docs/5.7/eloquent-relationships#has-many-through
I'm designing a very simple RBAC (Role Based Access Control) system in my PHP project and after giving it some thought I've came up with a solution, but without knowing much about building business systems I'm not sure if there are, or would be, any major design flaws with my solution.
So basically, I want to give a user a set of 'roles'. I will use these roles to allow or deny access to certain functionality on the application.
Here is the roles table:
# Roles
- id [auto-increment, unsigned]
- role [string, length:50]
# User_Roles
- user_id [FK:user_id, unsigned]
- role_id [FK:roles_id, unsigned]
Note: user_id and role_id to be unique index
The problem that concerned me was there was no information in the database about what the role actually does. But then I started to think if that was important. Because if the roles had meaningful names, and a structure, then you could query the user_roles table to get all the users roles and then within code something like:
# Fetch user with ID 1 from database
$user = User::find(1);
# Fetch the roles the user has from the database
# #returns : Array of roles
$userRoles = $user->roles()
# $userRoles = ['newmember', 'member.post', 'member.chat']
# Can the user send a message?
if(in_array('member.message', $userRoles)
{
# User can send a message
}
else
{
# User can not send a message
}
The roles can be managed, and can have whatever meaning they like within the organisation. I'm just concerned that the roles in the database have no meaning, I can't help but think there's probably a better way to achieve this.
Would my solution be feasible long term?
Thanks
Here's my approach to such RBAC systems, first I'd split the application into modules, at least logically.
Think of the modules as entities/resources in your application on which certain actions can be performed on. A resource could be a User, a Membership, a Product, ..
Let's assume we're building a site like Stackoverflow and we have the following modules:
Questions (Q&A section)
Chat
Each of these modules register themselves in the applications database table called modules which could look like:
# Modules
- id [an application-wide unique module identifier, provided by the module]
- name [string, human readable]
Each of these modules come with a set of actions which are pre-defined by the module, e.g actions to create a question, kick users from chat rooms etc. These actions get installed along with the modules into the applications database table called 'actions', which could look like:
# Actions
- module_id [reference to the modules table, is the namespace for the token]
- action [string / int, action identifier provided by the module, unique in the scope of the module]
- name [string, human readable name]
- Primary Key: [module_id, action]
Let's define the modules and actions for our Stackoverflow clone now:
Modules
+------------------------------------------+
| ID | Name |
+------------------------------------------+
| questions | Question and Answer Module |
| chat | Chat Module |
+------------------------------------------+
Along with the modules, the actions will be installed:
Actions
+-----------------------------------------------+
| Module | Action | Name |
+-----------------------------------------------+
| questions | read | Read Questions |
| questions | create | Create Questions |
| questions | edit | Edit Questions |
| questions | delete | Delete Questions |
| questions | vote | Vote on Questions |
| | | |
| chat | join | Join the Chat |
| chat | kick | Kick users |
| chat | create | Create Chatrooms |
+-----------------------------------------------+
The important thing here is that you can not modify the entries in these tables directly as admin of the systems so there's no GUI
to add/remove actions etc.
The next step is to create some roles for our system, this is done over the admin's user interface, roles can be defined arbitrarly.
Let's start off with some basic roles:
Q&A User:
- can read questions
- can create questions
Q&A Moderator:
- can read questions
- can create questions
- can vote on questiosn
- can edit questions
Q&A Admin:
- can read questions
- can create questions
- can vote on questiosn
- can edit questions
- can delete questions
Chat User:
- can join the chat
Chat Moderator:
- can join the chat
- can kick users from the chat
Chat Admin:
- can join the chat
- can kick users from the chat
- can create chat rooms
First, the roles are created in the roles table:
# Roles
- id [auto-increment, unsigned]
- name [string, length:50]
Populated with our custom definitions:
Roles
+-----------------------+
| ID | Name |
+-----------------------+
| 1 | Q&A User |
| 2 | Q&A Moderator |
| 3 | Q&A Admin |
| 4 | Chat User |
| 5 | Chat Moderator |
| 6 | Chat Admin |
+-----------------------+
In our super fancy admin UI we now have a sidebar with a list of all installed modules and their associated actions. Since
our typical admin is super lazy and doesn't know anything about programming he can now conveniently assign actions to each
role with drag&drop, i.e assigning permissions to roles.
These assigned permissions are stored in our mapping table Roles_Actions:
# Roles_Actions
- role_id
- module_id
- action
PK: [role_id, module_id, action]
The populated table:
Roles_Actions
+--------------------------------+
| Role ID | Module ID | Action |
+--------------------------------+
| 1 | questions | read |
| 1 | questions | create |
| 2 | questions | read |
| 2 | questions | create |
| 2 | questions | vote |
| 2 | questions | edit |
...
| 6 | chat | join |
| 6 | chat | kick |
| 6 | chat | create |
+--------------------------------+
Now we have to assign the roles to the users in the systems, let's say we have four users initially:
- Chuck Norris which is a Q&A Admin and also a Chat Admin (UID = 1)
- Bruce Willis which is a Q&A Moderator and a Chat User (UID = 2)
- Dilbert which is a Q&A Moderator and a Chat Moderator (UID = 3)
# User_Roles
- user_id [FK:user_id, unsigned]
- role_id [FK:roles_id, unsigned]
The populated Table:
Users_Roles
+---------------------------------+
| User ID | Role ID |
+---------------------------------+
| 1 | 3 (= Q&A Admin) |
| 1 | 6 (= Chat Admin) |
| 2 | 2 (= Q&A Moderator) |
| 2 | 4 (= Chat User) |
| 3 | 2 (= Q&A Moderator) |
| 3 | 5 (= Chat Moderator) |
+---------------------------------+
So one user can have multiple roles, and the permissions (module/action pairs) are then merged together in the application layer.
If you want to go one step further you could also provide some sort of inheritance in the role model, e.g the Q&A Moderator
could be defined as child of Q&A User, inheriting all permissions from the Q&A User and extending it with moderator rights.
So how could the authorization look like on the application layer?
Let's assume Dilbert which is a Q&A Moderator and Chat Moderator logs into the system, then you collect all his roles and all
permissions assigned to those roles. Next you start to build a permission tree and remove all duplicate permissions, the permission
tree could be represented as an associative array like:
Dilbert's permission tree:
$dilberts_permissions = array(
"questions" => array("read", "create", "vote", "edit")
"chat" => array("join", "kick")
)
For convenience we create a simple helper function like:
function has_permission($path, $tree) {
list($module, $action) = explode('.', $path);
return (array_key_exists($module, $tree) && in_array($action, $tree[$module]));
}
In our code we can now check if Dilbert is allowed to do certain things with a syntax like:
has_permission("questions.create", $dilberts_permissions); // => TRUE
has_permission("questions.delete", $dilberts_permissions); // => FALSE
has_permission("chat.join", $dilberts_permissions); // => TRUE
has_permission("chat.create", $dilberts_permissions); // => FALSE
I'm doubtful about how to approach this problem:
My Symfony2 app for this issue has two different profiles, name 'em "Seller" and "Buyer". The goal is to keep a bunch of attributes about a "Seller", and, on the other hand, not much about the "Buyer" (email and password would do it).
I realise that it's not easy to have two different entities log in and register using FOSUserBundle. There are quite some other bundles, such as PUGXMultiUserBundle that 'hack' FOSUserBundle, and are not easy to config/implement.
So my quiestion is: Is it more correct to use the hack PUGXMultiUserBundle proposes, dealing with its implementation and config, or is it better to stick to FOSUserBundle, have a User entity, and a one-to-one relation with a "Seller" entity that represents the attributes of a Seller different from a buyer? There would be another problem with that approach, with the registration form, that should be splitted in two, for Seller and Buyer register (which I don't know if it is possible/advisable using FOSUserBundle)
I hope you guys help me get it right. Cheers :)
I would definitely use the PUGXMultiUserBundle for that. I don't think it's a hack, it just implements a discrimination table that will be like:
1User Table
----------------------------------------
| id | username | type |------------
|-----|-----------|--------|------------
| 1 | mgdepoo | buyer |------------
| 2 | Sydney_o9 | seller |------------
----------------------------------------
2Buyer Table
----------------------------------------
| id | buyer_specific_field_1
|-----|---------------------------------
| 1 |
----------------------------------------
3Seller Table
----------------------------------------
| id | seller_specific_field_1
|-----|---------------------------------
| 2 |
----------------------------------------
That's all there is really.
I think that's exactly what you need especially if you wish to have different profiles for your users. You will have 2 registration forms as well (but you can make one with a choice box buyer/seller if you want to).
I personally use this bundle and it works great for me. Now, one of the question you might want to ask yourself is what if a buyer wants to be a seller as well. Does he want to have another email address? That is a choice that you'll have to make.
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.
I have a website that allows users with accounts. Account profiles are displayed on the front end of a website. Users can login and update their data, but it all must be approved by an admin (on a form) before the front end content reflects their update. The "live" data is stored across multiple tables in a Postgresql DB.
I'm looking for ideas for the (best / easiest) way to handle storing (db schema) this updated data that will allow an admin user to
approve/deny updates independently for a user (approve update A, deny update B, and ignore update C)
Be easy to maintain
Be easy for me to pull the updates to show admin and then process each individual field request.
Admin will need to be able to see a list of all users that have pending updates and then be able to see which fields for a specific user was updated so they can approve/deny the request.
Users can freely update a field as many times as they want, but admin will always see the current field content and the last update the user made.
I don't need to be able to see exact differences (although brownie points if you know how). They really just need to be able to see the two fields
ie:
Current Update
+--------------+-------------+-------------+
| | | (o) Approve |
| description | Description | |
| | | (o) Deny |
+--------------+-------------+-------------+
| | | (o) Approve |
| title | Title | |
| | | (o) Deny |
+--------------+-------------+-------------+
| [Submit] |
+------------------------------------------+
I'm open to any and all ideas, DB techniques, programming, or something else I haven't thought of.
Thanks in advance!
It's just a status of a record, an extra column in your tables holding the current status will be enough.