WordPress.org

Make WordPress Core

Opened 13 years ago

Closed 3 years ago

#2531 closed enhancement (wontfix)

Functions for registering additional capabilities and getting a list of all capabilities

Reported by: markjaquith Owned by:
Milestone: Priority: normal
Severity: normal Version: 2.0
Component: Role/Capability Keywords: needs-patch dev-feedback needs-nacin
Focuses: Cc:

Description

Plugin authors need a way of announcing the additional "add on" capabilities used in their plugins.

Additionally, role manager plugins need a way of getting a list of all registered capabilities.

Proposed functions:

register_cap('cap_name');

get_all_caps();

Patch is coming.

Initial milestone of 2.1, but it would be really nice to get this into 2.0.2 ... because 3rd party capabilities are largely useless until we have this.

Attachments (1)

register_cap.diff (2.9 KB) - added by markjaquith 13 years ago.

Download all attachments as: .zip

Change History (32)

#1 @davidhouse
13 years ago

I wouldn't call it "largely useless". Plugin authors are required to make a decision as to which of the default roles should have the cap, which is bad, but it doesn't make the cap system useless. This will probably be too big to risk 2.02.

#2 @markjaquith
13 years ago

If this doesn't make it in, what should we recommend plugin authors do in the interim? They can't assume that any of the default roles exist (by name). The best they can do is initially marry their capability to an existing capability (I suggested manage_options, as it is pretty much a given that anyone who can manage options can do anything else). Plugin authors will have to write the code to do this themselves, which leads to bloat, and confusion about the best way to do it. And then when this fix does come out for 2.1, they'll have to support both ways, or leave 2.0.2 users behind. Maybe not "largely useless," but defitely "highly annoying." :-)

I'm writing it for 2.1 regardless... but I really think it's not going to be that much code, and plugin authors (and consequently WP users) would benefit a lot from the code. Let me know how you feel once I have a patch up.

#3 @markjaquith
13 years ago

  • Keywords has-patch 2nd-opinion added

Ok, here's how it works:

register_cap('my_awesome_cap');

This adds (if not already there) 'my_awesome_cap' to an array option called 'caplist' in the options table. This is the same option that Owen's Role Manager plugin uses, so capabilities registered with register_cap automatically show up in the Role Manager

unregister_cap('my_awesome_cap');

This removes (if it exists) 'my_awesome_cap' from the 'caplist' option array. This could be done on deactivation of the plugin. Note that this does not remove the capability from any users who may still have it... if you reactivated the plugin, the capabilities would still be there. It is just a way of purging the capability altogether if no one is using it.

get_all_caps();

This returns a unique array of all available caps. This array is merged from three other arrays:

1) the list of caps stored in 'caplist'

2) the capabilities that are actually assigned to roles or users

3) the default capabilities that WordPress offers

number 3 was needed so that if you remove a built-in capability from all roles an users, it doesn't disappear forever.

Here is a test plugin. You can use this plus Owen's Role Manager to test.

1) Activate Role Manager

2) Activate "My Awesome Cap" plugin

3) View Role Manager, and verify that "My Awesome Cap" exists

4) Deactivate "My Awesome Cap" plugin

5) View Role Manager, and verify that "My Awesome Cap" is gone.

6) Reactivate "My Awesome Cap" plugin

7) Assign "My Awesome Cap" to a role in the Role Manager

8) Deactivate "My Awesome Cap" plugin

9) View Role Manager and verify that "My Awesome Cap" has remained, because it is assigned to a role.

<?php
/*
Plugin Name: My Awesome Cap
*/

register_activation_hook(__FILE__, create_function('$a=0', 'register_cap(\'my_awesome_cap\');'));
register_deactivation_hook(__FILE__, create_function('$a=0', 'unregister_cap(\'my_awesome_cap\');'));
?>

#4 @robmiller
13 years ago

+1, definitely needed as soon as possible.

#5 @ryan
13 years ago

If we're going to do all of this, we might as well move to tables. wp_roles holds the roles. wp_capabilities holds the capabilities. wp_user2role maps a user to a role. wp_role2capability maps a role to a capability. wp_user2capability maps a user to a capability.

#6 @robmiller
13 years ago

Any more input on this, particularly Ryan's table proposal? It'd be so nice to get this for 2.1.

#7 @ringmaster
13 years ago

I must assume that ryan's table idea was just a lark. Five new tables for capabilities? That's insane.

Here are my other thoughts:

I like the idea of the WP-native get_all_caps(). That seems practical.

I do not like the idea of registering caps using only the registration functions. Here's why:

Suppose you have a plugin that replaces a single intrinsic cap with three custom caps that provide different tiers of access to the same feature. Using only the registration functions, it is not possible to remove the intrinsic cap from the list. (In case you were wondering how this would be done technically, you could use hooks to grant/deny the intrinsic cap from users based on the application of your custom caps in whatever circumstances happened to apply.)

What difficulty would there be with moving the Role Manager's cap filter into the core? It allows direct access to the array of available caps so that caps can be added and removed.

If we're talking about making grander changes....

Un-array the caps. Currently, it's very difficult to answer questions like "Which users have cap X?" If we stored caps as multiple individual rows in the options table instead of as an array in a single row, these types of queries would be easier.

If you really needed to, the caps could be pushed to their own table- one row per cap per user. There would also need to be a store for caps in roles, which could probably just as efficiently stay in options as make a second table. So two new tables at most, but I don't see a lot of benefit there.

#8 @markjaquith
13 years ago

Un-array the caps. Currently, it's very difficult to answer questions like "Which users have cap X?" If we stored caps as multiple individual rows in the options table instead of as an array in a single row, these types of queries would be easier.

This would be great. "Which users have cap/role X?" is quite crazy with the current system, and it doesn't scale well at all.

#9 @ryan
13 years ago

Caps should move out of usermeta. usermeta is a global table but caps are a per-blog resource. Using the tables I mentioned follows the rule that you store something only once, but we don't have to be strictly by the book. Busting caps out of the array will definitely help. Keep in mind also that with roles we have a level of indirection. One can't directly query if the user has the "edit_users" cap since the user has only the "administrator" role/cap. You have to look up the role to get at the caps. Using the extra tables makes this easier.

#10 @matt
13 years ago

I think caps should move out of the DB entirely. At the end of the day, we basically have a boolean value for a number of different actions. Plugins should be able to create and check against a darn boolean value without modifying the DB. Defaults should be hardcoded into vars.php and plugins can modify them at will. Right now the system is far more complex than it needs to be, and it's extremely challenging to grasp. (The lack of documentation doesn't help either, but if the system was clearer it wouldn't need documentation, so I see that as a symptom not a cause.)

I think the fact that no one but extremely core hackers have even attempted to put anything on top of roles/caps speaks to that fact.

What if...

We had an indexed array of roles in a file in WP. Roles 0-6, which match the roles we currently have.

Each of these roles statically maps in an array by role_id to a set of caps.

Roles and caps both pass through filters, so plugins can edit current ones or add new ones at runtime. This would match how we allow plugins to modify similar systems throughout the rest of WP.

The role_id is stored in usermeta, but only in the key. If I'm Administrator (role_id = 6) I have a usermeta row which looks like: user_id 1, meta_key role_6, value $table_prefix.

For MU for a fast way to get all the users of a blog, set a meta_key = $table_prefix and meta_value = 1, which is set if someone is attached to a blog. Again, this would be a key lookup.

This would allow indexed lookups of people in different roles (scanning values is a full table scan) and we already have usermeta set for users so we could just check the user object, making everything ultra-fast as well.

I'm just throwing this out there, it might not be the best system but I'm sure between us we can think of something infinitely simpler than what we have now.

#11 @ringmaster
13 years ago

What we have now is pretty simple. I'm not sure how current_user_can('do_something') can be much more clear. In what way are you suggesting that it's not simple? Nevermind that most ACL systems work just like WP roles and capabilities, so the knowledge of the workings of similar systems ports to WordPress.

What is currently lacking in the core is a good way to let users manually set capabilities as required by plugins. Also, features to allow access to plugins for setting these capabilities are not as apparent as they might be. Remember though, the capabilities system was designed and implemented knowing that none of those things would exist in that release. We should now address those issues.

There are several plugins that were not written by core hackers that make use of the caps system. I'm not sure what would give the impression that the caps system is a barrier to developers.

The proposed 1-6 system seems less understandable than the caps system. How do you permit a single user access to something that is typically not permitted to users of their assigned role? How does the number mean anything to what functions you are allowed to access? The inability to answer this question is the exact reason why we ditched the userlevel nonsense in the first place.

WordPress is not WordPress MU. If the capabilities system is incompatible with MU (it isn't), then perhaps MU devs should consider using something that works better for them. Providing for discrete capabilities for WordPress when used as a CMS is something that we would be remiss to remove just to gain speed improvements in some other projects, which would likely not be used as a CMS.

This is the first complaint I've heard about the capabilities system other than from folks who were used to having arbitrarily defined hierarchical levels, and I'm shocked. I'm curious what code you've attempted to write with permissions that has been so difficult that would lead you to this severely regressive suggestion.

#12 @matt
13 years ago

  • Milestone changed from 2.1 to 2.2

Caps are scary.

#13 @foolswisdom
12 years ago

  • Milestone changed from 2.2 to 2.3

#14 @westi
12 years ago

  • Milestone changed from 2.3 to 2.4 (next)

This rewrite needs to wait for 2.4 I think.

Feature Freeze for 2.3 has happened and the level of discussion on this ticket makes me think we need to review roles/caps before adding more api for them.

#15 @josepo
12 years ago

I found I needed to add the following after line 422 of the diff file that was posted by markjaquith on 03/06/06 to get the get_all_caps() function to work properly:

if (!isset($wp_roles)) $wp_roles = new WP_Roles();

Otherwise, I would get an error in line 425 where the foreach - loop accesses the global $wp_roles variable, simply because the variable had not been set yet...

#16 @pishmishy
12 years ago

  • Milestone changed from 2.5 to 2.6

Bumping milestone for feature freeze.

#17 @santosj
11 years ago

  • Owner changed from anonymous to jacobsantos

#18 @jacobsantos
10 years ago

  • Component changed from Administration to Role/Capability
  • Milestone changed from 2.9 to Future Release

#19 @jacobsantos
10 years ago

  • Type changed from defect (bug) to enhancement

#20 @Denis-de-Bernardy
10 years ago

  • Keywords needs-patch added; has-patch removed

I can share some of my experiences with caps on a separate system. I had:

  • users
  • profiles
  • caps
  • user2profile
  • profile2cap
  • user2cap (for extra user caps if needed)

so one more table than ryan's suggestion. one view for convenient querying then does the trick:

  • user_caps

it worked fine, but, as point out above, it's a lot of tables... the view is potentially slow due to the cross join left join, but that turns out to be fine since it only ever gets queried against primary keys.

Trying to abstract any more than that is bound to fail: You'd then need owner_id fields or their equivalent all over the place when per db row permissions need to be enforced (such as, post can only be edited by its author and editors). Sometimes, you need several owner_id fields for a single row (a post with multiple authors, or an order placed by a reseller for his customer, that can be viewed by the reseller's referrer). It's a big mess that is more easily enforced through different queries (or stored procedures, if the db is made aware of capabilities), than through some kind of abstracted owner2node field.

If anything, the current system is a bit lacking when you need to query whether a user has a particular role or cap. But for those rare cases where it turns out to be necessary, you can always set up a separate system, using tables. It's what I do with my membership manager plugin. As a bonus, I can have members who are subscribers on blog A and contributors on blog B, some of who are editors on A and B, others who are moderators in forum C, etc.; each time independently.

I end up liking the end result: WP manages the permissions for everyday tasks. It could have managed the access restriction as well. The main reason I opted for tables was for reporting purposes (who has a running membership X?), and to enforce the proper expiration dates when several products can lead to the same membership. Extracting the necessary data from serialized arrays would have been nuts...

Anyway, suggesting either wontfix for this one, or the table solution outlined by Ryan (or my own if this gets delayed until Views are part of MySQL installs for all WP users).

#24 @Denis-de-Bernardy
10 years ago

  • Keywords 2nd-opinion removed

#25 @aaroncampbell
10 years ago

  • Cc aaroncampbell added

#26 @Denis-de-Bernardy
10 years ago

  • Milestone changed from Future Release to 2.9

see #10201

#27 @westi
10 years ago

  • Milestone changed from 2.9 to Future Release

As we are now feature frozen it is too late to include this into 2.9.

Moving to Future Release as there is currently no patch.

#28 @voyagerfan5761
10 years ago

  • Cc WordPress@… added

#29 @jacobsantos
7 years ago

  • Owner jacobsantos deleted
  • Status changed from new to assigned

#30 @chriscct7
4 years ago

  • Keywords dev-feedback needs-nacin added
  • Version changed from 2.0.1 to 2.0

So I hear @nacin was looking for a summer project. Is there interest in doing comment:3 and comment:5?

#31 @johnbillion
3 years ago

  • Milestone Future Release deleted
  • Resolution set to wontfix
  • Status changed from assigned to closed

Registering capabilities was a good idea 11 years ago, alas this boat has sailed.

Note: See TracTickets for help on using tickets.