I didn't have time to really dig deeper, but permissions_grant_permissions() intermittently fails to grant a role a permission. What i did find was that this returned nothing when the failure happens:

// Fetch the permissions string for the given role id
$permissions = permissions_get_permissions_for_role($role->name);

I ended up implementing my own simpler version of this function (scary though because of the security implications).

Comments

ebeyrent’s picture

Assigned: Unassigned » ebeyrent
Nick Robillard’s picture

Something i didn't mention - role granting always worked for "authenticated user" role, but failed for a custom role.

ebeyrent’s picture

Hi there,

I am sorry you're having some trouble with the module. In order to help you, I'm going to need some more specific information, and possibly a code sample to see what's happening.

- Are you running the latest stable version of the module?
- Have you checked watchdog for any errors?

I whipped up a sample module to test some basic functionality, and I'm not seeing any issues. Perhaps you could take a look at this example and compare it to what you've got?

// my_module.install

<?php

function my_module_update_6001() {
  $ret = array();
  permissions_create_role('demo');
  $role = permissions_get_role('demo');
  if($role) {
    $ret[] = array(
      'success' => true,
      'query' => t('Successfully created role %name with id %id', array('%name' => $role->name, '%id' => $role->rid)),
    );
  }
  else {
    $ret[] = array(
      'success' => false,
      'query' => 'Failed to create role \'demo\'',
    );
  }
  return $ret;
}

function my_module_update_6002() {
  $ret = array();
  $permissions = permissions_get_permissions_for_role('demo');
  if(empty($permissions)) {
    $ret[] = array(
      'success' => false,
      'query' => 'Role \'demo\' has no assigned permissions.',
    );
  }
  else {
    $ret[] = array(
      'success' => true,
      'query' => t('Role \'demo\' has %count permissions', array('%count' => count($permissions))),
    );
  }
  return $ret;
}

function my_module_update_6003() {
  $ret = array();
  permissions_grant_all_permissions_by_module('demo', 'node');
  $permissions = permissions_get_permissions_for_role('demo');
  if(empty($permissions)) {
    $ret[] = array(
      'success' => false,
      'query' => 'Role \'demo\' has no assigned permissions.',
    );
  }
  else {
    $ret[] = array(
      'success' => true,
      'query' => t('Role \'demo\' has %count permissions', array('%count' => count($permissions))),
    );
  }
  return $ret;
}

function my_module_update_6004() {
  $ret = array();
  permissions_grant_all_permissions('demo');
  $permissions = permissions_get_permissions_for_role('demo');
  if(empty($permissions)) {
    $ret[] = array(
      'success' => false,
      'query' => 'Role \'demo\' has no assigned permissions.',
    );
  }
  else {
    $ret[] = array(
      'success' => true,
      'query' => t('Role \'demo\' has %count permissions', array('%count' => count($permissions))),
    );
  }
  return $ret;
}

When I go to update.php and I run these updates, here's the results:

The following queries were executed
my_module module
Update #6001

* Successfully created role demo with id 5

Update #6002

* Failed: Role 'demo' has no assigned permissions.

Update #6003

* Role 'demo' has 21 permissions

Update #6004

* Role 'demo' has 61 permissions

You'll notice that update 2 failed because the newly created role didn't have any permissions, which is expected. The subsequent updates grant permissions to the role, and you can see how that's reflected in the results.

You can also take a look at the documentation for this module: http://drupal.org/node/802272

ebeyrent’s picture

Status: Active » Postponed (maintainer needs more info)
michaelraasch’s picture

Priority: Normal » Major
Status: Postponed (maintainer needs more info) » Active

I have come across the same problem and can give you more details. The culprit is in function permissions_grant_permissions()

      // Fetch the permissions string for the given role id
      $permissions = permissions_get_permissions_for_role($role->name);
      // Cross reference new permissions with those appropriate to the modules on the site.
      $defined_permissions = module_invoke_all('perm');
      $new_permissions = array_intersect($new_permissions, $defined_permissions);
      if(count($new_permissions) > 0) {
        // Check to see if there are existing permissions
        if(count($permissions) > 0) {
          // Add the new permissions if the role doesn't already have the permission
          foreach($new_permissions as $permission) {
            if(! in_array($permission, $permissions)) {
              $permissions[] = trim($permission);
            }
          }
    
          // rebuild the permission string
          $updated_permissions = join(', ', $permissions);
          db_query("UPDATE {permission} SET perm = '%s' WHERE rid = %d", $updated_permissions, $role->rid);
          return true;
        }
        else {
          // No permissions have been set for this role, so we need to insert some
          foreach($new_permissions as $permission){
            $permissions[] = trim($permission);
          }
          // rebuild the permission string
          $updated_permissions = join(', ', $permissions);
          db_query("INSERT INTO {permission} (rid, perm, tid) VALUES(%d,'%s',%d)",$role->rid,$updated_permissions,0);       
          return true;
        }
      }

in the check for if(count($permissions) > 0)

When a role is stripped of its last permission then the field {permission}.perm is set to an empty string. However when a new permission is applied, then if(count($permissions) > 0) returns FALSE and a new row is inserted instead of running an update of the existing row.

Try this to reproduce:
  1. create a role with no permissions
  2. add one permission
  3. revoke the permission
  4. add another permission
  5. check table {permission} and you should see two rows for the role, one with no permissions and one with the permission you have added last

I suggest either

  • db_query('DELETE FROM {permission} WHERE rid = %d', $role->rid); before the INSERT-statement

or

  • get rid of the if-statement completely, merge the $permissions and $new_permissions, run a DELETE and INSERT statement all the time.

Personally I think option 2 is better because clearer.

I hope this makes sense.
ebeyrent’s picture

I have confirmed this behavior and will fix it sometime this week. Thanks for tracking this down!

ebeyrent’s picture

Actually, the problem is in the permissions_revoke_permissions() function. If the user only has one permission and that permission is revoked, the row in the permissions table is getting updated instead of deleted.

michaelraasch’s picture

@ebeyrent: That would be even cleaner and more correct of course.

ebeyrent’s picture

This is fixed in the latest commit, and in 6.x-2.13.

ebeyrent’s picture

Status: Active » Fixed

Status: Fixed » Closed (fixed)

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