From fea1867da84858eaa2a57dad733eebb9796358cb Mon Sep 17 00:00:00 2001 From: John Santiago Jr Date: Fri, 16 Jun 2023 15:06:12 -0700 Subject: [PATCH] ... --- build.sh | 2 +- package.json | 2 +- src/authorization/permission-descriptors.ts | 41 ++++++++++++++++----- src/database/arangodb/aql-generator.ts | 13 ++++++- src/query-tree/lists.ts | 13 +++++++ 5 files changed, 58 insertions(+), 13 deletions(-) diff --git a/build.sh b/build.sh index 1af7d338..c1afacbf 100755 --- a/build.sh +++ b/build.sh @@ -1,4 +1,4 @@ -#!/bin/env bash +#!/bin/zsh set -euxo pipefail rimraf dist tsc --skipLibCheck diff --git a/package.json b/package.json index d2d8f7c4..946c884b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cruddl", - "version": "3.2.0", + "version": "3.2.0-beta.1", "description": "", "license": "MIT", "main": "dist/index.js", diff --git a/src/authorization/permission-descriptors.ts b/src/authorization/permission-descriptors.ts index 8b9ff4d9..92b0d035 100644 --- a/src/authorization/permission-descriptors.ts +++ b/src/authorization/permission-descriptors.ts @@ -5,7 +5,10 @@ import { BinaryOperationQueryNode, BinaryOperator, ConstBoolQueryNode, + ConstIntQueryNode, + CountQueryNode, FieldQueryNode, + IntersectionQueryNode, LiteralQueryNode, QueryNode, UnknownValueQueryNode, @@ -215,6 +218,20 @@ export class ProfileBasedPermissionDescriptor extends PermissionDescriptor { accessGroupConditionNode = ConstBoolQueryNode.TRUE; } + const intersectBinaryOperationQueryNode = ( + fieldNode: QueryNode, + values: ReadonlyArray, + ): QueryNode => { + const node = new BinaryOperationQueryNode( + new CountQueryNode( + new IntersectionQueryNode([fieldNode, new LiteralQueryNode(values)]), + ), + BinaryOperator.GREATER_THAN_OR_EQUAL, + ConstIntQueryNode.ONE, + ); + return node; + }; + const restrictionNodes = permission.restrictions.map((restriction) => { if (!this.rootEntityType) { throw new Error( @@ -239,11 +256,13 @@ export class ProfileBasedPermissionDescriptor extends PermissionDescriptor { if (restriction.valueTemplate !== undefined) { const values = permission.evaluateTemplate(restriction.valueTemplate, authContext); - return new BinaryOperationQueryNode( - fieldNode, - BinaryOperator.IN, - new LiteralQueryNode(values), - ); + return fieldPath.isList + ? intersectBinaryOperationQueryNode(fieldNode, values) + : new BinaryOperationQueryNode( + fieldNode, + BinaryOperator.IN, + new LiteralQueryNode(values), + ); } if (restriction.claim !== undefined) { @@ -255,11 +274,13 @@ export class ProfileBasedPermissionDescriptor extends PermissionDescriptor { if (!sanitizedClaimValues.length) { return ConstBoolQueryNode.FALSE; } - return new BinaryOperationQueryNode( - fieldNode, - BinaryOperator.IN, - new LiteralQueryNode(sanitizedClaimValues), - ); + return fieldPath.isList + ? intersectBinaryOperationQueryNode(fieldNode, sanitizedClaimValues) + : new BinaryOperationQueryNode( + fieldNode, + BinaryOperator.IN, + new LiteralQueryNode(sanitizedClaimValues), + ); } throw new Error(`Invalid permission restriction (field: ${restriction.field})`); diff --git a/src/database/arangodb/aql-generator.ts b/src/database/arangodb/aql-generator.ts index 954d90ce..5fbef1fc 100644 --- a/src/database/arangodb/aql-generator.ts +++ b/src/database/arangodb/aql-generator.ts @@ -28,6 +28,7 @@ import { FieldQueryNode, FirstOfListQueryNode, FollowEdgeQueryNode, + IntersectionQueryNode, ListItemQueryNode, ListQueryNode, LiteralQueryNode, @@ -504,6 +505,12 @@ register(FieldPathQueryNode, (node, context) => { return aql`${object}${getFieldPathAccessFragment(node.path)}`; }); +register(IntersectionQueryNode, (node, context) => { + const listNodes = node.listNodes.map((node) => processNode(node, context)); + const listNodeStr = aql.join(listNodes, aql`, `); + return aql`INTERSECTION(${listNodeStr})`; +}); + function getPropertyAccessFragment(propertyName: string) { if (aql.isSafeIdentifier(propertyName)) { return aql`.${aql.identifier(propertyName)}`; @@ -1383,6 +1390,8 @@ register(TraversalQueryNode, (node, context) => { } } + // console.log('sourceFrag', sourceFrag); + const frag = getRelationTraversalFragment({ segments: node.relationSegments, sourceFrag: fixedSourceFrag, @@ -1486,7 +1495,9 @@ function getRelationTraversalFragment({ segment.vertexFilter.lhs.objectNode === segment.vertexFilterVariable ) ) { - throw new Error(`Unsupported filter pattern for graph traversal`); + throw new Error( + `Unsupported filter pattern for graph traversal - ${segment.vertexFilter.describe()}`, + ); } const vertexInPathFrag = aql`${pathVar}.vertices[*]`; const pathFilterContext = context.introduceVariableAlias( diff --git a/src/query-tree/lists.ts b/src/query-tree/lists.ts index 7e1f6b70..9d91d042 100644 --- a/src/query-tree/lists.ts +++ b/src/query-tree/lists.ts @@ -189,3 +189,16 @@ export class AggregationQueryNode extends QueryNode { return `${this.operator}(${this.listNode.describe()})`; } } + +export class IntersectionQueryNode extends QueryNode { + constructor(public readonly listNodes: ReadonlyArray) { + super(); + } + + describe() { + if (!this.listNodes.length) { + return `[]`; + } + return `intersection(${this.listNodes.map((listNode) => listNode.describe()).join(',')})`; + } +}