From cfdad783d45e5fcfa693ff3dbcf4793ac78159c1 Mon Sep 17 00:00:00 2001 From: Mats Rydberg Date: Tue, 30 Aug 2016 17:20:47 +0200 Subject: [PATCH] Support naming multiple roles in procedure annotation The `allowed` attribute is now an array of strings, each mentioning a role name that gives its users access to the procedure regardless of their normal access privileges. --- .../executionplan/ProcedureCallMode.scala | 10 ++++---- .../v3_1/spi/DelegatingQueryContext.scala | 8 +++---- .../v3_1/spi/ProcedureSignature.scala | 8 +++---- .../compiler/v3_1/spi/QueryContext.scala | 8 +++---- .../v3_1/RewriteProcedureCallsTest.scala | 2 +- .../compiler/v3_1/ast/CallClauseTest.scala | 24 +++++++++---------- .../StatementConvertersTest.scala | 2 +- .../ProcedureCallExecutionPlanTest.scala | 8 +++---- .../v3_1/pipes/ProcedureCallPipeTest.scala | 23 +++++++++--------- .../v3_1/spi/QueryContextAdaptation.scala | 8 +++---- ...ceptionTranslatingQueryContextFor3_1.scala | 8 +++---- .../v3_1/TransactionBoundPlanContext.scala | 2 +- .../v3_1/TransactionBoundQueryContext.scala | 8 +++---- .../org/neo4j/cypher/ExecutionEngineIT.scala | 2 +- .../kernel/api/proc/ProcedureSignature.java | 10 ++++---- .../kernel/api/security/AuthSubject.java | 8 +++---- .../proc/ReflectiveProcedureCompiler.java | 2 +- .../java/org/neo4j/procedure/Procedure.java | 6 ++--- .../security/auth/BasicAuthSubject.java | 4 ++-- .../auth/EnterpriseAuthSubject.java | 12 ++++++++-- .../enterprise/auth/AuthTestBase.java | 6 ++--- 21 files changed, 89 insertions(+), 80 deletions(-) diff --git a/community/cypher/cypher-compiler-3.1/src/main/scala/org/neo4j/cypher/internal/compiler/v3_1/executionplan/ProcedureCallMode.scala b/community/cypher/cypher-compiler-3.1/src/main/scala/org/neo4j/cypher/internal/compiler/v3_1/executionplan/ProcedureCallMode.scala index 1ac6f5db699a0..613f60532e57f 100644 --- a/community/cypher/cypher-compiler-3.1/src/main/scala/org/neo4j/cypher/internal/compiler/v3_1/executionplan/ProcedureCallMode.scala +++ b/community/cypher/cypher-compiler-3.1/src/main/scala/org/neo4j/cypher/internal/compiler/v3_1/executionplan/ProcedureCallMode.scala @@ -36,17 +36,17 @@ sealed trait ProcedureCallMode { def call(ctx: QueryContext, name: QualifiedProcedureName, args: Seq[Any]): Iterator[Array[AnyRef]] - val allowed: String + val allowed: Array[String] } -case class LazyReadOnlyCallMode(allowed: String) extends ProcedureCallMode { +case class LazyReadOnlyCallMode(allowed: Array[String]) extends ProcedureCallMode { override val queryType: InternalQueryType = READ_ONLY override def call(ctx: QueryContext, name: QualifiedProcedureName, args: Seq[Any]): Iterator[Array[AnyRef]] = ctx.callReadOnlyProcedure(name, args, allowed) } -case class EagerReadWriteCallMode(allowed: String) extends ProcedureCallMode { +case class EagerReadWriteCallMode(allowed: Array[String]) extends ProcedureCallMode { override val queryType: InternalQueryType = READ_WRITE override def call(ctx: QueryContext, name: QualifiedProcedureName, args: Seq[Any]): Iterator[Array[AnyRef]] = { @@ -59,7 +59,7 @@ case class EagerReadWriteCallMode(allowed: String) extends ProcedureCallMode { } } -case class SchemaWriteCallMode(allowed: String) extends ProcedureCallMode { +case class SchemaWriteCallMode(allowed: Array[String]) extends ProcedureCallMode { override val queryType: InternalQueryType = SCHEMA_WRITE override def call(ctx: QueryContext, name: QualifiedProcedureName, args: Seq[Any]): Iterator[Array[AnyRef]] = { @@ -72,7 +72,7 @@ case class SchemaWriteCallMode(allowed: String) extends ProcedureCallMode { } } -case class DbmsCallMode(allowed: String) extends ProcedureCallMode { +case class DbmsCallMode(allowed: Array[String]) extends ProcedureCallMode { override val queryType: InternalQueryType = DBMS override def call(ctx: QueryContext, name: QualifiedProcedureName, args: Seq[Any]): Iterator[Array[AnyRef]] = { diff --git a/community/cypher/cypher-compiler-3.1/src/main/scala/org/neo4j/cypher/internal/compiler/v3_1/spi/DelegatingQueryContext.scala b/community/cypher/cypher-compiler-3.1/src/main/scala/org/neo4j/cypher/internal/compiler/v3_1/spi/DelegatingQueryContext.scala index 24c1d2441c5c2..4e171727d5377 100644 --- a/community/cypher/cypher-compiler-3.1/src/main/scala/org/neo4j/cypher/internal/compiler/v3_1/spi/DelegatingQueryContext.scala +++ b/community/cypher/cypher-compiler-3.1/src/main/scala/org/neo4j/cypher/internal/compiler/v3_1/spi/DelegatingQueryContext.scala @@ -181,16 +181,16 @@ class DelegatingQueryContext(val inner: QueryContext) extends QueryContext { filters: Seq[KernelPredicate[PropertyContainer]]): Iterator[Path] = manyDbHits(inner.allShortestPath(left, right, depth, expander, pathPredicate, filters)) - override def callReadOnlyProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: String) = + override def callReadOnlyProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: Array[String]) = singleDbHit(inner.callReadOnlyProcedure(name, args, allowed)) - override def callReadWriteProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: String) = + override def callReadWriteProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: Array[String]) = singleDbHit(inner.callReadWriteProcedure(name, args, allowed)) - override def callSchemaWriteProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: String) = + override def callSchemaWriteProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: Array[String]) = singleDbHit(inner.callSchemaWriteProcedure(name, args, allowed)) - override def callDbmsProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: String) = + override def callDbmsProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: Array[String]) = inner.callDbmsProcedure(name, args, allowed) override def isGraphKernelResultValue(v: Any): Boolean = diff --git a/community/cypher/cypher-compiler-3.1/src/main/scala/org/neo4j/cypher/internal/compiler/v3_1/spi/ProcedureSignature.scala b/community/cypher/cypher-compiler-3.1/src/main/scala/org/neo4j/cypher/internal/compiler/v3_1/spi/ProcedureSignature.scala index 1c7a82395edaf..a297bf72dc57c 100644 --- a/community/cypher/cypher-compiler-3.1/src/main/scala/org/neo4j/cypher/internal/compiler/v3_1/spi/ProcedureSignature.scala +++ b/community/cypher/cypher-compiler-3.1/src/main/scala/org/neo4j/cypher/internal/compiler/v3_1/spi/ProcedureSignature.scala @@ -47,7 +47,7 @@ case class FieldSignature(name: String, typ: CypherType, default: Option[CypherV sealed trait ProcedureAccessMode -case class ProcedureReadOnlyAccess(allowed: String) extends ProcedureAccessMode -case class ProcedureReadWriteAccess(allowed: String) extends ProcedureAccessMode -case class ProcedureSchemaWriteAccess(allowed: String) extends ProcedureAccessMode -case class ProcedureDbmsAccess(allowed: String) extends ProcedureAccessMode +case class ProcedureReadOnlyAccess(allowed: Array[String]) extends ProcedureAccessMode +case class ProcedureReadWriteAccess(allowed: Array[String]) extends ProcedureAccessMode +case class ProcedureSchemaWriteAccess(allowed: Array[String]) extends ProcedureAccessMode +case class ProcedureDbmsAccess(allowed: Array[String]) extends ProcedureAccessMode diff --git a/community/cypher/cypher-compiler-3.1/src/main/scala/org/neo4j/cypher/internal/compiler/v3_1/spi/QueryContext.scala b/community/cypher/cypher-compiler-3.1/src/main/scala/org/neo4j/cypher/internal/compiler/v3_1/spi/QueryContext.scala index f9515a7840cdd..87d014e57f22c 100644 --- a/community/cypher/cypher-compiler-3.1/src/main/scala/org/neo4j/cypher/internal/compiler/v3_1/spi/QueryContext.scala +++ b/community/cypher/cypher-compiler-3.1/src/main/scala/org/neo4j/cypher/internal/compiler/v3_1/spi/QueryContext.scala @@ -156,13 +156,13 @@ trait QueryContext extends TokenContext { def lockRelationships(relIds: Long*) - def callReadOnlyProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: String): Iterator[Array[AnyRef]] + def callReadOnlyProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: Array[String]): Iterator[Array[AnyRef]] - def callReadWriteProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: String): Iterator[Array[AnyRef]] + def callReadWriteProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: Array[String]): Iterator[Array[AnyRef]] - def callSchemaWriteProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: String): Iterator[Array[AnyRef]] + def callSchemaWriteProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: Array[String]): Iterator[Array[AnyRef]] - def callDbmsProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: String): Iterator[Array[AnyRef]] + def callDbmsProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: Array[String]): Iterator[Array[AnyRef]] // Check if a runtime value is a node, relationship, path or some such value returned from // other query context values by calling down to the underlying database diff --git a/community/cypher/cypher-compiler-3.1/src/test/scala/org/neo4j/cypher/internal/compiler/v3_1/RewriteProcedureCallsTest.scala b/community/cypher/cypher-compiler-3.1/src/test/scala/org/neo4j/cypher/internal/compiler/v3_1/RewriteProcedureCallsTest.scala index 18fb652e97835..1c42b903d1e05 100644 --- a/community/cypher/cypher-compiler-3.1/src/test/scala/org/neo4j/cypher/internal/compiler/v3_1/RewriteProcedureCallsTest.scala +++ b/community/cypher/cypher-compiler-3.1/src/test/scala/org/neo4j/cypher/internal/compiler/v3_1/RewriteProcedureCallsTest.scala @@ -32,7 +32,7 @@ class RewriteProcedureCallsTest extends CypherFunSuite with AstConstructionTestS val qualifiedName = QualifiedProcedureName(ns.parts, name.name) val signatureInputs = IndexedSeq(FieldSignature("a", CTInteger)) val signatureOutputs = Some(IndexedSeq(FieldSignature("x", CTInteger), FieldSignature("y", CTList(CTNode)))) - val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess("")) + val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess(Array.empty[String])) val lookup: (QualifiedProcedureName) => ProcedureSignature = _ => signature test("should resolve standalone procedure calls") { diff --git a/community/cypher/cypher-compiler-3.1/src/test/scala/org/neo4j/cypher/internal/compiler/v3_1/ast/CallClauseTest.scala b/community/cypher/cypher-compiler-3.1/src/test/scala/org/neo4j/cypher/internal/compiler/v3_1/ast/CallClauseTest.scala index 8f99ce63d26c5..332778a6a100e 100644 --- a/community/cypher/cypher-compiler-3.1/src/test/scala/org/neo4j/cypher/internal/compiler/v3_1/ast/CallClauseTest.scala +++ b/community/cypher/cypher-compiler-3.1/src/test/scala/org/neo4j/cypher/internal/compiler/v3_1/ast/CallClauseTest.scala @@ -35,7 +35,7 @@ class CallClauseTest extends CypherFunSuite with AstConstructionTestSupport { val unresolved = UnresolvedCall(ns, name, None, None)(pos) val signatureInputs = IndexedSeq(FieldSignature("a", CTInteger)) val signatureOutputs = Some(IndexedSeq(FieldSignature("x", CTInteger), FieldSignature("y", CTList(CTNode)))) - val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess("")) + val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess(Array.empty)) val callArguments = Seq(Parameter("a", CTAny)(pos)) val callResults = Seq(ProcedureResultItem(varFor("x"))(pos), ProcedureResultItem(varFor("y"))(pos)) @@ -60,7 +60,7 @@ class CallClauseTest extends CypherFunSuite with AstConstructionTestSupport { val unresolved = UnresolvedCall(ns, name, None, None)(pos) val signatureInputs = IndexedSeq(FieldSignature("a", CTInteger)) val signatureOutputs = None - val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess("")) + val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess(Array.empty)) val callArguments = Seq(Parameter("a", CTAny)(pos)) val callResults = Seq.empty @@ -84,7 +84,7 @@ class CallClauseTest extends CypherFunSuite with AstConstructionTestSupport { test("should resolve CALL my.proc.foo YIELD x, y") { val signatureInputs = IndexedSeq(FieldSignature("a", CTInteger)) val signatureOutputs = Some(IndexedSeq(FieldSignature("x", CTInteger), FieldSignature("y", CTList(CTNode)))) - val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess("")) + val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess(Array.empty)) val callArguments = Seq(Parameter("a", CTAny)(pos)) val callResults = Seq(ProcedureResultItem(varFor("x"))(pos), ProcedureResultItem(varFor("y"))(pos)) val unresolved = UnresolvedCall(ns, name, None, Some(callResults))(pos) @@ -109,7 +109,7 @@ class CallClauseTest extends CypherFunSuite with AstConstructionTestSupport { test("should resolve CALL my.proc.foo(a)") { val signatureInputs = IndexedSeq(FieldSignature("a", CTInteger)) val signatureOutputs = Some(IndexedSeq(FieldSignature("x", CTInteger), FieldSignature("y", CTList(CTNode)))) - val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess("")) + val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess(Array.empty)) val callArguments = Seq(Parameter("a", CTAny)(pos)) val callResults = Seq(ProcedureResultItem(varFor("x"))(pos), ProcedureResultItem(varFor("y"))(pos)) val unresolved = UnresolvedCall(ns, name, Some(callArguments), None)(pos) @@ -134,7 +134,7 @@ class CallClauseTest extends CypherFunSuite with AstConstructionTestSupport { test("should resolve void CALL my.proc.foo(a)") { val signatureInputs = IndexedSeq(FieldSignature("a", CTInteger)) val signatureOutputs = None - val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess("")) + val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess(Array.empty)) val callArguments = Seq(Parameter("a", CTAny)(pos)) val callResults = Seq.empty val unresolved = UnresolvedCall(ns, name, Some(callArguments), None)(pos) @@ -159,7 +159,7 @@ class CallClauseTest extends CypherFunSuite with AstConstructionTestSupport { test("should resolve CALL my.proc.foo(a) YIELD x, y AS z") { val signatureInputs = IndexedSeq(FieldSignature("a", CTInteger)) val signatureOutputs = Some(IndexedSeq(FieldSignature("x", CTInteger), FieldSignature("y", CTList(CTNode)))) - val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess("")) + val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess(Array.empty)) val callArguments = Seq(Parameter("a", CTAny)(pos)) val callResults = Seq( ProcedureResultItem(varFor("x"))(pos), @@ -183,7 +183,7 @@ class CallClauseTest extends CypherFunSuite with AstConstructionTestSupport { } test("pretends to be based on user-declared arguments and results upon request") { - val signature = ProcedureSignature(qualifiedName, IndexedSeq.empty, Some(IndexedSeq.empty), None, ProcedureReadOnlyAccess("")) + val signature = ProcedureSignature(qualifiedName, IndexedSeq.empty, Some(IndexedSeq.empty), None, ProcedureReadOnlyAccess(Array.empty)) val call = ResolvedCall(signature, null, null, declaredArguments = false, declaredResults = false)(pos) call.withFakedFullDeclarations.declaredArguments should be(true) @@ -193,7 +193,7 @@ class CallClauseTest extends CypherFunSuite with AstConstructionTestSupport { test("adds coercion of arguments to signature types upon request") { val signatureInputs = IndexedSeq(FieldSignature("a", CTInteger)) val signatureOutputs = Some(IndexedSeq(FieldSignature("x", CTInteger), FieldSignature("y", CTList(CTNode)))) - val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess("")) + val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess(Array.empty)) val callArguments = Seq(Parameter("a", CTAny)(pos)) val callResults = Seq( ProcedureResultItem(varFor("x"))(pos), @@ -220,7 +220,7 @@ class CallClauseTest extends CypherFunSuite with AstConstructionTestSupport { test("should verify number of arguments during semantic checking of resolved calls") { val signatureInputs = IndexedSeq(FieldSignature("a", CTInteger)) val signatureOutputs = Some(IndexedSeq(FieldSignature("x", CTInteger), FieldSignature("y", CTList(CTNode)))) - val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess("")) + val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess(Array.empty)) val callArguments = Seq.empty val callResults = Seq( ProcedureResultItem(varFor("x"))(pos), @@ -237,7 +237,7 @@ class CallClauseTest extends CypherFunSuite with AstConstructionTestSupport { test("should verify that result variables are unique during semantic checking of resolved calls") { val signatureInputs = IndexedSeq(FieldSignature("a", CTInteger)) val signatureOutputs = Some(IndexedSeq(FieldSignature("x", CTInteger), FieldSignature("y", CTList(CTNode)))) - val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess("")) + val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess(Array.empty)) val callArguments = Seq(Parameter("a", CTAny)(pos)) val callResults = Seq( ProcedureResultItem(varFor("x"))(pos), @@ -254,7 +254,7 @@ class CallClauseTest extends CypherFunSuite with AstConstructionTestSupport { test("should verify that output field names are correct during semantic checking of resolved calls") { val signatureInputs = IndexedSeq(FieldSignature("a", CTInteger)) val signatureOutputs = Some(IndexedSeq(FieldSignature("x", CTInteger), FieldSignature("y", CTList(CTNode)))) - val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess("")) + val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess(Array.empty)) val callArguments = Seq(Parameter("a", CTAny)(pos)) val callResults = Seq( ProcedureResultItem(varFor("x"))(pos), @@ -271,7 +271,7 @@ class CallClauseTest extends CypherFunSuite with AstConstructionTestSupport { test("should verify result types during semantic checking of resolved calls") { val signatureInputs = IndexedSeq(FieldSignature("a", CTInteger)) val signatureOutputs = Some(IndexedSeq(FieldSignature("x", CTInteger), FieldSignature("y", CTList(CTNode)))) - val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess("")) + val signature = ProcedureSignature(qualifiedName, signatureInputs, signatureOutputs, None, ProcedureReadOnlyAccess(Array.empty)) val callArguments = Seq(StringLiteral("nope")(pos)) val callResults = Seq( ProcedureResultItem(varFor("x"))(pos), diff --git a/community/cypher/cypher-compiler-3.1/src/test/scala/org/neo4j/cypher/internal/compiler/v3_1/ast/convert/plannerQuery/StatementConvertersTest.scala b/community/cypher/cypher-compiler-3.1/src/test/scala/org/neo4j/cypher/internal/compiler/v3_1/ast/convert/plannerQuery/StatementConvertersTest.scala index 15e9ce121ab7b..f8c794f09e63d 100644 --- a/community/cypher/cypher-compiler-3.1/src/test/scala/org/neo4j/cypher/internal/compiler/v3_1/ast/convert/plannerQuery/StatementConvertersTest.scala +++ b/community/cypher/cypher-compiler-3.1/src/test/scala/org/neo4j/cypher/internal/compiler/v3_1/ast/convert/plannerQuery/StatementConvertersTest.scala @@ -883,7 +883,7 @@ class StatementConvertersTest extends CypherFunSuite with LogicalPlanningTestSup inputSignature = IndexedSeq.empty, deprecationInfo = None, outputSignature = Some(IndexedSeq(FieldSignature("all", CTInteger))), - accessMode = ProcedureReadOnlyAccess("") + accessMode = ProcedureReadOnlyAccess(Array.empty) ) val query = buildPlannerQuery("CALL foo() YIELD all RETURN all", Some(_ => signature)) diff --git a/community/cypher/cypher-compiler-3.1/src/test/scala/org/neo4j/cypher/internal/compiler/v3_1/executionplan/procs/ProcedureCallExecutionPlanTest.scala b/community/cypher/cypher-compiler-3.1/src/test/scala/org/neo4j/cypher/internal/compiler/v3_1/executionplan/procs/ProcedureCallExecutionPlanTest.scala index e1cb433c49485..9281b2a4819c9 100644 --- a/community/cypher/cypher-compiler-3.1/src/test/scala/org/neo4j/cypher/internal/compiler/v3_1/executionplan/procs/ProcedureCallExecutionPlanTest.scala +++ b/community/cypher/cypher-compiler-3.1/src/test/scala/org/neo4j/cypher/internal/compiler/v3_1/executionplan/procs/ProcedureCallExecutionPlanTest.scala @@ -86,7 +86,7 @@ class ProcedureCallExecutionPlanTest extends CypherFunSuite { IndexedSeq(FieldSignature("a", symbols.CTInteger)), Some(IndexedSeq(FieldSignature("b", symbols.CTInteger))), None, - ProcedureReadOnlyAccess("") + ProcedureReadOnlyAccess(Array.empty) ) private val writeSignature = ProcedureSignature( @@ -94,7 +94,7 @@ class ProcedureCallExecutionPlanTest extends CypherFunSuite { IndexedSeq(FieldSignature("a", symbols.CTInteger)), Some(IndexedSeq(FieldSignature("b", symbols.CTInteger))), None, - ProcedureReadWriteAccess("") + ProcedureReadWriteAccess(Array.empty) ) private val pos = DummyPosition(-1) @@ -116,6 +116,6 @@ class ProcedureCallExecutionPlanTest extends CypherFunSuite { } when(ctx.transactionalContext).thenReturn(mock[QueryTransactionalContext]) - when(ctx.callReadOnlyProcedure(any[QualifiedProcedureName], any[Seq[Any]], any[String])).thenAnswer(procedureResult) - when(ctx.callReadWriteProcedure(any[QualifiedProcedureName], any[Seq[Any]], any[String])).thenAnswer(procedureResult) + when(ctx.callReadOnlyProcedure(any[QualifiedProcedureName], any[Seq[Any]], any[Array[String]])).thenAnswer(procedureResult) + when(ctx.callReadWriteProcedure(any[QualifiedProcedureName], any[Seq[Any]], any[Array[String]])).thenAnswer(procedureResult) } diff --git a/community/cypher/cypher-compiler-3.1/src/test/scala/org/neo4j/cypher/internal/compiler/v3_1/pipes/ProcedureCallPipeTest.scala b/community/cypher/cypher-compiler-3.1/src/test/scala/org/neo4j/cypher/internal/compiler/v3_1/pipes/ProcedureCallPipeTest.scala index c146f8b54e5f0..8384c360e0f5b 100644 --- a/community/cypher/cypher-compiler-3.1/src/test/scala/org/neo4j/cypher/internal/compiler/v3_1/pipes/ProcedureCallPipeTest.scala +++ b/community/cypher/cypher-compiler-3.1/src/test/scala/org/neo4j/cypher/internal/compiler/v3_1/pipes/ProcedureCallPipeTest.scala @@ -33,6 +33,7 @@ class ProcedureCallPipeTest with AstConstructionTestSupport { val procedureName = QualifiedProcedureName(List.empty, "foo") + val emptyStringArray = Array.empty[String] test("should execute read-only procedure calls") { val lhsData = List(Map("a" -> 1), Map("a" -> 2)) @@ -41,14 +42,14 @@ class ProcedureCallPipeTest val pipe = ProcedureCallPipe( source = lhs, name = procedureName, - callMode = LazyReadOnlyCallMode(""), + callMode = LazyReadOnlyCallMode(emptyStringArray), argExprs = Seq(Variable("a")), rowProcessing = FlatMapAndAppendToRow, resultSymbols = Seq("r" -> CTString), resultIndices = Seq(0 -> "r") )()(newMonitor) - val qtx = new FakeQueryContext(procedureName, resultsTransformer, ProcedureReadOnlyAccess("")) + val qtx = new FakeQueryContext(procedureName, resultsTransformer, ProcedureReadOnlyAccess(emptyStringArray)) pipe.createResults(QueryStateHelper.emptyWith(qtx)).toList should equal(List( ExecutionContext.from("a" ->1, "r" -> "take 1/1"), @@ -64,14 +65,14 @@ class ProcedureCallPipeTest val pipe = ProcedureCallPipe( source = lhs, name = procedureName, - callMode = EagerReadWriteCallMode(""), + callMode = EagerReadWriteCallMode(emptyStringArray), argExprs = Seq(Variable("a")), rowProcessing = FlatMapAndAppendToRow, resultSymbols = Seq("r" -> CTString), resultIndices = Seq(0 -> "r") )()(newMonitor) - val qtx = new FakeQueryContext(procedureName, resultsTransformer, ProcedureReadWriteAccess("")) + val qtx = new FakeQueryContext(procedureName, resultsTransformer, ProcedureReadWriteAccess(emptyStringArray)) pipe.createResults(QueryStateHelper.emptyWith(qtx)).toList should equal(List( ExecutionContext.from("a" -> 1, "r" -> "take 1/1"), ExecutionContext.from("a" -> 2, "r" -> "take 1/2"), @@ -86,14 +87,14 @@ class ProcedureCallPipeTest val pipe = ProcedureCallPipe( source = lhs, name = procedureName, - callMode = EagerReadWriteCallMode(""), + callMode = EagerReadWriteCallMode(emptyStringArray), argExprs = Seq(Variable("a")), rowProcessing = PassThroughRow, resultSymbols = Seq.empty, resultIndices = Seq.empty )()(newMonitor) - val qtx = new FakeQueryContext(procedureName, _ => Iterator.empty, ProcedureReadWriteAccess("")) + val qtx = new FakeQueryContext(procedureName, _ => Iterator.empty, ProcedureReadWriteAccess(emptyStringArray)) pipe.createResults(QueryStateHelper.emptyWith(qtx)).toList should equal(List( ExecutionContext.from("a" -> 1), ExecutionContext.from("a" -> 2) @@ -113,17 +114,17 @@ class ProcedureCallPipeTest expectedAccessMode: ProcedureAccessMode) extends QueryContext with QueryContextAdaptation { override def isGraphKernelResultValue(v: Any): Boolean = false - override def callReadOnlyProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: String) = { - expectedAccessMode should equal(ProcedureReadOnlyAccess("")) + override def callReadOnlyProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: Array[String]) = { + expectedAccessMode should equal(ProcedureReadOnlyAccess(emptyStringArray)) doIt(name, args, allowed) } - override def callReadWriteProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: String): Iterator[Array[AnyRef]] = { - expectedAccessMode should equal(ProcedureReadWriteAccess("")) + override def callReadWriteProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: Array[String]): Iterator[Array[AnyRef]] = { + expectedAccessMode should equal(ProcedureReadWriteAccess(emptyStringArray)) doIt(name, args, allowed) } - private def doIt(name: QualifiedProcedureName, args: Seq[Any], allowed: String): Iterator[Array[AnyRef]] = { + private def doIt(name: QualifiedProcedureName, args: Seq[Any], allowed: Array[String]): Iterator[Array[AnyRef]] = { name should equal(procedureName) args.length should be(1) result(args) diff --git a/community/cypher/cypher-compiler-3.1/src/test/scala/org/neo4j/cypher/internal/compiler/v3_1/spi/QueryContextAdaptation.scala b/community/cypher/cypher-compiler-3.1/src/test/scala/org/neo4j/cypher/internal/compiler/v3_1/spi/QueryContextAdaptation.scala index 19f8167aa28b0..8399de9b47ad1 100644 --- a/community/cypher/cypher-compiler-3.1/src/test/scala/org/neo4j/cypher/internal/compiler/v3_1/spi/QueryContextAdaptation.scala +++ b/community/cypher/cypher-compiler-3.1/src/test/scala/org/neo4j/cypher/internal/compiler/v3_1/spi/QueryContextAdaptation.scala @@ -122,13 +122,13 @@ trait QueryContextAdaptation { override def lockingUniqueIndexSeek(index: IndexDescriptor, value: Any): Option[Node] = ??? - override def callReadOnlyProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: String): scala.Iterator[Array[AnyRef]] = ??? + override def callReadOnlyProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: Array[String]): scala.Iterator[Array[AnyRef]] = ??? - override def callReadWriteProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: String): scala.Iterator[Array[AnyRef]] = ??? + override def callReadWriteProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: Array[String]): scala.Iterator[Array[AnyRef]] = ??? - override def callSchemaWriteProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: String): Iterator[Array[AnyRef]] = ??? + override def callSchemaWriteProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: Array[String]): Iterator[Array[AnyRef]] = ??? - override def callDbmsProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: String): Iterator[Array[AnyRef]] = ??? + override def callDbmsProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: Array[String]): Iterator[Array[AnyRef]] = ??? override def getOrCreateFromSchemaState[K, V](key: K, creator: => V): V = ??? diff --git a/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/ExceptionTranslatingQueryContextFor3_1.scala b/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/ExceptionTranslatingQueryContextFor3_1.scala index 114cb19480861..3c5cffc13feb2 100644 --- a/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/ExceptionTranslatingQueryContextFor3_1.scala +++ b/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/compatibility/ExceptionTranslatingQueryContextFor3_1.scala @@ -129,16 +129,16 @@ class ExceptionTranslatingQueryContextFor3_1(val inner: QueryContext) extends Qu override def dropRelationshipPropertyExistenceConstraint(relTypeId: Int, propertyKeyId: Int) = translateException(inner.dropRelationshipPropertyExistenceConstraint(relTypeId, propertyKeyId)) - override def callReadOnlyProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: String): Iterator[Array[AnyRef]] = + override def callReadOnlyProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: Array[String]): Iterator[Array[AnyRef]] = translateIterator(inner.callReadOnlyProcedure(name, args, allowed)) - override def callReadWriteProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: String): Iterator[Array[AnyRef]] = + override def callReadWriteProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: Array[String]): Iterator[Array[AnyRef]] = translateIterator(inner.callReadWriteProcedure(name, args, allowed)) - override def callSchemaWriteProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: String): Iterator[Array[AnyRef]] = + override def callSchemaWriteProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: Array[String]): Iterator[Array[AnyRef]] = translateIterator(inner.callSchemaWriteProcedure(name, args, allowed)) - override def callDbmsProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: String): Iterator[Array[AnyRef]] = + override def callDbmsProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: Array[String]): Iterator[Array[AnyRef]] = translateIterator(inner.callDbmsProcedure(name, args, allowed)) override def isGraphKernelResultValue(v: Any): Boolean = diff --git a/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v3_1/TransactionBoundPlanContext.scala b/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v3_1/TransactionBoundPlanContext.scala index e2efcbdf0a8f6..e88976ef99e98 100644 --- a/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v3_1/TransactionBoundPlanContext.scala +++ b/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v3_1/TransactionBoundPlanContext.scala @@ -143,7 +143,7 @@ class TransactionBoundPlanContext(tc: TransactionalContextWrapperv3_1) private def asOption[T](optional: Optional[T]): Option[T] = if (optional.isPresent) Some(optional.get()) else None - private def asCypherProcMode(mode: KernelProcedureSignature.Mode, allowed: String): ProcedureAccessMode = mode match { + private def asCypherProcMode(mode: KernelProcedureSignature.Mode, allowed: Array[String]): ProcedureAccessMode = mode match { case KernelProcedureSignature.Mode.READ_ONLY => ProcedureReadOnlyAccess(allowed) case KernelProcedureSignature.Mode.READ_WRITE => ProcedureReadWriteAccess(allowed) case KernelProcedureSignature.Mode.SCHEMA_WRITE => ProcedureSchemaWriteAccess(allowed) diff --git a/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v3_1/TransactionBoundQueryContext.scala b/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v3_1/TransactionBoundQueryContext.scala index 6ceb9d62615a8..31af7c9ec6f46 100644 --- a/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v3_1/TransactionBoundQueryContext.scala +++ b/community/cypher/cypher/src/main/scala/org/neo4j/cypher/internal/spi/v3_1/TransactionBoundQueryContext.scala @@ -584,7 +584,7 @@ final class TransactionBoundQueryContext(val transactionalContext: Transactional pathFinder.findAllPaths(left, right).iterator().asScala } - override def callReadOnlyProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: String) = { + override def callReadOnlyProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: Array[String]) = { val revertable = transactionalContext.accessMode match { case a: AuthSubject if a.allowsProcedureWith(allowed) => Some(transactionalContext.restrictCurrentTransaction(AccessMode.Static.OVERRIDE_READ)) @@ -593,7 +593,7 @@ final class TransactionBoundQueryContext(val transactionalContext: Transactional callProcedure(name, args, transactionalContext.statement.readOperations().procedureCallRead, revertable.foreach(_.close)) } - override def callReadWriteProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: String) = { + override def callReadWriteProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: Array[String]) = { val revertable = transactionalContext.accessMode match { case a: AuthSubject if a.allowsProcedureWith(allowed) => Some(transactionalContext.restrictCurrentTransaction(AccessMode.Static.OVERRIDE_WRITE)) @@ -604,7 +604,7 @@ final class TransactionBoundQueryContext(val transactionalContext: Transactional } - override def callSchemaWriteProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: String) = { + override def callSchemaWriteProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: Array[String]) = { val revertable = transactionalContext.accessMode match { case a: AuthSubject if a.allowsProcedureWith(allowed) => Some(transactionalContext.restrictCurrentTransaction(AccessMode.Static.OVERRIDE_SCHEMA)) @@ -613,7 +613,7 @@ final class TransactionBoundQueryContext(val transactionalContext: Transactional callProcedure(name, args, transactionalContext.statement.schemaWriteOperations().procedureCallSchema, revertable.foreach(_.close)) } - override def callDbmsProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: String) = { + override def callDbmsProcedure(name: QualifiedProcedureName, args: Seq[Any], allowed: Array[String]) = { callProcedure(name, args, transactionalContext.dbmsOperations.procedureCallDbms(_, _), ()) } diff --git a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/ExecutionEngineIT.scala b/community/cypher/cypher/src/test/scala/org/neo4j/cypher/ExecutionEngineIT.scala index da611406b46b1..7ef43df2b37d6 100644 --- a/community/cypher/cypher/src/test/scala/org/neo4j/cypher/ExecutionEngineIT.scala +++ b/community/cypher/cypher/src/test/scala/org/neo4j/cypher/ExecutionEngineIT.scala @@ -682,7 +682,7 @@ class ExecutionEngineIT extends CypherFunSuite with GraphIcing { val procedureName = new ProcedureSignature.ProcedureName(Array[String]("org", "neo4j", "bench"), "getAllNodes") val emptySignature = List.empty[ProcedureSignature.FieldSignature].asJava val signature: ProcedureSignature = new ProcedureSignature( - procedureName, paramSignature, resultSignature, ProcedureSignature.Mode.READ_ONLY, java.util.Optional.empty(), "", + procedureName, paramSignature, resultSignature, ProcedureSignature.Mode.READ_ONLY, java.util.Optional.empty(), Array.empty, java.util.Optional.empty()) def paramSignature = List.empty[ProcedureSignature.FieldSignature].asJava diff --git a/community/kernel/src/main/java/org/neo4j/kernel/api/proc/ProcedureSignature.java b/community/kernel/src/main/java/org/neo4j/kernel/api/proc/ProcedureSignature.java index b20adb295b6e6..05b538690a4d1 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/api/proc/ProcedureSignature.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/api/proc/ProcedureSignature.java @@ -156,7 +156,7 @@ public int hashCode() private final List outputSignature; private final Mode mode; private final Optional deprecated; - private final String allowed; + private final String[] allowed; private final Optional description; /** @@ -180,7 +180,7 @@ public ProcedureSignature( ProcedureName name, List outputSignature, Mode mode, Optional deprecated, - String allowed, + String[] allowed, Optional description ) { this.name = name; @@ -204,7 +204,7 @@ public Optional deprecated() return deprecated; } - public String allowed() { return allowed; } + public String[] allowed() { return allowed; } public List inputSignature() { @@ -269,7 +269,7 @@ public static class Builder private List outputSignature = new LinkedList<>(); private Mode mode = Mode.READ_ONLY; private Optional deprecated = Optional.empty(); - private String allowed = ""; + private String[] allowed = new String[0]; private Optional description = Optional.empty(); public Builder( String[] namespace, String name ) @@ -315,7 +315,7 @@ public Builder out( List fields ) return this; } - public Builder allowed( String allowed ) + public Builder allowed( String[] allowed ) { this.allowed = allowed; return this; diff --git a/community/kernel/src/main/java/org/neo4j/kernel/api/security/AuthSubject.java b/community/kernel/src/main/java/org/neo4j/kernel/api/security/AuthSubject.java index fd93f3c3ae918..68e9640da025f 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/api/security/AuthSubject.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/api/security/AuthSubject.java @@ -41,11 +41,11 @@ public interface AuthSubject extends AccessMode /** * Determines whether this subject is allowed to execute a procedure with the parameter string in its procedure annotation. - * @param roleName + * @param roleNames * @return * @throws InvalidArgumentsException */ - boolean allowsProcedureWith( String roleName ) throws InvalidArgumentsException; + boolean allowsProcedureWith( String[] roleNames ) throws InvalidArgumentsException; /** * Implementation to use when authentication has not yet been performed. Allows nothing. @@ -70,7 +70,7 @@ public void setPassword( String password ) throws IOException, InvalidArgumentsE } @Override - public boolean allowsProcedureWith( String roleName ) + public boolean allowsProcedureWith( String[] roleNames ) { return false; } @@ -170,7 +170,7 @@ public void setPassword( String password ) throws IOException, InvalidArgumentsE } @Override - public boolean allowsProcedureWith( String roleName ) + public boolean allowsProcedureWith( String[] roleNames ) { return true; } diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/proc/ReflectiveProcedureCompiler.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/proc/ReflectiveProcedureCompiler.java index b7de1357de5c9..b6b73e95aff37 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/proc/ReflectiveProcedureCompiler.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/proc/ReflectiveProcedureCompiler.java @@ -72,7 +72,7 @@ public List compile( Class procDefinition ) throws KernelE { try { - List procedureMethods = asList( procDefinition.getDeclaredMethods() ).stream() + List procedureMethods = Arrays.stream( procDefinition.getDeclaredMethods() ) .filter( m -> m.isAnnotationPresent( Procedure.class ) ) .collect( Collectors.toList() ); diff --git a/community/kernel/src/main/java/org/neo4j/procedure/Procedure.java b/community/kernel/src/main/java/org/neo4j/procedure/Procedure.java index 92c2619204d4d..74348c9203f65 100644 --- a/community/kernel/src/main/java/org/neo4j/procedure/Procedure.java +++ b/community/kernel/src/main/java/org/neo4j/procedure/Procedure.java @@ -140,14 +140,14 @@ String deprecatedBy() default ""; /** - * Specifies a role name such that a user that is assigned to that role is allowed to execute this procedure, + * Specifies role names such that a user that is assigned to any of these roles is allowed to execute this procedure, * regardless of any other permissions that the user has or doesn't have. The use-case for this attribute is to * allow users with no or little privileges to access the parts of the database that the annotated procedure * exposes. * - * @return the name of the role whose users are allowed to execute this procedure. + * @return an array of role names whose users are allowed to execute this procedure. */ - String allowed() default ""; + String[] allowed() default {}; enum Mode { diff --git a/community/security/src/main/java/org/neo4j/server/security/auth/BasicAuthSubject.java b/community/security/src/main/java/org/neo4j/server/security/auth/BasicAuthSubject.java index b1b2072f75bb2..a2fb0a4343fc0 100644 --- a/community/security/src/main/java/org/neo4j/server/security/auth/BasicAuthSubject.java +++ b/community/security/src/main/java/org/neo4j/server/security/auth/BasicAuthSubject.java @@ -98,9 +98,9 @@ public void setPassword( String password ) throws IOException, InvalidArgumentsE } @Override - public boolean allowsProcedureWith( String roleName ) + public boolean allowsProcedureWith( String[] roleName ) { - return false; + return true; } public BasicAuthManager getAuthManager() diff --git a/enterprise/security/src/main/java/org/neo4j/server/security/enterprise/auth/EnterpriseAuthSubject.java b/enterprise/security/src/main/java/org/neo4j/server/security/enterprise/auth/EnterpriseAuthSubject.java index 1989b12bc375d..bb6bad7a03970 100644 --- a/enterprise/security/src/main/java/org/neo4j/server/security/enterprise/auth/EnterpriseAuthSubject.java +++ b/enterprise/security/src/main/java/org/neo4j/server/security/enterprise/auth/EnterpriseAuthSubject.java @@ -20,6 +20,10 @@ package org.neo4j.server.security.enterprise.auth; import java.io.IOException; +import java.util.Collections; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.neo4j.graphdb.security.AuthorizationViolationException; import org.neo4j.kernel.api.security.AccessMode; @@ -27,6 +31,8 @@ import org.neo4j.kernel.api.security.AuthenticationResult; import org.neo4j.kernel.api.security.exception.InvalidArgumentsException; +import static java.util.stream.Collectors.toSet; + public class EnterpriseAuthSubject implements AuthSubject { static final String SCHEMA_READ_WRITE = "schema:read,write"; @@ -76,9 +82,11 @@ public void setPassword( String password ) throws IOException, InvalidArgumentsE } @Override - public boolean allowsProcedureWith( String roleName ) throws InvalidArgumentsException + public boolean allowsProcedureWith( String[] roleNames ) throws InvalidArgumentsException { - return getUserManager().getRoleNamesForUser( name() ).contains( roleName ); + Set roleNamesForUser = getUserManager().getRoleNamesForUser( name() ); + Set allowedRoleNames = Stream.of( roleNames ).collect( toSet() ); + return roleNamesForUser.stream().anyMatch( allowedRoleNames::contains ); } public EnterpriseUserManager getUserManager() diff --git a/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/AuthTestBase.java b/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/AuthTestBase.java index d12867ea5affb..b25e41fbbb645 100644 --- a/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/AuthTestBase.java +++ b/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/AuthTestBase.java @@ -416,14 +416,14 @@ public Stream numNodes() return Stream.of( new CountResult( nNodes ) ); } - @Procedure( name = "test.allowedProcedure1", allowed = "role1", mode = Procedure.Mode.READ ) + @Procedure( name = "test.allowedProcedure1", allowed = {"role1"}, mode = Procedure.Mode.READ ) public Stream allowedProcedure1() { db.execute( "MATCH (:Foo) RETURN 'foo' AS foo" ); return Stream.of( new AuthProcedures.StringResult( "foo" ) ); } - @Procedure( name = "test.allowedProcedure2", allowed = "role1", mode = Procedure.Mode.WRITE ) + @Procedure( name = "test.allowedProcedure2", allowed = {"otherRole", "role1"}, mode = Procedure.Mode.WRITE ) public Stream allowedProcedure2() { db.execute( "CREATE (:VeryUniqueLabel {prop: 'a'})" ); @@ -431,7 +431,7 @@ public Stream allowedProcedure2() .map( r -> new AuthProcedures.StringResult( (String) r.get( "a" ) ) ); } - @Procedure( name = "test.allowedProcedure3", allowed = "role1", mode = Procedure.Mode.SCHEMA ) + @Procedure( name = "test.allowedProcedure3", allowed = {"role1"}, mode = Procedure.Mode.SCHEMA ) public Stream allowedProcedure3() { db.execute( "CREATE INDEX ON :VeryUniqueLabel(prop)" );