{-# LANGUAGE QuasiQuotes #-} -- | -- Tests for backend-only permissions. -- -- https://hasura.io/docs/latest/schema/postgres/data-validations/#using-hasura-permissions module Test.Postgres.BackendOnlyPermissionsSpec (spec) where import Data.Aeson (Value) import Data.ByteString (ByteString) import Data.List.NonEmpty qualified as NE import Harness.Backend.Postgres qualified as Postgres import Harness.GraphqlEngine (postGraphqlWithHeaders, postMetadata_) import Harness.Quoter.Graphql (graphql) import Harness.Quoter.Yaml (yaml) import Harness.Test.Fixture qualified as Fixture import Harness.Test.Schema (Table (..), table) import Harness.Test.Schema qualified as Schema import Harness.TestEnvironment (TestEnvironment) import Harness.Yaml (shouldReturnYaml) import Hasura.Prelude import Network.HTTP.Types.Header (HeaderName) import Test.Hspec (SpecWith, describe, it) spec :: SpecWith TestEnvironment spec = Fixture.run ( NE.fromList [ (Fixture.fixture $ Fixture.Backend Fixture.Postgres) { Fixture.setupTeardown = \(testEnvironment, _) -> [ Postgres.setupTablesAction schema testEnvironment, setupMetadata testEnvironment ] } ] ) tests -------------------------------------------------------------------------------- -- Schema schema :: [Schema.Table] schema = [ (table "author") { tableColumns = [ Schema.column "id" Schema.TInt, Schema.column "name" Schema.TStr ], tablePrimaryKey = ["id"], tableData = [ [Schema.VInt 1, Schema.VStr "Author 1"], [Schema.VInt 2, Schema.VStr "Author 2"] ] }, (table "article") { tableColumns = [ Schema.column "id" Schema.TInt, Schema.column "title" Schema.TStr, Schema.columnNull "content" Schema.TStr, Schema.column "author_id" Schema.TInt ], tablePrimaryKey = ["id"], tableReferences = [Schema.Reference "author_id" "author" "id"] } ] ------------------------------------------------------------------------------- -- Tests tests :: Fixture.Options -> SpecWith TestEnvironment tests opts = do let shouldBe :: IO Value -> Value -> IO () shouldBe = shouldReturnYaml opts describe "Backend-only fields should be exposed to a role with `backend_only` set to `true`" do let headers = [ ("X-Hasura-Role", "backend_only_role"), ("X-Hasura-User-Id", "1"), ("X-Hasura-use-backend-only-permissions", "true") ] it "Update root field should be accesible" \testEnvironment -> do let expected :: Value expected = [yaml| data: update_hasura_author: affected_rows: 1 |] actual :: IO Value actual = postGraphqlWithHeaders testEnvironment headers [graphql| mutation { update_hasura_author (_set: { name: "Author 1 modified" }, where: {}) { affected_rows } } |] actual `shouldBe` expected it "Delete root field should be accesible" \testEnvironment -> do let expected :: Value expected = [yaml| data: delete_hasura_author: affected_rows: 1 |] actual :: IO Value actual = postGraphqlWithHeaders testEnvironment headers [graphql| mutation { delete_hasura_author (where: {}) { affected_rows } } |] actual `shouldBe` expected describe "Backend-only fields should not be exposed to a role with `backend_only` set to `true` but `X-Hasura-use-backend-only-permissions` set to `false`" do let headers :: [(HeaderName, ByteString)] headers = [ ("X-Hasura-Role", "backend_only_role"), ("X-Hasura-User-Id", ""), ("X-Hasura-use-backend-only-permissions", "false") ] it "Update root field should not be accesible" \testEnvironment -> do let expected :: Value expected = [yaml| errors: - extensions: path: $ code: validation-failed message: no mutations exist |] actual :: IO Value actual = postGraphqlWithHeaders testEnvironment headers [graphql| mutation { update_hasura_author ( _set: { name: "Author 1 modified" }, where: {} ) { affected_rows } } |] actual `shouldBe` expected it "Delete root field should not be accesible" \testEnvironment -> do let expected :: Value expected = [yaml| errors: - extensions: path: $ code: validation-failed message: no mutations exist |] actual :: IO Value actual = postGraphqlWithHeaders testEnvironment headers [graphql| mutation { delete_hasura_author (where: {}) { affected_rows } } |] actual `shouldBe` expected describe "Backend-only fields should be exposed to a role with `backend_only` set to `false`" do let headers = [ ("X-Hasura-Role", "frontend_only_role"), ("X-Hasura-User-Id", "2"), ("X-Hasura-use-backend-only-permissions", "true") ] it "Update root field should be accesible" \testEnvironment -> do let expected :: Value expected = [yaml| data: update_hasura_author: affected_rows: 1 |] actual :: IO Value actual = postGraphqlWithHeaders testEnvironment headers [graphql| mutation { update_hasura_author ( _set: { name: "Author 2 modified" }, where: {} ) { affected_rows } } |] actual `shouldBe` expected it "Delete root field should be accesible" \testEnvironment -> do let expected :: Value expected = [yaml| data: delete_hasura_author: affected_rows: 1 |] actual :: IO Value actual = postGraphqlWithHeaders testEnvironment headers [graphql| mutation { delete_hasura_author (where: {}) { affected_rows } } |] actual `shouldBe` expected -------------------------------------------------------------------------------- -- Metadata setupMetadata :: TestEnvironment -> Fixture.SetupAction setupMetadata testEnvironment = do let setup :: IO () setup = postMetadata_ testEnvironment [yaml| type: bulk args: - type: pg_create_update_permission args: table: schema: hasura name: author source: postgres role: backend_only_role permission: columns: - name filter: id: x-hasura-user-id check: name: _ne: "" backend_only: true - type: pg_create_update_permission args: table: schema: hasura name: author source: postgres role: frontend_only_role permission: columns: - name filter: id: x-hasura-user-id check: name: _ne: "" - type: pg_create_delete_permission args: table: schema: hasura name: author source: postgres role: backend_only_role permission: filter: id: x-hasura-user-id backend_only: true - type: pg_create_delete_permission args: table: schema: hasura name: author source: postgres role: frontend_only_role permission: filter: id: x-hasura-user-id |] teardown :: IO () teardown = postMetadata_ testEnvironment [yaml| type: bulk args: - type: pg_drop_delete_permission args: table: schema: hasura name: author source: postgres role: frontend_only_role - type: pg_drop_delete_permission args: table: schema: hasura name: author source: postgres role: backend_only_role - type: pg_drop_update_permission args: table: schema: hasura name: author source: postgres role: frontend_only_role - type: pg_drop_update_permission args: table: schema: hasura name: author source: postgres role: backend_only_role |] Fixture.SetupAction setup \_ -> teardown