diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/factory/DataSourceModule.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/factory/DataSourceModule.java
index a94fa5141bbff..e77b8761900e0 100644
--- a/community/kernel/src/main/java/org/neo4j/kernel/impl/factory/DataSourceModule.java
+++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/factory/DataSourceModule.java
@@ -38,15 +38,12 @@
import org.neo4j.kernel.AvailabilityGuard;
import org.neo4j.kernel.DatabaseAvailability;
import org.neo4j.kernel.NeoStoreDataSource;
-import org.neo4j.kernel.api.security.SecurityContext;
-import org.neo4j.kernel.impl.proc.TerminationGuardProvider;
-import org.neo4j.procedure.TerminationGuard;
import org.neo4j.kernel.api.KernelAPI;
import org.neo4j.kernel.api.KernelTransaction;
import org.neo4j.kernel.api.Statement;
import org.neo4j.kernel.api.exceptions.KernelException;
import org.neo4j.kernel.api.legacyindex.AutoIndexing;
-import org.neo4j.kernel.api.security.AuthSubject;
+import org.neo4j.kernel.api.security.SecurityContext;
import org.neo4j.kernel.builtinprocs.SpecialBuiltInProcedures;
import org.neo4j.kernel.configuration.Config;
import org.neo4j.kernel.guard.Guard;
@@ -66,8 +63,10 @@
import org.neo4j.kernel.impl.core.ThreadToStatementContextBridge;
import org.neo4j.kernel.impl.core.TokenNotFoundException;
import org.neo4j.kernel.impl.logging.LogService;
+import org.neo4j.kernel.impl.proc.ProcedureAllowedConfig;
import org.neo4j.kernel.impl.proc.ProcedureGDSFactory;
import org.neo4j.kernel.impl.proc.Procedures;
+import org.neo4j.kernel.impl.proc.TerminationGuardProvider;
import org.neo4j.kernel.impl.proc.TypeMappers.SimpleConverter;
import org.neo4j.kernel.impl.query.QueryExecutionEngine;
import org.neo4j.kernel.impl.store.StoreId;
@@ -83,9 +82,10 @@
import org.neo4j.kernel.lifecycle.LifeSupport;
import org.neo4j.kernel.lifecycle.LifecycleAdapter;
import org.neo4j.logging.Log;
+import org.neo4j.procedure.TerminationGuard;
-import static org.neo4j.kernel.api.proc.Context.SECURITY_CONTEXT;
import static org.neo4j.kernel.api.proc.Context.KERNEL_TRANSACTION;
+import static org.neo4j.kernel.api.proc.Context.SECURITY_CONTEXT;
import static org.neo4j.kernel.api.proc.Neo4jTypes.NTGeometry;
import static org.neo4j.kernel.api.proc.Neo4jTypes.NTNode;
import static org.neo4j.kernel.api.proc.Neo4jTypes.NTPath;
@@ -352,7 +352,7 @@ private Procedures setupProcedures( PlatformModule platform, EditionModule editi
Procedures procedures = new Procedures(
new SpecialBuiltInProcedures( Version.getNeo4jVersion(),
platform.databaseInfo.edition.toString() ),
- pluginDir, internalLog );
+ pluginDir, internalLog, new ProcedureAllowedConfig( platform.config ) );
platform.life.add( procedures );
platform.dependencies.satisfyDependency( procedures );
diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/proc/ProcedureAllowedConfig.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/proc/ProcedureAllowedConfig.java
new file mode 100644
index 0000000000000..e26c7ba980337
--- /dev/null
+++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/proc/ProcedureAllowedConfig.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2002-2016 "Neo Technology,"
+ * Network Engine for Objects in Lund AB [http://neotechnology.com]
+ *
+ * This file is part of Neo4j.
+ *
+ * Neo4j is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.neo4j.kernel.impl.proc;
+
+import org.neo4j.kernel.configuration.Config;
+
+public class ProcedureAllowedConfig
+{
+ public static final String PROC_ALLOWED_SETTING_DEFAULT_NAME = "dbms.security.procedures.default_allowed";
+
+ private final String defaultValue;
+
+ private ProcedureAllowedConfig()
+ {
+ this.defaultValue = "";
+ }
+
+ public ProcedureAllowedConfig( Config config )
+ {
+ this.defaultValue = config.getParams().get( PROC_ALLOWED_SETTING_DEFAULT_NAME );
+ }
+
+ public String[] getDefaultValue()
+ {
+ return defaultValue == null || defaultValue.isEmpty() ? new String[0]: new String[]{defaultValue};
+ }
+
+ static final ProcedureAllowedConfig DEFAULT = new ProcedureAllowedConfig();
+}
diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/proc/Procedures.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/proc/Procedures.java
index 677ef39be6e35..8ea5caa9700b5 100644
--- a/community/kernel/src/main/java/org/neo4j/kernel/impl/proc/Procedures.java
+++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/proc/Procedures.java
@@ -55,15 +55,16 @@ public class Procedures extends LifecycleAdapter
public Procedures()
{
- this( new SpecialBuiltInProcedures( "N/A", "N/A" ), null, NullLog.getInstance() );
+ this( new SpecialBuiltInProcedures( "N/A", "N/A" ), null, NullLog.getInstance(), ProcedureAllowedConfig.DEFAULT );
}
- public Procedures( ThrowingConsumer builtin, File pluginDir, Log log )
+ public Procedures( ThrowingConsumer builtin, File pluginDir, Log log,
+ ProcedureAllowedConfig config )
{
this.builtin = builtin;
this.pluginDir = pluginDir;
this.log = log;
- this.compiler = new ReflectiveProcedureCompiler(typeMappers, components, log);
+ this.compiler = new ReflectiveProcedureCompiler( typeMappers, components, log, config );
}
/**
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 6cee3c87f71a1..6daaccd4db363 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
@@ -65,14 +65,17 @@ public class ReflectiveProcedureCompiler
private final FieldInjections fieldInjections;
private final Log log;
private final TypeMappers typeMappers;
+ private final ProcedureAllowedConfig config;
- public ReflectiveProcedureCompiler( TypeMappers typeMappers, ComponentRegistry components, Log log )
+ public ReflectiveProcedureCompiler( TypeMappers typeMappers, ComponentRegistry components, Log log,
+ ProcedureAllowedConfig config )
{
inputSignatureDeterminer = new MethodSignatureCompiler( typeMappers );
outputMappers = new OutputMappers( typeMappers );
this.fieldInjections = new FieldInjections( components );
this.log = log;
this.typeMappers = typeMappers;
+ this.config = config;
}
public List compileFunction( Class> fcnDefinition ) throws KernelException
@@ -174,9 +177,10 @@ private ReflectiveProcedure compileProcedure( Class> procDefinition, MethodHan
Optional deprecated = deprecated( method, procedure::deprecatedBy,
"Use of @Procedure(deprecatedBy) without @Deprecated in " + procName );
+ String[] allowed = procedure.allowed().length == 0 ? config.getDefaultValue() : procedure.allowed();
ProcedureSignature signature =
new ProcedureSignature( procName, inputSignature, outputMapper.signature(),
- mode, deprecated, procedure.allowed(), description );
+ mode, deprecated, allowed, description );
return new ReflectiveProcedure( signature, constructor, procedureMethod, outputMapper, setters );
}
@@ -207,8 +211,10 @@ private ReflectiveUserFunction compileFunction( Class> procDefinition, MethodH
Optional deprecated = deprecated( method, function::deprecatedBy,
"Use of @UserFunction(deprecatedBy) without @Deprecated in " + procName );
+ String[] allowed = function.allowed().length == 0 ? config.getDefaultValue() : function.allowed();
UserFunctionSignature signature =
- new UserFunctionSignature( procName, inputSignature, valueConverter.type(), deprecated, function.allowed(), description );
+ new UserFunctionSignature( procName, inputSignature, valueConverter.type(), deprecated,
+ allowed, description );
return new ReflectiveUserFunction( signature, constructor, procedureMethod, valueConverter, setters );
}
diff --git a/community/kernel/src/test/java/org/neo4j/kernel/builtinprocs/BuiltInProceduresTest.java b/community/kernel/src/test/java/org/neo4j/kernel/builtinprocs/BuiltInProceduresTest.java
index b60dcb02a173e..b4984fc7a71ab 100644
--- a/community/kernel/src/test/java/org/neo4j/kernel/builtinprocs/BuiltInProceduresTest.java
+++ b/community/kernel/src/test/java/org/neo4j/kernel/builtinprocs/BuiltInProceduresTest.java
@@ -45,13 +45,13 @@
import org.neo4j.kernel.api.index.IndexDescriptor;
import org.neo4j.kernel.api.index.InternalIndexState;
import org.neo4j.kernel.api.proc.BasicContext;
-import org.neo4j.kernel.api.proc.CallableProcedure;
import org.neo4j.kernel.api.proc.Key;
import org.neo4j.kernel.api.proc.ProcedureSignature;
import org.neo4j.kernel.impl.factory.Edition;
import org.neo4j.kernel.impl.proc.Procedures;
import org.neo4j.kernel.impl.proc.TypeMappers;
import org.neo4j.kernel.internal.GraphDatabaseAPI;
+import org.neo4j.kernel.internal.Version;
import org.neo4j.storageengine.api.Token;
import static java.util.Collections.emptyIterator;
diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/proc/ProcedureJarLoaderTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/proc/ProcedureJarLoaderTest.java
index 84405b0d56361..48153f25a8900 100644
--- a/community/kernel/src/test/java/org/neo4j/kernel/impl/proc/ProcedureJarLoaderTest.java
+++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/proc/ProcedureJarLoaderTest.java
@@ -57,7 +57,7 @@ public class ProcedureJarLoaderTest
private final ProcedureJarLoader jarloader =
new ProcedureJarLoader( new ReflectiveProcedureCompiler( new TypeMappers(), new ComponentRegistry(),
- NullLog.getInstance() ), NullLog.getInstance() );
+ NullLog.getInstance(), ProcedureAllowedConfig.DEFAULT ), NullLog.getInstance() );
@Test
public void shouldLoadProcedureFromJar() throws Throwable
diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/proc/ReflectiveProcedureTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/proc/ReflectiveProcedureTest.java
index aceaa727b4519..2b46a5c2c1a3b 100644
--- a/community/kernel/src/test/java/org/neo4j/kernel/impl/proc/ReflectiveProcedureTest.java
+++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/proc/ReflectiveProcedureTest.java
@@ -66,7 +66,7 @@ public class ReflectiveProcedureTest
public void setUp() throws Exception
{
components = new ComponentRegistry();
- procedureCompiler = new ReflectiveProcedureCompiler( new TypeMappers(), components, NullLog.getInstance() );
+ procedureCompiler = new ReflectiveProcedureCompiler( new TypeMappers(), components, NullLog.getInstance(), ProcedureAllowedConfig.DEFAULT );
}
@Test
@@ -271,7 +271,8 @@ public void shouldSupportProcedureDeprecation() throws Throwable
{
// Given
Log log = mock(Log.class);
- ReflectiveProcedureCompiler procedureCompiler = new ReflectiveProcedureCompiler( new TypeMappers(), components, log );
+ ReflectiveProcedureCompiler procedureCompiler = new ReflectiveProcedureCompiler( new TypeMappers(), components, log,
+ ProcedureAllowedConfig.DEFAULT );
// When
List procs = procedureCompiler.compileProcedure( ProcedureWithDeprecation.class );
diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/proc/ReflectiveProcedureWithArgumentsTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/proc/ReflectiveProcedureWithArgumentsTest.java
index 7dedc753d9ae5..e9e34c5832b49 100644
--- a/community/kernel/src/test/java/org/neo4j/kernel/impl/proc/ReflectiveProcedureWithArgumentsTest.java
+++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/proc/ReflectiveProcedureWithArgumentsTest.java
@@ -214,6 +214,6 @@ public Stream defaultValues( @Name( value = "a", defaultValue =
private List compile( Class> clazz ) throws KernelException
{
- return new ReflectiveProcedureCompiler( new TypeMappers(), new ComponentRegistry(), NullLog.getInstance() ).compileProcedure( clazz );
+ return new ReflectiveProcedureCompiler( new TypeMappers(), new ComponentRegistry(), NullLog.getInstance(), ProcedureAllowedConfig.DEFAULT ).compileProcedure( clazz );
}
}
diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/proc/ReflectiveUserFunctionTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/proc/ReflectiveUserFunctionTest.java
index c662c493ed7ea..0a4aec5ff309e 100644
--- a/community/kernel/src/test/java/org/neo4j/kernel/impl/proc/ReflectiveUserFunctionTest.java
+++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/proc/ReflectiveUserFunctionTest.java
@@ -65,7 +65,7 @@ public class ReflectiveUserFunctionTest
public void setUp() throws Exception
{
components = new ComponentRegistry();
- procedureCompiler = new ReflectiveProcedureCompiler( new TypeMappers(), components, NullLog.getInstance() );
+ procedureCompiler = new ReflectiveProcedureCompiler( new TypeMappers(), components, NullLog.getInstance(), ProcedureAllowedConfig.DEFAULT );
}
@Test
@@ -249,7 +249,7 @@ public void shouldSupportFunctionDeprecation() throws Throwable
{
// Given
Log log = mock(Log.class);
- ReflectiveProcedureCompiler procedureCompiler = new ReflectiveProcedureCompiler( new TypeMappers(), components, log );
+ ReflectiveProcedureCompiler procedureCompiler = new ReflectiveProcedureCompiler( new TypeMappers(), components, log, ProcedureAllowedConfig.DEFAULT );
// When
List funcs = procedureCompiler.compileFunction( FunctionWithDeprecation.class );
diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/proc/ResourceInjectionTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/proc/ResourceInjectionTest.java
index 5a2c16e665d28..1afff3e099aa0 100644
--- a/community/kernel/src/test/java/org/neo4j/kernel/impl/proc/ResourceInjectionTest.java
+++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/proc/ResourceInjectionTest.java
@@ -128,6 +128,6 @@ private List compile( Class> clazz ) throws KernelException
{
ComponentRegistry components = new ComponentRegistry();
components.register( MyAwesomeAPI.class, (ctx) -> new MyAwesomeAPI() );
- return new ReflectiveProcedureCompiler( new TypeMappers(), components, NullLog.getInstance() ).compileProcedure( clazz );
+ return new ReflectiveProcedureCompiler( new TypeMappers(), components, NullLog.getInstance(), ProcedureAllowedConfig.DEFAULT ).compileProcedure( clazz );
}
}
diff --git a/enterprise/security/src/main/java/org/neo4j/server/security/enterprise/configuration/SecuritySettings.java b/enterprise/security/src/main/java/org/neo4j/server/security/enterprise/configuration/SecuritySettings.java
index 40dfd77413cc2..29279f6156883 100644
--- a/enterprise/security/src/main/java/org/neo4j/server/security/enterprise/configuration/SecuritySettings.java
+++ b/enterprise/security/src/main/java/org/neo4j/server/security/enterprise/configuration/SecuritySettings.java
@@ -42,6 +42,7 @@
import static org.neo4j.kernel.configuration.Settings.min;
import static org.neo4j.kernel.configuration.Settings.options;
import static org.neo4j.kernel.configuration.Settings.setting;
+import static org.neo4j.kernel.impl.proc.ProcedureAllowedConfig.PROC_ALLOWED_SETTING_DEFAULT_NAME;
/**
* Settings for security module
@@ -281,4 +282,16 @@ public class SecuritySettings
@Description( "Maximum number of history files for the security log." )
public static final Setting store_security_log_max_archives =
setting( "dbms.logs.security.rotation.keep_number", INTEGER, "7", min(1) );
+
+ //=========================================================================
+ // Procedure security settings
+ //=========================================================================
+
+ @Description( "The default role to assign to each procedure and user-defined function with an empty `allowed` " +
+ "annotation field. This can be used to enable fine grained permission " +
+ "control over third-party procedures and functions for which modifying source code is not possible. " +
+ "Procedures with non-empty `allowed` fields will be unaffected by this setting. " +
+ "If this setting is the empty string (default), procedures will be executed according to the same " +
+ "security rules as normal Cypher statements." )
+ public static final Setting default_allowed = setting( PROC_ALLOWED_SETTING_DEFAULT_NAME, STRING, "" );
}
diff --git a/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/BuiltInProceduresInteractionTestBase.java b/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/BuiltInProceduresInteractionTestBase.java
index 59016eef08377..f8417643dd6b2 100644
--- a/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/BuiltInProceduresInteractionTestBase.java
+++ b/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/BuiltInProceduresInteractionTestBase.java
@@ -29,6 +29,7 @@
import java.time.OffsetDateTime;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -40,12 +41,16 @@
import org.neo4j.graphdb.Result;
import org.neo4j.graphdb.factory.GraphDatabaseSettings;
import org.neo4j.kernel.api.KernelTransaction;
+import org.neo4j.kernel.api.proc.ProcedureSignature;
+import org.neo4j.kernel.api.proc.QualifiedName;
+import org.neo4j.kernel.api.proc.UserFunctionSignature;
import org.neo4j.kernel.enterprise.builtinprocs.QueryId;
import org.neo4j.kernel.impl.proc.Procedures;
import org.neo4j.procedure.Context;
import org.neo4j.procedure.Procedure;
import org.neo4j.procedure.TerminationGuard;
import org.neo4j.server.security.enterprise.auth.plugin.api.PredefinedRoles;
+import org.neo4j.server.security.enterprise.configuration.SecuritySettings;
import org.neo4j.test.Barrier;
import org.neo4j.test.DoubleLatch;
import org.neo4j.test.rule.concurrent.ThreadingRule;
@@ -58,6 +63,8 @@
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.anyOf;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.isA;
@@ -676,6 +683,93 @@ public void shouldTerminateLongRunningProcedureThatChecksTheGuardRegularlyOnTime
result.close();
}
+ @Test
+ public void shouldSetAllowedToConfigSetting() throws Throwable
+ {
+ neo.tearDown();
+ neo = setUpNeoServer( stringMap( SecuritySettings.default_allowed.name(), "nonEmpty" ) );
+ Procedures procedures = neo
+ .getLocalGraph()
+ .getDependencyResolver()
+ .resolveDependency( Procedures.class );
+ procedures.registerProcedure( ClassWithProcedures.class );
+
+ ProcedureSignature numNodes = procedures.procedure( new QualifiedName( new String[]{"test"}, "numNodes" ) );
+ assertThat( Arrays.asList( numNodes.allowed() ), containsInAnyOrder( "nonEmpty" ) );
+
+ ProcedureSignature allowedRead = procedures.procedure( new QualifiedName( new String[]{"test"}, "allowedReadProcedure" ) );
+ assertThat( Arrays.asList( allowedRead.allowed() ), containsInAnyOrder( "role1" ) );
+ }
+
+ @Test
+ public void shouldSetAllowedToDefaultValueAndRunningWorks() throws Throwable
+ {
+ neo.tearDown();
+ neo = setUpNeoServer( stringMap( SecuritySettings.default_allowed.name(), "role1" ) );
+ reSetUp();
+
+ userManager.newRole( "role1", "noneSubject" );
+ assertSuccess( noneSubject, "CALL test.numNodes", itr -> assertKeyIs( itr, "count", "3" ) );
+ }
+
+ @Test
+ public void shouldNotSetProcedureAllowedIfSettingNotSet() throws Throwable
+ {
+ Procedures procedures = neo
+ .getLocalGraph()
+ .getDependencyResolver()
+ .resolveDependency( Procedures.class );
+
+ ProcedureSignature numNodes = procedures.procedure( new QualifiedName( new String[]{"test"}, "numNodes" ) );
+ assertThat( Arrays.asList( numNodes.allowed() ), empty() );
+ }
+
+ @SuppressWarnings( "OptionalGetWithoutIsPresent" )
+ @Test
+ public void shouldSetAllowedToConfigSettingForUDF() throws Throwable
+ {
+ neo.tearDown();
+ neo = setUpNeoServer( stringMap( SecuritySettings.default_allowed.name(), "nonEmpty" ) );
+ Procedures procedures = neo
+ .getLocalGraph()
+ .getDependencyResolver()
+ .resolveDependency( Procedures.class );
+ procedures.registerFunction( ClassWithFunctions.class );
+
+ UserFunctionSignature funcSig = procedures.function(
+ new QualifiedName( new String[]{"test"}, "nonAllowedFunc" ) ).get();
+ assertThat( Arrays.asList( funcSig.allowed() ), containsInAnyOrder( "nonEmpty" ) );
+
+ UserFunctionSignature f2 = procedures.function( new QualifiedName( new String[]{"test"}, "allowedFunc" ) ).get();
+ assertThat( Arrays.asList( f2.allowed() ), containsInAnyOrder( "role1" ) );
+ }
+
+ @Test
+ public void shouldSetAllowedToDefaultValueAndRunningWorksForUDF() throws Throwable
+ {
+ neo.tearDown();
+ neo = setUpNeoServer( stringMap( SecuritySettings.default_allowed.name(), "role1" ) );
+ reSetUp();
+
+ userManager.newRole( "role1", "noneSubject" );
+ assertSuccess( neo.login( "noneSubject", "abc" ), "RETURN test.allowedFunc() AS c",
+ itr -> assertKeyIs( itr, "c", "success for role1" ) );
+ }
+
+ @SuppressWarnings( "OptionalGetWithoutIsPresent" )
+ @Test
+ public void shouldNotSetProcedureAllowedIfSettingNotSetForUDF() throws Throwable
+ {
+ Procedures procedures = neo
+ .getLocalGraph()
+ .getDependencyResolver()
+ .resolveDependency( Procedures.class );
+
+ UserFunctionSignature funcSig = procedures.function(
+ new QualifiedName( new String[]{"test"}, "nonAllowedFunc" ) ).get();
+ assertThat( Arrays.asList( funcSig.allowed() ), empty() );
+ }
+
@Test
public void shouldHandleWriteAfterAllowedReadProcedureWithAuthDisabled() throws Throwable
{
diff --git a/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/ProcedureInteractionTestBase.java b/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/ProcedureInteractionTestBase.java
index 7e8d1a5282923..065b2e3ac9611 100644
--- a/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/ProcedureInteractionTestBase.java
+++ b/enterprise/security/src/test/java/org/neo4j/server/security/enterprise/auth/ProcedureInteractionTestBase.java
@@ -665,6 +665,18 @@ public static class ClassWithFunctions
@Context
public GraphDatabaseService db;
+ @UserFunction( name = "test.nonAllowedFunc" )
+ public String nonAllowedFunc()
+ {
+ return "success";
+ }
+
+ @UserFunction( name = "test.allowedFunc", allowed = {"role1"} )
+ public String allowedFunc()
+ {
+ return "success for role1";
+ }
+
@UserFunction( name = "test.allowedFunction1", allowed = {"role1"} )
public String allowedFunction1()
{