A flexible, driver based Acl package for PHP 5.4+

Overview

Lock - Acl for PHP 5.4+

Build Status Code Climate Test Coverage Software License Packagist Version Total Downloads

I'm sad to say that Lock is currently not maintained. I won't be able to offer support or accept new contributions for the current time being. Other priorities are keeping me from putting the work into Lock that it deserves. Eventually I'll try to pick up work again but unfortunately I cannot say when. My thanks goes out to all the contributors and users.

-- Dries

Lock is a flexible, driver based Acl package for PHP 5.4+.

Created by Dries Vints. Made possible thanks to BeatSwitch. Inspired by Authority by Matthew Machuga. Logo by Jerry Low.

Table of Contents

Terminology

  • Lock: An acl instance for a subject. This package currently ships with a CallerLock and a RoleLock
  • Caller: An identity object that can have permissions to do something
  • Driver: A storage system for permissions which can either be static or persistent
  • Permission: A permission holds an action and an optional (unique) resource. Can be either a Restriction or a Privilege
  • Restriction: A restriction denies you from being able to perform an action (on an optional resource)
  • Privilege: A privilege allows you to perform an action (on an optional resource)
  • Action: An action is something you are either allowed or denied to do
  • Resource: A resource can be an object where you can perform one or more actions on. It can either target a certain type of resource or a specific resource by its unique identifier
  • Role: A role can also hold multiple permissions. A caller can have multiple roles. Roles can inherit permissions from other roles

Features

  • Flexible acl permissions for multiple identities (callers)
  • Static or persistent drivers to store permissions
  • Action aliases
  • Roles
  • Conditions (Asserts)
  • Easily implement acl functionality on your caller or role with a trait

Introduction

Lock differs from other acl packages by trying to provide the most flexible way for working with multiple permission callers and storing permissions.

By working with Lock's Caller contract you can set permissions on multiple identities.

The Driver contract allows for an easy way to store permissions to a persistent or static storage system. A default static ArrayDriver ships with this package. Check out the list below for more drivers which have already been prepared for you. Or build your own by implementing the Driver contract.

You can set and check permissions for resources by manually passing along a resource's type and (optional) identifier or you can implement the Resource contract onto your objects so you can pass them along to lock more easily.

The Manager allows for an easy way to instantiate new Lock instances, set action aliases or register roles.

Drivers

If you need a framework-specific implementation, pick one of the already prepared drivers below.

  • ArrayDriver (ships with this package)
  • Laravel 5

Roadmap

  • Group Permissions
  • More drivers (Symfony, Zend Framework, Doctrine, ...)
  • Event Listeners

Installation

Install this package through Composer.

$ composer require beatswitch/lock

Usage

Implementing the Caller contract

Every identity which should have permissions to do something must implement the BeatSwitch\Lock\Callers\Caller contract. The Caller contract identifies a caller by requiring it to return its type and its unique identifier. Let's look at an example below.

<?php

use BeatSwitch\Lock\Callers\Caller;

class User implements Caller
{
    public function getCallerType()
    {
        return 'users';
    }

    public function getCallerId()
    {
        return $this->id;
    }

    public function getCallerRoles()
    {
        return ['editor', 'publisher'];
    }
}

By adding the getCallerType function we can identify a group of callers through a unique type. If we would at some point wanted to set permissions on another group of callers we could easily implement the contract on another object.

<?php

use BeatSwitch\Lock\Callers\Caller;

class Organization implements Caller
{
    public function getCallerType()
    {
        return 'organizations';
    }

    public function getCallerId()
    {
        return $this->id;
    }

    public function getCallerRoles()
    {
        return ['enterprise'];
    }
}

And thus we can easily retrieve permissions for a specific caller type through a driver.

Working with a static driver

If you'd like to configure all of your permissions beforehand you can use the static ArrayDriver which ships with the package. This allows you to set a list of permissions for a caller before your application is run.

use \BeatSwitch\Lock\Drivers\ArrayDriver;
use \BeatSwitch\Lock\Lock;
use \BeatSwitch\Lock\Manager;

// Create a new Manager instance.
$manager = new Manager(new ArrayDriver());

// Instantiate a new Lock instance for an object which implements the Caller contract.
$lock = $manager->caller($caller);

// Set some permissions.
$lock->allow('manage_settings');
$lock->allow('create', 'events');

// Use the Lock instance to validate permissions on the given caller.
$lock->can('manage_settings'); // true: can manage settings
$lock->can('create', 'events'); // true: can create events
$lock->cannot('update', 'events'); // true: cannot update events
$lock->can('delete', 'events'); // false: cannot delete events

Working with a persistent driver

Working with a persistent driver allows you to store permissions to a persistent storage layer and adjust them during runtime. For example, if you'd implement the Laravel 5 driver, it would store the permissions to a database using Laravel's database component. By creating your own UI, you could easily attach the acl functionality from this package to create, for example, a user management system where different users have different permissions.

Let's take a look at a very basic user management controller to see how that's done. We'll assume we get a bootstrapped lock manager instance with our Laravel DB driver.

<?php

use BeatSwitch\Lock\Manager;

class UserManagementController extends BaseController
{
    protected $lockManager;

    public function __construct(Manager $lockManager)
    {
        $this->lockManager = $lockManager;
    }

    public function togglePermission()
    {
        $userId = Input::get('user');
        $action = Input::get('action');
        $resource = Input::get('resource');

        $user = User::find($userId);

        $this->lockManager->caller($user)->toggle($action, $resource);

        return Redirect::route('user_management');
    }
}

Every time the togglePermission method is used, the user's permission for the given action and resource type will be toggled.

Setting and checking permissions

You can either allow or deny a caller from doing something. Here are a couple of ways to set and check permissions.

Allow a caller to create everything.

$lock->allow('create');

$lock->can('create'); // true

Allow a caller to only create posts.

$lock->allow('create', 'posts');

$lock->can('create'); // false
$lock->can('create', 'posts'); // true

Allow a caller to only edit a specific post with an ID of 5.

$lock->allow('edit', 'posts', 5);

$lock->can('edit'); // false
$lock->can('edit', 'posts'); // false
$lock->can('edit', 'posts', 5); // true

Allow a caller to edit all posts but deny them from editing one with the id of 5.

$lock->allow('edit', 'posts');
$lock->deny('edit', 'posts', 5);

$lock->can('edit', 'posts'); // true
$lock->can('edit', 'posts', 5); // false

Toggle a permission's value.

$lock->allow('create');
$lock->can('create'); // true

$lock->toggle('create');
$lock->can('create'); // false

You can allow or deny multiple actions at once and also check multiple actions at once.

$lock->allow(['create', 'edit'], 'posts');

$lock->can('create', 'posts'); // true
$lock->can(['create', 'edit'], 'posts'); // true
$lock->can(['create', 'delete'], 'posts'); // false

Clearing permissions

You can easily clear permissions for a set specific combination of actions and resources.

$lock->allow(['create', 'edit'], 'posts');

$lock->clear('edit', 'posts');

$lock->can('edit', 'posts'); // false
$lock->can('create', 'posts'); // true

You can also just clear all permissions for a lock instance.

$lock->allow('manage-posts');
$lock->allow(['create', 'edit'], 'users');

$lock->clear();

$lock->can('manage-posts'); // false
$lock->can('create', 'users'); // false

Setting an action alias

To group multiple actions and set them all at once you might want to set an action alias.

$lock->alias('manage', ['create', 'read', 'delete']);
$lock->allow('manage', 'posts');

$lock->can('manage', 'posts'); // true
$lock->can('create', 'posts'); // true
$lock->can('delete', 'posts', 1); // true
$lock->can('update', 'posts'); // false

Setting a God caller

You could easily set a caller which has all permissions for everything by passing the all wildcard as an action on the lock instance.

$lock->allow('all');

Now every "can" method call will validate to true for this caller.

Working with roles

Lock provides an easy way to working with roles. You can work with roles out of the box but if you want to work with inheritance, you'll need to register the roles to the manager instance.

$manager->setRole('guest');
$manager->setRole('user', 'guest'); // "user" will inherit all permissions from "guest"

Or register multiple roles at once.

$manager->setRole(['editor', 'admin'], 'user'); // "editor" and "admin" will inherit all permissions from "user".

Let's set some permissions and see how they are resolved.

// Allow a guest to read everything.
$manager->role('guest')->allow('guest', 'read');

// Allow a user to create posts.
$manager->role('user')->allow('create', 'posts');

// Allow an editor and admin to publish posts.
$manager->role('editor')->allow('publish', 'posts');
$manager->role('admin')->allow('publish', 'posts');

// Allow an admin to delete posts.
$manager->role('admin')->allow('delete', 'posts');

// Let's assume our caller has the role of "editor" and check some permissions.
$lock = $manager->caller($caller);
$lock->can('read'); // true
$lock->can('delete', 'posts'); // false
$lock->can('publish'); // false: we can't publish everything, just posts.
$lock->can(['create', 'publish'], 'posts'); // true

Something you need to be aware of is that caller-level permissions supersede role-level permissions. Let's see how that works.

Our caller will have the user role.

$manager->caller($caller)->allow('create', 'posts');

// Notice that we don't need to set the role in the
// manager first if we don't care about inheritance.
$manager->role('user')->deny('user', 'create', 'posts');

$manager->caller($caller)->can('create', 'posts'); // true: the user has explicit permission to create posts.

Working with conditions

Conditions are actually asserts which are extra checks you can set for permissions. You can pass an array with them as the last parameter of allow and deny. All conditions must implement the BeatSwitch\Lock\Permissions\Condition interface.

Warning: please note that conditions currently only work with static drivers.

Let's setup a condition.

<?php

use BeatSwitch\Lock\Lock;
use BeatSwitch\Lock\Permissions\Condition;
use BeatSwitch\Lock\Permissions\Permission;
use BeatSwitch\Lock\Resources\Resource;
use Illuminate\Auth\AuthManager;

class LoggedInCondition implements Condition
{
    /**
     * The Laravel AuthManager instance
     *
     * @var \Illuminate\Auth\AuthManager
     */
    protected $auth;

    /**
     * @param \Illuminate\Auth\AuthManager $auth
     */
    public function __construct(AuthManager $auth)
    {
        $this->auth = $auth;
    }

    /**
     * Assert if the condition is correct
     *
     * @param \BeatSwitch\Lock\Lock $lock                         The current Lock instance that's being used
     * @param \BeatSwitch\Lock\Permissions\Permission $permission The Permission that's being checked
     * @param string $action                                      The action passed to the can or cannot method
     * @param \BeatSwitch\Lock\Resources\Resource|null $resource  The resource passed to the can or cannot method
     * @return bool
     */
    public function assert(Lock $lock, Permission $permission, $action, Resource $resource = null)
    {
        // Condition will succeed if the user is logged in.
        return $this->auth->check();
    }
}

Now let's see how this will work when setting up a permission.

$condition = App::make('LoggedInCondition');

$lock->allow('create', 'posts', null, $condition);
$lock->can('create', 'posts'); // true if logged in, otherwise false.

You can also pass along multiple conditions.

$lock->allow('create', 'posts', null, [$falseCondition, $trueCondition]);
$lock->can('create', 'posts'); // false: there's at least one false condition

You can pass along as many conditions as you like but they all need to succeed in order for the permission to work.

You can also use a callback if you like.

$lock->allow('create', 'posts', null, function ($lock, $permission, $action, $resource = null) {
    return false;
});
$lock->can('create', 'posts'); // false because the callback returns false.

Retrieving allowed or denied resources

If you'd like to retrieve a list of resources which are allowed or denied to perform a particularly action you can use the allowed and denied methods on a Lock instance.

$lock->allow('update', 'users', 1);
$lock->allow('update', 'users', 2);
$lock->allow('update', 'users', 3);
$lock->deny('update', 'users', 2);

$lock->allowed('update', 'users'); // Returns [1, 3];
$lock->denied('update', 'users'); // Returns [2];

Please keep in mind that you can only retrieve id's from resources which have permissions set. Resources which aren't registered through Lock won't be returned.

Using the LockAware trait

You can easily add acl functionality to your caller or role by implementing the BeatSwitch\Lock\LockAware trait.

<?php

use BeatSwitch\Lock\Callers\Caller;
use BeatSwitch\Lock\LockAware;

class Organization implements Caller
{
    use LockAware;

    public function getCallerType()
    {
        return 'organizations';
    }

    public function getCallerId()
    {
        return $this->id;
    }

    public function getCallerRoles()
    {
        return ['enterprise'];
    }
}

Now we need to set its lock instance.

$caller->setLock($lock);

And now your caller can use all of the lock methods onto itself.

$caller->can('create', 'posts');
$caller->allow('edit', 'pages');

If you have a caller which implements the LockAware trait but haven't bootstrapped the caller's lock instance yet you can easily make the caller lock aware by using the manager's makeCallerLockAware method.

$caller = $manager->makeCallerLockAware($caller);

And now your caller will be able to use the LockAware methods. There's a similar method for roles.

$role = $manager->makeRoleLockAware('guest');

This will bootstrap a SimpleRole object which already comes with the LockAware trait in place.

Api

BeatSwitch\Lock\Lock

The following methods can all be called on a BeatSwitch\Lock\Lock instance.

can

Checks to see if the current caller has permission to do something.

can(
    string|array $action,
    string|\BeatSwitch\Lock\Resources\Resource $resource = null,
    int $resourceId = null
)

cannot

Checks to see if it's forbidden for the current caller to do something.

cannot(
    string|array $action,
    string|\BeatSwitch\Lock\Resources\Resource $resource = null,
    int $resourceId = null
)

allow

Sets a Privilege permission on a caller to allow it to do something. Removes any matching restrictions.

allow(
    string|array $action,
    string|\BeatSwitch\Lock\Resources\Resource $resource = null,
    int $resourceId = null,
    \BeatSwitch\Lock\Permissions\Condition[] $conditions = []
)

deny

Sets a Restriction permission on a caller to prevent it from doing something. Removes any matching privileges.

deny(
    string|array $action,
    string|\BeatSwitch\Lock\Resources\Resource $resource = null,
    int $resourceId = null,
    \BeatSwitch\Lock\Permissions\Condition[] $conditions = []
)

toggle

Toggles the value for the given permission.

toggle(
    string|array $action,
    string|\BeatSwitch\Lock\Resources\Resource $resource = null,
    int $resourceId = null
)

allowed

Returns all the id's in an array of the given resource type to which the subject is allowed to perform the given action on.

allowed(
    string|array $action,
    string|\BeatSwitch\Lock\Resources\Resource $resourceType
)

denied

Returns all the id's in an array of the given resource type to which the subject is denied to perform the given action on.

denied(
    string|array $action,
    string|\BeatSwitch\Lock\Resources\Resource $resourceType
)

BeatSwitch\Lock\Manager

The following methods can all be called on a BeatSwitch\Lock\Manager instance.

caller

Returns a BeatSwitch\Lock\Lock instance for a caller.

caller(
    \BeatSwitch\Lock\Callers\Caller $caller
)

role

Returns a BeatSwitch\Lock\Lock instance for a role.

role(
    \BeatSwitch\Lock\Roles\Role $role
)

alias

Add an alias for one or more actions.

alias(
    string $name,
    string|array $actions
)

setRole

Set one or more roles and an optional role to inherit permissions from.

setRole(
    string|array $name,
    string $inherit = null
)

makeCallerLockAware

Sets the lock instance for a caller which implements the LockAware trait. Returns the caller with the lock instance set.

makeCallerLockAware(
    \BeatSwitch\Lock\Callers\Caller $caller
)

makeRoleLockAware

Sets the lock instance for a role which implements the LockAware trait. Returns the role with the lock instance set.

makeRoleLockAware(
    \BeatSwitch\Lock\Roles\Role|string $role
)

Building a driver

You can easily build a driver by implementing the BeatSwitch\Lock\Drivers\Driver contract. Below we'll demonstrate how to create our own persistent driver using Laravel's Eloquent ORM as our storage mechanism.

We'll assume we have a CallerPermission model class with at least the following database columns:

  • caller_type (varchar, 100)
  • caller_id (int, 11)
  • type (varchar, 10)
  • action (varchar, 100)
  • resource_type (varchar, 100, nullable)
  • resource_id (int, 11, nullable)

And we have a RolePermission model with the following database columns:

  • role (varchar, 100)
  • type (varchar, 10)
  • action (varchar, 100)
  • resource_type (varchar, 100, nullable)
  • resource_id (int, 11, nullable)

Let's check out a full implementation of the driver below. Notice that for the getCallerPermissions method we're using the PermissionFactory class to easily map the data and create Permission objects from them. The PermissionFactory's createFromData method will accept both arrays and objects.

<?php

use BeatSwitch\Lock\Callers\Caller;
use BeatSwitch\Lock\Drivers\Driver;
use BeatSwitch\Lock\Permissions\Permission;
use BeatSwitch\Lock\Permissions\PermissionFactory;
use BeatSwitch\Lock\Roles\Role;
use CallerPermission;
use RolePermission;

class EloquentDriver implements Driver
{
    /**
     * Returns all the permissions for a caller
     *
     * @param \BeatSwitch\Lock\Callers\Caller $caller
     * @return \BeatSwitch\Lock\Permissions\Permission[]
     */
    public function getCallerPermissions(Caller $caller)
    {
        $permissions = CallerPermission::where('caller_type', $caller->getCallerType())
            ->where('caller_id', $caller->getCallerId())
            ->get();

        return PermissionFactory::createFromData($permissions->toArray());
    }

    /**
     * Stores a new permission into the driver for a caller
     *
     * @param \BeatSwitch\Lock\Callers\Caller $caller
     * @param \BeatSwitch\Lock\Permissions\Permission
     * @return void
     */
    public function storeCallerPermission(Caller $caller, Permission $permission)
    {
        $eloquentPermission = new CallerPermission;
        $eloquentPermission->caller_type = $caller->getCallerType();
        $eloquentPermission->caller_id = $caller->getCallerId();
        $eloquentPermission->type = $permission->getType();
        $eloquentPermission->action = $permission->getAction();
        $eloquentPermission->resource_type = $permission->getResourceType();
        $eloquentPermission->resource_id = $permission->getResourceId();
        $eloquentPermission->save();
    }

    /**
     * Removes a permission from the driver for a caller
     *
     * @param \BeatSwitch\Lock\Callers\Caller $caller
     * @param \BeatSwitch\Lock\Permissions\Permission
     * @return void
     */
    public function removeCallerPermission(Caller $caller, Permission $permission)
    {
        CallerPermission::where('caller_type', $caller->getCallerType())
            ->where('caller_id', $caller->getCallerId())
            ->where('type', $permission->getType())
            ->where('action', $permission->getAction())
            ->where('resource_type', $permission->getResourceType())
            ->where('resource_id', $permission->getResourceId())
            ->delete();
    }

    /**
     * Checks if a permission is stored for a user
     *
     * @param \BeatSwitch\Lock\Callers\Caller $caller
     * @param \BeatSwitch\Lock\Permissions\Permission
     * @return bool
     */
    public function hasCallerPermission(Caller $caller, Permission $permission)
    {
        return (bool) CallerPermission::where('caller_type', $caller->getCallerType())
            ->where('caller_id', $caller->getCallerId())
            ->where('type', $permission->getType())
            ->where('action', $permission->getAction())
            ->where('resource_type', $permission->getResourceType())
            ->where('resource_id', $permission->getResourceId())
            ->first();
    }

    /**
     * Returns all the permissions for a role
     *
     * @param \BeatSwitch\Lock\Roles\Role $role
     * @return \BeatSwitch\Lock\Permissions\Permission[]
     */
    public function getRolePermissions(Role $role)
    {
        $permissions = RolePermission::where('role', $role->getRoleName())->get();

        return PermissionFactory::createFromData($permissions->toArray());
    }

    /**
     * Stores a new permission for a role
     *
     * @param \BeatSwitch\Lock\Roles\Role $role
     * @param \BeatSwitch\Lock\Permissions\Permission
     * @return void
     */
    public function storeRolePermission(Role $role, Permission $permission)
    {
        $eloquentPermission = new RolePermission;
        $eloquentPermission->role = $role->getRoleName();
        $eloquentPermission->type = $permission->getType();
        $eloquentPermission->action = $permission->getAction();
        $eloquentPermission->resource_type = $permission->getResourceType();
        $eloquentPermission->resource_id = $permission->getResourceId();
        $eloquentPermission->save();
    }

    /**
     * Removes a permission for a role
     *
     * @param \BeatSwitch\Lock\Roles\Role $role
     * @param \BeatSwitch\Lock\Permissions\Permission
     * @return void
     */
    public function removeRolePermission(Role $role, Permission $permission)
    {
        RolePermission::where('role', $role->getRoleName())
            ->where('type', $permission->getType())
            ->where('action', $permission->getAction())
            ->where('resource_type', $permission->getResourceType())
            ->where('resource_id', $permission->getResourceId())
            ->delete();
    }

    /**
     * Checks if a permission is stored for a role
     *
     * @param \BeatSwitch\Lock\Roles\Role $role
     * @param \BeatSwitch\Lock\Permissions\Permission
     * @return bool
     */
    public function hasRolePermission(Role $role, Permission $permission)
    {
        return (bool) RolePermission::where('role', $role->getRoleName())
            ->where('type', $permission->getType())
            ->where('action', $permission->getAction())
            ->where('resource_type', $permission->getResourceType())
            ->where('resource_id', $permission->getResourceId())
            ->first();
    }
}

Notice that we're not checking if the permission already exists when we're attempting to store it. You don't need to worry about that because that's all been done for you in the Lock instance.

Now we have a driver which supports storing of permissions for callers and roles.

Testing your driver

It's very easy for you to make sure your driver works as expected. If you're building a persistent driver you can easily test it by creating a PHPUnit test which extends the PersistentDriverTestCase class.

<?php

use BeatSwitch\Lock\Tests\PersistentDriverTestCase;

class EloquentDriverTest extends PersistentDriverTestCase
{
    public function setUp()
    {
        // Don't forget to reset your DB here.
        
        // Bootstrap your driver.
        $this->driver = new EloquentDriver();

        parent::setUp();
    }
}

And this is all you need! The PersistentDriverTestCase contains all the tests you'll need to make sure your driver works as expected. So if all those tests pass then your driver was set up correctly. No need to mock anything, this is a pure integration test case. Of course in this specific example above, for Eloquent to work you'll need to bootstrap Laravel. Working with a database like sqlite would be the best way here to test your driver.

Maintainer

Lock is unmaintained at this moment.

This package is currently maintained by Dries Vints.
If you have any questions please don't hesitate to ask them in an issue.

Contributing

Please see the contributing file for details.

Changelog

You can see a list of changes for each release in our changelog file.

License

The MIT License. Please see the license file for more information.

Comments
  • Update .travis.yml

    Update .travis.yml

    Setting sudo: false allows us to access travis' new faster environment, and I've improved the composer install. Also, we don't need to allow hhvm failures since the tests are passing fine.

    opened by GrahamCampbell 9
  • Implement $lock->clear() method?

    Implement $lock->clear() method?

    Would it make sense to implement a clear method for a lock instance to clear any matching permissions, either privileges or restrictions? This could be an easy way to clear permissions to get a fresh start. But I'm not sure if it would be very useful for many people.

    The api would be:

    public function clear(
        string|array $action,
        string|\BeatSwitch\Lock\Contracts\Resource $resource = null,
        int $resourceId = null
    )
    

    Thoughts?

    question 
    opened by driesvints 6
  • Should the Manager class handle multiple drivers?

    Should the Manager class handle multiple drivers?

    Currently the Manager class accepts a single driver to store all permissions. I was thinking about allowing multiple drivers to be set. So some can be ready only with the storePermission and deletePermission methods not do anything on a read-only driver. But then you can do things like:

    $manager->driver('production')->caller($caller)->can(...)
    $manager->driver('local')->caller($user)->can(...)
    

    This makes it easy to determine permissions locally for production settings.

    Would this be a nice to have?

    question feature 
    opened by driesvints 5
  • PhalconDriver assertion failures

    PhalconDriver assertion failures

    Hi guys,

    I'm trying to write a Phalcon driver for Lock, but testing it with the PersistentDriverTestCase you've provided, I get 10 failures. Since Phalcon's ORM isn't much different from Laravel's Eloquent, to which you've kindly provided a driver, I felt it should be a pretty easy job converting that driver to Phalcon, but I'm at a loss now. Maybe these failures give you a clue what I've done wrong or where to look for the problems? The failures are as follows:

    it_can_check_multiple_permissions_at_once PersistentDriverTestCase.php:230

    it_can_clear_multiple_permissions_at_once PersistentDriverTestCase.php:242

    it_only_clears_the_requested_permissions PersistentDriverTestCase.php:255

    it_only_clears_permissions_on_the_given_resource PersistentDriverTestCase.php:268

    it_can_clear_every_single_permission_for_a_lock_instance PersistentDriverTestCase.php:282

    it_can_toggle_multiple_permissions_at_once PersistentDriverTestCase.php:329

    it_can_check_actions_from_aliases PersistentDriverTestCase.php:367

    it_can_work_with_roles PersistentDriverTestCase.php:402

    it_can_return_allowed_resource_ids PersistentDriverTestCase.php:452

    it_can_return_denied_resource_ids PersistentDriverTestCase.php:470

    I'm grateful and happy about any feedback you can give me. Thank you for coding this very cool package. Cheers, Christian

    opened by ghost 3
  • Should there be a find() method?

    Should there be a find() method?

    There seems to be one thing missing from this library that I miss from most ACL implementations but which IMHO is important for developers: Getting a list of resource IDs.

    Imagine I have a couple of hundred users and a couple of thousand events. Right now it seems that the only way to get a list of events that a user can access is to load them all and check them in a loop. That is terribly inefficient.

    I would like a method like this:

    $lock->find('update', 'events'); // returns array of event IDs the caller is allowed to update
    
    feature 
    opened by sandermarechal 3
  • Additional tests, eg: allow edit of own posts

    Additional tests, eg: allow edit of own posts

    Maybe I missed it in the README, but how would you go about allowing a role/caller to only allow something based on an assertion (function, method, etc)? Like if you have a user/role, and you only want to allow them editing their own posts. I see the example of using a static integer, but not sure how you could instead do something like this. Just wondering. Thank you.

    enhancement question 
    opened by coreyworrell 3
  • Update Lock.php

    Update Lock.php

    if you return false you say that it is not allowed by default, you use a whitelist approach. if you return true they are allowed by defualt, you use a blacklist.

    whitelist > blacklist

    opened by RobinMalfait 3
  • Split Driver interface into Driver\Read and Driver\Write?

    Split Driver interface into Driver\Read and Driver\Write?

    I've currently created a ReadOnlyDriver but something tells me I could best split up the Driver interface into a Driver\Read and a Driver\Write and require the developer to optionally implement the write one. This would make it easier to set up a Lock instance which is only used for reading permissions from a storage.

    Good idea?

    question 
    opened by driesvints 3
  • Why differs the sample implementation of a driver from the laravel lock?

    Why differs the sample implementation of a driver from the laravel lock?

    The Laravel lock uses a single table, see this file. But the example implementation in the readme.md shows two tables. Why? And why would I use the two tables instead of the single table from the Laravel plugin?

    I'm asking because I'm building a CakePHP 3.0 plugin which has been so far surprisingly easy because the ORM is almost the same as Laravals. I copied the Laravel driver and started adapting the code and I think I'm already done with the very basics (test aside). My only show stopper right now is if I should or have to use one or two tables for a better implementation.

    Edit: After reading the code a little more I guess the two table solutions would be better because I can see that the driver interface features Role and Caller methods? So I assume the methods that have Role in their name should use the roles table and the other methods the callers table? Is that how it is thought?

    question 
    opened by burzum 2
  • An example of permission checking on a specific instance(s)

    An example of permission checking on a specific instance(s)

    Consider the following url: /users/1/tasks/2

    I want to ensure that only the user with an id of 1 can get to their specific task.

    I would also like to have a super admin (let's say that user as an id of 8) be able to access both

    /users/8/tasks/4 and /users/1/tasks/2

    The super admin can access any task. Standard users that go to a task that isn't their own will get denied.

    Is there a fetchOwn('tasks') method or a similar way to implement the example above?

    opened by cegrif01 2
  • Split driver interface into CallerDriver and RoleDriver?

    Split driver interface into CallerDriver and RoleDriver?

    I'm thinking about splitting these contracts so people can optionally implement the RoleDriver contract. This allows them to not specifically have to user the role functionality.

    This could get messy if we also do #4 since we would have 4 interfaces that way.

    Thoughts?

    question 
    opened by driesvints 2
  • Rename resources to something else

    Rename resources to something else

    The word "resource" is not soft reserved in PHP 7. We better find some other name for it to avoid conflicts in future versions.

    http://php.net/manual/en/reserved.other-reserved-words.php

    opened by driesvints 0
  • Changed visibility of method getPermissions from protected to public

    Changed visibility of method getPermissions from protected to public

    This change is useful to call getPermission() directly and get the list of permissions for a user, especially if the service who's asking for the permission doesn't have access to the list of allowed permission.

    opened by feulf 4
  • Expose permissions collection through all method

    Expose permissions collection through all method

    Good Evening!

    Recently I've had the need to fetch all permissions of a caller for display on a table, the only way to accomplish this at the moment is to explicitly check $action on a caller. The alternative is to code around this library which is not wonderful.

    This feature exposes a collection of Permission objects. Useful in situations where you need to display tables of permissions a user is allowed or denied.

    The underlying getPermissions method is exposed as a public function "all".

    Take care, Nicholas

    opened by njohns-grovo 0
  • Add voters

    Add voters

    I don't really like the Security component in Symfony, because I think it is overcomplicated. One feature I really like though is the Voter next to ACL access checking:

    https://symfony.com/doc/master/book/security.html#access-control-lists-acls-securing-individual-database-objects

    opened by sagikazarmark 1
Owner
Beatswitch
The world's leading music event and festival management software.
Beatswitch
An authorization library that supports access control models like ACL, RBAC, ABAC in PHP .

PHP-Casbin Documentation | Tutorials | Extensions Breaking News: Laravel-authz is now available, an authorization library for the Laravel framework. P

PHP-Casbin 1.1k Dec 14, 2022
An authorization library that supports access control models like ACL, RBAC, ABAC in Laravel.

Laravel Authorization Laravel-authz is an authorization library for the laravel framework. It's based on Casbin, an authorization library that support

PHP-Casbin 243 Jan 4, 2023
An authorization library that supports access control models like ACL, RBAC, ABAC for webman plugin

An authorization library that supports access control models like ACL, RBAC, ABAC for webman plugin

PHP-Casbin 18 Dec 30, 2022
Laravel 5 Active Directory LDAP Authentication driver.

Active Directory LDAP Authentication Laravel 5 Active Directory LDAP Authentication driver. Fork This is a fork of Cody Covey's ldap-auth package. Unf

Manuel Strebel 33 Aug 19, 2019
Hierarchical Rol-Permission Based Laravel Auth Package with Limitless Hierarchical Level of Organizations

AAuth for Laravel Hierarchical Rol-Permission Based Laravel Auth Package with Limitless Hierarchical Level of Organizations Features Organization Base

Aurora Web Software Team 23 Dec 27, 2022
A Laravel 5 package for OAuth Social Login/Register implementation using Laravel socialite and (optionally) AdminLTE Laravel package

laravel-social A Laravel 5 package for OAuth Social Login/Register implementation using Laravel socialite and (optionally) AdminLTE Laravel package. I

Sergi Tur Badenas 42 Nov 29, 2022
Single file PHP that can serve as a JWT based authentication provider to the PHP-CRUD-API project

Single file PHP that can serve as a JWT based authentication provider to the PHP-CRUD-API project

Maurits van der Schee 163 Dec 18, 2022
A PHP implementation of ActivityPub protocol based upon the ActivityStreams 2.0 data format.

ActivityPhp ActivityPhp is an implementation of ActivityPub layers in PHP. It provides two layers: A client to server protocol, or "Social API" This p

Landrok 175 Dec 24, 2022
Kaiju is an open source verification bot based on Discord's OAuth written in C# and PHP, with the functionality of being able to integrate the user to a new server in case yours is suspended.

What is Kaiju? Kaiju is an open source verification bot for Discord servers, based on OAuth and with permission for the server owner, to be able to mi

in the space 10 Nov 20, 2022
A PHP boilerplate based on Slim Framework, for start projects with Eloquent ORM, Validation, Auth (JWT), Repositories and Transformers ready

A PHP boilerplate based on Slim Framework, for start projects with Eloquent ORM, Validation, Auth (JWT), Repositories and Transformers ready.

Damiano Petrungaro 58 Aug 10, 2022
Role-based Permissions for Laravel 5

ENTRUST (Laravel 5 Package) Entrust is a succinct and flexible way to add Role-based Permissions to Laravel 5. If you are looking for the Laravel 4 ve

Zizaco 6.1k Jan 5, 2023
Minimalistic token-based authorization for Laravel API endpoints.

Bearer Minimalistic token-based authorization for Laravel API endpoints. Installation You can install the package via Composer: composer require ryang

Ryan Chandler 74 Jun 17, 2022
A support-rich server software for Minecraft Bedrock Edition based on PocketMine-MP.

A support-rich server software for Minecraft Bedrock Edition based on PocketMine-MP. Credits @ItzLightyHD -> Adding features to OpenTouch (new API, mo

ApexieDevelopment 27 Aug 31, 2021
Light-weight role-based permissions system for Laravel 6+ built in Auth system.

Kodeine/Laravel-ACL Laravel ACL adds role based permissions to built in Auth System of Laravel 8.0+. ACL middleware protects routes and even crud cont

Kodeine 781 Dec 15, 2022
Configurable Basic Auth based on Pimcore Documents

CORS Property Basic Auth This bundles allows to add basic auth based on Properties on Pimcore Documents. Simply use these properties password_enabled

CORS GmbH 1 Nov 12, 2021
Manage authorization with granular role-based permissions in your Laravel Apps.

Governor For Laravel Manage authorization with granular role-based permissions in your Laravel apps. Goal Provide a simple method of managing ACL in a

GeneaLabs, LLC 149 Dec 23, 2022
Tech-Admin is Laravel + Bootstrap Admin Panel With User Management And Access Control based on Roles and Permissions.

Tech-Admin | Laravel 8 + Bootstrap 4 Tech-Admin is Admin Panel With Preset of Roles, Permissions, ACL, User Management, Profile Management. Features M

TechTool India 39 Dec 23, 2022
Multi-factor Authentication using a Public PGP key for web based applications

PGPmfa() a PHP Class for PGP Multi-factor Authentication using a Public PGP key for web based applications Multi-factor Authentication with PGP Second

null 2 Nov 27, 2022
PHP package built for Laravel 5.* to easily handle a user email verification and validate the email

jrean/laravel-user-verification is a PHP package built for Laravel 5.* & 6.* & 7.* & 8.* to easily handle a user verification and validate the e-mail.

Jean Ragouin 802 Dec 29, 2022