Giter Club home page Giter Club logo

geggleto-acl's Introduction

Build Status

geggleto-acl

Provides a ACL repository and Middleware using Zend/Permissions/Acl library PSR-7 Compliant

How it works

  • Resources are end-points
  • Roles are a group of resources
  • You can either allow or deny those roles.

The roles a user has are loaded into the AclRepo on every request. I suggest loading them into a session variable rather than pulling them from storage everytime (usage case depending).

The current route is then inspected and compared to the list of accessable resources in a middleware. a 401 is returned if a user is not allowed. If the user is allowed the application is allowed to continue.

By default no message body is provided on the 401, and if you require a page to be rendered then you will need to write your own middleware.

Usage Example

//Define or Pull your ACL's into the following format
/*
$config = [
    "resources" => ["/", "/no", "/yes"],
    "roles" => ["guest", "user1", "user2"],
    "assignments" => [
        "allow" => [
            "guest" => ["/"],
            "user1" => ["/", "/no"],
            "user2" => ["/", "/yes"]
        ],
        "deny" => [
            "guest" => ["/no", "/yes"],
            "user1" => ["/yes"],
            "user2" => ["/no"]
        ]
    ]
];
*/

//In Slim v3
$app->add(\Geggleto\Acl\AclRepository(["guest"], 
//This should be in a nice php file by itself for easy inclusion... include '/path/to/acl/definition.php'
[
    "resources" => ["/", "/no", "/yes"],
    "roles" => ["guest", "user1", "user2"],
    "assignments" => [
        "allow" => [
            "guest" => ["/"],
            "user1" => ["/", "/no"],
            "user2" => ["/", "/yes"]
        ],
        "deny" => [
            "guest" => ["/no", "/yes"],
            "user1" => ["/yes"],
            "user2" => ["/no"]
        ]
    ]
]));

Dynamic Routes

In the case where your resource changes, it is possible to still correctly match by setting a resources with a Route Pattern. By default the system will inspect the $request's 'route' attribute and this Object should return the route pattern with ->getPatter(); Out of the box this will work with Slim 3 routes if you have turned on the 'determineRouteBeforeAppMiddleware' => true option.

Example Config:

return [
    "resources" => ["/", "/login", "/grid", "/404", "/logout", "/roles", "/roles/{pein}"],
    "roles" => ["guest", "grid", "roles"],
    "assignments" => [
        "allow" => [
            "guest" => ["/", "/404", "/login"],
            "grid" => [ '/grid', '/logout' ],
            "roles" => ['/roles', '/roles/{pein}']
        ],
        "deny" => []
    ]
];

If this does not fit your usage, feel free to override the default handler by setting your own via setHandler(callable)

Middleware

You can use the repo class directly which contains this code block... or modify this code block to suit your needs.

$app->add(function (Request $request, Response $res, $next) {
    /** @var $aclRepo AclRepository */ 
    $aclRepo = $this->get(AclRepository::class); //In Slim 3 the container is bound to function definitions
    $allowed = false; // We assume that the user cannot access the route

    $route = '/' . ltrim($request->getUri()->getPath(), '/'); //We construct our path

    try { //Check here... This will pass when a route is simple and there is no route parameters
        $allowed = $aclRepo->isAllowedWithRoles($aclRepo->getRole(), $route);
    } catch (InvalidArgumentException $iae) { //This is executed in cases where there is a route parameters... /user/{id:} 
        $fn = function (ServerRequestInterface $requestInterface, AclRepository $aclRepo) {
            //This will likely only work in Slim 3... This requires the determineRouteBeforeAppMiddleware => true to be set in the container
            $route = $requestInterface->getAttribute('route'); // Grab the route to get the pattern
            if (!empty($route)) {
                foreach ($aclRepo->getRole() as $role) {
                    if ($aclRepo->isAllowed($role, $route->getPattern())) { // check to see fi the user can access the pattern
                        return true; //Is allowed
                    }
                }
            }
            return false;
        };

        $allowed = $fn($request, $aclRepo); // Execute the fail-safe
    }

    if ($allowed) {
        return $next($request, $res);
    } else {
        return $res->withStatus(401); //Is not allowed. if you need to render a template then do that.
    }
});

White listing

You may add a URI path for white listing. The whitelisting is based upon strpos() so you may use a URI fragment to whitelist a whole class of URIs. With this it is possible to whitelist URIs by accident.

Example:

$acl = new Acl();
$acl->addWhitelistItem('/api');

In this example any URI with /api will be whitelisted.

  • /api/*
  • /myexample/api/*

geggleto-acl's People

Contributors

geggleto avatar lotharthesavior avatar matthewtrask avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

geggleto-acl's Issues

ACL usage and a Singleton implementation for a facilitated granularity of the access control

Hello,

I was thinking about the usage of the ACL in the applications, and the granularity of it. I'm thinking this because we might give a deeper level of verification, other than controlling access to routes.

If we go to the zend-permissions-acl, which happens to be used by us (https://framework.zend.com/blog/2017-05-09-zend-permissions-acl.html), we will find that it is expected the used spread through the application, and we would be able to check the role and the permission at many other levels.

But once we focus here only in the routes, we don't have a way of doing that because for that we will have to keep an instance in the app container.

Wouldn't it be facilitated by a Singleton implementation? It could be achieved by turning the constructor protected (not private because it will be extended) and by adding this method to the AclRepository:

public static function Instance()
{
    static $inst = null;
    if ($inst === null) {
        $inst = new MyAcl();
    }
    return $inst;
}

Am I misunderstanding the goal of this tool, or it makes sense?

401 (Unauthorized) for all routes

Hi,

I have the following code placed in my router.php file:

$app->add(new \Geggleto\Acl\AclRepository(["nobody"],    
 [
    "resources" => ["/", "/public"],
    "roles" => ["nobody"],
    "assignments" => [
        "allow" => [
            "nobody" => ["/", "/public"]
        ],
        "deny" => [
            "nobody" => []
        ]
    ]
])); 

However, when I try to access my homepage locally, I get the 401 (Unauthorized) message and a blank screen. My home route for php is 'http://localhost/drm/public/'

One thing I noticed from your blog post is that you excluded the 'new' instance part in the $app->add statement, which was throwing an error for me: Fatal error: Call to undefined function Geggleto\Acl\AclRepository() in /Applications/XAMPP/xamppfiles/htdocs/drm/src/routers/dashboard.router.php on line 48

Adding the 'new' removed the error but now I'm finding all my routes being blocked. Can you please help? Thanks in advance

resources for groups

Hi,

I'm using Slim v3 and I want to secure a single group with ACL.
I have a "problem".

When I secure a group like this:

$app->group('/extremelyLongStringThatIsNotFunToType', function() {
  $this->get('/short', function($req, $res, $args) {...});
  $this->get('/and', function($req, $res, $args) {...});
  $this->get('/readable', function($req, $res, $args) {...});
})->add(new \Geggleto\Acl\AclRepository([$_SESSION["role"]], [
  "resources" => ["/extremelyLongStringThatIsNotFunToType/short", "/extremelyLongStringThatIsNotFunToType/and", "/extremelyLongStringThatIsNotFunToType/readable"],
  "roles" => ["admin", "user"],
  "assignments" => [
    "allow" => [
      "admin" => ["/extremelyLongStringThatIsNotFunToType/short", "/extremelyLongStringThatIsNotFunToType/and", "/extremelyLongStringThatIsNotFunToType/easy"],
      "user" => ["/extremelyLongStringThatIsNotFunToType/short"]
    "deny" => []
  ]
]));

the resources get rather long, is there a way to use a shorter form (like only the path inside the group and not the whole group/path string?)

Throw exception instead of sending 401 response in default middleware definition

The current behaviour of the middleware is to send a 401 response.
To customize that behaviour, I have to copy/paste the middleware to change it.

The package update process is less easier because I have to check if the default middleware definition has change. Also, a lot of information is lost in the way out (who/when/where/why/...).

To change that, I suggest that the default middleware throws a custom exception (AccessDeniedException?) with all informations. It will enter into errorHandler and allows to log or customize the expected response.

Where to set current role

Hi,

Thanks for the code! I think it will be very useful for me. I'm having a few issues though if you don't mind a quick comment.

I see you have mentioned in the readme about using sessions to store roles. Currently I've implemented a sign in system that sets various session variables like "username", "first_name", etc. Could you provide a bit of information about using a session variable as the role?

What is the purpose of the $role variable in the ACL repo instantiation? In your example you use "guest" but then have multiple roles in $config['roles']. Do you need to instantiate a new ACL repo for every user that require ACL?

Thanks for any help!
-Aric

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.