Search code examples
haskellauthorizationyesodaccess-controlrbac

How to do (role-based) access control in Yesod?


I'm wondering what the best approach for adding roles to users / permissions to Handlers or resources in Yesod is. Does anyone have prior art for this kind of thing? Are there any approaches that leverage the type-system to help prevent slip-ups (and also keep database queries to check for ownership etc to a minimum)?

EDIT: Sorry for missing this before-hand - I do notice that there's actually a section, which I somehow missed at first glance (I think because there's no mention of access/roles/permissions), on Authorization in the Yesod book. This appears to do access control at the router level with a write flag for PUT/POST. It doesn't seem terribly sophisticated by itself, but it looks just fine for building abstractions on top...


Solution

  • In the time since posting this I have found this very helpful blog post Abstracting permissions with Yesod by Felipe Lessa. It builds on top of the existing isAuthorized function, demonstrating a simple strategy for adding roles to users and access permissions to resources.

    Basically it defines

    isAuthorizedTo :: Maybe (UserId, User) -> [Permission] -> YesodDB sub Blog AuthResult
    permissionsRequiredFor :: Route Blog -> Bool -> [Permission]
    

    in order to get something like this:

    isAuthorized route isWrite = do
      mauth <- maybeAuth
      runDB $ mauth `isAuthorizedTo` permissionsRequiredFor route isWrite
    

    where permissionsRequiredFor returns a list of some user defined Permission data type like this:

    data Permission = Post              -- Permission to create blog posts
                    | CommentOn EntryId -- Permission to comment on a particular blog entry
                    | View EntryId      -- Permission to view a particular blog entry
    

    This is simple and practical, thank you Felipe. (It might be nice if someone tries to capture this kind of thing in library form and publish to Hackage in order to find & drop access control into your app as quickly as possible! Alternatively perhaps in the Yesod scaffold?)