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>
|
||||
Integration/migration tutorials <integrations/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
|
||||
-- When it is a special variable
|
||||
val@(String t)
|
||||
| isXHasuraTxt t -> asCurrentSetting t
|
||||
| isReqUserId t -> asCurrentSetting userIdHeader
|
||||
| otherwise -> txtRHSBuilder columnType val
|
||||
|
||||
| isXHasuraTxt t -> return $ fromCurSess t
|
||||
| isReqUserId t -> return $ fromCurSess userIdHeader
|
||||
| otherwise -> txtRHSBuilder columnType val
|
||||
-- Typical value as Aeson's value
|
||||
val -> txtRHSBuilder columnType val
|
||||
where
|
||||
asCurrentSetting hdr = return $ S.SEUnsafe $
|
||||
"current_setting('hasura." <> dropAndSnakeCase hdr
|
||||
<> "')::" <> T.pack (show columnType)
|
||||
curSess = S.SEUnsafe "current_setting('hasura.user')::json"
|
||||
fromCurSess hdr =
|
||||
S.SEOpApp (S.SQLOp "->>") [curSess, S.SELit $ T.toLower hdr]
|
||||
`S.SETyAnn` (S.AnnType $ T.pack $ show columnType)
|
||||
|
||||
-- Convert where clause into SQL BoolExp
|
||||
convFilterExp :: (MonadError QErr m)
|
||||
|
@ -65,7 +65,8 @@ $(J.deriveJSON (J.aesonDrop 4 J.camelCase){J.omitNothingFields=True}
|
||||
)
|
||||
|
||||
adminUserInfo :: UserInfo
|
||||
adminUserInfo = UserInfo adminRole Map.empty
|
||||
adminUserInfo =
|
||||
UserInfo adminRole $ Map.singleton "x-hasura-role" "admin"
|
||||
|
||||
data PermType
|
||||
= PTInsert
|
||||
|
@ -172,7 +172,7 @@ getUserInfo logger manager rawHeaders = \case
|
||||
userInfoFromHeaders =
|
||||
case M.lookup userRoleHeader headers of
|
||||
Just v -> UserInfo (RoleName v) headers
|
||||
Nothing -> UserInfo adminRole M.empty
|
||||
Nothing -> adminUserInfo
|
||||
|
||||
userInfoWhenAccessKey key reqKey = do
|
||||
when (reqKey /= getAccessKey key) $ throw401 $ "invalid " <> accessKeyHeader
|
||||
|
@ -10,10 +10,11 @@ import Data.Aeson.Casing
|
||||
import Data.Aeson.TH
|
||||
import Language.Haskell.TH.Syntax (Lift)
|
||||
|
||||
import qualified Data.Aeson.Text as AT
|
||||
import qualified Data.ByteString.Builder as BB
|
||||
import qualified Data.ByteString.Lazy as BL
|
||||
import qualified Data.HashMap.Strict as Map
|
||||
import qualified Data.Sequence as Seq
|
||||
import qualified Data.Text.Lazy as LT
|
||||
import qualified Data.Vector as V
|
||||
|
||||
import Hasura.Prelude
|
||||
@ -25,7 +26,6 @@ import Hasura.RQL.DDL.Schema.Table
|
||||
import Hasura.RQL.DML.QueryTemplate
|
||||
import Hasura.RQL.DML.Returning (encodeJSONVector)
|
||||
import Hasura.RQL.Types
|
||||
import Hasura.Server.Utils
|
||||
import Hasura.SQL.Types
|
||||
|
||||
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 =
|
||||
forM_ hdrs $ \h -> Q.unitQE defaultTxErrorHandler (mkQ h) () False
|
||||
Q.unitQE defaultTxErrorHandler setSess () False
|
||||
where
|
||||
hdrs = Map.toList $ Map.delete accessKeyHeader
|
||||
$ userHeaders userInfo
|
||||
mkQ (h, v) = Q.fromText $
|
||||
"SET LOCAL hasura." <> dropAndSnakeCase h <> " = " <> pgFmtLit v
|
||||
toStrictText = LT.toStrict . AT.encodeToLazyText
|
||||
setSess = Q.fromText $
|
||||
"SET LOCAL \"hasura.user\" = " <>
|
||||
pgFmtLit (toStrictText $ userHeaders userInfo)
|
||||
|
@ -21,16 +21,6 @@ import qualified Text.Ginger as TG
|
||||
|
||||
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.isInfixOf "x-hasura-" . T.toLower
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user