Question

I would like to use Zend's ACL (Zend\Permissions\Acl) not (only) based on static roles but also on (variable) user points.

In my application every user has points. A resource has a minimum of points needed to view it. Access to a resource should be based on the number of points the user currently has.

Example

Resources:

  • Resource 1: 20 points
  • Resource 2: 100 points
  • Resource 3: 150 points

Users:

  • User 1: 70 points => Access to resource 1
  • User 2: 135 points => Access to resources 1, 2
  • User 3: 170 points => Access to resources 1, 2, 3

What would be the best way to do this?

My thoughts so far

  1. Create ACL object dynamically for the currently logged in user based on his points (set each $acl->allow() based on points). This isn't clean.
  2. Create a generic ACL and somehow pass the user's points (I managed to do it with assertions. See my answer below.)
  3. Some (possibly easier/cleaner) way suggested here...

I would greatly appreciate a push in the right direction :)

Was it helpful?

Solution

So this is not just about Zend but working with ACLs in general.

Usually when you implement access rights in an ACL you assign it to a group rather than an individual user. Then you can easily (and dynamically) add or remove users from groups.

In Zend ACL you can think of these groups as the roles. In your case you assign the access rights for a resource to a group (or role) that represent a certain number of points. Now you only have to worry about moving users between these groups based on the points they have earned.

OTHER TIPS

Okay, I tried to implement it myself. Maybe it's not pretty, but it's the best solution I came up with myself. Is this the right direction? I would appreciate any feedback!

Solution:

Instead of strings as resources and roles i use my models (suggested here). I use PointResourceInterface to mark resources that require a specific number of points and implement Zend\Permissions\Acl\Role\RoleInterface in my user class. Now I create a new NeededPointsAssertion:

class NeededPointsAssertion implements AssertionInterface
{
    public function assert(Acl $acl, RoleInterface $role = null, 
            ResourceInterface $resource = null, $privilege = null) {
        // Resource must have points, otherwise not applicable
        if (!($resource instanceof PointResourceInterface)) {
            throw new Exception('Resource is not an PointResourceInterface. NeededPointsAssertion is not applicable.');
        }

        //check if points are high enough, in my app only users have points
        $hasEnoughPoints = false;
        if ($role instanceof User) {
            // role is User and resource is PointResourceInterface
            $hasEnoughPoints = ($role->getPoints() >= $resource->getPoints());
        }

        return $hasEnoughPoints;
    }
}

PointResourceInterface looks like this:

use Zend\Permissions\Acl\Resource\ResourceInterface;

interface PointResourceInterface extends ResourceInterface {
    public function getPoints();
}

Setup:

$acl->allow('user', $pointResource, null, new NeededPointsAssertion());

Users have access to resources that need points. But additionally the NeededPointsAssertion is checked.

Access: I'm checking whether access is allowed like this:

$acl->isAllowed($role, $someResource);

If there's a user $role = $user otherwise it's guest or something else.

Inspiration is from http://www.aviblock.com/blog/2009/03/19/acl-in-zend-framework/

Update: Looking back at it now, it would have also been possible to add the needed points via the constructor and store it as an attribute. Decide for yourself and what makes sense in your application...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top