packages feed

biscuit-servant-0.3.0.0: test/AppWithAuthorizer.hs

{-# LANGUAGE DataKinds         #-}
{-# LANGUAGE NamedFieldPuns    #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes       #-}
{-# LANGUAGE TypeApplications  #-}
{-# LANGUAGE TypeFamilies      #-}
{-# LANGUAGE TypeOperators     #-}
module AppWithAuthorizer where

import           Auth.Biscuit
import           Auth.Biscuit.Servant
import           Control.Monad.IO.Class (liftIO)
import           Control.Monad.Reader   (ask)
import           Data.Text              (Text)
import           Data.Time              (getCurrentTime)
import           Servant
import           Servant.Client

import           ClientHelpers

call1 :: Text -> ClientM Int
call1 b =
  let (e1 :<|> _) = client @API Proxy (protect b)
   in e1

call2 :: Text -> Int -> ClientM Int
call2 b =
  let (_ :<|> e2 :<|> _) = client @API Proxy (protect b)
   in e2

call3 :: Text -> ClientM Int
call3 b =
  let (_ :<|> _ :<|> e3 :<|> _) = client @API Proxy (protect b)
   in e3

call4 :: Text -> ClientM Int
call4 b =
  let (_ :<|> _ :<|> _ :<|> e4) = client @API Proxy (protect b)
   in e4

type H = WithAuthorizer Handler
type H' = WithAuthorizer' Int Handler
type API = RequireBiscuit :> ProtectedAPI
type ProtectedAPI =
       "endpoint1" :> Get '[JSON] Int
  :<|> "endpoint2" :> Capture "int" Int :> Get '[JSON] Int
  :<|> "endpoint3" :> Get '[JSON] Int
  :<|> "endpoint4" :> Get '[JSON] Int

app :: PublicKey -> Application
app appPublicKey =
  serveWithContext @API Proxy (genBiscuitCtx appPublicKey) server

server :: Server API
server b =
  let nowFact = do
        now <- liftIO getCurrentTime
        pure [authorizer|time({now});|]
      handleAuth :: WithAuthorizer Handler x -> Handler x
      handleAuth =
          handleBiscuit b
        . withPriorityAuthorizerM nowFact
        . withPriorityAuthorizer [authorizer|allow if right("admin");|]
        . withFallbackAuthorizer [authorizer|allow if right("anon");|]
      handlers = handler1 :<|> handler2 :<|> handler3 :<|> handler4
   in hoistServer @ProtectedAPI Proxy handleAuth handlers

handler1 :: H Int
handler1 = withAuthorizer [authorizer|allow if right("one");|] $ pure 1

handler2 :: Int -> H Int
handler2 v = withAuthorizer [authorizer|allow if right("two", {v});|] $ pure 2

handler3 :: H Int
handler3 = withAuthorizer [authorizer|deny if true;|] $ pure 3

handler4 :: H Int
handler4 = withTransformation extractUserId $
  withAuthorizer [authorizer|allow if user($user_id); |] $ do
    ask

extractUserId :: AuthorizedBiscuit OpenOrSealed -> Handler Int
extractUserId AuthorizedBiscuit{authorizationSuccess} = do
  let b = getBindings authorizationSuccess
   in maybe (throwError err403) pure $ getSingleVariableValue b "user_id"