Skip to content

Commit

Permalink
Abstracted AuthProcedureTestBase to eventually support testing at dif…
Browse files Browse the repository at this point in the history
…ferent Neo4j depth
  • Loading branch information
fickludd committed Jun 28, 2016
1 parent 886dbb4 commit 225812d
Show file tree
Hide file tree
Showing 5 changed files with 314 additions and 208 deletions.
Expand Up @@ -24,7 +24,6 @@
import org.junit.Before; import org.junit.Before;


import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Consumer; import java.util.function.Consumer;
Expand All @@ -33,85 +32,72 @@


import org.neo4j.graphdb.QueryExecutionException; import org.neo4j.graphdb.QueryExecutionException;
import org.neo4j.graphdb.Result; import org.neo4j.graphdb.Result;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.security.AuthorizationViolationException; import org.neo4j.graphdb.security.AuthorizationViolationException;
import org.neo4j.kernel.api.KernelTransaction;
import org.neo4j.kernel.api.security.AuthSubject;
import org.neo4j.kernel.internal.GraphDatabaseAPI;
import org.neo4j.server.security.auth.BasicPasswordPolicy;
import org.neo4j.server.security.auth.InMemoryUserRepository;
import org.neo4j.server.security.auth.RateLimitedAuthenticationStrategy;
import org.neo4j.test.TestEnterpriseGraphDatabaseFactory;

import static java.time.Clock.systemUTC;
import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static org.neo4j.server.security.auth.SecurityTestUtils.authToken;
import static org.neo4j.server.security.enterprise.auth.PredefinedRolesBuilder.ADMIN; import static org.neo4j.server.security.enterprise.auth.PredefinedRolesBuilder.ADMIN;
import static org.neo4j.server.security.enterprise.auth.PredefinedRolesBuilder.ARCHITECT; import static org.neo4j.server.security.enterprise.auth.PredefinedRolesBuilder.ARCHITECT;
import static org.neo4j.server.security.enterprise.auth.PredefinedRolesBuilder.PUBLISHER; import static org.neo4j.server.security.enterprise.auth.PredefinedRolesBuilder.PUBLISHER;
import static org.neo4j.server.security.enterprise.auth.PredefinedRolesBuilder.READER; import static org.neo4j.server.security.enterprise.auth.PredefinedRolesBuilder.READER;


public class AuthProcedureTestBase abstract class AuthProcedureTestBase<S>
{ {
protected EnterpriseAuthSubject adminSubject; final String EMPTY_ROLE = "empty";
protected EnterpriseAuthSubject schemaSubject;
protected EnterpriseAuthSubject writeSubject; S adminSubject;
protected EnterpriseAuthSubject readSubject; S schemaSubject;
protected EnterpriseAuthSubject pwdSubject; S writeSubject;
protected EnterpriseAuthSubject noneSubject; S readSubject;

S pwdSubject;
protected String[] initialUsers = { "adminSubject", "readSubject", "schemaSubject", S noneSubject;

String[] initialUsers = { "adminSubject", "readSubject", "schemaSubject",
"readWriteSubject", "pwdSubject", "noneSubject", "neo4j" }; "readWriteSubject", "pwdSubject", "noneSubject", "neo4j" };
protected String[] initialRoles = { "admin", "architect", "publisher", "reader", "empty" }; String[] initialRoles = { ADMIN, ARCHITECT, PUBLISHER, READER, EMPTY_ROLE };


protected GraphDatabaseAPI db;
protected MultiRealmAuthManager manager;
protected InternalFlatFileRealm internalRealm;
protected EnterpriseUserManager userManager; protected EnterpriseUserManager userManager;


protected NeoInteractionLevel<S> neo;

@Before @Before
public void setUp() throws Throwable public void setUp() throws Throwable
{ {
db = (GraphDatabaseAPI) new TestEnterpriseGraphDatabaseFactory().newImpermanentDatabase(); neo = setUpNeoServer();
internalRealm = new InternalFlatFileRealm( new InMemoryUserRepository(), new InMemoryRoleRepository(), userManager = neo.getManager();
new BasicPasswordPolicy(), new RateLimitedAuthenticationStrategy( systemUTC(), 3 ) );
manager = new MultiRealmAuthManager( internalRealm, Collections.singletonList( internalRealm ) );
manager.init();
manager.start();
userManager = manager.getUserManager();
userManager.newUser( "noneSubject", "abc", false ); userManager.newUser( "noneSubject", "abc", false );
userManager.newUser( "pwdSubject", "abc", true ); userManager.newUser( "pwdSubject", "abc", true );
userManager.newUser( "adminSubject", "abc", false ); userManager.newUser( "adminSubject", "abc", false );
userManager.newUser( "schemaSubject", "abc", false ); userManager.newUser( "schemaSubject", "abc", false );
userManager.newUser( "readWriteSubject", "abc", false ); userManager.newUser( "readWriteSubject", "abc", false );
userManager.newUser( "readSubject", "123", false ); userManager.newUser( "readSubject", "123", false );
// Currently admin, architect, publisher and reader roles are created by default // Currently admin role is created by default
userManager.addUserToRole( "adminSubject", ADMIN ); userManager.addUserToRole( "adminSubject", ADMIN );
userManager.addUserToRole( "schemaSubject", ARCHITECT ); userManager.addUserToRole( "schemaSubject", ARCHITECT );
userManager.addUserToRole( "readWriteSubject", PUBLISHER ); userManager.addUserToRole( "readWriteSubject", PUBLISHER );
userManager.addUserToRole( "readSubject", READER ); userManager.addUserToRole( "readSubject", READER );
userManager.newRole( "empty" ); userManager.newRole( EMPTY_ROLE );
noneSubject = manager.login( authToken( "noneSubject", "abc" ) ); noneSubject = neo.login( "noneSubject", "abc" );
pwdSubject = manager.login( authToken( "pwdSubject", "abc" ) ); pwdSubject = neo.login( "pwdSubject", "abc" );
readSubject = manager.login( authToken( "readSubject", "123" ) ); readSubject = neo.login( "readSubject", "123" );
writeSubject = manager.login( authToken( "readWriteSubject", "abc" ) ); writeSubject = neo.login( "readWriteSubject", "abc" );
schemaSubject = manager.login( authToken( "schemaSubject", "abc" ) ); schemaSubject = neo.login( "schemaSubject", "abc" );
adminSubject = manager.login( authToken( "adminSubject", "abc" ) ); adminSubject = neo.login( "adminSubject", "abc" );
db.execute( "UNWIND range(0,2) AS number CREATE (:Node {number:number})" ); executeQuery( writeSubject, "UNWIND range(0,2) AS number CREATE (:Node {number:number})" );
} }


abstract NeoInteractionLevel<S> setUpNeoServer() throws Throwable;

@After @After
public void tearDown() throws Throwable public void tearDown() throws Throwable
{ {
db.shutdown(); neo.tearDown();
manager.stop();
manager.shutdown();
} }


protected String[] with( String[] strs, String... moreStr ) protected String[] with( String[] strs, String... moreStr )
Expand All @@ -126,46 +112,46 @@ protected List<String> listOf( String... values )


//------------- Helper functions--------------- //------------- Helper functions---------------


protected void testSuccessfulRead( AuthSubject subject, int count ) void testSuccessfulRead( S subject, int count )
{ {
testCallCount( subject, "MATCH (n) RETURN n", null, count ); testCallCount( subject, "MATCH (n) RETURN n", null, count );
} }


protected void testFailRead( AuthSubject subject, int count ) void testFailRead( S subject, int count )
{ {
// TODO: this should be permission denied instead // TODO: this should be permission denied instead
testCallFail( subject, testCallFail( subject,
"MATCH (n) RETURN n", "MATCH (n) RETURN n",
AuthorizationViolationException.class, "Read operations are not allowed" ); AuthorizationViolationException.class, "Read operations are not allowed" );
} }


protected void testSuccessfulWrite( AuthSubject subject ) void testSuccessfulWrite( S subject )
{ {
testCallEmpty( subject, "CREATE (:Node)" ); testCallEmpty( subject, "CREATE (:Node)" );
} }


protected void testFailWrite( AuthSubject subject ) void testFailWrite( S subject )
{ {
// TODO: this should be permission denied instead // TODO: this should be permission denied instead
testCallFail( subject, testCallFail( subject,
"CREATE (:Node)", "CREATE (:Node)",
AuthorizationViolationException.class, "Write operations are not allowed" ); AuthorizationViolationException.class, "Write operations are not allowed" );
} }


protected void testSuccessfulSchema( AuthSubject subject ) void testSuccessfulSchema( S subject )
{ {
testCallEmpty( subject, "CREATE INDEX ON :Node(number)" ); testCallEmpty( subject, "CREATE INDEX ON :Node(number)" );
} }


protected void testFailSchema( AuthSubject subject ) void testFailSchema( S subject )
{ {
// TODO: this should be permission denied instead // TODO: this should be permission denied instead
testCallFail( subject, testCallFail( subject,
"CREATE INDEX ON :Node(number)", "CREATE INDEX ON :Node(number)",
AuthorizationViolationException.class, "Schema operations are not allowed" ); AuthorizationViolationException.class, "Schema operations are not allowed" );
} }


protected void testFailCreateUser( AuthSubject subject ) void testFailCreateUser( S subject )
{ {
testCallFail( subject, "CALL dbms.createUser('Craig', 'foo', false)", QueryExecutionException.class, testCallFail( subject, "CALL dbms.createUser('Craig', 'foo', false)", QueryExecutionException.class,
AuthProcedures.PERMISSION_DENIED ); AuthProcedures.PERMISSION_DENIED );
Expand All @@ -175,84 +161,84 @@ protected void testFailCreateUser( AuthSubject subject )
AuthProcedures.PERMISSION_DENIED ); AuthProcedures.PERMISSION_DENIED );
} }


protected void testFailAddUserToRole( AuthSubject subject ) void testFailAddUserToRole( S subject )
{ {
testCallFail( subject, "CALL dbms.addUserToRole('Craig', '" + PUBLISHER + "')", testCallFail( subject, "CALL dbms.addUserToRole('Craig', '" + PUBLISHER + "')",
QueryExecutionException.class, AuthProcedures.PERMISSION_DENIED ); QueryExecutionException.class, AuthProcedures.PERMISSION_DENIED );
} }


protected void testFailRemoveUserFromRole( AuthSubject subject ) void testFailRemoveUserFromRole( S subject )
{ {
testCallFail( subject, "CALL dbms.removeUserFromRole('Craig', '" + PUBLISHER + "')", testCallFail( subject, "CALL dbms.removeUserFromRole('Craig', '" + PUBLISHER + "')",
QueryExecutionException.class, AuthProcedures.PERMISSION_DENIED ); QueryExecutionException.class, AuthProcedures.PERMISSION_DENIED );
} }


protected void testFailDeleteUser( AuthSubject subject ) void testFailDeleteUser( S subject )
{ {
testCallFail( subject, "CALL dbms.deleteUser('Craig')", QueryExecutionException.class, testCallFail( subject, "CALL dbms.deleteUser('Craig')", QueryExecutionException.class,
AuthProcedures.PERMISSION_DENIED ); AuthProcedures.PERMISSION_DENIED );
testCallFail( subject, "CALL dbms.deleteUser('')", QueryExecutionException.class, testCallFail( subject, "CALL dbms.deleteUser('')", QueryExecutionException.class,
AuthProcedures.PERMISSION_DENIED ); AuthProcedures.PERMISSION_DENIED );
} }


protected void testSuccessfulListUsers( AuthSubject subject, String[] users ) void testSuccessfulListUsers( S subject, String[] users )
{ {
testResult( subject, "CALL dbms.listUsers() YIELD username", executeQuery( subject, "CALL dbms.listUsers() YIELD value AS users RETURN users",
r -> assertKeyIsArray( r, "username", users ) ); r -> assertKeyIsArray( r, "users", users ) );
} }


protected void testFailListUsers( AuthSubject subject, int count ) void testFailListUsers( S subject, int count )
{ {
testCallFail( subject, testCallFail( subject,
"CALL dbms.listUsers() YIELD username", "CALL dbms.listUsers() YIELD username",
QueryExecutionException.class, AuthProcedures.PERMISSION_DENIED ); QueryExecutionException.class, AuthProcedures.PERMISSION_DENIED );
} }


protected void testSuccessfulListRoles( AuthSubject subject, String[] roles ) void testSuccessfulListRoles( S subject, String[] roles )
{ {
testResult( subject, "CALL dbms.listRoles() YIELD role", executeQuery( subject, "CALL dbms.listRoles() YIELD value AS roles RETURN roles",
r -> assertKeyIsArray( r, "role", roles ) ); r -> assertKeyIsArray( r, "roles", roles ) );
} }


protected void testFailListRoles( AuthSubject subject ) void testFailListRoles( S subject )
{ {
testCallFail( subject, testCallFail( subject,
"CALL dbms.listRoles() YIELD role", "CALL dbms.listRoles() YIELD role",
QueryExecutionException.class, AuthProcedures.PERMISSION_DENIED ); QueryExecutionException.class, AuthProcedures.PERMISSION_DENIED );
} }


protected void testFailListUserRoles( AuthSubject subject, String username ) void testFailListUserRoles( S subject, String username )
{ {
testCallFail( subject, testCallFail( subject,
"CALL dbms.listRolesForUser('" + username + "') YIELD value AS roles RETURN count(roles)", "CALL dbms.listRolesForUser('" + username + "') YIELD value AS roles RETURN count(roles)",
QueryExecutionException.class, AuthProcedures.PERMISSION_DENIED ); QueryExecutionException.class, AuthProcedures.PERMISSION_DENIED );
} }


protected void testFailListRoleUsers( AuthSubject subject, String roleName ) void testFailListRoleUsers( S subject, String roleName )
{ {
testCallFail( subject, testCallFail( subject,
"CALL dbms.listUsersForRole('" + roleName + "') YIELD value AS users RETURN count(users)", "CALL dbms.listUsersForRole('" + roleName + "') YIELD value AS users RETURN count(users)",
QueryExecutionException.class, AuthProcedures.PERMISSION_DENIED ); QueryExecutionException.class, AuthProcedures.PERMISSION_DENIED );
} }


protected List<Object> getObjectsAsList( Result r, String key ) List<Object> getObjectsAsList( Result r, String key )
{ {
return r.stream().map( s -> s.get( key ) ).collect( Collectors.toList() ); return r.stream().map( s -> s.get( key ) ).collect( Collectors.toList() );
} }


protected void assertKeyIs( Result r, String key, String... items ) void assertKeyIs( Result r, String key, String... items )
{ {
assertKeyIsArray( r, key, items ); assertKeyIsArray( r, key, items );
} }


protected void assertKeyIsArray( Result r, String key, String[] items ) void assertKeyIsArray( Result r, String key, String[] items )
{ {
List<Object> results = getObjectsAsList( r, key ); List<Object> results = getObjectsAsList( r, key );
Assert.assertThat( results, containsInAnyOrder( items ) ); Assert.assertThat( results, containsInAnyOrder( items ) );
assertEquals( Arrays.asList( items ).size(), results.size() ); assertEquals( Arrays.asList( items ).size(), results.size() );
} }


protected void resultContainsMap( Result r, String keyKey, String valueKey, Map<String,Object> expected ) protected void assertKeyIsMap( Result r, String keyKey, String valueKey, Map<String,Object> expected )
{ {
r.stream().forEach( s -> { r.stream().forEach( s -> {
String key = (String) s.get( keyKey ); String key = (String) s.get( keyKey );
Expand All @@ -266,25 +252,8 @@ protected void resultContainsMap( Result r, String keyKey, String valueKey, Map<
} ); } );
} }


protected void testCall( AuthSubject subject, String call, Consumer<Map<String,Object>> consumer )
{
testCall( subject, call, null, consumer );
}

protected void testCall( AuthSubject subject, String call, Map<String,Object> params,
Consumer<Map<String,Object>> consumer )
{
testResult( subject, call, params, ( res ) -> {
if ( res.hasNext() )
{
Map<String,Object> row = res.next();
consumer.accept( row );
}
assertFalse( res.hasNext() );
} );
}


protected void testCallFail( AuthSubject subject, String call, void testCallFail( S subject, String call,
Class expectedExceptionClass, String partOfErrorMsg ) Class expectedExceptionClass, String partOfErrorMsg )
{ {
try try
Expand All @@ -299,14 +268,12 @@ protected void testCallFail( AuthSubject subject, String call,
} }
} }


protected void testUnAunthenticated( AuthSubject subject ) void testUnAuthenticated( S subject )
{ {
//TODO: improve me to be less gullible! assertFalse( neo.isAuthenticated( subject ) );
assertTrue( subject instanceof EnterpriseAuthSubject );
assertFalse( ((EnterpriseAuthSubject) subject).getShiroSubject().isAuthenticated() );
} }


protected void testUnAunthenticated( EnterpriseAuthSubject subject, String call ) protected void testUnAuthenticated( S subject, String call )
{ {
//TODO: OMG improve thrown exception //TODO: OMG improve thrown exception
try try
Expand All @@ -320,20 +287,20 @@ protected void testUnAunthenticated( EnterpriseAuthSubject subject, String call
} }
} }


protected void testCallEmpty( AuthSubject subject, String call ) void testCallEmpty( S subject, String call )
{ {
testCallEmpty( subject, call, null ); testCallEmpty( subject, call, null );
} }


protected void testCallEmpty( AuthSubject subject, String call, Map<String,Object> params ) void testCallEmpty( S subject, String call, Map<String,Object> params )
{ {
testResult( subject, call, params, ( res ) -> assertFalse( "Expected no results", res.hasNext() ) ); neo.executeQuery( subject, call, params, ( res ) -> assertFalse( "Expected no results", res.hasNext() ) );
} }


protected void testCallCount( AuthSubject subject, String call, Map<String,Object> params, void testCallCount( S subject, String call, Map<String,Object> params,
final int count ) final int count )
{ {
testResult( subject, call, params, ( res ) -> { neo.executeQuery( subject, call, params, ( res ) -> {
int left = count; int left = count;
while ( left > 0 ) while ( left > 0 )
{ {
Expand All @@ -345,20 +312,13 @@ protected void testCallCount( AuthSubject subject, String call, Map<String,Objec
} ); } );
} }


protected void testResult( AuthSubject subject, String call, void executeQuery( S subject, String call )
Consumer<Result> resultConsumer )
{ {
testResult( subject, call, null, resultConsumer ); neo.executeQuery( subject, call, null, r -> {} );
} }


protected void testResult( AuthSubject subject, String call, Map<String,Object> params, void executeQuery( S subject, String call, Consumer<Result> resultConsumer )
Consumer<Result> resultConsumer )
{ {
try ( Transaction tx = db.beginTransaction( KernelTransaction.Type.explicit, subject ) ) neo.executeQuery( subject, call, null, resultConsumer );
{
Map<String,Object> p = (params == null) ? Collections.emptyMap() : params;
resultConsumer.accept( db.execute( call, p ) );
tx.success();
}
} }
} }

0 comments on commit 225812d

Please sign in to comment.