Skip to content

Commit

Permalink
Started working on suspending users.
Browse files Browse the repository at this point in the history
Boolean impl on User.
  • Loading branch information
fickludd committed Jun 9, 2016
1 parent 8bb1283 commit e392a0d
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 6 deletions.
Expand Up @@ -38,11 +38,20 @@ public class User
/** Whether a password change is needed */ /** Whether a password change is needed */
private final boolean passwordChangeRequired; private final boolean passwordChangeRequired;


public User(String name, Credential credential, boolean passwordChangeRequired) /** User is suspended */
private final boolean isSuspended;

public User( String name, Credential credential, boolean passwordChangeRequired )
{
this(name, credential, passwordChangeRequired, false);
}

private User( String name, Credential credential, boolean passwordChangeRequired, boolean isSuspended )
{ {
this.name = name; this.name = name;
this.credential = credential; this.credential = credential;
this.passwordChangeRequired = passwordChangeRequired; this.passwordChangeRequired = passwordChangeRequired;
this.isSuspended = isSuspended;
} }


public String name() public String name()
Expand All @@ -56,6 +65,7 @@ public Credential credentials()
} }


public boolean passwordChangeRequired() { return passwordChangeRequired; } public boolean passwordChangeRequired() { return passwordChangeRequired; }
public boolean isSuspended() { return isSuspended; }


/** Use this user as a base for a new user object */ /** Use this user as a base for a new user object */
public Builder augment() { return new Builder(this); } public Builder augment() { return new Builder(this); }
Expand All @@ -78,6 +88,10 @@ public boolean equals( Object o )
{ {
return false; return false;
} }
if ( isSuspended != user.isSuspended )
{
return false;
}
if ( credential != null ? !credential.equals( user.credential ) : user.credential != null ) if ( credential != null ? !credential.equals( user.credential ) : user.credential != null )
{ {
return false; return false;
Expand All @@ -96,6 +110,7 @@ public int hashCode()
int result = name != null ? name.hashCode() : 0; int result = name != null ? name.hashCode() : 0;
result = 31 * result + ( credential != null ? credential.hashCode() : 0); result = 31 * result + ( credential != null ? credential.hashCode() : 0);
result = 31 * result + (passwordChangeRequired ? 1 : 0); result = 31 * result + (passwordChangeRequired ? 1 : 0);
result = 31 * result + (isSuspended ? 1 : 0);
return result; return result;
} }


Expand All @@ -106,6 +121,7 @@ public String toString()
"name='" + name + '\'' + "name='" + name + '\'' +
", credentials=" + credential + ", credentials=" + credential +
", passwordChangeRequired=" + passwordChangeRequired + ", passwordChangeRequired=" + passwordChangeRequired +
", isSuspended=" + isSuspended +
'}'; '}';
} }


Expand All @@ -114,6 +130,7 @@ public static class Builder
private String name; private String name;
private Credential credential = Credential.INACCESSIBLE; private Credential credential = Credential.INACCESSIBLE;
private boolean pwdChangeRequired; private boolean pwdChangeRequired;
private boolean isSuspended;


public Builder() { } public Builder() { }


Expand All @@ -122,15 +139,17 @@ public Builder( User base )
name = base.name; name = base.name;
credential = base.credential; credential = base.credential;
pwdChangeRequired = base.passwordChangeRequired; pwdChangeRequired = base.passwordChangeRequired;
isSuspended = base.isSuspended;
} }


public Builder withName( String name ) { this.name = name; return this; } public Builder withName( String name ) { this.name = name; return this; }
public Builder withCredentials( Credential creds ) { this.credential = creds; return this; } public Builder withCredentials( Credential creds ) { this.credential = creds; return this; }
public Builder withRequiredPasswordChange( boolean change ) { this.pwdChangeRequired = change; return this; } public Builder withRequiredPasswordChange( boolean change ) { this.pwdChangeRequired = change; return this; }
public Builder withIsSuspended( boolean suspended ) { this.isSuspended = suspended; return this; }


public User build() public User build()
{ {
return new User(name, credential, pwdChangeRequired ); return new User(name, credential, pwdChangeRequired, isSuspended );
} }
} }
} }
Expand Up @@ -73,20 +73,35 @@ private String serialize( User user )
{ {
return join( userSeparator, user.name(), return join( userSeparator, user.name(),
serialize( user.credentials() ), serialize( user.credentials() ),
user.passwordChangeRequired() ? "password_change_required" : "" ); user.passwordChangeRequired() ? "password_change_required" : "",
user.isSuspended() ? "is_suspended" : "" );
} }


private User deserializeUser( String line, int lineNumber ) throws FormatException private User deserializeUser( String line, int lineNumber ) throws FormatException
{ {
String[] parts = line.split( userSeparator, -1 ); String[] parts = line.split( userSeparator, -1 );
if ( parts.length != 3 ) boolean isSuspended;
if ( parts.length == 3 ) // Before suspension impl. assume non-suspended user
{ {
throw new FormatException( format( "wrong number of line fields [line %d]", lineNumber ) ); isSuspended = false;
} }
else if ( parts.length == 4 )
{
isSuspended = parts[3].equals( "is_suspended" );
}
else
{
throw new FormatException( format( "wrong number of line fields, expected 3 or 4, got %d [line %d]",
parts.length,
lineNumber
) );
}

return new User.Builder() return new User.Builder()
.withName( parts[0] ) .withName( parts[0] )
.withCredentials( deserializeCredentials( parts[1], lineNumber ) ) .withCredentials( deserializeCredentials( parts[1], lineNumber ) )
.withRequiredPasswordChange( parts[2].equals( "password_change_required" ) ) .withRequiredPasswordChange( parts[2].equals( "password_change_required" ) )
.withIsSuspended( isSuspended )
.build(); .build();
} }


Expand Down
Expand Up @@ -28,6 +28,7 @@
import org.neo4j.procedure.Name; import org.neo4j.procedure.Name;
import org.neo4j.procedure.PerformsDBMS; import org.neo4j.procedure.PerformsDBMS;
import org.neo4j.procedure.Procedure; import org.neo4j.procedure.Procedure;
import org.neo4j.server.security.auth.exception.ConcurrentModificationException;


public class AuthProcedures public class AuthProcedures
{ {
Expand Down Expand Up @@ -86,4 +87,28 @@ public void deleteUser( @Name( "username" ) String username ) throws IllegalCred
} }
shiroSubject.getUserManager().deleteUser( username ); shiroSubject.getUserManager().deleteUser( username );
} }

@PerformsDBMS
@Procedure( "dbms.suspendUser" )
public void suspendUser( @Name( "username" ) String username ) throws IOException, ConcurrentModificationException
{
ShiroAuthSubject shiroSubject = ShiroAuthSubject.castOrFail( authSubject );
if ( !shiroSubject.isAdmin() )
{
throw new AuthorizationViolationException( PERMISSION_DENIED );
}
shiroSubject.getUserManager().suspendUser( username );
}

@PerformsDBMS
@Procedure( "dbms.activateUser" )
public void activateUser( @Name( "username" ) String username ) throws IOException, ConcurrentModificationException
{
ShiroAuthSubject shiroSubject = ShiroAuthSubject.castOrFail( authSubject );
if ( !shiroSubject.isAdmin() )
{
throw new AuthorizationViolationException( PERMISSION_DENIED );
}
shiroSubject.getUserManager().activateUser( username );
}
} }
Expand Up @@ -270,6 +270,26 @@ boolean deleteUser( String username ) throws IOException
return result; return result;
} }


void suspendUser( String username ) throws IOException, ConcurrentModificationException
{
User user = userRepository.findByName( username );
if ( user != null && !user.isSuspended() )
{
User suspendedUser = user.augment().withIsSuspended( true ).build();
userRepository.update( user, suspendedUser );
}
}

void activateUser( String username ) throws IOException, ConcurrentModificationException
{
User user = userRepository.findByName( username );
if ( user != null && user.isSuspended() )
{
User activatedUser = user.augment().withIsSuspended( false ).build();
userRepository.update( user, activatedUser );
}
}

private void removeUserFromAllRoles( String username ) throws IOException private void removeUserFromAllRoles( String username ) throws IOException
{ {
try try
Expand Down
Expand Up @@ -42,6 +42,7 @@
import org.neo4j.server.security.auth.RateLimitedAuthenticationStrategy; import org.neo4j.server.security.auth.RateLimitedAuthenticationStrategy;
import org.neo4j.server.security.auth.User; import org.neo4j.server.security.auth.User;
import org.neo4j.server.security.auth.UserRepository; import org.neo4j.server.security.auth.UserRepository;
import org.neo4j.server.security.auth.exception.ConcurrentModificationException;


public class ShiroAuthManager extends BasicAuthManager implements RoleManager public class ShiroAuthManager extends BasicAuthManager implements RoleManager
{ {
Expand Down Expand Up @@ -216,6 +217,18 @@ public boolean deleteUser( String username ) throws IOException
return realm.deleteUser( username ); return realm.deleteUser( username );
} }


void suspendUser( String username ) throws IOException, ConcurrentModificationException
{
assertAuthEnabled();
realm.suspendUser( username );
}

void activateUser( String username ) throws IOException, ConcurrentModificationException
{
assertAuthEnabled();
realm.activateUser( username );
}

private Subject buildSubject( String username ) private Subject buildSubject( String username )
{ {
Subject.Builder subjectBuilder = new Subject.Builder( securityManager ); Subject.Builder subjectBuilder = new Subject.Builder( securityManager );
Expand Down
Expand Up @@ -78,7 +78,7 @@ public RoleManager getRoleManager()
return authManager; return authManager;
} }


public UserManager getUserManager() public ShiroAuthManager getUserManager()
{ {
return authManager; return authManager;
} }
Expand Down
Expand Up @@ -543,6 +543,35 @@ public void userDeletion4() throws Exception
} }
} }


//----------User suspension/activation scenarios-----------

/*
Admin suspends user Henrik
User Henrik logs in with correct password → fail
*/
@Test
public void userSuspension1() throws Exception
{
testCallEmpty( db, adminSubject, "CALL dbms.suspendUser('Henrik')", null );
AuthSubject subject = manager.login( "Henrik", "bar" );
assertEquals( AuthenticationResult.FAILURE, subject.getAuthenticationResult() );
}

/*
Admin suspends user Henrik
User Henrik logs in with correct password → fail
*/
@Test
public void userActivation1() throws Exception
{
testCallEmpty( db, adminSubject, "CALL dbms.suspendUser('Henrik')", null );
AuthSubject subject = manager.login( "Henrik", "bar" );
assertEquals( AuthenticationResult.FAILURE, subject.getAuthenticationResult() );
testCallEmpty( db, adminSubject, "CALL dbms.activateUser('Henrik')", null );
subject = manager.login( "Henrik", "bar" );
assertEquals( AuthenticationResult.SUCCESS, subject.getAuthenticationResult() );
}

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


private void testSuccessfulReadAction( AuthSubject subject, Long count ) private void testSuccessfulReadAction( AuthSubject subject, Long count )
Expand Down

0 comments on commit e392a0d

Please sign in to comment.