It’s been a busy few months. I’ve been busy at Totsy.com a startup that is taking the best of Lithium, MongoDB, and my time. If you want the lowdown of what I’m talking about check out the slides from Mitch Pirtle CTO at Totsy.

To the business at hand – Simple ACL with Lithium and MongoDB. Let’s get down to it!

The heart of this ACL functionality rests with the Li3 Router. With the Router you can connect a URL to a specific Class::action. This is handy when you want to have /login route to app\controllers\UsersController::login(). Of course, your routes can become more complicated and robust with a little regex goodness. Take a look at a default route for example:

    Router::connect('/{:controller}/{:action}/{:args}');

When going to the url http://yourapp.com/users/login/23423  then Li3 will map to the users controller, login action (or method) and pass the 23423 as the argument or input to that method. That’s all we really need to hookup a route and in turn give us the ability to setup ACL.

If there are no routes hooked up in your app/config/routes.php then requests will go nowhere. Without some default routes hooked up you’ll app will be sitting dead without anywhere to go. This is the perfect time to setup an ACL. How do we get this all going?

The next step is to call upon MongoDB. With MongoDB we can embed an array with the route and parameter into a mongo document object. I would embed this array with the user document that has the information used for login. This way, you can pull the entire document with one query from Mongo. Too simple to be true? Lets take a step by step approach:

1) Hook up your code in routes.php to loop through the document pulled from your user document:

    if (isset($session['acls'])) {
        foreach ($session['acls'] as $acl) {
             Router::connect($acl['route'], $acl['connection']);
        }
    }

Note that in my app I put the necessary information in the users session after they successfully login.

2) Embed the acls array as part of your user document schema. For example:

{
    "_id" : ObjectId("4c8d53e0ce64e5f449992400"),
    "created_date" : "Sun Sep 12 2010 18:27:44 GMT-0400 (EDT)",
    "email" : "test@lcs.com",
    "firstname" : "Test",
    "lastname" : "User",
    "password" : "b119670d125b0bd4271b25ce39fa166bc3bf79a0",
    "acls" : [
        {
            "route" : "/orders",
            "connection":"Orders::index"
        },
        {
           "route" : "/posts/view/{:item:[a-z0-9\-]+}",
           "connection":"Events::view"
        }
    ]
}

3) Do a happy dance because you’re done!!!

With a little admin work you can put together groups and more robust configurations for ACL.  At the end of the day all you really need to do is loop through an array of routes and connections and you’ll have the exact ACL you need to keep everyone in order.

6 thoughts on “Simple ACL with Lithium and MongoDB

  1. Hi, I tried this out, but I’m having problems loading the $session or $_SESSION var when routes.php is being called, do you use the default bootstrap or have you changed it?

    Reply
    • Thanks for the question. I use the lithium session storage class to access the PHP session info directly in the route.php file. In part it would look something like:

      use \lithium\storage\Session;

      $session = Session::read(‘userLogin’);

      Then I work directly with the variable $session['acls'];

      In order to save the session info in the first place I use the built in lithium class Auth. If authentication is sucessful then the users document will be written to the session. Just to bring things full circle:

      Auth::check(“userLogin”, $this->request);

      $this->request is the information that was submitted in the login form.

      I hope that helps.

      Reply
  2. Pingback: List of useful Lithium (Li3) resources « Web mellom fjell

  3. Hello,

    It might be of some use to, at some point, differentiate between non-existent routes and “existing” ones that are not connected.

    If there is a need to display a not authorized page instead of a 404, there is probably a way to add a last “catch all” route that introspect the raised exception.

    Lithium is already doing this when evaluating the default catch all routes, there must be a way to do the same and if the controller/action/args combo exists, display a 503 instead of a 404.

    But.. is it really worth it?

    Reply
  4. Not sure I like this. Shouldn’t ACLs be protecting the resource? You’re storing ACLs in the user table. So if you want to change the acl on the route you now have to loop through all the users and change the ACLs *there*. Messy. I’m not an ACL expert but seems to me it would be much better to do this:

    Build Roles. Store the roles in the user table.
    Store ACL rules in the routes somehow. Mapping them to roles.

    Never done ACL w/ Lithium so I may be missing information. But that’s why I found your link ;)

    Reply
    • @joedevon, I agree with you. I dont’ think it’s a solid solution for a large production environment (hence the “simple” approach). My intent was to demonstrate the ease with which you could utilize Lithium and MongoDB to do something simple that is typically complicated. I think your thought is right on to put a layer of roles to protect the resource.

      For something more robust I would check out li3_access

      Reply

Leave a reply

required

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>