haskellyesodhamlet

Check for admin permissions in Yesod using database


I'm trying to implement the isAdmin function in a similar fashion to these Yesod book examples. The problem is that I want to keep my 'admin' emails in the database, but when I make a database query, isAdmin loses its purity, so I have to return Handler-something IO Bool instead of just Bool. Now, this scraps the very nice syntax sugar in hamlet that allows to do so:

$if isAdmin
    <p ...

Is there a way to still have a pure isAdmin function even if I make a call to the database (I seriously doubt that... by the way, I don't want to pass any parameters to isAdmin)? Or maybe I could still use the $if syntax with the impure function?

P.S.: Currently, I am using a hack in the handler module:

isadmin <- isAdmin

So, I can write in the hamlet corresponding to that handler:

$if isadmin

But it is not universal and I like the first method more.


Solution

  • N.B.: I don't actually know Yesod; this answer is based on general Haskell practice.

    Is there a way to still have a pure isAdmin function even if I make a call to the database (I seriously doubt that

    It is not possible indeed. Access to an external database must, in one way or another, live in IO; there is no way around it.

    Currently, I am using a hack in the handler module:

    isadmin <- isAdmin

    So, I can write in the hamlet corresponding to that handler:

    $if isadmin

    That is not a hack; it is one of the standard ways of using the result from a monadic computation. In fact, in the Yesod book page you linked to, right before the isAdmin example you mentioned, there is a snippet that runs a database query and uses the result with $if in a very similar way to what you are doing now:

    getBlogR :: Handler Html
    getBlogR = do
        -- etc.
        entries <- runDB $ selectList [] [Desc EntryPosted]
        -- etc.
    $if null entries
        -- etc.
    

    P.S.:

    N.B.: I don't want to pass any parameters to isAdmin

    I wonder why you don't want to do so. Perhaps it is because your only need to check whether a single specific user is an admin. In that case, you should consider renaming your computation to isCurrentUserAdmin (or whatever makes sense in your use case). Seeing a name like isAdmin begs the question "who is an admin?", and so it is rather surprising that something with that name doesn't take any arguments.