@@ -21,15 +21,16 @@ import Cypher from "@neo4j/cypher-builder";
2121import type { ConcreteEntityAdapter } from "../../../../schema-model/entity/model-adapters/ConcreteEntityAdapter" ;
2222import type { RelationshipAdapter } from "../../../../schema-model/relationship/model-adapters/RelationshipAdapter" ;
2323import { filterTruthy } from "../../../../utils/utils" ;
24- import { getEntityLabels } from "../../utils/create-node-from-entity" ;
24+ import { checkEntityAuthentication } from "../../../authorization/check-authentication" ;
25+ import { isConcreteEntity } from "../../utils/is-concrete-entity" ;
2526import { wrapSubqueriesInCypherCalls } from "../../utils/wrap-subquery-in-calls" ;
2627import type { QueryASTContext } from "../QueryASTContext" ;
2728import type { QueryASTNode } from "../QueryASTNode" ;
2829import type { Filter } from "../filters/Filter" ;
2930import type { AuthorizationFilters } from "../filters/authorization-filters/AuthorizationFilters" ;
3031import type { InputField } from "../input-fields/InputField" ;
32+ import { ParamInputField } from "../input-fields/ParamInputField" ;
3133import type { SelectionPattern } from "../selection/SelectionPattern/SelectionPattern" ;
32- import type { ReadOperation } from "./ReadOperation" ;
3334import { MutationOperation , type OperationTranspileResult } from "./operations" ;
3435
3536export class DisconnectOperation extends MutationOperation {
@@ -38,6 +39,7 @@ export class DisconnectOperation extends MutationOperation {
3839
3940 private selectionPattern : SelectionPattern ;
4041 protected readonly authFilters : AuthorizationFilters [ ] = [ ] ;
42+ protected readonly sourceAuthFilters : AuthorizationFilters [ ] = [ ] ;
4143
4244 public readonly inputFields : Map < string , InputField > = new Map ( ) ;
4345 private filters : Filter [ ] = [ ] ;
@@ -75,6 +77,9 @@ export class DisconnectOperation extends MutationOperation {
7577 public addAuthFilters ( ...filter : AuthorizationFilters [ ] ) {
7678 this . authFilters . push ( ...filter ) ;
7779 }
80+ public addSourceAuthFilters ( ...filter : AuthorizationFilters [ ] ) {
81+ this . sourceAuthFilters . push ( ...filter ) ;
82+ }
7883
7984 /**
8085 * Get and set field methods are utilities to remove duplicate fields between separate inputs
@@ -116,6 +121,29 @@ export class DisconnectOperation extends MutationOperation {
116121 const { nestedContext, pattern : matchPattern } = this . selectionPattern . apply ( context ) ;
117122 this . nestedContext = nestedContext ;
118123
124+ checkEntityAuthentication ( {
125+ context : nestedContext . neo4jGraphQLContext ,
126+ entity : this . target . entity ,
127+ targetOperations : [ "DELETE_RELATIONSHIP" ] ,
128+ } ) ;
129+ if ( isConcreteEntity ( this . relationship . source ) ) {
130+ checkEntityAuthentication ( {
131+ context : nestedContext . neo4jGraphQLContext ,
132+ entity : this . relationship . source . entity ,
133+ targetOperations : [ "DELETE_RELATIONSHIP" ] ,
134+ } ) ;
135+ }
136+ this . inputFields . forEach ( ( field ) => {
137+ if ( field . attachedTo === "node" && field instanceof ParamInputField ) {
138+ checkEntityAuthentication ( {
139+ context : nestedContext . neo4jGraphQLContext ,
140+ entity : this . target . entity ,
141+ targetOperations : [ "DELETE_RELATIONSHIP" ] ,
142+ field : field . name ,
143+ } ) ;
144+ }
145+ } ) ;
146+
119147 const allFilters = [ ...this . authFilters , ...this . filters ] ;
120148
121149 const filterSubqueries = wrapSubqueriesInCypherCalls ( nestedContext , allFilters , [ nestedContext . target ] ) ;
@@ -124,13 +152,13 @@ export class DisconnectOperation extends MutationOperation {
124152 if ( filterSubqueries . length > 0 ) {
125153 const predicate = Cypher . and ( ...allFilters . map ( ( f ) => f . getPredicate ( nestedContext ) ) ) ;
126154 matchClause = Cypher . utils . concat (
127- new Cypher . Match ( matchPattern ) ,
155+ new Cypher . OptionalMatch ( matchPattern ) ,
128156 ...filterSubqueries ,
129157 new Cypher . With ( "*" ) . where ( predicate )
130158 ) ;
131159 } else {
132160 const predicate = Cypher . and ( ...allFilters . map ( ( f ) => f . getPredicate ( nestedContext ) ) ) ;
133- matchClause = new Cypher . Match ( matchPattern ) . where ( predicate ) ;
161+ matchClause = new Cypher . OptionalMatch ( matchPattern ) . where ( predicate ) ;
134162 }
135163
136164 const relVar = new Cypher . Relationship ( ) ;
@@ -145,15 +173,33 @@ export class DisconnectOperation extends MutationOperation {
145173
146174 const deleteClause = new Cypher . With ( nestedContext . relationship ! ) . delete ( nestedContext . relationship ! ) ;
147175
148- const clauses = Cypher . utils . concat (
149- matchClause ,
150- ...this . getAuthorizationClauses ( nestedContext ) , // THESE ARE "BEFORE" AUTH
151- ...mutationSubqueries ,
152- deleteClause ,
153- ...this . getAuthorizationClausesAfter ( nestedContext ) // THESE ARE "AFTER" AUTH
154- ) ;
176+ const authClausesBefore = this . getAuthorizationClauses ( nestedContext ) ;
177+ const sourceAuthClausesBefore = this . getSourceAuthorizationClausesBefore ( context ) ;
178+
179+ const bothAuthClausesBefore : Cypher . Clause [ ] = [ ] ;
180+ if ( authClausesBefore . length === 0 && sourceAuthClausesBefore . length > 0 ) {
181+ bothAuthClausesBefore . push ( new Cypher . With ( "*" ) , ...sourceAuthClausesBefore ) ;
182+ } else {
183+ bothAuthClausesBefore . push ( Cypher . utils . concat ( ...authClausesBefore , ...sourceAuthClausesBefore ) ) ;
184+ }
185+
186+ const clauses = Cypher . utils . concat ( matchClause , ...bothAuthClausesBefore , ...mutationSubqueries , deleteClause ) ;
155187
156- return { projectionExpr : context . returnVariable , clauses : [ clauses ] } ;
188+ const authClausesAfter = this . getAuthorizationClausesAfter ( nestedContext ) ;
189+ const sourceAuthClausesAfter = this . getSourceAuthorizationClausesAfter ( context ) ;
190+
191+ const callClause = new Cypher . Call ( clauses , [ context . target ] ) ;
192+ const authClauses : Cypher . Clause [ ] = [ ] ;
193+ if ( authClausesAfter . length > 0 || sourceAuthClausesAfter . length > 0 ) {
194+ authClauses . push ( Cypher . utils . concat ( ...authClausesAfter , ...sourceAuthClausesAfter ) ) ;
195+ }
196+
197+ return {
198+ projectionExpr : context . returnVariable ,
199+ clauses : [ callClause , ...authClauses ] ,
200+ } ;
201+
202+ // return { projectionExpr: context.returnVariable, clauses: [clauses] };
157203 }
158204
159205 private getAuthorizationClauses ( context : QueryASTContext ) : Cypher . Clause [ ] {
@@ -187,6 +233,35 @@ export class DisconnectOperation extends MutationOperation {
187233 return [ ] ;
188234 }
189235
236+ private getSourceAuthorizationClausesAfter ( context : QueryASTContext ) : Cypher . Clause [ ] {
237+ const validationsAfter : Cypher . VoidProcedure [ ] = [ ] ;
238+ for ( const authFilter of this . sourceAuthFilters ) {
239+ const validationAfter = authFilter . getValidation ( context , "AFTER" ) ;
240+ if ( validationAfter ) {
241+ validationsAfter . push ( validationAfter ) ;
242+ }
243+ }
244+
245+ if ( validationsAfter . length > 0 ) {
246+ return [ new Cypher . With ( "*" ) , ...validationsAfter ] ;
247+ }
248+ return [ ] ;
249+ }
250+ private getSourceAuthorizationClausesBefore ( context : QueryASTContext ) : Cypher . Clause [ ] {
251+ const validationsAfter : Cypher . VoidProcedure [ ] = [ ] ;
252+ for ( const authFilter of this . sourceAuthFilters ) {
253+ const validationAfter = authFilter . getValidation ( context , "BEFORE" ) ;
254+ if ( validationAfter ) {
255+ validationsAfter . push ( validationAfter ) ;
256+ }
257+ }
258+
259+ if ( validationsAfter . length > 0 ) {
260+ return [ new Cypher . With ( "*" ) , ...validationsAfter ] ;
261+ }
262+ return [ ] ;
263+ }
264+
190265 private transpileAuthClauses ( context : QueryASTContext ) : {
191266 selections : ( Cypher . With | Cypher . Match ) [ ] ;
192267 subqueries : Cypher . Clause [ ] ;
0 commit comments