Overhauling roles and capabilities, part 2

This is a follow-up to my initial overview of the roles and capabilities system in WordPress. I’d check out Part I first.

I’ve previously explained the complexities of the roles and capabilities system, but I haven’t adequately argued why they are too complex.

Numerous developers have weighed in at Trac ticket #10201, but I want to touch on this anyway. More or less, the current system is fine if you want to know what the current user can do. We load up their roles and merge the capabilities that make up those roles, add any other capabilities the user was granted, and remove any revoked capabilities. Then, we check if the capability we’re checking is in that list of capabilities.

The problem is apparent when you want to know the answer to this question: How many users have capability X? Querying for abstract capability X strikes some as edge case at first, so a better question might be: how many users should be considered at least an author?

To answer this question, we have to load up each user individually and build their derived capability list, by loading up their roles, add-on capabilities, and revoked capabilities. It should be fairly apparent that on a site with many users, the capabilities system can be a performance issue, to say the least.

Let’s remove user-level capabilities from the equation. Now, we can load up all of the roles, figure out which have capability X, and figure out which users have those roles. Clearly a huge performance benefit, as we’re running one query (to fetch the role definitions), unserializing its result, looping through the roles and checking for a cap, then querying the users for that role.

If only it were that easy. We store a user’s roles and capabilities in a serialized array in usermeta. That means we have to again leave SQL with every user’s usermeta value, unserialize it, and check if the user has the role. Core does employ a hack in a few spots for performance reasons, by using LIKE "%editor%" against serialized data. This very unpoetic code is the epitomy of the overly complex capabilities system.

But, we’ll no longer be storing capabilities, or multiple roles. Thus, we can now have our usermeta value be a simple text value with the name of the role. Two example queries:

SELECT * FROM $wpdb->users u INNER JOIN $wpdb->usermeta m WHERE u.id = m.user_id AND m.meta_key = 'wp_capabilities' AND m.meta_value = 'Editor';

SELECT user_id FROM $wpdb->usermeta WHERE meta_key = 'wp_capabilities' AND m.meta_value = 'Editor';

Now that is poetic code, and it would scale much better in an environment with many users. (When I mean many, I mean many, many thousands.) And I’ll point out here that there have been suggestions for a new table, to get this out of usermeta entirely.

The proposed overhaul is controversial, which explains its postponement for multiple releases, but appears to be gaining traction for 3.1. As the wp-hackers thread progressed, I suggested that the proposed overhaul could always end up being watered down a bit before entering core as a compromise, notwithstanding rather solid support from the core developers for the proposal on the table.

That said, I cannot imagine that user-specific capabilities would remain in core. If there is a compromise, it would end up being multiple roles, because we could still avoid serialized data, by using the same meta key multiple times, with different meta values. The one thing I don’t believe we would want to do is take an overly complicated system and oversimplify it, especially given the growth and scale of WordPress as a CMS. If we can sanely implement multiple roles in the new schema, keeping them may be something to consider.

The counter-argument is that we have a chance to simplify the capabilities API to make it manageable, and we should take advantage of that. We could sanely build a core plugin for role management. Multiple roles not supported in the user interface, nor would they be supported in such a plugin, leaving them to be the only piece of the API that would not be exposed.

That brings me to a final question: Is anyone actually using multiple roles? Because I’m noticing that WP_User::remove_role() was broken and WP_User::add_role() had problems as well. Both were fixed in the 3.0 development cycle after having issues for a few versions. Makes you wonder.

Published by

Andrew Nacin

Lead developer of WordPress, living in Washington, D.C. Follow me on Twitter.

10 thoughts on “Overhauling roles and capabilities, part 2”

  1. I’m pretty sure people would start using multiple roles when they loose the ability to assign (and revoke) individual capabilities.

    I think having multiple roles is the perfect compromise between performance and flexibility.

  2. Not having mutliple Roles per user smells like to not have them any longer. Whole Roles and Caps can be easily removed with having a fixed and constant role field in the user table – but isn’t it like it is today already?

  3. After supporting and talking to hundreds of people that use my Members plugin, I’ve come to the conclusion that most people that need role management don’t need multiple roles and user-specific capabilities. In fact, I don’t even offer those options within the plugin, and I’ve only had about two or three people ask about it. In every instance where people have needed that functionality, we’ve built custom roles with the caps needed.

    I’d also argue that only people with a handful of users would need those things, which could easily be replicated with custom roles. With a few thousand users, it would be unmanageable to use those features anyway.

    I’m all for making this more scalable by removing multiple roles and user-specific capabilities. I’ve got one site with 20,000+ users and another with 30,000+ users. I can’t even imagine what’ll happen if those numbers double.

    1. Good points. I think others might argue that with a few thousand users, some sites would be unmanageable without multiple roles (if user-specific capabilities are eliminated). But it’s an interesting angle, thanks for stopping by!

  4. I am using multiple roles with my amProtect plugin for WordPress. It integrates aMember Professional membership software with WordPress and it does it by assigning a unique role in WordPress for every product or subscription level that the person purchases.

    In fact, multiple roles is the cornerstone of my amProtect plugin for aMember. My plugin automatically creates and assigns the roles based on products/subscriptions purchased.

    I instruct users of my plugin to use Justin Tadlock’s Members plugin, just the edit roles component, to do any management that is needed on the roles because it is not available in the core WP functions. (We used to all use the Role Manager plugin but I like Justin’s plugin better.)

    Private forums in Simple Press, an extremely popular plugin, works best with multiple roles (which is why I’ve changed my amProtect plugin to work with them so it can be the most compatible membership plugin if you want forums with Simple Press).

    Thousands of web sites use WordPress to offer online classes in the expertise and niche of the web site (membership site) owner. A single member can sign up for multiple classes and/or memberships.

    WishList Member, which is gaining traction in the membership community, works best with multiple roles.

    Integrating with the Simple Press forum is done best by having unique roles to assign to users who need special restricted access to a private forum for the level they are on.

    If multiple roles were removed from WordPress then all of this would be in jeopardy so I hope this capability never goes away.

    To me, the way it has finally become is what it should have been all along.

    For me, the need is to identify them by a label…a role…more than to give users certain capabilities. So do what you want with capabilities, but leave the multiple roles feature in place please.

    I absolutely, positively would be against limiting users to only one role. It would hurt quite a few WordPress sites if you did this.

    Just thought I’d weigh in on this and give you something to think about.

    Thanks!

    Ken

  5. Hmm – I’ve just made a pitch to a prospective client today for a membership site that would need multiple roles or per user capabilities. A user’s capabilities would depend upon which of nine course modules they were studying or had completed and I don’t fancy creating 9! roles to synthesise this.

    I can see the performance arguments, of course.

    Time to investigate the map_meta_cap filter and see if my requirements can be met there. From a development point of view, as long as the right filters are in core to layer on alternative security models, I guess no-one should complain about the proposed simplifications.

  6. Another case for multiple roles here –

    we use a third party plugin (s2member) and our members get roles from that (e.g. free, paid, premium)

    we also want to assign standard WP roles – i.e. to make some of them authors, some editors, etc.

    I don’t want to hack the capabilities in the 3rd party plugin to support this – instead I want to be able to assign roles – e.g. member A is paid and an author

Comments are closed.