Search code examples
haskell-snap-framework

Snap framework - Restrict access to the whole website including its subsnaplets


I have a simple snaplet, which has its own routing and pretty much independent of the whole application behavior. However, as for most of the application, I want to hide the new simple snaplet under the restricted area, where only logged in users can enter.

For the root snaplet the problem solved by using simple function restricted which accepts a handler and checks if the user logged in, proceeding further with the given handler or redirecting to the login screen.

Here is the whole configuration:

appInit :: SnapletInit App App
appInit = makeSnaplet "myapp" "My Example Application" Nothing $ do
  fs <- nestSnaplet "foo" foo fooInit
  ss <- nestSnaplet "sess" sess $ 
    initCookieSessionManager "site_key.txt" "sess" (Just 3600)
  as <- nestSnaplet "auth" auth $ 
    initJsonFileAuthManager defAuthSettings sess "users.json"
  addRoutes [("content", restricted $ render "content"),
             ("login", login)]
  return $ App ss as fs

restricted :: Handler App App () -> Handler App App ()
restricted = requireUser auth (redirect "/login")

fooInit :: SnapletInit b Foo
fooInit = makeSnaplet "foo" "A nested snaplet" Nothing $ do
    addRoutes [("info", writeText "Only registered users can have acess to it")]
    return Foo

If I enter http://mywebsite/foo/info, I will be able to see the content of the subsnaplet without logging it. It seems to me, that I cannot protect all of the handlers implemented inside of my new Foo without changing that snaplet and modifying its routing. Or am I wrong?

P.S.: There is an option to use weapSite and check the request URL, but since it implies verification based on URL, not on the recourse, (handler in this case) it doesn't seem right to me.


Solution

  • The answer here is to use the wrapSite function. It takes an argument (Handler b v () -> Handler b v ()), which is exactly the type signature of your restricted function.