Skip to content

Commit

Permalink
Moved enterprise auth procs logging and admin checks to personal user…
Browse files Browse the repository at this point in the history
… manager
  • Loading branch information
fickludd committed Oct 5, 2016
1 parent 143751e commit 5fb06a7
Show file tree
Hide file tree
Showing 10 changed files with 298 additions and 280 deletions.
Expand Up @@ -25,6 +25,7 @@
import java.util.Set; import java.util.Set;
import java.util.stream.Stream; import java.util.stream.Stream;


import org.neo4j.graphdb.security.AuthorizationViolationException;
import org.neo4j.kernel.api.security.AuthSubject; import org.neo4j.kernel.api.security.AuthSubject;
import org.neo4j.kernel.api.exceptions.InvalidArgumentsException; import org.neo4j.kernel.api.exceptions.InvalidArgumentsException;
import org.neo4j.procedure.Context; import org.neo4j.procedure.Context;
Expand Down Expand Up @@ -78,6 +79,10 @@ public void changePasswordDeprecated( @Name( "password" ) String password ) thro
@Procedure( name = "dbms.security.changePassword", mode = DBMS ) @Procedure( name = "dbms.security.changePassword", mode = DBMS )
public void changePassword( @Name( "password" ) String password ) throws InvalidArgumentsException, IOException public void changePassword( @Name( "password" ) String password ) throws InvalidArgumentsException, IOException
{ {
if ( authSubject == AuthSubject.ANONYMOUS )
{
throw new AuthorizationViolationException( "Anonymous cannot change password" );
}
userManager.setUserPassword( authSubject.username(), password, false ); userManager.setUserPassword( authSubject.username(), password, false );
} }


Expand Down
Expand Up @@ -23,43 +23,19 @@
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException; import org.junit.rules.ExpectedException;


import org.neo4j.collection.RawIterator;
import org.neo4j.graphdb.factory.GraphDatabaseSettings; import org.neo4j.graphdb.factory.GraphDatabaseSettings;
import org.neo4j.kernel.api.exceptions.ProcedureException; import org.neo4j.kernel.api.exceptions.ProcedureException;
import org.neo4j.kernel.api.security.AccessMode; import org.neo4j.kernel.api.security.AccessMode;
import org.neo4j.kernel.api.security.AuthSubject;
import org.neo4j.kernel.impl.api.integrationtest.KernelIntegrationTest; import org.neo4j.kernel.impl.api.integrationtest.KernelIntegrationTest;
import org.neo4j.test.TestGraphDatabaseBuilder; import org.neo4j.test.TestGraphDatabaseBuilder;


import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.emptyIterable;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.neo4j.helpers.collection.Iterators.asList;
import static org.neo4j.kernel.api.proc.ProcedureSignature.procedureName; import static org.neo4j.kernel.api.proc.ProcedureSignature.procedureName;


public class AuthProceduresTest extends KernelIntegrationTest public class AuthProceduresTest extends KernelIntegrationTest
{ {
@Rule @Rule
public ExpectedException exception = ExpectedException.none(); public ExpectedException exception = ExpectedException.none();


@Test
public void callDeprecatedChangePasswordWithAccessModeInDbmsMode() throws Throwable
{
// Given
Object[] inputArray = new Object[1];
inputArray[0] = "newPassword";
AuthSubject authSubject = mock( AuthSubject.class );

// When
RawIterator<Object[], ProcedureException> stream = dbmsOperations().procedureCallDbms(
procedureName( "dbms", "changePassword" ), inputArray, authSubject );

// Then
verify( authSubject ).setPassword( (String) inputArray[0], false );
assertThat( asList( stream ), emptyIterable() );
}

@Test @Test
public void shouldFailWhenDeprecatedChangePasswordWithStaticAccessModeInDbmsMode() throws Throwable public void shouldFailWhenDeprecatedChangePasswordWithStaticAccessModeInDbmsMode() throws Throwable
{ {
Expand All @@ -76,23 +52,6 @@ public void shouldFailWhenDeprecatedChangePasswordWithStaticAccessModeInDbmsMode
.procedureCallDbms( procedureName( "dbms", "changePassword" ), inputArray, AccessMode.Static.NONE ); .procedureCallDbms( procedureName( "dbms", "changePassword" ), inputArray, AccessMode.Static.NONE );
} }


@Test
public void callChangePasswordWithAccessModeInDbmsMode() throws Throwable
{
// Given
Object[] inputArray = new Object[1];
inputArray[0] = "newPassword";
AuthSubject authSubject = mock( AuthSubject.class );

// When
RawIterator<Object[],ProcedureException> stream = dbmsOperations().procedureCallDbms(
procedureName( "dbms", "security", "changePassword" ), inputArray, authSubject );

// Then
verify( authSubject ).setPassword( (String) inputArray[0], false );
assertThat( asList( stream ), emptyIterable() );
}

@Test @Test
public void shouldFailWhenChangePasswordWithStaticAccessModeInDbmsMode() throws Throwable public void shouldFailWhenChangePasswordWithStaticAccessModeInDbmsMode() throws Throwable
{ {
Expand Down
Expand Up @@ -35,8 +35,10 @@
import org.neo4j.kernel.impl.core.ThreadToStatementContextBridge; import org.neo4j.kernel.impl.core.ThreadToStatementContextBridge;
import org.neo4j.kernel.internal.GraphDatabaseAPI; import org.neo4j.kernel.internal.GraphDatabaseAPI;
import org.neo4j.procedure.Context; import org.neo4j.procedure.Context;
import org.neo4j.server.security.auth.User;
import org.neo4j.server.security.enterprise.log.SecurityLog; import org.neo4j.server.security.enterprise.log.SecurityLog;


import static java.util.Collections.emptyList;
import static org.neo4j.graphdb.security.AuthorizationViolationException.PERMISSION_DENIED; import static org.neo4j.graphdb.security.AuthorizationViolationException.PERMISSION_DENIED;
import static org.neo4j.kernel.impl.api.security.OverriddenAccessMode.getUsernameFromAccessMode; import static org.neo4j.kernel.impl.api.security.OverriddenAccessMode.getUsernameFromAccessMode;


Expand All @@ -51,8 +53,26 @@ public class AuthProceduresBase
@Context @Context
public SecurityLog securityLog; public SecurityLog securityLog;


@Context
public EnterpriseUserManager userManager;

// ----------------- helpers --------------------- // ----------------- helpers ---------------------


protected void kickoutUser( String username, String reason )
{
try
{
terminateTransactionsForValidUser( username );
terminateConnectionsForValidUser( username );
}
catch ( Exception e )
{
securityLog.error( authSubject, "failed to terminate running transaction and bolt connections for " +
"user `%s` following %s: %s", username, reason, e.getMessage() );
throw e;
}
}

protected void terminateTransactionsForValidUser( String username ) protected void terminateTransactionsForValidUser( String username )
{ {
KernelTransaction currentTx = getCurrentTx(); KernelTransaction currentTx = getCurrentTx();
Expand Down Expand Up @@ -119,6 +139,14 @@ public static class StringResult
} }
} }


protected UserResult userResultForName( String username )
{
User user = userManager.silentlyGetUser( username );
Iterable<String> flags = user == null ? emptyList() : user.getFlags();
Set<String> roles = userManager.silentlyGetRoleNamesForUser( username );
return new UserResult( username, roles, flags );
}

public static class UserResult public static class UserResult
{ {
public final String username; public final String username;
Expand All @@ -135,6 +163,11 @@ public static class UserResult
} }
} }


protected RoleResult roleResultForName( String roleName )
{
return new RoleResult( roleName, userManager.silentlyGetUsernamesForRole( roleName ) );
}

public static class RoleResult public static class RoleResult
{ {
public final String role; public final String role;
Expand Down
Expand Up @@ -37,6 +37,8 @@ public interface EnterpriseUserManager extends UserManager


RoleRecord getRole( String roleName ) throws InvalidArgumentsException; RoleRecord getRole( String roleName ) throws InvalidArgumentsException;


RoleRecord silentlyGetRole( String roleName );

/** /**
* Assign a role to a user. The role and the user have to exist. * Assign a role to a user. The role and the user have to exist.
* *
Expand All @@ -61,5 +63,9 @@ public interface EnterpriseUserManager extends UserManager


Set<String> getRoleNamesForUser( String username ) throws InvalidArgumentsException; Set<String> getRoleNamesForUser( String username ) throws InvalidArgumentsException;


Set<String> silentlyGetRoleNamesForUser( String username );

Set<String> getUsernamesForRole( String roleName ) throws InvalidArgumentsException; Set<String> getUsernamesForRole( String roleName ) throws InvalidArgumentsException;

Set<String> silentlyGetUsernamesForRole( String roleName );
} }
Expand Up @@ -61,6 +61,7 @@
import org.neo4j.server.security.enterprise.configuration.SecuritySettings; import org.neo4j.server.security.enterprise.configuration.SecuritySettings;


import static java.lang.String.format; import static java.lang.String.format;
import static java.util.Collections.emptySet;


/** /**
* Shiro realm wrapping FileUserRepository and FileRoleRepository * Shiro realm wrapping FileUserRepository and FileRoleRepository
Expand All @@ -71,7 +72,7 @@ public class InternalFlatFileRealm extends AuthorizingRealm implements RealmLife
* This flag is used in the same way as User.PASSWORD_CHANGE_REQUIRED, but it's * This flag is used in the same way as User.PASSWORD_CHANGE_REQUIRED, but it's
* placed here because of user suspension not being a part of community edition * placed here because of user suspension not being a part of community edition
*/ */
private int MAX_READ_ATTEMPTS = 10; private static int MAX_READ_ATTEMPTS = 10;


static final String IS_SUSPENDED = "is_suspended"; static final String IS_SUSPENDED = "is_suspended";


Expand Down Expand Up @@ -467,6 +468,12 @@ public RoleRecord getRole( String roleName ) throws InvalidArgumentsException
return role; return role;
} }


@Override
public RoleRecord silentlyGetRole( String roleName )
{
return roleRepository.getRoleByName( roleName );
}

@Override @Override
public void addRoleToUser( String roleName, String username ) throws IOException, InvalidArgumentsException public void addRoleToUser( String roleName, String username ) throws IOException, InvalidArgumentsException
{ {
Expand Down Expand Up @@ -650,13 +657,26 @@ public Set<String> getRoleNamesForUser( String username ) throws InvalidArgument
return roleRepository.getRoleNamesByUsername( username ); return roleRepository.getRoleNamesByUsername( username );
} }


@Override
public Set<String> silentlyGetRoleNamesForUser( String username )
{
return roleRepository.getRoleNamesByUsername( username );
}

@Override @Override
public Set<String> getUsernamesForRole( String roleName ) throws InvalidArgumentsException public Set<String> getUsernamesForRole( String roleName ) throws InvalidArgumentsException
{ {
RoleRecord role = getRole( roleName ); RoleRecord role = getRole( roleName );
return role.users(); return role.users();
} }


@Override
public Set<String> silentlyGetUsernamesForRole( String roleName )
{
RoleRecord role = silentlyGetRole( roleName );
return role == null ? emptySet() : role.users();
}

@Override @Override
public Set<String> getAllUsernames() public Set<String> getAllUsernames()
{ {
Expand Down

0 comments on commit 5fb06a7

Please sign in to comment.