Eliminates SET role duplication and changes Auth module interface
This commit is contained in:
+18
-11
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user