mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 09:22:43 +03:00
set all session data in a single paramater, 'hasura.user' (closes #825)
This commit is contained in:
parent
cd030068c2
commit
ab9692da4d
22
docs/graphql/manual/guides/auditing-tables.rst
Normal file
22
docs/graphql/manual/guides/auditing-tables.rst
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
Auditing actions on tables in Postgres
|
||||||
|
======================================
|
||||||
|
|
||||||
|
Typically audit logging is added to some of the tables to comply with various certifications.
|
||||||
|
You may want to capture the user information (role and the session variables) for every change in Postgres that is done through graphql-engine.
|
||||||
|
|
||||||
|
For every mutation, hasura roughly executes the following transaction:
|
||||||
|
|
||||||
|
.. code-block:: sql
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
SET local "hasura.user" = '{"x-hasura-role": "role", ... various session variables}'
|
||||||
|
SQL related to the mutation
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
This information can therefore be captured in any trigger on the underlying tables by using the ``current_setting`` function as follows:
|
||||||
|
|
||||||
|
.. code-block:: sql
|
||||||
|
|
||||||
|
current_setting('hasura.user');
|
||||||
|
|
||||||
|
We've set up some utility functions that'll let you quickly get started with auditing in this `repo <https://github.com/hasura/audit-trigger>`__.
|
@ -54,3 +54,4 @@ Articles:
|
|||||||
Sample apps <sample-apps/index>
|
Sample apps <sample-apps/index>
|
||||||
Integration/migration tutorials <integrations/index>
|
Integration/migration tutorials <integrations/index>
|
||||||
Integrating with monitoring frameworks <monitoring/index>
|
Integrating with monitoring frameworks <monitoring/index>
|
||||||
|
Auditing tables <auditing-tables>
|
||||||
|
@ -210,16 +210,16 @@ valueParser :: (MonadError QErr m) => PGColType -> Value -> m S.SQLExp
|
|||||||
valueParser columnType = \case
|
valueParser columnType = \case
|
||||||
-- When it is a special variable
|
-- When it is a special variable
|
||||||
val@(String t)
|
val@(String t)
|
||||||
| isXHasuraTxt t -> asCurrentSetting t
|
| isXHasuraTxt t -> return $ fromCurSess t
|
||||||
| isReqUserId t -> asCurrentSetting userIdHeader
|
| isReqUserId t -> return $ fromCurSess userIdHeader
|
||||||
| otherwise -> txtRHSBuilder columnType val
|
| otherwise -> txtRHSBuilder columnType val
|
||||||
|
|
||||||
-- Typical value as Aeson's value
|
-- Typical value as Aeson's value
|
||||||
val -> txtRHSBuilder columnType val
|
val -> txtRHSBuilder columnType val
|
||||||
where
|
where
|
||||||
asCurrentSetting hdr = return $ S.SEUnsafe $
|
curSess = S.SEUnsafe "current_setting('hasura.user')::json"
|
||||||
"current_setting('hasura." <> dropAndSnakeCase hdr
|
fromCurSess hdr =
|
||||||
<> "')::" <> T.pack (show columnType)
|
S.SEOpApp (S.SQLOp "->>") [curSess, S.SELit $ T.toLower hdr]
|
||||||
|
`S.SETyAnn` (S.AnnType $ T.pack $ show columnType)
|
||||||
|
|
||||||
-- Convert where clause into SQL BoolExp
|
-- Convert where clause into SQL BoolExp
|
||||||
convFilterExp :: (MonadError QErr m)
|
convFilterExp :: (MonadError QErr m)
|
||||||
|
@ -65,7 +65,8 @@ $(J.deriveJSON (J.aesonDrop 4 J.camelCase){J.omitNothingFields=True}
|
|||||||
)
|
)
|
||||||
|
|
||||||
adminUserInfo :: UserInfo
|
adminUserInfo :: UserInfo
|
||||||
adminUserInfo = UserInfo adminRole Map.empty
|
adminUserInfo =
|
||||||
|
UserInfo adminRole $ Map.singleton "x-hasura-role" "admin"
|
||||||
|
|
||||||
data PermType
|
data PermType
|
||||||
= PTInsert
|
= PTInsert
|
||||||
|
@ -172,7 +172,7 @@ getUserInfo logger manager rawHeaders = \case
|
|||||||
userInfoFromHeaders =
|
userInfoFromHeaders =
|
||||||
case M.lookup userRoleHeader headers of
|
case M.lookup userRoleHeader headers of
|
||||||
Just v -> UserInfo (RoleName v) headers
|
Just v -> UserInfo (RoleName v) headers
|
||||||
Nothing -> UserInfo adminRole M.empty
|
Nothing -> adminUserInfo
|
||||||
|
|
||||||
userInfoWhenAccessKey key reqKey = do
|
userInfoWhenAccessKey key reqKey = do
|
||||||
when (reqKey /= getAccessKey key) $ throw401 $ "invalid " <> accessKeyHeader
|
when (reqKey /= getAccessKey key) $ throw401 $ "invalid " <> accessKeyHeader
|
||||||
|
@ -10,10 +10,11 @@ import Data.Aeson.Casing
|
|||||||
import Data.Aeson.TH
|
import Data.Aeson.TH
|
||||||
import Language.Haskell.TH.Syntax (Lift)
|
import Language.Haskell.TH.Syntax (Lift)
|
||||||
|
|
||||||
|
import qualified Data.Aeson.Text as AT
|
||||||
import qualified Data.ByteString.Builder as BB
|
import qualified Data.ByteString.Builder as BB
|
||||||
import qualified Data.ByteString.Lazy as BL
|
import qualified Data.ByteString.Lazy as BL
|
||||||
import qualified Data.HashMap.Strict as Map
|
|
||||||
import qualified Data.Sequence as Seq
|
import qualified Data.Sequence as Seq
|
||||||
|
import qualified Data.Text.Lazy as LT
|
||||||
import qualified Data.Vector as V
|
import qualified Data.Vector as V
|
||||||
|
|
||||||
import Hasura.Prelude
|
import Hasura.Prelude
|
||||||
@ -25,7 +26,6 @@ import Hasura.RQL.DDL.Schema.Table
|
|||||||
import Hasura.RQL.DML.QueryTemplate
|
import Hasura.RQL.DML.QueryTemplate
|
||||||
import Hasura.RQL.DML.Returning (encodeJSONVector)
|
import Hasura.RQL.DML.Returning (encodeJSONVector)
|
||||||
import Hasura.RQL.Types
|
import Hasura.RQL.Types
|
||||||
import Hasura.Server.Utils
|
|
||||||
import Hasura.SQL.Types
|
import Hasura.SQL.Types
|
||||||
|
|
||||||
import qualified Database.PG.Query as Q
|
import qualified Database.PG.Query as Q
|
||||||
@ -220,9 +220,9 @@ buildTxAny userInfo sc rq = case rq of
|
|||||||
|
|
||||||
setHeadersTx :: UserInfo -> Q.TxE QErr ()
|
setHeadersTx :: UserInfo -> Q.TxE QErr ()
|
||||||
setHeadersTx userInfo =
|
setHeadersTx userInfo =
|
||||||
forM_ hdrs $ \h -> Q.unitQE defaultTxErrorHandler (mkQ h) () False
|
Q.unitQE defaultTxErrorHandler setSess () False
|
||||||
where
|
where
|
||||||
hdrs = Map.toList $ Map.delete accessKeyHeader
|
toStrictText = LT.toStrict . AT.encodeToLazyText
|
||||||
$ userHeaders userInfo
|
setSess = Q.fromText $
|
||||||
mkQ (h, v) = Q.fromText $
|
"SET LOCAL \"hasura.user\" = " <>
|
||||||
"SET LOCAL hasura." <> dropAndSnakeCase h <> " = " <> pgFmtLit v
|
pgFmtLit (toStrictText $ userHeaders userInfo)
|
||||||
|
@ -21,16 +21,6 @@ import qualified Text.Ginger as TG
|
|||||||
|
|
||||||
import Hasura.Prelude
|
import Hasura.Prelude
|
||||||
|
|
||||||
|
|
||||||
dropAndSnakeCase :: T.Text -> T.Text
|
|
||||||
dropAndSnakeCase = T.drop 9 . toSnakeCase . T.toLower
|
|
||||||
|
|
||||||
toSnakeCase :: T.Text -> T.Text
|
|
||||||
toSnakeCase = T.pack . map change . T.unpack
|
|
||||||
where
|
|
||||||
change '-' = '_'
|
|
||||||
change c = c
|
|
||||||
|
|
||||||
isXHasuraTxt :: T.Text -> Bool
|
isXHasuraTxt :: T.Text -> Bool
|
||||||
isXHasuraTxt = T.isInfixOf "x-hasura-" . T.toLower
|
isXHasuraTxt = T.isInfixOf "x-hasura-" . T.toLower
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user