{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
module Network.Wai.Middleware.Biscuit
(
parseBiscuit
, getBiscuit
, parseBiscuitWith
, ExtractionConfig (..)
, defaultExtractionConfig
, authorizeBiscuit'
, getAuthorizedBiscuit
, authorizeBiscuitWith
, AuthorizationConfig (..)
, defaultAuthorizationConfig
, defaultExtractToken
, defaultHandleError
) where
import Auth.Biscuit (AuthorizedBiscuit, Authorizer, Biscuit,
ExecutionError, OpenOrSealed, ParseError,
PublicKey, Verified, authorizeBiscuit,
parseB64)
import Control.Monad ((<=<))
import Data.Bifunctor (first)
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import qualified Data.List as List
import qualified Data.Vault.Lazy as Vault
import GHC.IO (unsafePerformIO)
import Network.HTTP.Types (forbidden403, hAuthorization,
unauthorized401)
import Network.Wai (Middleware, Request (..), Response,
responseLBS)
{-# NOINLINE biscuitKey #-}
biscuitKey :: Vault.Key (Biscuit OpenOrSealed Verified)
biscuitKey :: Key (Biscuit OpenOrSealed Verified)
biscuitKey = forall a. IO a -> a
unsafePerformIO forall a. IO (Key a)
Vault.newKey
{-# NOINLINE authorizedBiscuitKey #-}
authorizedBiscuitKey :: Vault.Key (AuthorizedBiscuit OpenOrSealed)
authorizedBiscuitKey :: Key (AuthorizedBiscuit OpenOrSealed)
authorizedBiscuitKey = forall a. IO a -> a
unsafePerformIO forall a. IO (Key a)
Vault.newKey
getBiscuit :: Request -> Maybe (Biscuit OpenOrSealed Verified)
getBiscuit :: Request -> Maybe (Biscuit OpenOrSealed Verified)
getBiscuit = forall a. Key a -> Vault -> Maybe a
Vault.lookup Key (Biscuit OpenOrSealed Verified)
biscuitKey forall b c a. (b -> c) -> (a -> b) -> a -> c
. Request -> Vault
vault
getAuthorizedBiscuit :: Request -> Maybe (AuthorizedBiscuit OpenOrSealed)
getAuthorizedBiscuit :: Request -> Maybe (AuthorizedBiscuit OpenOrSealed)
getAuthorizedBiscuit = forall a. Key a -> Vault -> Maybe a
Vault.lookup Key (AuthorizedBiscuit OpenOrSealed)
authorizedBiscuitKey forall b c a. (b -> c) -> (a -> b) -> a -> c
. Request -> Vault
vault
parseBiscuit :: PublicKey -> Middleware
parseBiscuit :: PublicKey -> Middleware
parseBiscuit = forall e. ExtractionConfig e -> Middleware
parseBiscuitWith forall b c a. (b -> c) -> (a -> b) -> a -> c
. PublicKey -> ExtractionConfig BiscuitError
defaultExtractionConfig
parseBiscuitWith :: ExtractionConfig e -> Middleware
parseBiscuitWith :: forall e. ExtractionConfig e -> Middleware
parseBiscuitWith ExtractionConfig e
config Application
app Request
req Response -> IO ResponseReceived
sendResponse = do
let ExtractionConfig{Request -> IO (Either e ByteString)
$sel:extractToken:ExtractionConfig :: forall e. ExtractionConfig e -> Request -> IO (Either e ByteString)
extractToken :: Request -> IO (Either e ByteString)
extractToken,ByteString -> IO (Either e (Biscuit OpenOrSealed Verified))
$sel:parseToken:ExtractionConfig :: forall e.
ExtractionConfig e
-> ByteString -> IO (Either e (Biscuit OpenOrSealed Verified))
parseToken :: ByteString -> IO (Either e (Biscuit OpenOrSealed Verified))
parseToken,e -> IO Response
$sel:handleError:ExtractionConfig :: forall e. ExtractionConfig e -> e -> IO Response
handleError :: e -> IO Response
handleError} = ExtractionConfig e
config
onError :: e -> IO ResponseReceived
onError = Response -> IO ResponseReceived
sendResponse forall (m :: * -> *) b c a.
Monad m =>
(b -> m c) -> (a -> m b) -> a -> m c
<=< e -> IO Response
handleError
forward :: Biscuit OpenOrSealed Verified -> IO ResponseReceived
forward Biscuit OpenOrSealed Verified
t = do
let newVault :: Vault
newVault = forall a. Key a -> a -> Vault -> Vault
Vault.insert Key (Biscuit OpenOrSealed Verified)
biscuitKey Biscuit OpenOrSealed Verified
t (Request -> Vault
vault Request
req)
Application
app Request
req { vault :: Vault
vault = Vault
newVault } Response -> IO ResponseReceived
sendResponse
Either e (Biscuit OpenOrSealed Verified)
eBiscuit <- forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. a -> Either a b
Left) ByteString -> IO (Either e (Biscuit OpenOrSealed Verified))
parseToken forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Request -> IO (Either e ByteString)
extractToken Request
req
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either e -> IO ResponseReceived
onError Biscuit OpenOrSealed Verified -> IO ResponseReceived
forward Either e (Biscuit OpenOrSealed Verified)
eBiscuit
authorizeBiscuit' :: PublicKey -> (Request -> IO Authorizer) -> Middleware
authorizeBiscuit' :: PublicKey -> (Request -> IO Authorizer) -> Middleware
authorizeBiscuit' PublicKey
publicKey = forall e. AuthorizationConfig e -> Middleware
authorizeBiscuitWith forall b c a. (b -> c) -> (a -> b) -> a -> c
. PublicKey
-> (Request -> IO Authorizer) -> AuthorizationConfig BiscuitError
defaultAuthorizationConfig PublicKey
publicKey
authorizeBiscuitWith :: AuthorizationConfig e -> Middleware
authorizeBiscuitWith :: forall e. AuthorizationConfig e -> Middleware
authorizeBiscuitWith AuthorizationConfig e
config Application
app Request
req Response -> IO ResponseReceived
sendResponse = do
let AuthorizationConfig{Request -> IO (Either e ByteString)
$sel:extractToken:AuthorizationConfig :: forall e.
AuthorizationConfig e -> Request -> IO (Either e ByteString)
extractToken :: Request -> IO (Either e ByteString)
extractToken,ByteString -> IO (Either e (Biscuit OpenOrSealed Verified))
$sel:parseToken:AuthorizationConfig :: forall e.
AuthorizationConfig e
-> ByteString -> IO (Either e (Biscuit OpenOrSealed Verified))
parseToken :: ByteString -> IO (Either e (Biscuit OpenOrSealed Verified))
parseToken,Request
-> Biscuit OpenOrSealed Verified
-> IO (Either e (AuthorizedBiscuit OpenOrSealed))
$sel:authorizeToken:AuthorizationConfig :: forall e.
AuthorizationConfig e
-> Request
-> Biscuit OpenOrSealed Verified
-> IO (Either e (AuthorizedBiscuit OpenOrSealed))
authorizeToken :: Request
-> Biscuit OpenOrSealed Verified
-> IO (Either e (AuthorizedBiscuit OpenOrSealed))
authorizeToken,e -> IO Response
$sel:handleError:AuthorizationConfig :: forall e. AuthorizationConfig e -> e -> IO Response
handleError :: e -> IO Response
handleError} = AuthorizationConfig e
config
onError :: e -> IO ResponseReceived
onError = Response -> IO ResponseReceived
sendResponse forall (m :: * -> *) b c a.
Monad m =>
(b -> m c) -> (a -> m b) -> a -> m c
<=< e -> IO Response
handleError
forward :: AuthorizedBiscuit OpenOrSealed -> IO ResponseReceived
forward AuthorizedBiscuit OpenOrSealed
t = do
let newVault :: Vault
newVault = forall a. Key a -> a -> Vault -> Vault
Vault.insert Key (AuthorizedBiscuit OpenOrSealed)
authorizedBiscuitKey AuthorizedBiscuit OpenOrSealed
t (Request -> Vault
vault Request
req)
Application
app Request
req { vault :: Vault
vault = Vault
newVault } Response -> IO ResponseReceived
sendResponse
Either e (Biscuit OpenOrSealed Verified)
eBiscuit <- forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. a -> Either a b
Left) ByteString -> IO (Either e (Biscuit OpenOrSealed Verified))
parseToken forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Request -> IO (Either e ByteString)
extractToken Request
req
Either e (AuthorizedBiscuit OpenOrSealed)
eResult <- forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. a -> Either a b
Left) (Request
-> Biscuit OpenOrSealed Verified
-> IO (Either e (AuthorizedBiscuit OpenOrSealed))
authorizeToken Request
req) Either e (Biscuit OpenOrSealed Verified)
eBiscuit
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either e -> IO ResponseReceived
onError AuthorizedBiscuit OpenOrSealed -> IO ResponseReceived
forward Either e (AuthorizedBiscuit OpenOrSealed)
eResult
data e
=
{ :: Request -> IO (Either e ByteString)
, :: ByteString -> IO (Either e (Biscuit OpenOrSealed Verified))
, forall e. ExtractionConfig e -> e -> IO Response
handleError :: e -> IO Response
}
data AuthorizationConfig e
= AuthorizationConfig
{ :: Request -> IO (Either e ByteString)
, forall e.
AuthorizationConfig e
-> ByteString -> IO (Either e (Biscuit OpenOrSealed Verified))
parseToken :: ByteString -> IO (Either e (Biscuit OpenOrSealed Verified))
, forall e.
AuthorizationConfig e
-> Request
-> Biscuit OpenOrSealed Verified
-> IO (Either e (AuthorizedBiscuit OpenOrSealed))
authorizeToken :: Request -> Biscuit OpenOrSealed Verified -> IO (Either e (AuthorizedBiscuit OpenOrSealed))
, forall e. AuthorizationConfig e -> e -> IO Response
handleError :: e -> IO Response
}
data BiscuitError
= NoToken
| ParseError ParseError
| AuthorizationError ExecutionError
defaultExtractionConfig :: PublicKey -> ExtractionConfig BiscuitError
PublicKey
publicKey = ExtractionConfig
{ $sel:extractToken:ExtractionConfig :: Request -> IO (Either BiscuitError ByteString)
extractToken = forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall b a. b -> (a -> b) -> Maybe a -> b
maybe (forall a b. a -> Either a b
Left BiscuitError
NoToken) forall a b. b -> Either a b
Right forall b c a. (b -> c) -> (a -> b) -> a -> c
. Request -> Maybe ByteString
defaultExtractToken
, $sel:parseToken:ExtractionConfig :: ByteString
-> IO (Either BiscuitError (Biscuit OpenOrSealed Verified))
parseToken = forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
Data.Bifunctor.first ParseError -> BiscuitError
ParseError forall b c a. (b -> c) -> (a -> b) -> a -> c
. PublicKey
-> ByteString -> Either ParseError (Biscuit OpenOrSealed Verified)
parseB64 PublicKey
publicKey
, $sel:handleError:ExtractionConfig :: BiscuitError -> IO Response
handleError = BiscuitError -> IO Response
defaultHandleError
}
defaultAuthorizationConfig :: PublicKey -> (Request -> IO Authorizer) -> AuthorizationConfig BiscuitError
defaultAuthorizationConfig :: PublicKey
-> (Request -> IO Authorizer) -> AuthorizationConfig BiscuitError
defaultAuthorizationConfig PublicKey
publicKey Request -> IO Authorizer
mkAuthorizer = AuthorizationConfig
{ $sel:extractToken:AuthorizationConfig :: Request -> IO (Either BiscuitError ByteString)
extractToken = forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall b a. b -> (a -> b) -> Maybe a -> b
maybe (forall a b. a -> Either a b
Left BiscuitError
NoToken) forall a b. b -> Either a b
Right forall b c a. (b -> c) -> (a -> b) -> a -> c
. Request -> Maybe ByteString
defaultExtractToken
, $sel:parseToken:AuthorizationConfig :: ByteString
-> IO (Either BiscuitError (Biscuit OpenOrSealed Verified))
parseToken = forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
Data.Bifunctor.first ParseError -> BiscuitError
ParseError forall b c a. (b -> c) -> (a -> b) -> a -> c
. PublicKey
-> ByteString -> Either ParseError (Biscuit OpenOrSealed Verified)
parseB64 PublicKey
publicKey
, $sel:authorizeToken:AuthorizationConfig :: Request
-> Biscuit OpenOrSealed Verified
-> IO (Either BiscuitError (AuthorizedBiscuit OpenOrSealed))
authorizeToken = \Request
req Biscuit OpenOrSealed Verified
token -> forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first ExecutionError -> BiscuitError
AuthorizationError forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (forall proof.
Biscuit proof Verified
-> Authorizer
-> IO (Either ExecutionError (AuthorizedBiscuit proof))
authorizeBiscuit Biscuit OpenOrSealed Verified
token forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Request -> IO Authorizer
mkAuthorizer Request
req)
, $sel:handleError:AuthorizationConfig :: BiscuitError -> IO Response
handleError = BiscuitError -> IO Response
defaultHandleError
}
defaultExtractToken :: Request -> Maybe ByteString
Request
req = do
(HeaderName
_, ByteString
authHeader) <- forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
List.find ((forall a. Eq a => a -> a -> Bool
== HeaderName
hAuthorization) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a, b) -> a
fst) forall a b. (a -> b) -> a -> b
$ Request -> ResponseHeaders
requestHeaders Request
req
ByteString -> ByteString -> Maybe ByteString
BS.stripPrefix ByteString
"Bearer " ByteString
authHeader
defaultHandleError :: BiscuitError -> IO Response
defaultHandleError :: BiscuitError -> IO Response
defaultHandleError = \case
BiscuitError
NoToken -> do
String -> IO ()
putStrLn String
"Missing biscuit token"
forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ Status -> ResponseHeaders -> ByteString -> Response
responseLBS Status
unauthorized401 forall a. Monoid a => a
mempty forall a. Monoid a => a
mempty
ParseError ParseError
e -> do
String -> IO ()
putStrLn forall a b. (a -> b) -> a -> b
$ String
"Parsing or verification error: " forall a. Semigroup a => a -> a -> a
<> forall a. Show a => a -> String
show ParseError
e
forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ Status -> ResponseHeaders -> ByteString -> Response
responseLBS Status
forbidden403 forall a. Monoid a => a
mempty forall a. Monoid a => a
mempty
AuthorizationError ExecutionError
e -> do
String -> IO ()
putStrLn forall a b. (a -> b) -> a -> b
$ String
"Authorization error: " forall a. Semigroup a => a -> a -> a
<> forall a. Show a => a -> String
show ExecutionError
e
forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ Status -> ResponseHeaders -> ByteString -> Response
responseLBS Status
forbidden403 forall a. Monoid a => a
mempty forall a. Monoid a => a
mempty