Eliminates SET role duplication and changes Auth module interface

This commit is contained in:
Diogo Biazus
2015-10-22 00:08:02 -04:00
parent 81ee7cbd5e
commit d000a6c61a
2 changed files with 29 additions and 17 deletions
+18 -11
View File
@@ -1,3 +1,4 @@
{-# LANGUAGE FlexibleContexts #-}
{-| {-|
Module : PostgREST.Auth Module : PostgREST.Auth
Description : PostgREST authorization functions. Description : PostgREST authorization functions.
@@ -10,10 +11,10 @@ Authentication should always be implemented in an external service.
In the test suite there is an example of simple login function that can be used for a In the test suite there is an example of simple login function that can be used for a
very simple authentication system inside the PostgreSQL database. very simple authentication system inside the PostgreSQL database.
-} -}
{-# LANGUAGE FlexibleContexts #-}
module PostgREST.Auth ( module PostgREST.Auth (
setRole setRole
, setJWTEnv , claimsToSQL
, jwtClaims
, tokenJWT , tokenJWT
) where ) where
@@ -32,22 +33,28 @@ import qualified Web.JWT as JWT
import qualified Data.HashMap.Lazy as H import qualified Data.HashMap.Lazy as H
{-| {-|
Receives the JWT secret (from config) and a JWT and Receives a map of JWT claims and returns a list
returns a list of PostgreSQL statements to set the claims of PostgreSQL statements to set the claims as user defined GUCs.
as user defined GUCs. Except if we have a claim called role,
Except if we have a claim called role, this one is mapped to this one is mapped to a SET ROLE statement.
a SET ROLE statement.
In case there is any problem decoding the JWT it returns Nothing. In case there is any problem decoding the JWT it returns Nothing.
-} -}
setJWTEnv :: Text -> Text -> Maybe [Text] claimsToSQL :: JWT.ClaimsMap -> [Text]
setJWTEnv secret input = setDBEnv jwtClaims claimsToSQL = map setVar . toList
where where
setDBEnv maybeClaims = (map setVar . toList) <$> maybeClaims
setVar ("role", String val) = setRole val setVar ("role", String val) = setRole val
setVar (k, val) = "set local postgrest.claims." <> pgFmtIdent k <> setVar (k, val) = "set local postgrest.claims." <> pgFmtIdent k <>
" = " <> valueToVariable val <> ";" " = " <> valueToVariable val <> ";"
valueToVariable = pgFmtLit . unquoted valueToVariable = pgFmtLit . unquoted
jwtClaims = JWT.unregisteredClaims <$> JWT.claims <$> decoded
{-|
Receives the JWT secret (from config) and a JWT and
returns a map of JWT claims
In case there is any problem decoding the JWT it returns Nothing.
-}
jwtClaims :: Text -> Text -> Maybe JWT.ClaimsMap
jwtClaims secret input = JWT.unregisteredClaims <$> JWT.claims <$> decoded
where
decoded = JWT.decodeAndVerifySignature (JWT.secret secret) input decoded = JWT.decodeAndVerifySignature (JWT.secret secret) input
-- | Receives the name of a role and returns a SET ROLE statement -- | Receives the name of a role and returns a SET ROLE statement
+11 -6
View File
@@ -26,13 +26,14 @@ import Network.Wai.Middleware.Gzip (def, gzip)
import Network.Wai.Middleware.Static (only, staticPolicy) import Network.Wai.Middleware.Static (only, staticPolicy)
import PostgREST.App (contentTypeForAccept) import PostgREST.App (contentTypeForAccept)
import PostgREST.Auth (setRole, setJWTEnv) import PostgREST.Auth (setRole, jwtClaims, claimsToSQL)
import PostgREST.Config (AppConfig (..), corsPolicy) import PostgREST.Config (AppConfig (..), corsPolicy)
import Prelude hiding(concat) import Prelude hiding(concat)
import qualified Data.Vector as V import qualified Data.Vector as V
import qualified Hasql.Backend as B import qualified Hasql.Backend as B
import qualified Data.Map.Lazy as M
runWithClaims :: forall s. AppConfig -> runWithClaims :: forall s. AppConfig ->
(Request -> H.Tx P.Postgres s Response) -> (Request -> H.Tx P.Postgres s Response) ->
@@ -41,16 +42,20 @@ runWithClaims conf app req = do
mapM_ H.unitEx $ stmt <$> env mapM_ H.unitEx $ stmt <$> env
app req app req
where where
stmt = (flip $ flip B.Stmt V.empty) True
hdrs = requestHeaders req hdrs = requestHeaders req
jwtSecret = (cs $ configJwtSecret conf) :: Text jwtSecret = (cs $ configJwtSecret conf) :: Text
auth = fromMaybe "" $ lookup hAuthorization hdrs auth = fromMaybe "" $ lookup hAuthorization hdrs
anon = cs $ configAnonRole conf anon = cs $ configAnonRole conf
jwtEnv = claims =
fromMaybe (M.fromList []) $
case split (==' ') (cs auth) of case split (==' ') (cs auth) of
("Bearer" : jwt : _) -> fromMaybe [] (setJWTEnv jwtSecret jwt) ("Bearer" : jwt : _) -> jwtClaims jwtSecret jwt
_ -> [] _ -> Nothing
env = setRole anon : jwtEnv env = if M.member "role" claims
stmt = (flip $ flip B.Stmt V.empty) True then jwtEnv
else setRole anon : jwtEnv
jwtEnv = claimsToSQL claims
redirectInsecure :: Application -> Application redirectInsecure :: Application -> Application
redirectInsecure app req respond = do redirectInsecure app req respond = do