Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a permission system like the following with Symfony 2?

General description

The system should handle a lot of items organized into nested categories (see visual example below) while giving the clients the ability to define the permission rules (see permission rules below). It should also handle different general permissions that are independent of any items (e.g. 'can view specific page?' or 'can invite new members?').

All users are organized into groups. Each user has a main group that he's belongs to, but he may also have some additional secondary groups.

A few users could be configured as super-admins and should be allowed to do anything.

When deciding if a user is allowed to do something, the permission inheritance is as follow:

  • Start with the main group permissions
  • Allow all permissions allowed by any of the user's additional groups
  • If defined, check user specific permissions (to allow or deny, independently of the above). User specific permissions doesn't have to be defined, if they aren't defined the user just inherit the permission from the groups permissions

When defining the group permission, the client could use inheritance to say something like:

  • Users allowed to ...
  • Editors has all permissions of Users + ...
  • Moderators has all permissions of Editors + ... - ...
  • Admins has all permissions of Editors + Moderators

NOTE: I should be able to request the last 10 items from the database that the current user can edit/view, the permission for each item should be decided by the database, I don't want to filter items for permission in the application level if I could decide that based on info stored in the database.

Permission rules

Rules could depend on any property of the item (e.g. item's owner's main group, creation time, category etc...), that info is stored in the database.

Rules could also depend on any property of the current user (e.g. signing-up date, members invited), requested action (e.g. view, list, rename, undo delete etc), and other info already available on run-time (e.g. url parameters, quota limits, item's content, server load), that info is available to the php script.

See sample rules below.

Visual example of database schema:

Category 1
   Nested Category A
      item x
   Nested Category B
      Deeply Nested Category
         item w
      item y
Category 2
   item z

Currently, the database schema is like the following but I can change it if it's required: (of course this is only part of the schema, there are other tables and fields as well)

items:

id | title  | owner_id | category_id
====================================
1  | item x | 2        | 3
2  | item y | 1        | 4
3  | item z | 3        | 2
4  | item w | 1        | 5

categories:

id | parents | title
=====================================
1  | null    | Category 1
2  | null    | Category 2
3  | 1       | Nested Category A
4  | 1       | Nested Category B
5  | 1/4     | Deeply Nested Category

users:

id | name | group | all_groups | is_super_admin
===============================================
1  | Tony | 5     | 5          | 1
2  | John | 5     | 5,8,6      | 0
3  | Mike | 4     | 4,7        | 0
4  | Ryan | 6     | 6          | 0

Sample rules

The following rules are just sample of real world cases that should be implemented.

  • Users can edit their own items within 5 minutes from submission ()
  • User 'John' can edit all items inside 'Category 1' and all its nested categories.
  • Editors can edit all items except items that their owner is marked as super admin.

Notice that these rules could be decided in the database level like most of the rules in my case.

Implementation

I've searched symfony docs, stackoverflow etc. There are a lot of interesting articles and questions on the topic of security and acls but I couldn't find the best way to handle such a system.

It's clear that I need some type of dynamic query builder to filter rows based on info stored in the database according to the defined rules. I'm assuming the second step (that involve info that isn't stored in the database like current server load) is probably to implement a voter (see voter example in this article or this question), or sometimes even simpler solution (such as rules that depends on the requested path). If the solution involving more than one thing to handle permissions, please also describe how to integrate and use them together.

Question

I'm asking how to implement such a system, please do not answer with links to symfony documentation or other resources with general idea and common simple cases. Please read and understand my case before answering.

like image 646
Xuni Avatar asked Nov 14 '22 06:11

Xuni


1 Answers

I realize this question is old, but answer is applicable for Symfony 2.3+, so I post it here.

For this approach you should use things like SecurityVoters.

With security voters you can implement any logic for access controls, which you can imagine.

Voters works like

$this->get('security.authorization_checker')->isGranted('update',$post);

which will check all voters, who can operate a Post entities (see docs) and combine their votes with choosen strategy (affirmative, consensus, unanimous) to decide if the current user is authorized to update post (for your first tule example)

You can implement several voters for single action and define strategy hove to decide resulting vote.

All groups and permissions you could store in the repository and fetch them with doctrine inside voter.

Also you can define custom role hierarhy for simplifying security works

Also read these docs on SecurityComponent

like image 119
ScayTrase Avatar answered Dec 16 '22 00:12

ScayTrase