chore(server): fix Logical Model permissions for nested fields

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/8889
GitOrigin-RevId: c86f5328170aaa40cc5bf469f5cbb98e01ac521d
This commit is contained in:
Daniel Harvey 2023-04-25 12:58:15 +01:00 committed by hasura-bot
parent 2a68a4ac18
commit 15ce4818b2
5 changed files with 110 additions and 32 deletions

View File

@ -786,6 +786,69 @@ tests = do
shouldReturnYaml testEnvironment actual expected shouldReturnYaml testEnvironment actual expected
it "Adding a native query with a valid array relationship returns table data along with results when permissions are sufficient" $ \testEnvironment -> do
let backendTypeMetadata = fromMaybe (error "Unknown backend") $ getBackendTypeConfig testEnvironment
sourceName = BackendType.backendSourceName backendTypeMetadata
schemaName = Schema.getSchemaName testEnvironment
backendType = BackendType.backendTypeString backendTypeMetadata
schemaKeyword :: String
schemaKeyword = Key.toString $ Fixture.backendSchemaKeyword backendTypeMetadata
Schema.trackLogicalModel sourceName articleLogicalModel testEnvironment
Schema.trackLogicalModel sourceName authorLogicalModel testEnvironment
Schema.trackNativeQuery sourceName (nativeQueryWithRelationship schemaKeyword schemaName) testEnvironment
void $
GraphqlEngine.postMetadata
testEnvironment
[interpolateYaml|
type: bulk
args:
- type: #{backendType}_create_logical_model_select_permission
args:
source: #{sourceName}
name: author
role: "sufficient"
permission:
columns: "*"
filter: {}
|]
let expected =
[yaml|
data:
relationship_test:
- id: 1
name: "Marenghi"
articles:
- title: "Fright Knight"
- id: 2
name: "Learner"
articles:
- title: "Man to Man"
|]
actual :: IO Value
actual =
GraphqlEngine.postGraphqlWithHeaders
testEnvironment
[ ("X-Hasura-Role", "sufficient")
]
[graphql|
query {
relationship_test {
id
name
articles {
title
}
}
}
|]
shouldReturnYaml testEnvironment actual expected
-- I don't think this is the test we want - ideally checks of this kind -- I don't think this is the test we want - ideally checks of this kind
-- would happen before the resolve the schema -- would happen before the resolve the schema
-- however for now let's just check that doing a stupid thing is not -- however for now let's just check that doing a stupid thing is not

View File

@ -1,17 +1,20 @@
module Hasura.LogicalModel.Common module Hasura.LogicalModel.Common
( toFieldInfo, ( toFieldInfo,
columnsFromFields, columnsFromFields,
logicalModelFieldsToFieldInfo,
) )
where where
import Data.Bifunctor (bimap)
import Data.HashMap.Strict qualified as Map
import Data.HashMap.Strict.InsOrd qualified as InsOrd import Data.HashMap.Strict.InsOrd qualified as InsOrd
import Data.Text.Extended (ToTxt (toTxt)) import Data.Text.Extended (ToTxt (toTxt))
import Hasura.LogicalModel.Types (LogicalModelField (..)) import Hasura.LogicalModel.Types (LogicalModelField (..))
import Hasura.NativeQuery.Types (NullableScalarType (..)) import Hasura.NativeQuery.Types (NullableScalarType (..))
import Hasura.Prelude import Hasura.Prelude
import Hasura.RQL.Types.Backend (Backend (..)) import Hasura.RQL.Types.Backend (Backend (..))
import Hasura.RQL.Types.Column (ColumnInfo (..), ColumnMutability (..), ColumnType (..)) import Hasura.RQL.Types.Column (ColumnInfo (..), ColumnMutability (..), ColumnType (..), fromCol)
import Hasura.RQL.Types.Table (FieldInfo (..)) import Hasura.RQL.Types.Table (FieldInfo (..), FieldInfoMap)
import Language.GraphQL.Draft.Syntax qualified as G import Language.GraphQL.Draft.Syntax qualified as G
columnsFromFields :: columnsFromFields ::
@ -34,20 +37,34 @@ toFieldInfo fields =
traverseWithIndex traverseWithIndex
(\i -> fmap FIColumn . logicalModelToColumnInfo i) (\i -> fmap FIColumn . logicalModelToColumnInfo i)
(InsOrd.toList fields) (InsOrd.toList fields)
where
traverseWithIndex :: (Applicative m) => (Int -> aa -> m bb) -> [aa] -> m [bb]
traverseWithIndex f = zipWithM f [0 ..]
logicalModelToColumnInfo :: Int -> (Column b, NullableScalarType b) -> Maybe (ColumnInfo b) traverseWithIndex :: (Applicative m) => (Int -> aa -> m bb) -> [aa] -> m [bb]
logicalModelToColumnInfo i (column, NullableScalarType {..}) = do traverseWithIndex f = zipWithM f [0 ..]
name <- G.mkName (toTxt column)
pure $ logicalModelToColumnInfo :: forall b. (Backend b) => Int -> (Column b, NullableScalarType b) -> Maybe (ColumnInfo b)
ColumnInfo logicalModelToColumnInfo i (column, NullableScalarType {..}) = do
{ ciColumn = column, name <- G.mkName (toTxt column)
ciName = name, pure $
ciPosition = i, ColumnInfo
ciType = ColumnScalar nstType, { ciColumn = column,
ciIsNullable = nstNullable, ciName = name,
ciDescription = G.Description <$> nstDescription, ciPosition = i,
ciMutability = ColumnMutability {_cmIsInsertable = False, _cmIsUpdatable = False} ciType = ColumnScalar nstType,
} ciIsNullable = nstNullable,
ciDescription = G.Description <$> nstDescription,
ciMutability = ColumnMutability {_cmIsInsertable = False, _cmIsUpdatable = False}
}
logicalModelFieldsToFieldInfo ::
forall b.
(Backend b) =>
InsOrd.InsOrdHashMap (Column b) (LogicalModelField b) ->
FieldInfoMap (FieldInfo b)
logicalModelFieldsToFieldInfo =
Map.fromList
. fmap (bimap (fromCol @b) FIColumn)
. fromMaybe mempty
. traverseWithIndex
(\i (column, lmf) -> (,) column <$> logicalModelToColumnInfo i (column, lmf))
. InsOrd.toList
. columnsFromFields

View File

@ -41,7 +41,8 @@ import Data.Sequence qualified as Seq
import Data.Text.Extended import Data.Text.Extended
import Hasura.Base.Error import Hasura.Base.Error
import Hasura.EncJSON import Hasura.EncJSON
import Hasura.LogicalModel.Types (LogicalModelName) import Hasura.LogicalModel.Common (logicalModelFieldsToFieldInfo)
import Hasura.LogicalModel.Types (LogicalModelField (..), LogicalModelName)
import Hasura.Prelude import Hasura.Prelude
import Hasura.RQL.DDL.Permission.Internal import Hasura.RQL.DDL.Permission.Internal
import Hasura.RQL.IR.BoolExp import Hasura.RQL.IR.BoolExp
@ -245,7 +246,7 @@ buildLogicalModelPermInfo ::
) => ) =>
SourceName -> SourceName ->
LogicalModelName -> LogicalModelName ->
FieldInfoMap (FieldInfo b) -> OMap.InsOrdHashMap (Column b) (LogicalModelField b) ->
PermDefPermission b perm -> PermDefPermission b perm ->
m (WithDeps (PermInfo perm b)) m (WithDeps (PermInfo perm b))
buildLogicalModelPermInfo sourceName logicalModelName fieldInfoMap = \case buildLogicalModelPermInfo sourceName logicalModelName fieldInfoMap = \case
@ -431,17 +432,19 @@ buildLogicalModelSelPermInfo ::
) => ) =>
SourceName -> SourceName ->
LogicalModelName -> LogicalModelName ->
FieldInfoMap (FieldInfo b) -> OMap.InsOrdHashMap (Column b) (LogicalModelField b) ->
SelPerm b -> SelPerm b ->
m (WithDeps (SelPermInfo b)) m (WithDeps (SelPermInfo b))
buildLogicalModelSelPermInfo source logicalModelName fieldInfoMap sp = withPathK "permission" do buildLogicalModelSelPermInfo source logicalModelName logicalModelFieldMap sp = withPathK "permission" do
let columns :: [Column b] let columns :: [Column b]
columns = interpColSpec (ciColumn <$> getCols fieldInfoMap) (spColumns sp) columns = interpColSpec (lmfName <$> OMap.elems logicalModelFieldMap) (spColumns sp)
-- Interpret the row permissions in the 'SelPerm' definition. -- Interpret the row permissions in the 'SelPerm' definition.
-- TODO: do row permisions work on non-scalar fields? Going to assume not and
-- filter out the non-scalars.
(spiFilter, boolExpDeps) <- (spiFilter, boolExpDeps) <-
withPathK "filter" $ withPathK "filter" $
procLogicalModelBoolExp source logicalModelName fieldInfoMap (spFilter sp) procLogicalModelBoolExp source logicalModelName (logicalModelFieldsToFieldInfo logicalModelFieldMap) (spFilter sp)
let -- What parts of the metadata are interesting when computing the let -- What parts of the metadata are interesting when computing the
-- permissions? These dependencies bubble all the way up to -- permissions? These dependencies bubble all the way up to

View File

@ -46,7 +46,6 @@ import Hasura.GraphQL.Schema.NamingCase
import Hasura.Incremental qualified as Inc import Hasura.Incremental qualified as Inc
import Hasura.Logging import Hasura.Logging
import Hasura.LogicalModel.Cache (LogicalModelCache, LogicalModelInfo (..)) import Hasura.LogicalModel.Cache (LogicalModelCache, LogicalModelInfo (..))
import Hasura.LogicalModel.Common (columnsFromFields, toFieldInfo)
import Hasura.LogicalModel.Metadata (LogicalModelMetadata (..)) import Hasura.LogicalModel.Metadata (LogicalModelMetadata (..))
import Hasura.Metadata.Class import Hasura.Metadata.Class
import Hasura.NativeQuery.Cache (NativeQueryCache, NativeQueryInfo (..)) import Hasura.NativeQuery.Cache (NativeQueryCache, NativeQueryInfo (..))
@ -768,12 +767,8 @@ buildSchemaCacheRule logger env = proc (MetadataWithResourceVersion metadataNoDe
unless (_cdcAreNativeQueriesEnabled dynamicConfig) $ unless (_cdcAreNativeQueriesEnabled dynamicConfig) $
throw400 InvalidConfiguration "The Logical Model feature is disabled" throw400 InvalidConfiguration "The Logical Model feature is disabled"
fieldInfoMap <- case toFieldInfo (columnsFromFields _lmmFields) of
Nothing -> pure mempty
Just fields -> pure (mapFromL fieldInfoName fields)
logicalModelPermissions <- logicalModelPermissions <-
buildLogicalModelPermissions sourceName tableCoreInfos _lmmName fieldInfoMap _lmmSelectPermissions orderedRoles buildLogicalModelPermissions sourceName tableCoreInfos _lmmName _lmmFields _lmmSelectPermissions orderedRoles
pure pure
LogicalModelInfo LogicalModelInfo

View File

@ -17,7 +17,7 @@ import Data.Sequence qualified as Seq
import Data.Text.Extended import Data.Text.Extended
import Hasura.Base.Error import Hasura.Base.Error
import Hasura.LogicalModel.Metadata (WithLogicalModel (..)) import Hasura.LogicalModel.Metadata (WithLogicalModel (..))
import Hasura.LogicalModel.Types (LogicalModelName) import Hasura.LogicalModel.Types (LogicalModelField (..), LogicalModelName)
import Hasura.Prelude import Hasura.Prelude
import Hasura.RQL.DDL.Permission import Hasura.RQL.DDL.Permission
import Hasura.RQL.DDL.Schema.Cache.Common import Hasura.RQL.DDL.Schema.Cache.Common
@ -292,7 +292,7 @@ buildLogicalModelPermissions ::
SourceName -> SourceName ->
TableCoreCache b -> TableCoreCache b ->
LogicalModelName -> LogicalModelName ->
FieldInfoMap (FieldInfo b) -> InsOrdHashMap (Column b) (LogicalModelField b) ->
InsOrdHashMap RoleName (SelPermDef b) -> InsOrdHashMap RoleName (SelPermDef b) ->
OrderedRoles -> OrderedRoles ->
m (RolePermInfoMap b) m (RolePermInfoMap b)