We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

Acl Role Wildcard issue (possible bug)

I'm trying to make use of the Phalcon Acl in my application but I'm running into some very strange behavior. I'm using the Vokuro app as a guideline to persist Profiles (Acl Roles) to the database.

The following is my schema/data for a Role which is Identical to the Vokuro sample application

CREATE TABLE IF NOT EXISTS profiles (
  id int(10) unsigned NOT NULL AUTO_INCREMENT,
  name varchar(64) NOT NULL,
  active char(1) NOT NULL,
  PRIMARY KEY (id),
  KEY active (active)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=4 ;

INSERT INTO profiles (id, name, active) VALUES
(1, 'Administrators', 'Y'),
(2, 'Users', 'Y'),
(3, 'Read-Only', 'Y');

In my script, I fetch profiles and add them as roles (same as in the Acl for vokuro)

// Register roles
$profiles = Profiles::find('active = "Y"');

foreach ($profiles as $profile) {
    $acl->addRole(new Role($profile->name));
}

Resources are defined as follows:

$acl->addResource(new Resource('index'), array('index'));
$acl->addResource(new Resource('user'), array('search', 'create'));
$acl->addResource(new Resource('admin'), array('create', 'update', 'delete'));

Once I have resources and roles added to the ACL, I try to set up some permissions, using the wildcard role first

EX1

$acl->allow('Read-Only', 'user', 'create'); // THIS WORKS
$acl->allow('*', 'index', 'index'); // allow everyone to access index/index THIS LINE CAUSES PROBLEMS
$acl->allow('Read-Only', 'user', 'search'); // FAILS HERE with error 'Role "Read-Only" does not exist in ACL'

The above code fails on the last line, where I try to set permissions on the Read-Only role, ONLY if I set a permission using '*' as the role before it (as in the example). It is strange because the first line executes fine, so the role Read-Only, exists at that point but doesn't exist after the 2nd line. So, the 2nd line is problematic.

A few things I noted about this issue is it only occurs for the very last role added to the ACL. In the above case, 'Read-Only' was last to be added to the ACL as a role.

The following code works fine

EX2

$acl->allow('Read-Only', 'user', 'create'); // THIS WORKS
$acl->allow('*', 'index', 'index'); // allow everyone to access index/index 
$acl->allow('Users', 'user', 'search'); // THIS WORKS because 'Users'  was not the last role added

Of course, If I don't use the wildcard role in line 2, I never have any problems for EX1.

If you use a wildcard role a second time, you get a different error

EX3

$acl->allow('Read-Only', 'user', 'create'); // THIS WORKS
$acl->allow('*', 'index', 'index'); // allow everyone to access index/index CAUSES PROBLEMS
$acl->allow('*', 'user', 'search'); // THIS FAILS

// ABOVE LINE PRODUCES THE Following stack trace
/*
PHP Fatal error:  Uncaught exception 'Phalcon\Acl\Exception' with message 'Role "' in /www/project/scripts/acltest.php:130
Stack trace:
#0 [internal function]: Phalcon\Acl\Adapter\Memory->_allowOrDeny('\x00 \x00\x00\x00\x00\x00\x00y', 'user', 'search', 1)
#1 /www/project/scripts/acltest.php(130): Phalcon\Acl\Adapter\Memory->allow('*', 'user', 'search')
#
*/

The wildcard is clearly problematic.

Another strange thing I noticed is this behaviour doesn't occur if we hardcode the roles instead of retrieving from the db. For example if I define the above roles as follows:

// Hardcode the roles
$acl->addRole(new Role('Administrators'));
$acl->addRole(new Role('Users'));
// $profiles[2]->name == 'Read-Only'    is true.
$acl->addRole(new Role('Read-Only')); // but we need to pass in a string literal

If I use this approach, EX1 gives me no issues

I realize that there are several work arounds for this. I could use role inheritance to avoid using a '*' role.

To confirm that my database environment is not at fault, I set up the vokuro app on the same mysql server, in the ACL rebuild method, I added code similar to EX1 and I get the exact same error. So that rules out the possibility my dabase setup is at fault.

I am fairly certain this is a bug, the $acl->allow() should not affect the collection of roles in the ACL. Has anyone else had this issue?

I'd really like for this behaviour to be fixed and work properly.

I dont understand how this is a bug. It looks like it was never implemented and this is a feature request.

  1. I have yet to see any tutorial/example/code that uses a wildcard role
  2. You can solve it with inheritance as you said
  3. Or you could solve it with a loop.

As far as I know, the only situation where the wildcard works in ACL is for a permission. You can wildcard permissions but not roles or resources.

Is there a reason why you do not want to do any of the above solutions that you or I have stated? If performance is an issue for you keep in mind that just about every example using phalcon acl will show you how to cache it.



3.0k

Actually, If you take a look at the php Doc for the allow method on the Phalcon\Acl\Adapter\Memory class,

You will see the following

/**
         * Allow access to a role on a resource
         *
         * You can use '*' as wildcard
         *
         * Example:
         * <code>
         * //Allow access to guests to search on customers
         * $acl->allow('guests', 'customers', 'search');
         *
         * //Allow access to guests to search or create on customers
         * $acl->allow('guests', 'customers', array('search', 'create'));
         *
         * //Allow access to any role to browse on products
         * $acl->allow('*', 'products', 'browse');
         *
         * //Allow access to any role to browse on any resource
         * $acl->allow('*', '*', 'browse');
         * </code>
         *
         * @param string $roleName
         * @param string $resourceName
         * @param mixed $access
         */
        public function allow($roleName, $resourceName, $access){ }

From the examples provided in the doc block for the method, the clearly give an example of using a wildcard for a role. Unless they accidently put this in the docs, it should be a valid working feature.

I realize this can be solved by other means, but It would be nice if the functionality stated in the docs was working properly. You'll notice that the * role works fine even in my examples, as long as you hardcode the roles.

I stand corrected I misspoke.

https://github.com/phalcon/cphalcon/pull/1411

Looks like similar issue was fixed in 1.3.0. If you are on a more recent version I would file an issue on the tracker