Skip to content

Commit

Permalink
Support multiple roles per procedure mapping
Browse files Browse the repository at this point in the history
A comma-separated list of roles after the procedure name pattern may be
specified, such that all roles mentioned will be mapped.
  • Loading branch information
Mats-SX committed Oct 26, 2016
1 parent 94c976b commit fec9eb8
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 21 deletions.
Expand Up @@ -28,11 +28,14 @@

import org.neo4j.kernel.configuration.Config;

import static java.util.Arrays.stream;

public class ProcedureAllowedConfig
{
public static final String PROC_ALLOWED_SETTING_DEFAULT_NAME = "dbms.security.procedures.default_allowed";
public static final String PROC_ALLOWED_SETTING_ROLES = "dbms.security.procedures.roles";

private static final String ROLES_DELIMITER = ",";
private static final String SETTING_DELIMITER = ";";
private static final String MAPPING_DELIMITER = ":";

Expand All @@ -52,9 +55,12 @@ public ProcedureAllowedConfig( Config config )
if ( params.containsKey( PROC_ALLOWED_SETTING_ROLES ) )
{
this.matchers = Stream.of( params.get( PROC_ALLOWED_SETTING_ROLES ).split( SETTING_DELIMITER ) )
.map( procToRoleSpec -> {
.map( procToRoleSpec ->
{
String[] spec = procToRoleSpec.split( MAPPING_DELIMITER );
return new ProcMatcher( spec[0].trim(), spec[1].trim() );
String[] roles = stream( spec[1].split( ROLES_DELIMITER ) )
.map( String::trim ).toArray( String[]::new );
return new ProcMatcher( spec[0].trim(), roles );
} ).collect( Collectors.toList() );
}
else
Expand All @@ -65,9 +71,9 @@ public ProcedureAllowedConfig( Config config )

String[] rolesFor( String procedureName )
{
String[] wildCardRoles =
matchers.stream().filter( matcher -> matcher.matches( procedureName ) ).map( ProcMatcher::role )
.toArray( String[]::new );
String[] wildCardRoles = matchers.stream().filter( matcher -> matcher.matches( procedureName ) )
.map( ProcMatcher::roles ).reduce( new String[0],
( acc, next ) -> Stream.concat( stream( acc ), stream( next ) ).toArray( String[]::new ) );
if ( wildCardRoles.length > 0 )
{
return wildCardRoles;
Expand All @@ -80,30 +86,30 @@ String[] rolesFor( String procedureName )

private String[] getDefaultValue()
{
return defaultValue == null || defaultValue.isEmpty() ? new String[0]: new String[]{defaultValue};
return defaultValue == null || defaultValue.isEmpty() ? new String[0] : new String[]{defaultValue};
}

static final ProcedureAllowedConfig DEFAULT = new ProcedureAllowedConfig();

private static class ProcMatcher
{
private final Pattern pattern;
private final String role;
private final String[] roles;

private ProcMatcher(String procedurePattern, String role)
private ProcMatcher( String procedurePattern, String[] roles )
{
this.pattern = Pattern.compile( procedurePattern.replaceAll( "\\.", "\\\\." ).replaceAll( "\\*", ".*" ) );
this.role = role;
this.roles = roles;
}

boolean matches( String procedureName )
{
return pattern.matcher( procedureName ).matches();
}

String role()
String[] roles()
{
return role;
return roles;
}
}
}
Expand Up @@ -26,6 +26,8 @@
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.neo4j.helpers.collection.MapUtil.genericMap;
import static org.neo4j.kernel.impl.proc.ProcedureAllowedConfig.PROC_ALLOWED_SETTING_DEFAULT_NAME;
import static org.neo4j.kernel.impl.proc.ProcedureAllowedConfig.PROC_ALLOWED_SETTING_ROLES;

public class ProcedureAllowedConfigTest
{
Expand All @@ -48,7 +50,7 @@ public void shouldHaveEmptyDefaultConfigs()
public void shouldHaveConfigsWithDefaultProcedureAllowed()
{
Config config = Config.defaults()
.with( genericMap( ProcedureAllowedConfig.PROC_ALLOWED_SETTING_DEFAULT_NAME, "role1" ) );
.with( genericMap( PROC_ALLOWED_SETTING_DEFAULT_NAME, "role1" ) );
ProcedureAllowedConfig procConfig = new ProcedureAllowedConfig( config );
assertThat( procConfig.rolesFor( "x" ), equalTo( arrayOf( "role1" ) ) );
}
Expand All @@ -57,8 +59,8 @@ public void shouldHaveConfigsWithDefaultProcedureAllowed()
public void shouldHaveConfigsWithExactMatchProcedureAllowed()
{
Config config = Config.defaults()
.with( genericMap( ProcedureAllowedConfig.PROC_ALLOWED_SETTING_DEFAULT_NAME, "role1",
ProcedureAllowedConfig.PROC_ALLOWED_SETTING_ROLES, "xyz:anotherRole" ) );
.with( genericMap( PROC_ALLOWED_SETTING_DEFAULT_NAME, "role1",
PROC_ALLOWED_SETTING_ROLES, "xyz:anotherRole" ) );
ProcedureAllowedConfig procConfig = new ProcedureAllowedConfig( config );
assertThat( procConfig.rolesFor( "xyz" ), equalTo( arrayOf( "anotherRole" ) ) );
assertThat( procConfig.rolesFor( "abc" ), equalTo( arrayOf( "role1" ) ) );
Expand All @@ -68,8 +70,8 @@ public void shouldHaveConfigsWithExactMatchProcedureAllowed()
public void shouldHaveConfigsWithWildcardProcedureAllowed()
{
Config config = Config.defaults()
.with( genericMap( ProcedureAllowedConfig.PROC_ALLOWED_SETTING_DEFAULT_NAME, "role1",
ProcedureAllowedConfig.PROC_ALLOWED_SETTING_ROLES, "xyz*:anotherRole" ) );
.with( genericMap( PROC_ALLOWED_SETTING_DEFAULT_NAME, "role1",
PROC_ALLOWED_SETTING_ROLES, "xyz*:anotherRole" ) );
ProcedureAllowedConfig procConfig = new ProcedureAllowedConfig( config );
assertThat( procConfig.rolesFor( "xyzabc" ), equalTo( arrayOf( "anotherRole" ) ) );
assertThat( procConfig.rolesFor( "abcxyz" ), equalTo( arrayOf( "role1" ) ) );
Expand All @@ -79,7 +81,7 @@ public void shouldHaveConfigsWithWildcardProcedureAllowed()
public void shouldHaveConfigsWithWildcardProcedureAllowedAndNoDefault()
{
Config config = Config.defaults()
.with( genericMap( ProcedureAllowedConfig.PROC_ALLOWED_SETTING_ROLES, "xyz*:anotherRole" ) );
.with( genericMap( PROC_ALLOWED_SETTING_ROLES, "xyz*:anotherRole" ) );
ProcedureAllowedConfig procConfig = new ProcedureAllowedConfig( config );
assertThat( procConfig.rolesFor( "xyzabc" ), equalTo( arrayOf( "anotherRole" ) ) );
assertThat( procConfig.rolesFor( "abcxyz" ), equalTo( EMPTY ) );
Expand All @@ -90,7 +92,7 @@ public void shouldHaveConfigsWithMultipleWildcardProcedureAllowedAndNoDefault()
{
Config config = Config.defaults()
.with( genericMap(
ProcedureAllowedConfig.PROC_ALLOWED_SETTING_ROLES,
PROC_ALLOWED_SETTING_ROLES,
"apoc.convert.*:apoc_reader;apoc.load.json:apoc_writer;apoc.trigger.add:TriggerHappy"
) );
ProcedureAllowedConfig procConfig = new ProcedureAllowedConfig( config );
Expand All @@ -111,8 +113,8 @@ public void shouldHaveConfigsWithOverlappingMatchingWildcards()
{
Config config = Config.defaults()
.with( genericMap(
ProcedureAllowedConfig.PROC_ALLOWED_SETTING_DEFAULT_NAME, "default",
ProcedureAllowedConfig.PROC_ALLOWED_SETTING_ROLES,
PROC_ALLOWED_SETTING_DEFAULT_NAME, "default",
PROC_ALLOWED_SETTING_ROLES,
"apoc.*:apoc;apoc.load.*:loader;apoc.trigger.*:trigger;apoc.trigger.add:TriggerHappy"
) );
ProcedureAllowedConfig procConfig = new ProcedureAllowedConfig( config );
Expand All @@ -123,4 +125,16 @@ public void shouldHaveConfigsWithOverlappingMatchingWildcards()
assertThat( procConfig.rolesFor( "apoc.trigger.remove" ), equalTo( arrayOf( "apoc", "trigger" ) ) );
assertThat( procConfig.rolesFor( "apoc.load-xml" ), equalTo( arrayOf( "apoc" ) ) );
}

@Test
public void shouldSupportSeveralRolesPerPattern()
{
Config config = Config.defaults().with( genericMap( PROC_ALLOWED_SETTING_ROLES,
"xyz*:role1,role2, role3 , role4 ; abc: role3 ,role1" ) );
ProcedureAllowedConfig procConfig = new ProcedureAllowedConfig( config );

assertThat( procConfig.rolesFor( "xyzabc" ), equalTo( arrayOf( "role1", "role2", "role3", "role4" ) ) );
assertThat( procConfig.rolesFor( "abc" ), equalTo( arrayOf( "role3", "role1" ) ) );
assertThat( procConfig.rolesFor( "abcxyz" ), equalTo( EMPTY ) );
}
}
Expand Up @@ -205,6 +205,21 @@ public void shouldHandleWriteAfterAllowedReadProcedureWithAuthDisabled() throws
.registerProcedure( ClassWithProcedures.class );

S subject = neo.login( "no_auth", "" );
assertEmpty( subject, "CALL test.allowedReadProcedure() YIELD value CREATE (:NEWNODE {name:value})" );
assertEmpty( subject, "CALL test.allowedReadProcedure() YIELD value CREATE (:NewNode {name: value})" );
}

@Test
public void shouldHandleMultipleRolesSpecifiedForMapping() throws Throwable
{
// Given
configuredSetup( stringMap( SecuritySettings.procedure_roles.name(), "test.*:tester, other" ) );

// When
userManager.newRole( "tester", "noneSubject" );
userManager.newRole( "other", "readSubject" );

// Then
assertSuccess( readSubject, "CALL test.createNode", ResourceIterator::close );
assertSuccess( noneSubject, "CALL test.numNodes", itr -> assertKeyIs( itr, "count", "4" ) );
}
}

0 comments on commit fec9eb8

Please sign in to comment.