During my Google Summer of Code project I am implementing hierarchical permissions and I hope my work will become a part of Drupal 8 core. We have started the work with my mentor, Károly ‘chx’ Négyesi one month ago.

What problems are we trying to solve?

http://chalcedony.co.nz/gemsofwisdom/permissions

  • The current permission system is hard to understand for inexperienced Drupalers.
  • In many cases we get the clear picture of what a permission means only if we try out things, or read the code itself.
  • Permissions often overlap with others (e.g., administer content), but administrators doesn’t notice this.
  • Managing permissions is difficult, and could become hard when we work on a larger website. The user interface isn't transparent enough, and sometimes it needs serious concentration to work with.

Introducing hierarchy as a solution

Introduction of a hierarchical rights management system could be a solution for the formerly mentioned cases.

The current permission system is hard to understand for inexperienced Drupalers.

In many cases we get the clear picture of what a permission means only if we try out things, or read the code itself.

Let’s imagine that modules describe trees instead of simple lists when they determine their permissions. Leaves of these trees can be very specific about what rights and functionalities the users get if we grant that certain permission for them. Without a tree, this would mean an unmanagable number of permissions but with the hierarchy, users can be as specific or generic as necessary when granting permissions. There is no more situation like enable the ‘administer something’ permission where we have no idea what power we give our users, unless we have experience of that certain topic and module. A well-structured permission tree could be a huge help for everyone.

Permissions often overlap with others (e.g., administer content), but administrators doesn’t noticed about this.

If the permission tree has been built in a proper way, the overlap is not an issue anymore. It’s up to the maintainers who design the tree of course. You will see examples for this.

Managing permissions is difficult, and could become hard when we work on a larger website. The user interface isn’t transparent enough, and sometimes it needs serious concentration to work with.

One of the most interesting part of my project is the design of the new user interface. Representing the hierarchical structure is an objective which offers itself. It’s also an important objective to work out an approach which allows us to come along from simple to difficult. So, if someone doesn’t want to fine-tune their permissions, then we don’t let the interface to distract her/him with unnecessary elements. I imagine a minimalist UI which can become more complex only if the task requires.
Let’s say someone writes a blog with her/his friends, and would like to allow those beloved friends to edit all of the fields of their profiles. There should be an easy way to achieve this with a minimum effort without constantly seeing the options to define the rights at field level. In short: the interface must be “good enough”.

With creating the proper UI we can simplify our administrators’ life. But maintainers will face a good achievement when they design permission trees for their modules. This will be a serious responsibility in the future for them.

Hierarchy in action

Architect permission trees

In every cases our most important goal is simplicity. The purpose of introducing hierarchy is distinctness, so don’t ruin this with designing permission trees which are too difficult.

Permissions can be interpreted by leaves of the permission tree. At upper levels, the nodes should only be “containers”, they shouldn’t cover real functionalities. Their purpose should be to summarize the subtree rooted at that node. We can have exceptions to this rule in reasonable cases, there will be an example for this.

A possible permission tree for User module

A possible permission tree for User module

I’d like to share some of my thoughts on the designing procedure of this tree. The first part of the work was outlining the current permission list of the User module:

  • Administer permissions
  • Administer users
  • View user profiles
  • Change own username
  • Cancel own user account
  • Select method for cancelling own account

Some of these permissions are overly broad. The Administer users is especially problematic, but we can say in general that the ‘administer something’-type permissions will cause real headache for us. So we created a first draft for the list of permissions. Then we started to think about real-life problems.

The first branch of the tree is User profiles, all of the profile editing related options are structured under this node. Interesting detail is that the view permissions are under the edit permissions. This is a logical decision; if we want to retain the hierarchy, this structure is necessary. This solution is one of that extraordinary cases, where the parent node means real functionality, and not just acts as a container. But it seems reasonable, doesn’t it? We can see divisions into user roles, and additionally the own profile option. We can meet this structure in other cases as well, towards dealing as much use cases as possible.

Add users is a branch itself. It cannot be placed under somewhere else, and doesn’t worth to divide further parts. Configure account settings is also a separated branch, but you can refine whether a user can access a certain setting. For instance we can grant the ability for editing text of various e-mails to our trusted editors. Isn’t it convenient that we can delegate this, and we don’t need to take care of this anymore?

Administer permissions is an adventurous story. Here we can see the importance of handling visibility of UI elements, because we will list all of the permissions under this node. We can define which permissions can be administered by users. A further option for refining this, that users can delegate permissions only if they have it.

Wait! It seems more complicated than before!

On one hand, yes. If we define complexity that we have to deal with more options, than yes, it’s more complicated. But let’s think about the advantages of the formerly outlined implementation. With well defined permission trees we can help our users, because we can show them all of their possibilities in a structured, clear system. Everyone can decide on which level (s)he wants to use this feature. If someone wants to refine permissions substantially, it would be much more difficult without this approach, needs a lot of knowledge, contrib modules etc. So this system will be auspicious for them. But what if someone wishes pure simplicity? Well, the new user interface will help. Anyway, they can check checkboxes with fearlessness and confidence in the future. Exactly what they need.

Further advantages

When we check a permission, we can refer any node in a permission tree, due to the hierarchical structure, the whole subtree rooted at that node will be checked. What does it mean? Let’s see some examples using the User module’s permission tree above.

user_access examples

Any of the following strings could be an argument for the user_access method. In their decriptions I’ll explain what rights the user has, if the method returns TRUE.

User module.User profiles

Our first example is an extensive permission. If we look at the tree, we can easily understand how the example can be interpreted: in this case, users can do anything with profiles in any role: they can edit and view all of the fields, and grant any role for other users. We can check this with one simple query, it’s not necessary to list all the child permissions. In every case we should find the corresponding node in the permission tree, and examine the subtree rooted at that node. With analyzing the nodes of the subtree we could get the functions and rights which are covered by the User module.User profiles permission.

User module.User profiles.Own profile

Adding Own Profile narrows down the generic permission to the user's own profile. The user can see an edit everything (s)he wants.

User module.User profiles.Own profile.Edit property X

The user can view and edit only the property X field inside her/his own profile. As you can see, it's similar to the previous one, but the permissions is restricted to the property X field.

User module.User profiles.Own profile.Edit property X.View property X

The user can view the property X field inside her/his own profile, but (s)he can’t edit it. Now we are in the lowest level in the tree, and narrows the previous permission, just like in every other cases. E.g. this permission could be useful when we don’t want to let our users to edit their username.

If we look through the permission tree, we can notice a simple and important rule: each node narrows its parent permission.

Using wildcards

When we administer permissions, sometimes we should click a lot to accomplish a given situation. Staying in example above, let’s say we’d like to grant someone the right to administer all of the permissions, but only if (s)he has that certain permission. We can use wildcards when we adjust permissions for that role, which looks like this: ’User module.Administer permissions.*.Only if user has it’. It will be the goal of the UI to provide the possibility for this operation.

Further examples for wildcards:

User module.Cancel profiles.*.Delete the account and its content

Permission for delete all of the accounts and their contents.

User profiles.Role X.*.*

Permission for view all of the profile fields in Role X.

Wildcards vs. performance

Using wildcards could be a convenient way to handle certain cases. But we won’t introduce wildcards on the query side in user_access calls, because this feature would have performance issues. When an administrator grants permissions, it doesn’t matter how quick it is, and how much time it takes to save, but the query side is critical, the user_access is used frequently in every page load. So it's worth to make this decision.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

moshe weitzman’s picture

Dupe of #381584: Hierarchical Permissions System, but that is old so no worries. Please do read Crell's blog post linked from there if you have not done so already.

chx’s picture

We discussed this with Crell before the project begun.

Crell’s picture

This SoC project is essentially an attempt to implement what I proposed in the aforementioned blog post, which I fully support.

My one issue with the above at the moment is that it implies permissions are still rooted by module namespace. I do not believe that is wise. Permissions should be clustered by action or by object they relate to, not by the code bundle that happens to have declared them.

sun’s picture

I think this needs more architectural discussion.

Looking at the examples,

User module.User profiles.Own profile.Edit property X.View property X

I don't quite get where "hierarchy" is involved here. Under hierarchy, I'd understand this:

profile
profile.add
profile.edit
profile.edit.own
profile.delete
profile.delete.own
profile.view
profile.view.unpublished
profile.view.category_machine_name
profile.edit.category_machine_name
profile.edit.field.picture
profile.edit.field.*

Additionally, in the given examples, the human readable terminology, containing spaces and different letter cases, makes the permission identifiers really hard to parse and remember. I think we need to use internal machine-name-alike permission identifiers for this.

Lastly, I agree with @Crell that permissions should be assembled around objects/actions, not modules.

balintk’s picture

Actually your example based on the same concept. The difference is the approach to the permissions of the user module, but you can find similarity as well. We was thinking about the structure for days, and we realized at the beginning, there is no solution which will fit everyone's needs. Our main goal was presenting the concept, and we've created a permission tree for user module for demonstrating the possibilities, not to determine the eventual structure. It was useful to see real problems, what we are trying to solve.

About the permission identifiers: yes, you're absolutely right. We didn't make a decision about this, it would be great to discuss it here.

And sure, why not assemble the permissions around objects/actions instead of modules? But IMHO on the UI should be an option to switch between the two groupings. What do you think?

Crell’s picture

A UI option to show permissions created by each module requires that each module have a definitive control over the permission it creates. With a hierarchical approach, I'm not sure if we can guarantee that to be the case. Eg, what module owns Node.Article.SubscribeTo?

joachim’s picture

Subscribe.

And also, just to mention http://drupal.org/project/node_permissions_grid, which produces a limited sort-of hierarchy for node permissions, which is the sort of thing I think we need for permissions that naturally fall into a group.

David.Hamilton’s picture

Subscribe.

Gribnif’s picture

I don't have a strong opinion about using a hierarchy to represent permissions from the user_access() perspective, but I do feel strongly about the UI aspect.

Personally, I'm not too optimistic that representing a fluid set of permissions where each module's position is determined by the author is going to scale very well. It's fine for the case of core, where a unified hierarchy can be dictated, but when it comes to contrib, one module author's idea of where his permissions should fit in the tree may be drastically different from another's. Witness the way modules themselves get (mis-)categorized in the module admin page--and that is only a single level of categorization.

Then, there's the problem that, even if you do find the holy grail and get things organized sensibly, you're very likely asking the user to do several more clicks than he does now, to change the same permission.

I think the current UI could be improved, instead. Add a bar across the top (or even make it sticky, like the table headers.) Have one option to let the user choose just one column (role) to display from a select list, or perhaps do the same by clicking on a role's name in the header. Have a textfield which is a typeahead that does real-time filtering of the rows (permissions) based upon what's typed. Completely hide any permissions that do not match the filter.

IMHO, this could be written using jQuery in a few hours, and would probably go a long way toward making the interface more useful. Most importantly, there are no API changes needed, and it could be implemented in D6 and D7 as an add-on module.

While I admit that this does not fix the problem of not being able to understand a permission's intent, it does make the UI more usable.

Crell’s picture

Gribnif: While that would improve the UI we have now, the goal of revamping the API is not simply to facilitate UI changes. It's to make a better permission and access architecture. We will need to come up with a good UI for that either way. If you have thoughts on making that UI work well, please share. As you note it's not an easy problem space.

jstoller’s picture

Subscribe.

balintk’s picture

Title: Summary of preparing to implement hierarchical permissions » Concept of a hierarchical permission system
Status: Active » Needs work

A more generic issue title.

larowlan’s picture

When implementing the database layer, please investigate using nested sets (see http://www.weckert.org/nst/index.html), ie left id, right id notation, so we dont end up with p1, p2, p3 like some other existing core hierarchies use/have used in the past.
In terms of storage, we could keep the standard relational table, ie permissions with a parent id but after writing to this table we could update a tree version of the table with columns, permission id, depth, left id, right id.

Then you'd have the following records for a parent permissions (permission id 2) and child (permission id 3).

+------------------+--------+----------+---------+
 | Permission Id | Left Id | Right Id | Depth  |
+------------------+--------+----------+---------+
 |                     2 |         1 |            4|          1 |
 |                     3 |         2 |            3|          1 |
+------------------+--------+----------+---------+

This lets you do queries like this:
Fetch all first level children of a particular permission
<?code
/* First get the permissions' details */
SELECT * from permission_tree
WHERE permission_id = 1;
/* Then get it's children
SELECT * from permission_tree
WHERE depth_id = :parent_depth_plus_one
AND left_id BETWEEN :parent_left_id AND :parent_right_id
?>
Just my 2c
[Edit: elaborated further on my initial comment once I had access to a real keyboard]

balintk’s picture

I don't think that we need to store the hierarchical relations in the database. We can store the permissions as simple strings, in the deepest form in the hierarchy, using a delimiter (a period in the current implementation). Although your suggested method looks really smart, we don't really need this, because we can easily parse these strings and do the access checking, which is faster then dealing with these advanced queries.

larowlan’s picture

fair enough :)

Michelle’s picture

I'm in the process of figuring out the permission system for Artesian, which will have global as well as per-forum permissions and this work looks very interesting. Not much to add right now but I wanted to do more than silently follow and to raise my had as someone with a real right now use case. :)

Michelle

sun’s picture

FileSize
50.27 KB

g.d.o doesn't have comment_upload, so attaching annotated screenshot for http://groups.drupal.org/node/223189 here.

fmizzell’s picture

I am still a little confused about how the concept of "hierarchy" is being used. sun's example in #5 is a little clearer, but still not 100%.
if we have a hierarchy of objects/containers like this:

entity_type
entity_type.node
entity_type.node.properties
entity_type.node.properties.id
entity_type.node.fields
entity_type.user
etc..

[I am leaving actions out on purpose, for simplicity]
Then I would think that granting someone (a role, or a specific user) permission at the entity_type level would give them access to entity_type.node, entity_type.user, and any other permission deeper in the tree.

If I want someone to have access to everything entity_type related, but not to properties, then I can deny access to entity_type.*.properties

Am I thinking of things in the right way? because this model doesn't seem to match the examples shown here, and in the model I am describing, if we try to add "actions" to each object (entity_type: view, edit, delete, etc), it seem like these should be somewhat independent of the object tree of permissions, just to keep things clean, and easier to structure.

finally I was thinking about how this more structure/constrained hierarchical model would allow fancy permissions like the ones shown by sun and balintk: profile.view.unpublish for example.

if we had an object tree, and have defined what actions can be performed on each object, some pseudo code for that permission might look something like this:

if(entity_type.profile.property.status == unpublish){
  grant entity_type.profile: view [some user or role]
}

I also have some ideas and how this could simplify the UI but I will wait until I get some feedback :)

Letharion’s picture

I just watched a presentation by balintk on these hierachical permissions, and I must say, I like it very much.

I have yet to dig into any of the code, and while there are definitely improvements to be made on the UI, this seems like a big step forward.

saltednut’s picture

I agree that this system needs an overhaul. One issue I have noticed in D7 is the clear lack of CRUD permissions on a per-bundle level. I am unsure why node READ permissions are set on the macro level using 'View published content'

Ideally, each bundle would (to the user) appear to have a full access control system:

  • Article: Create new content
  • Article: Read own published content
  • Article: Read any published content
  • Article: Read own unpublished content
  • Article: Read any unpublished content
  • Article: Update own content
  • Article: Update any content
  • Article: Delete own content
  • Article: Delete any content

Note the addition of 'Read own' and 'Read any' here. This would allow for site builders to easily create a private bundle without the need for custom code or contribs to 'protect' specific nodes. There are a lot of modules that do this in various ways.

(Note: I am also a proponent of getting rid of the term 'Edit' but I do not want to bike-shed - I understand the general lexicon has 'Edit' as a term for 'Update'. Semantically speaking, editing something could actually involve deleting it. Anyway...)

I suppose what I am talking about here is somewhat of a backport of this functionality into D7. It would not re-architect the problem with permissions being rooted in the module namespace.

Going forward, the system described in #5 seems to make a lot of sense - a machine name style declaration is much easier to read. Clustering permissions around actions/objects also seems like a logical step forward.

Even using this architecture, the user permissions screen can remain relatively unchanged minus the additional permission listings. It might also be helpful to users if each permission set was listed using vertical tabs.

Crell’s picture

brantwynn: Per-type-view-permissions is a separate problem that is more complex than most people give it credit for. See: #282404: Add view [TYPE] content permission. It's not something to be resolved in this issue.

saltednut’s picture

@crell++ Thank you for pointing that thread out. I was having difficulty finding a more focused discussion about this when searching.

So lets ignore my moaning about the current system and I'll just reiterate my agreement from #21:

a machine name style declaration is much easier to read. Clustering permissions around actions/objects also seems like a logical step forward.

xjm’s picture

Version: 8.x-dev » 9.x-dev
Category: task » feature

Closed #381584: Hierarchical Permissions System as a duplicate of this issue. Also moving this to 9.x based on http://buytaert.net/code-freeze-and-thresholds.

Marq’s picture

Version: 9.x-dev » 8.x-dev

Will this be anything like I have described here? https://drupal.org/node/2061553 Please see img.

If there is a better place for me to post things like this, Please let me know. I am new to drupal and could use some guidance.

Thanks.

Crell’s picture

Version: 8.x-dev » 9.x-dev

It's hard to say, but this is definitely not in scope for Drupal 8 at this point.

If you're new, I would suggest starting out with something a bit easier. :-) There's a LOT of work to do on Drupal 8 yet just with the stuff we've already got, and we could certainly use the help. Look for issues tagged Novice, or stop by core mentoring hours in IRC: http://drupalmentoring.org/

Marq’s picture

Thanks Crell,

The mentoring program is just what I was trying to find. I know D8 is only in the Alpha stage, but I have run in to (Unresolved) issues in D7 as well. By the time D8 is released, I hope to have a better knowledge of its foundation to build from.

xjm’s picture

Issue summary: View changes

(Fixing text format so the summary is editable.)

catch’s picture

Project: Drupal core » Drupal core ideas
Version: 9.x-dev »
Component: user system » Idea

This is a major architectural and user interface change (only one interface, but it's an important one), so moving to the ideas queue.

mxr576’s picture

So there are no plans to introduce this before 9.x? Or not even in that version?

cilefen’s picture

@mxr576 FWIW the ideas queue is the place to form plans.

colan’s picture

Unless someone with time/energy/money picks this up & runs with it, then no.

What we usually do is get something into contrib first, and then move to core if it makes sense. Right now, there's not much in contrib so it's doubtful that anything will get into core before D9.

While Permissions Subset appears to be relevant (although a different approach, possibly better or worse), it's only for D7.

I should mention, however, that this could be a major architectural change rather than just adding a contrib module. If that's really what we're doing here, then this definitely won't get into Drupal 8.

hyperlogos’s picture

Is anyone working on anything like this? Literally changing nothing but roles having hierarchy with inheritance (which I realize is complicated) would be a huge improvement in the way permissions work.

Gribnif’s picture

@hyperlogos: We have been working on Monster Menus since 2006, which does most of what this thread describes, in a different way.

The problem with using roles for this is that they simply don't scale well when there are more than a few dozen. Instead, we store the permissions in database tables. In our college's installation we have many thousands of permissions groups under Monster Menus.

hyperlogos’s picture

@Gribnif: Unfortunately MM appears to require MySQL (or similar) and I am not only using Postgres right now, but also generally don't want to be tied to a single database - which is part of why I chose Drupal to begin with. It otherwise looks like it could be a good solution.