Node access control in Drupal 5

Access rights for all node types come from the node_access function in the node module. Unfortunately this does not give modules full control on the node types' access permissions, especially on the types provided by the node module itself ("story", "page", and all custom types created in "Administer" / "Content management" / "Content types" / "Add content type").

For the node module's types you can set permissions in the "Access control" screen of Administration. Modules are allowed to implement their own node access control by manipulating the contents of the node_access table.

There're two points in the node_access function, where an external module can influence the access rights of nodes:
  1. The provider module of the node type must implement the hook_access hook and it can control the access rights of the nodes in the given node type in almost every way it likes. Note that the provider of the default node types ("story", "page") and the customer (user created) node types is the node module itself and its access function (node_content_access() does not allow further hooks or the like. So you cannot plug in any more control on these types at this point.
  2. If the provider module did not return any result for the hook call AND the operation is not creation of a new node AND the node is published (!), then the contents of the node_access table are evaluated.
(Note: the documentation of hook_access tells that "if this hook is not defined for a node type, all access checks will fail, so only the administrator will be able to see content of that type". However this doesn't seem to be the case. AFAIK hook_access is called only in one place: node_access. And as it is implemented there, the checks go on if this hook is not implemented in the provider module of the node type ... thus in this case the contents of the node_access table tell the permission for the node and the given operation.)

There're some other rules that affect the access rights of a node, but a third party module can plug itself in only at these two points. So if the provider module does not return a definitive right ('allow' or 'deny') for the current user on the given node, then the node_access table is only consulted if the node is published!

I had to dig myself into all this, because I had to implement a workflow for a news portal with proper access control. There's already an excellent module for this (guess what ... it's called Workflow and it has a supplementary module called workflow_access Smile). But regardless of how (or with which module) I implement the required functionality, I cannot control permissions of unpublished nodes (eg. grant permission to view or edit the node, or just change its workflow state) without giving "administer content" permission, because the node module won't let any third party modules have control over unpublished nodes. Sad

The only way to achieve this through a module would be to create a copy of the node module that would allow creation of custom node types and allow control on unpublished modules too. Of course creating a "copy" in case of the node module is not a five minute job (actually it looks a lot more like 5 days, since you've to copy its functionality), so I opted for patching the node_access functionality to remove the restriction.

(Note: thinking about this a bit more ... most probably one could copy the node module by replacing all occurances of the "node" string with the name of the new module and replace "story", "page" and probably a couple of other strings with something else ... but in the end you'd get something that you could not use with any other module in the system. And I'm quite sure that the whole Drupal core depends on "nodes" as they are ... so half of Drupal's built-in functionality should be copied as well. Definitely not a five minute job. Smile)

To patch the node module, you should look for the following line in node_access:
  if ($op != 'create' && $node->nid && $node->status) {

and change it into this:
  if ($op != 'create' && $node->nid) {

The $node->status contains the published/unpublished flag of the node and removing this condition will allow the node_access based access control modules to set permissions on unpublished nodes too.

Important: this patch changes the default behaviour of node publishing! The node_access table contains one single record by default that allows everybody to view any node in the system. So apply this patch only if you use an access control module and have set the "view" permissions for all roles explicitly. Otherwise all your nodes will be made visible (aka. published) to all visitors (anonymous ones too)!