The attached patch is a continuation from a really great BoF in Szeged last year on how to make the node access system more flexible. (http://groups.drupal.org/node/14419) It is also a continuation of multiple issues from Drupal 6 that stalled for various reasons. It's a fresh rewrite, but not a totally new concept.

This patch does the following:

1) Obliterates the inflexible mess that is hook_access(). Begone, vile pseudo-hook!

2) Instead introduces hook_node_access(), which is a "proper" hook in that it is called with module_invoke_all().

3) Converts node_content_access() (a hook_access() implementation) into a new module called nodeperms, which is enabled by default.

Why in the world would we do this?

In Drupal 6, the module that owns a node type has absolute control over its access callback. That is actually quite bad, as 99% of node types are owned by node.module since they're created through the admin. That means there is absolutely no way to vary the access control rules from the permission-based system that node.module provides.

Instead, in the new mechanism we allow any module to throw in its two cents on node access operations. For any given node/user/operation, it can do one of three things:

1) Return "yes, I say this user should be able to do this".
2) Return "no, I hate this user and he shouldn't be able to do this".
3) Return "Whatev, I don't care."

The node system will then allow access if at least one module said YES and no modules said NO, ignoring any Whatevers. If everyone says Whatever, then we fall back to the node_access table (which is the same logic we use now).

What does this buy us?

This change allows us to swap out, per node type, the way access controls are enforced. By default, it's controlled via Drupal permissions. (That's what nodeperms does.) However, we can then very very easily disable the permission-based controls entirely for one or more node types and add our own logic. Think OG-specific node access controls. (Can anyone say "WIN!") Or allowing nodes to be edited by the node owner only for some fixed period of time after they're created, but by admins at any time. And being able to apply both of those rules to any node type, purely by pushing the right buttons in the admin. (Are you feeling sexy? I'm feeling sexy.)

This also leaves open the potential to completely remove all node access modules, at which point we fall back to the node_access table exclusively. The behavior of the node_access table has not changed in any way. (In fact the behavior of the "view" operation hasn't changed in any way either, as the node_content_access() function didn't care about that op so I didn't either.)

The nodeperms module is enabled by default, and is also configured to affect all node types unless specifically set not to. That means that, by default, if you never bother to configure anything then core's behavior is almost identical to what it is in Drupal 6 for node.module-defined nodes. Any nodes defined by other modules automagically pick up the same 5 permissions as any other node. (create, edit, edit own, delete, delete own.) That also lets us remove some redundant hooks from other core modules like forum and blog that simply replicated node.module's permissions exactly.

As noted above, this patch owes a great deal to previous work and input from merlinofchaos, Moshe Weitzman, and Ken Rickard. Please credit them as well when committing.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

moshe weitzman’s picture

Status: Needs review » Needs work

Great stuff - exactly as we discussed at the BoF. This lets all modules participate in the allow/deny decision, not just the "owning" content type. Previously, modules had to to use the grants system for that and who likes that (beside me).

My feedback is nitpicky - beware:

@see http://api.drupal.org/api/group/node_access/7 Replace the URL with node_access(). API.module will hyperlink.

// $Id:$. I have always done // $Id$. No colon. Not sure it matters.

+ // We grant access to the node if the following conditions are met". i would like clarify "...if either of the following conditions ..."

if (in_array(NODE_ACCESS_DENY, $access, TRUE)) {
+  	return FALSE;
+  }
+  elseif (in_array(NODE_ACCESS_ALLOW, $access, TRUE)) {
+  	return TRUE;
   }

Indentation troubles.

"node type" should be "content type"

Crell’s picture

Status: Needs work » Needs review
FileSize
19.05 KB

- @see converted to @link. That was inherited from the hook_access() docblock, which I guess was indeed wrong. This syntax now based on IRC conversation with moshe and sdboyer that seems to be what api.module wants. Check.

- CVS Id tag: I've never understood how those things work, so whatev: Check.

- Comment on granting access: Actually it's if *both* of those conditions are met. If they're not both met, we don't get access, or fall back to the rest of that function. Comment updated accordingly. Check.

- Indentation: Bah, stupid eclipse and its random tabs. Check.

- "node type" -> "content type" in the UI: Check.

- New patch attached: Check.

Crell’s picture

For record keeping purposes, I've just marked as duplicate the following predecessor issues:

#21613: Remove hook_access
#143075: Grant equal time to nodeapi for access control responsibilities
#122173: Extensible Node Access/Authorisation Capability (simple patch!)

This has been wanted for a looooong time it looks like. :-)

Dave Cohen’s picture

Can we make is so that Blindly returning FALSE will throw an error instead of Blindly returning FALSE will break other node access modules? I think it would be smarter to make constants (NODE_ACCESS_ALLOW, etc) be 1,2,3 or 'allow', 'deny', 'ignore' instead of 1, 0, NULL. Force people to implement the function correctly.

I think the documentation for hook_node_access should state that NODE_ACCESS_DENY trumps NODE_ACCESS_ALLOW. And that either response trumps the node_access table.

Dave Cohen’s picture

Status: Needs review » Needs work

In nodeperms_node_access(), every instance of NODE_ACCESS_DENY needs to be replaced with NODE_ACCESS_IGNORE.

Otherwise, the node_access table will not have any effect on update or delete permissions.

Conceptually, checking the permission on the permission page means "this is allowed", while not checking it means "some other module might allow this, or might not". Leaving the permission unchecked is not the same as deny.

Crell’s picture

I want to keep NODE_ACCESS_IGNORE as NULL, so that it is the same effect as returning nothing. I can be convinced of going to "allow" and "deny" if it's actually a DX improvement. I'm undecided there.

As for nodeperms_node_access(), well, that's a very good question. Since we can set a node type to be unaffected by the nodeperms module, do we want a lack of permission to mean "deny" or "ignore"? I can see an argument both ways. (And never quite understood what it does in D6 in the first place, which is one reason for this patch. :-) )

Anyone else want to weigh in on either point?

webchick’s picture

Subscribing for now.

Gribnif’s picture

This is exactly what I've been wanting for the last 3 1/2 years. Thank you.

Crell’s picture

@Gribnif: If you want it, it needs to get committed first. :-) Do you have any feedback on the questions in #6?

Dave Cohen’s picture

In D6, leaving a node permission unchecked means "defer to node_access table". So if you choose DENY instead of IGNORE, its a change people will have to get used to. One effect of the current system is that I can check a permission for admin roles, while leaving it unchecked for non-admin roles. That way admins always get the privilege but for other users it defers to node_access.

You could argue that users should either use nodeperms or node_access, but never mix the two. If that's your choice, just bear in mind how it is different from the current behavior that people have sites configured for. And if you're into enforcing that discipline, wouldn't you also force hook implementers to return the proper value (instead of NULL)? ;)

Gribnif’s picture

OK, here's my idea. It's so radical, that I don't think it's going to get much love, though. :-)

Coming from a UX perspective, I think it makes more sense to completely abstract the permissions system, so that everything currently provided using the grants table is moved in to a removable "grants" module. I don't think it's necessary to configure which node types use grants and which don't; this can be an all-or-nothing, based upon whether or not the module is active.

If the grants module is not active, there would be significantly fewer entries in admin/user/permissions. All content would be wide-open to view, but not to create/update/delete.

Any module can affect the permissions by adding its ALLOW/DENY vote about a given node. This includes the grants module, which is treated identically to all others. If any module returns a definitive ALLOW or DENY, then it takes precedence. If not, the default is TRUE for view, and FALSE for everything else.

(Stepping into flame-retardant suit)

Crell’s picture

Gribnif, please see the issues linked previously where we've discussed these issues at painful length. There's a lot in the permission system that has nothing to do with nodes. Node access is also quite complicated because of "list" operations, which *must* happen in the database for paging or multi-loading to work.

Please don't derail this issue. We've already had 3 years of radical ideas that went nowhere for a reason. That's why this is non-radical, so hopefully it can get into Drupal 7, finally. We just need to work out the finer details.

Gribnif’s picture

I didn't realize the idea had been brought up before, so consider it dropped. I'm happy enough with the proposed change.

As far as the remaining questions go, I would vote for NODE_ACCESS_IGNORE===NULL and lack of permission means "ignore". Maybe there's something you can do in the UI to make this more obvious, though.

Crell’s picture

Just marked #153998: Access checks returning NULL is ugly. as a duplicate, too.

Crell’s picture

Status: Needs work » Needs review
FileSize
18.98 KB

Revised patch. We now use the string values "allow" and "deny" for the allow/deny constants, but still NULL for "ignore". The nodeperms module now also defaults to ignore rather than deny to more closely match D6 behavior.

amitaibu’s picture

Patch removes tabs from #15, and adds a test. The test allows a user to edit the node if the title of the node is the same as the user name.

Status: Needs review » Needs work

The last submitted patch failed testing.

amitaibu’s picture

Status: Needs work » Needs review

oh my, I've re-rolled something wired. Will re-roll soon.

amitaibu’s picture

@Crell, The test that I've added doesn't pass, although I thought it would - maybe you can see what I did wrong there... :/

So, I'm putting to patches here - one with the test and one without (just with the tabs fix).

salvis’s picture

Priority: Normal » Critical

As Crell has dismissed #153998: Access checks returning NULL is ugly., this needs to be committed, because node access is just as broken in D7 now as it was in D6 before the commit in #153998-99: Access checks returning NULL is ugly..

Returning NODE_ACCESSS_IGNORE/NULL/whatever to allow the node access table to kick in if hook_node_access() is undecided is a very important feature.

salvis’s picture

Status: Needs review » Needs work

@Amitaibu: You've lost the fixes to the currently broken Blog, Forum, and Poll modules, as well as 'details' like

=== added directory 'modules/nodeperms'

and

=== modified file 'profiles/default/default.info'
--- profiles/default/default.info	2009-07-15 02:08:40 +0000
+++ profiles/default/default.info	2009-08-02 05:48:32 +0000
@@ -14,3 +14,4 @@ dependencies[] = taxonomy
 dependencies[] = dblog
 dependencies[] = search
 dependencies[] = toolbar
+dependencies[] = nodeperms

@Crell: You have a few tabs (in nodeperms_configuration() and nodeperms_node_access()) that should be removed.

amitaibu’s picture

Status: Needs work » Needs review
FileSize
19.01 KB

Here's Crell's patch - without the tabs.

moshe weitzman’s picture

Status: Needs review » Reviewed & tested by the community

I read through this one more time. IMO, this is RTBC.

salvis’s picture

Status: Reviewed & tested by the community » Needs review
FileSize
19 KB

Very nice patch, +1! Just three nits:

1.

function poll_permission() {
$perms = array();
$perms +=
array(

2.

* Impelement hook_menu().

(removed an 'e')

3.

// If we have not configured whether or not to manage a given notnode type, assume

No other changes.

moshe weitzman’s picture

Status: Needs review » Reviewed & tested by the community
tic2000’s picture

+++ modules/nodeperms/nodeperms.module	2009-08-06 03:31:53 +0000
@@ -0,0 +1,126 @@
+  // Build standard list of node permissions for this type.
+  $perms = array(
+    "create $type content" => array(
+      'title' => t('Create %type_name content', array('%type_name' => $info->name)),
+      'description' => t('Create new %type_name content.', array('%type_name' => $info->name)),
+    ),
+    "edit own $type content" => array(
+      'title' => t('Edit own %type_name content', array('%type_name' => $info->name)),
+      'description' => t('Edit %type_name content created by the user.', array('%type_name' => $info->name)),
+    ),
+    "edit any $type content" => array(
+      'title' => t('Edit any %type_name content', array('%type_name' => $info->name)),
+      'description' => t('Edit any %type_name content, regardless of its author.', array('%type_name' => $info->name)),
+    ),
+    "delete own $type content" => array(
+      'title' => t('Delete own %type_name content', array('%type_name' => $info->name)),
+      'description' => t('Delete %type_name content created by the user.', array('%type_name' => $info->name)),
+    ),
+    "delete any $type content" => array(
+      'title' => t('Delete any %type_name content', array('%type_name' => $info->name)),
+      'description' => t('Delete any %type_name content, regardless of its author.', array('%type_name' => $info->name)),
+    ),
+  );

Am I the only one that think the descriptions are not needed?

+++ modules/nodeperms/nodeperms.module	2009-08-06 03:31:53 +0000
@@ -0,0 +1,126 @@
+  // that we do want to.  That ensures that a newly created node type will have

There is an extra space here.

This review is powered by Dreditor.

Crell’s picture

The descriptions are already in core. Whether or not to keep them is a question for a separate issue; we're just moving code around here.

Thanks, everyone!

salvis’s picture

Status: Reviewed & tested by the community » Needs review

In D6, node_access() used to call filter_access() to help decide whether to allow updating:

  // If the node is in a restricted format, disallow editing.
  if ($op == 'update' && !filter_access($node->format)) {
    return FALSE;
  }

Removing this was first discussed in #372743-51: Body and teaser as fields and decided in #192 and following. It was committed with the understanding that something like #91663-63: Permission of text format is not checked when editing an entity and instead reset to something a user can use. would be committed, too, but that issue is not moving.

What is the plan now?

(D6 node_access() is currently broken because it ignores $account when calling filter_access(). This is being worked on in #470840: Bug in node_access if we specify an account when check the filter_access.)

moshe weitzman’s picture

@salvis - are you sure that this issue needs to move out of rtbc for that bug. filter_access() is related gto what we are doing here but i don't think it is right to make this dependant on an unresolved issue in nearby code. or i am misunderstanding the relationship.

Crell’s picture

Status: Needs review » Reviewed & tested by the community

IMO, filter-based access checks like that are a WTF to begin with. They're not part of the node access system; they're a sideband from it, probably best handled in the form itself. (You can throw an access-denied from literally anywhere, which is another WTF design in Drupal but also a separate issue.)

This issue is not related to those other issues, and they should not hold this one up. Since I didn't post the most recent patch and there's been no code change since then, I'm going to move this back up to RTBC. :-)

salvis’s picture

Ok, fine.

(Throwing "an access-denied from literally anywhere" isn't so great. I'm working on devel_node_access to explain why a given user has or hasn't CRUD rights on any given node, and I'm hoping for something better than having to try to edit to find out whether something gets thrown or not... I'm happy we were able to talk about it :-))

sethcohn’s picture

+1, just discovered the mess of hook_access, for a site that needs to allow anonymous users to edit newly created (by them) nodes in an ubercart cart, and found this issue, which would make the solution trivial, as opposed to the hacky method I need to solve it now (redefine the nodetype(s) as owned by my own custom module, just to catch the hook_access for it so I can deal with the special case)

webchick’s picture

Status: Reviewed & tested by the community » Needs work

This is a great little patch. Eliminates basically all of the WTF from the node access system, and makes it actually understandable to developers. Hooray!

Question: Why bother with the node_access table at all anymore after this?

+++ modules/node/node.api.php	2009-08-06 03:47:31 +0000
@@ -342,6 +342,63 @@ function hook_node_load($nodes, $types) 
+ * block access, return NODE_ACCES_IGNORE or simply return nothing.

ACCESS

+++ modules/node/node.api.php	2009-08-06 03:47:31 +0000
@@ -342,6 +342,63 @@ function hook_node_load($nodes, $types) 
+  $type = is_string($node) ? $node : (is_array($node) ? $node['type'] : $node->type);

Ew? Can we please clean up the calling code so that we always get passed in a full $node object?

+++ modules/node/node.api.php	2009-08-06 03:47:31 +0000
@@ -342,6 +342,63 @@ function hook_node_load($nodes, $types) 
+  return NODE_ACCESS_IGNORE;

How about document something like:

// Alternately, simply do not return a value from this function.

+++ modules/node/node.module	2009-08-06 03:29:45 +0000
@@ -16,6 +16,21 @@
+define('NODE_ACCESS_ALLOW', 'allow');

Why strings rather than numbers? Not really opposed, but it's different than what we normally do. I would've expected -1 (deny), 0 (neutral), and 1 (allow).

+++ modules/nodeperms/nodeperms.admin.inc	2009-08-03 02:04:54 +0000
@@ -0,0 +1,29 @@
+
+  $form = array();

Minor, but you don't need to instantiate an array if you're specifying the key when adding an index, like you are below.

+++ modules/nodeperms/nodeperms.admin.inc	2009-08-03 02:04:54 +0000
@@ -0,0 +1,29 @@
+    '#description' => t('Select which content types should use Drupal\'s permission system for controlling access. Note that you may have more than one module affecting access to the same content type.')

Please use double quotes here so you don't have to escape the apostrophe.

+++ modules/nodeperms/nodeperms.admin.inc	2009-08-03 02:04:54 +0000
--- modules/nodeperms/nodeperms.info	1970-01-01 00:00:00 +0000
+++ modules/nodeperms/nodeperms.info	2009-08-03 02:05:07 +0000
+++ modules/nodeperms/nodeperms.info	2009-08-03 02:05:07 +0000
@@ -0,0 +1,8 @@
+name = Node Permissions

Hm.

a) We don't use the word "Node" in user-facing text.
b) We don't usually abbreviate the names of things, i.e. "perms"
c) "nodeperms" is not a word. :P

Why not 'contentaccess' if we're going to smoosh two words together? And why are we so opposed to underscores, again?

+++ modules/nodeperms/nodeperms.module	2009-08-06 03:31:53 +0000
@@ -0,0 +1,126 @@
+  // Build standard list of node permissions for this type.

While we're at it, why not 'view'? That would eliminate a *HUGE* WTF for the vast majority of people installing Drupal, and would allow people not to have to resort to choosing from the absolutely confusing array of node access modules for what most would assume is basic CMS functionality. I could also see this being a 'killer feature' of Drupal 7.

+++ modules/nodeperms/nodeperms.module	2009-08-06 03:31:53 +0000
@@ -0,0 +1,126 @@
+  $configured_types = variable_get('nodeperms_types', array());

Should we statically cache this? Looks like it might be re-generated several times on a page.

+++ profiles/default/default.info	2009-08-02 05:48:32 +0000
@@ -14,3 +14,4 @@ dependencies[] = taxonomy
+dependencies[] = nodeperms

Do we want to add this to expert profile as well?

19 days to code freeze. Better review yourself.

Gribnif’s picture

@webchick:

Why strings rather than numbers? Not really opposed, but it's different than what we normally do. I would've expected -1 (deny), 0 (neutral), and 1 (allow).

Personally, I'd vote for -1 (neutral), 0 (deny), and 1 (allow). That way it's more analogous to a boolean.

Crell’s picture

I only have a few minutes to respond, but here's a partial response:

1) node_access table is still needed for listing queries. hook_node_access() requires a full node, and is of no help at all when selecting out, say, the most recent 10 nodes that I have access to see (the default front page). That's the same as before.

2) No we can't clean up the calling code, as for "create" operations there is no node yet. That line of weirdness is pulled directly from the old node_access() function.

3) For why strings and not ints, see #4 above and following. This is actually more robust as it makes it harder to deny-by-accident, which a number of D5 and D6 modules did and it broke stuff. Project module was one of those modules. :-)

4) "nodepermissions" was too long to type. We DO use the word node, for the name of modules. There are hundreds of modules that have "node" in their name. It's also not just about access but permission-based access specifically, so "contentaccess" is misleading. I'm not going to bikeshed here. Suggest something better that's not more misleading. :-P

5) We don't include "view" because the old system didn't, because it doesn't work for list operations. We could add it, but that has a lot of other considerations as it introduces other WTFs. I recommend we punt on that until the rest of this patch is in, because that's a debate that's been going on for years.

Crell’s picture

Status: Needs work » Needs review
FileSize
19.43 KB

Attached patch addresses webchick's notes above, aside from those I noted above. Caveats:

1) The only way to split up that weird ternary (which has been in core forever) is to split hook_node_access() into four separate hooks, one for each op. That's not something I want to deal with in this patch, but we could discuss in a follow-up.

2) I realized why I didn't statically cache the $configured_types variable. It works fine in normal usage, but during unit tests it would need to get manually reset several times in various places that I couldn't quite figure out. If someone else wants to, go for it, but I want to keep it simple. :-) That's a small performance optimization we can deal with later when someone has the time to figure out how to make simpletest play nice. It has no effect on the API.

moshe weitzman’s picture

Status: Needs review » Reviewed & tested by the community

I read through this again and am quite satisfied.

Dries’s picture

Status: Reviewed & tested by the community » Needs work

While it seems to be moving in the proper direction, I'm not really satisfied yet. It is reasonably close though.

1. I don't like the module name 'nodeperms'. Why does this need to be a separate module? I recommend that we move it back into node.module. Now that the behavior is configurable, the value of making it a separate module is really low, and just makes it more confusing for new users. It is such a small module. I personally don't think it is worth making a module.

2. Don't abbreviate permission(s) as perm(s).

3. + $type = is_string($node) ? $node : (is_array($node) ? $node['type'] : $node->type); is still pretty ugly. Can we fix our parameter handling? If not, we can leave that for a follow-up patch.

4. I didn't understand the 'Node permissions' settings at first. What does it mean not to control permissions? That everyone can create and edit the nodes? The description didn't properly educate me and I had to go and look at the code to make sense of it ... So when I install an alternative node access module, I need to go and uncheck those checkboxes? When I create a new node type, do I need to go and check the permissions or does it come with sane defaults? Also, shouldn't the node permissions settings be part of the per-content type settings? This sounds like it could be a usability regression so I'd like to see this discussed some more so we can nail the help text, the default behavior and the configuration workflow.

5. Do we need a couple of new/extra tests for this? We don't test the configuration setting, and given that this has security implications, I would suggest we write some small tests to make sure the configuration actually works.

dmitrig01’s picture

Re 35.4, too long to type is not an excuse. If it does end up being a separate module, it should be called node_permissions

Crell’s picture

1) It's a separate module because we want the ability to rip it out completely. There are plenty of use cases where a simple role-based approach is simply useless for node access controls, and having extra permissions floating about just makes the UI more complicated. For example, I run a site where every single node type is either an OG or part of an OG, and I want the node access controls to always be OG-sensitive. Why burden the system with extra permissions, admin screens, and code weight that I don't need?

2) I didn't want to be the person to introduce underscores to core module names. But if Dries wants it, I can blame him if someone complains about it later. :-)

3) Yes that line is ugly, but it's no uglier than the same line that's in core now. The only way to clean that up sanely is to split hook_node_access() into hook_node_access_view(), hook_node_access_update(), etc. Given that more complex setups may share a lot of code between $ops (like active OG context detection), I'm not sure that's wise. If a committer mandates it I'll do it, but I would rather save that for a follow-up patch in case that doesn't get in. (Better one hook than no hook, and we're close to freeze.)

4) I welcome new suggestions on the description text. I made it its own admin screen mostly for simplicity, as it means we can use a single variable instead of many. (And because the current way that we save node type settings in Drupal is a general train wreck, but that's not an issue I can solve here.) Bear in mind that other node access modules may for whatever reason not be able to put their configuration per-node-type (that's the whole point), so I'm wary of standardizing on the node type config page here. That could put some selections on that page and some elsewhere. I will defer to Drieschick on this one but it may complicate the code somewhat.

5) Sigh. I'll see what I can do on the plane I'm about to get on.

Crell’s picture

FileSize
60.17 KB

Per webchick's suggestion, here's a screen of the nodeperms module's admin page. As noted above, alternate text is more than welcome.

Crell’s picture

Further thoughts after talking with webchick:

Right now, several core modules (upload and comment in particular) have a nasty habit of turning themselves on for every node type you create until you explicitly go in and disable them. There's no central place to manage that, so you have to go in and do it separately for every single node type. (Try a site with 20 node types and one of them wants comments. Yes, I have done that site before.) That's highly annoying and I've never heard anyone say anything good about that default behavior.

If node permissions are integrated back into node module so that they cannot be globally disabled (by disabling the module), AND we move the configuration to per-node-type-page, then we recreate the same problem. If you don't want to use permissions for node access, you have to go to a separate page for every single node type to disable an admittedly not-inherently-obvious setting. (Does that mean we document what "node access" is on every node type page? That could be a lot of non-trivial text on an already busy page.)

So from a usability perspective, we could make permissions required and force users to disable them if they don't want to use them OR we could move the configuration per-node-type-page. I don't think it's wise to do both, however.

dmitrig01’s picture

we move the configuration to per-node-type-page, then we recreate the same problem. If you don't want to use permissions for node access, you have to go to a separate page for every single node type to disable an admittedly not-inherently-obvious setting. (Does that mean we document what "node access" is on every node type page? That could be a lot of non-trivial text on an already busy page.)

On the flip side, if consolidate them into one page, we introduce inconsistencies in the interface - some things will be on the individual node type pages, while other things will be on other pages. If one thing puts on a separate page, everything should be on seaprate pages. In fact, according to your model (although exaggerating it greatly), there should be no node type admin pages - we should have pages like "names of node types, descriptions of node types." While I admit that that never should and never will happen, it's the concept that matters.

webchick’s picture

This is OT for this issue, but I personally would love us to introduce a nice hook_node_settings() or something so that these could all be aggregated and we could define a "defaults" page. Right now your only prayer is grepping through the code of modules that implement hook_form_alter() and looking for $form_id node_type. :P Ugh. :P

Crell’s picture

I'm also +1 on hook_node_settings() or something, at least better storage for that then the variable system, and mentioned such over in the hook_variable_info() patch. That's out of scope here, though, I agree, and likely won't happen until Drupal 8. :-)

catch made an interesting suggestion on Saturday in IRC to have no UI at all in core; move the functionality back into node.module and then have a variable that only gets edited by contrib, since, legitimately, most sites won't be using an alternate mechanism (probably?). I'm skeptical of that, as it feels like a dodge on the UI question and having core variables that can only be edited by a contrib makes me feel uneasy. It also means another module I'd have to maintain. :-) However, if Drieschick would prefer that I am willing to do so.

catch’s picture

Crell's summary of what I said is dead on. The only situation where you need to override default behaviour, is where you 1. have a contrib or custom node access module installed 2. the core behaviour conflicts with how you want to use it. Since that's not a common scenario in the scheme of things, I think a core UI (and/or a new core module) is a bit overkill. But, we should allow the flexibility to override things like this, which is either a variable without a settings page (we already have several of these in core, it's not a new idea), or some other way to switch it off without patching node_node_access().

webchick’s picture

I agree with Dries that I'm -1 to a new module just for this. It makes things needlessly confusing for the default behaviour that probably at least 99.5% of sites are going to use, since this is all that Drupal can do now, and has been able to do since its inception. Also, another thing catch said during that conversation that I strongly agree with is "If I don't like what OG does by default in hook_node_view(), I don't make a og_hook_node_view.module".

Hidden variables for advanced features that power users and/or contrib modules with edge cases use is the standard way we have of dealing with this situation, so makes sense to re-use here IMO.

Crell’s picture

I admit that the use case for the admin in core is less after the permission hook switched from using no-perm-is-deny to no-perm-is-ignore logic, although I can certainly see a use case for no-perm-is-deny, too, especially once you only apply it to certain node types. I also disagree that the og_node_view() example is equivalent. Hooks like hook_node_view() are designed to come in "sets", quite often. hook_node_access() is intended to be its own little animal to be swapped out separately. If OG directly implements it for all OG node types and doesn't let you turn that off, I'm filing a bug against OG. :-) (Although I think OG does way too much in one module now to begin with, and we'd be better served by it breaking up into a series of pluggable modules, but that's also well-OT.)

That said, if that's what needs to happen to get this in core I'll see about rolling an admin-free patch tonight. It will probably be an exact copy of the last patch with the hook moved back to node.module and the rest of nodeperms/node_permissions moved to a contrib. Hopefully that means it doesn't need new simpletests, since core's intended behavior will not be changing at all. :-)

Gribnif’s picture

In our case, we have our own module that completely replaces all node access. If the permissions code was to remain in core in such a fashion that it could not be removed, then I would at the very least like to have a way to automatically default newly-added content types so that the permissions code does not affect them.

Otherwise, we would end up having to uncheck yet another checkbox every time we add a new content type--or wonder why permissions are acting in undesirable ways.

It's fine with me if this is controlled by a hidden variable.

Crell’s picture

I'm not sure how to make node permissions default-off from contrib without yet another hidden variable. Even just the one strikes me as ugly enough, I don't want to add a second. :-( If you have another suggestion, let me know by tonight. :-)

Crell’s picture

Status: Needs work » Needs review
FileSize
16.62 KB

OK, here's a revised patch with no UI. After some discussion with webchick, I also converted from one big variable to a separate variable for each node type, with a default of 1. That simplifies the code a little, and also makes it easier for modules to selectively exclude their own module-defined node types if they want to. In practice it won't mean any new variables in the variables table, because unless another module starts messing with the settings the variables will never get saved and always use the default. That means we still need no update hook.

The net result is that core's behavior absent a contrib module is entirely unchanged by this patch, except that blog, poll, and forum's permissions move to the node module's heading on the permissions page. Everything else is contrib-enabled.

I guess after code freeze I'll revisit this and make a UI in contrib to ensure that there's a single standard mechanism for messing with the permissions rather than every other hook_node_access module inventing its own method. :-)

Crell’s picture

FileSize
19.73 KB

webchick asked for improved comments on hook_node_access() in node.api.php. However, on further review that docblock already references the node_access group's docblock, but that was never updated for these API changes. Oopsies. :-) Attached patch changes no code from the previous version but revamps the node_access defgroup's docblock to explain how the system now works.

It also occurs to me that there's really no good reason to lock down the possible operations to view, update, create, and delete, but I'll leave removing that check for another patch as I really really really want to get this hook in ASAP.

moshe weitzman’s picture

Status: Needs review » Needs work
+++ modules/node/node.module	2009-08-18 07:09:54 +0000
@@ -2188,28 +2202,35 @@ function node_search_validate($form, &$f
  * proper functioning of the pager system. When adding a node listing to your
  * module, be sure to use db_rewrite_sql() to add
  * the appropriate clauses to your query for access checks.

ewww. lets not advocate use of db_rewrite_sql() anymore. i know you are not changing this line but you got close to it :) ... How about "Be sure to use a db_select() and add a query tag of 'node_access'. This insures that only accessible nodes are retrieved."

After that change, this is RTBC.

Crell’s picture

Status: Needs work » Needs review
FileSize
19.9 KB

Meh. May as well since the new method works and db_rewrite_sql() is dead as soon as search.module is converted. :-) Here you go.

moshe weitzman’s picture

Status: Needs review » Reviewed & tested by the community
Dave Cohen’s picture

On topic: nice work everyone.

Off topic: what?!? db_rewrite_sql is dead? Is there doc anywhere about how to rewrite code that relies on it?

Crell’s picture

Yes, the new DB documentation covers how to use dynamic queries and tags, and the docblock here now covers it, too. Let's get this committed so that it's properly documented. (db_rewrite_sql() has been deprecated now for almost exactly a year.)

agentrickard’s picture

Status: Reviewed & tested by the community » Needs work

Throwing a little more node_access developer weight behind this patch. This standardizes the access system, so that 'node' modules are no longer first-class citizens with rights that other modules can never attain. And a huge +1 to letting modules act on node 'create' permissions.

The rest we can iron out later.

One note:

Users with the "administer
+ * nodes" permission may always view and edit content through the
+ * administrative interface.

We swapped this for the 'bypass node access' permission because 'administer nodes' was too all-powerful, so the docs need at least one change.

Crell’s picture

Status: Needs work » Reviewed & tested by the community
FileSize
19.9 KB

Right you are. Fixed.

Dries’s picture

Priority: Critical » Normal
Status: Reviewed & tested by the community » Needs work

It looks great now. Thanks for incorporating our suggestions. I committed this to CVS HEAD.

I'm marking this 'code needs work' for three reasons:
1. Let's get the upgrade instructions updated before we mark this fixed.
2. I recommend that we follow-up with a CHANGELOG.txt entry.
3. I didn't see any tests for this yet. Given that this touches security, I'd still like to see some tests.

Thanks Crell, Amitaibu, salvis et al.

moshe weitzman’s picture

Well done.

3. is covered by NodeAccessUnitTest IMO.

Crell’s picture

I've updated the module update docs accordingly.

amitaibu’s picture

I shouldn't have been credited for the issue, but I'm still happy it's in :)

webchick’s picture

Issue tags: +Needs documentation

Tagging.

Crell’s picture

Issue tags: -Needs documentation

Webchick, I just added the documentation in #62. :-) All we still need is a CHANGELOG entry.

webchick’s picture

Issue tags: +Novice

I am sleepy. Sorry. :)

Tagging Novice. Hello, Novice! We need a quick one-liner patch to add an entry to CHANGELOG.txt for this patch. Feel free to drop by #drupal if you have questions on how to do that.

Crell’s picture

Status: Needs work » Reviewed & tested by the community
FileSize
711 bytes

I am not a novice, but I hate seeing patches marked Needs Work. :-) I noted that we didn't have any mention of node access changes in the changelog yet so I mentioned a few of them.

webchick’s picture

Status: Reviewed & tested by the community » Fixed
Issue tags: -Novice

I added another one for:

* Access control affects both published and unpublished nodes.

And committed to HEAD. Thanks! :D

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

Status: Closed (fixed) » Needs review

adrian.mar requested that failed test be re-tested.

Status: Needs review » Needs work

The last submitted patch failed testing.

Crell’s picture

Status: Needs work » Closed (fixed)

Dude, seriously?