Skip to content

Commit

Permalink
Moved user management to community edition
Browse files Browse the repository at this point in the history
  • Loading branch information
fickludd committed Aug 24, 2016
1 parent dbcc0ba commit a224b4c
Show file tree
Hide file tree
Showing 10 changed files with 493 additions and 66 deletions.
6 changes: 6 additions & 0 deletions community/security/pom.xml
Expand Up @@ -96,6 +96,12 @@ the relevant Commercial Agreement.
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-cypher</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j-logging</artifactId>
Expand Down
Expand Up @@ -20,6 +20,10 @@
package org.neo4j.server.security.auth;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;

import org.neo4j.kernel.api.security.AuthSubject;
import org.neo4j.kernel.api.security.exception.InvalidArgumentsException;
Expand All @@ -34,9 +38,68 @@ public class AuthProcedures
@Context
public AuthSubject authSubject;

@Procedure( name = "dbms.communityAuth.createUser", mode = DBMS )
public void createUser( @Name( "username" ) String username, @Name( "password" ) String password,
@Name( "requirePasswordChange" ) boolean requirePasswordChange )
throws InvalidArgumentsException, IOException
{
BasicAuthSubject subject = BasicAuthSubject.castOrFail( authSubject );
subject.getAuthManager().newUser( username, password, requirePasswordChange );
}

@Procedure( name = "dbms.communityAuth.deleteUser", mode = DBMS )
public void deleteUser( @Name( "username" ) String username ) throws InvalidArgumentsException, IOException
{
BasicAuthSubject subject = BasicAuthSubject.castOrFail( authSubject );
if ( subject.doesUsernameMatch( username ) )
{
throw new InvalidArgumentsException( "Deleting yourself (user '" + username + "') is not allowed." );
}
subject.getAuthManager().deleteUser( username );
}

@Procedure( name = "dbms.changePassword", mode = DBMS )
public void changePassword( @Name( "password" ) String password ) throws InvalidArgumentsException, IOException
{
authSubject.setPassword( password );
}

@Procedure( name = "dbms.communityAuth.showCurrentUser", mode = DBMS )
public Stream<UserResult> showCurrentUser() throws InvalidArgumentsException, IOException
{
BasicAuthSubject subject = BasicAuthSubject.castOrFail( authSubject );
return Stream.of( new UserResult(
subject.name(),
subject.getAuthManager().getUser( subject.name() ).getFlags()
) );
}

@Procedure( name = "dbms.communityAuth.listUsers", mode = DBMS )
public Stream<UserResult> listUsers() throws InvalidArgumentsException, IOException
{
BasicAuthSubject subject = BasicAuthSubject.castOrFail( authSubject );
Set<String> usernames = subject.getAuthManager().getAllUsernames();
List<UserResult> results = new ArrayList<>();
for ( String username : usernames )
{
results.add( new UserResult(
username,
subject.getAuthManager().getUser( username ).getFlags()
) );
}
return results.stream();
}

public static class UserResult
{
public final String username;
public final List<String> flags;

UserResult( String username, Iterable<String> flags )
{
this.username = username;
this.flags = new ArrayList<>();
for ( String f : flags ) {this.flags.add( f );}
}
}
}
Expand Up @@ -22,6 +22,7 @@
import java.io.IOException;
import java.time.Clock;
import java.util.Map;
import java.util.Set;

import org.neo4j.graphdb.security.AuthorizationViolationException;
import org.neo4j.kernel.api.security.AuthManager;
Expand All @@ -35,7 +36,7 @@
/**
* Manages server authentication and authorization.
* <p>
* Through the BasicAuthManager you can create, update and delete users, and authenticate using credentials.
* Through the BasicAuthManager you can create, update and delete userRepository, and authenticate using credentials.
* <p>>
* NOTE: AuthManager will manage the lifecycle of the given UserRepository,
* so the given UserRepository should not be added to another LifeSupport.
Expand All @@ -44,33 +45,33 @@
public class BasicAuthManager implements AuthManager, UserManager, UserManagerSupplier
{
protected final AuthenticationStrategy authStrategy;
protected final UserRepository users;
protected final UserRepository userRepository;
protected final PasswordPolicy passwordPolicy;

public BasicAuthManager( UserRepository users, PasswordPolicy passwordPolicy, AuthenticationStrategy authStrategy )
public BasicAuthManager( UserRepository userRepository, PasswordPolicy passwordPolicy, AuthenticationStrategy authStrategy )
{
this.users = users;
this.userRepository = userRepository;
this.passwordPolicy = passwordPolicy;
this.authStrategy = authStrategy;
}

public BasicAuthManager( UserRepository users, PasswordPolicy passwordPolicy, Clock clock )
public BasicAuthManager( UserRepository userRepository, PasswordPolicy passwordPolicy, Clock clock )
{
this( users, passwordPolicy, new RateLimitedAuthenticationStrategy( clock, 3 ) );
this( userRepository, passwordPolicy, new RateLimitedAuthenticationStrategy( clock, 3 ) );
}

@Override
public void init() throws Throwable
{
users.init();
userRepository.init();
}

@Override
public void start() throws Throwable
{
users.start();
userRepository.start();

if ( users.numberOfUsers() == 0 )
if ( userRepository.numberOfUsers() == 0 )
{
newUser( "neo4j", "neo4j", true );
}
Expand All @@ -79,22 +80,22 @@ public void start() throws Throwable
@Override
public void stop() throws Throwable
{
users.stop();
userRepository.stop();
}

@Override
public void shutdown() throws Throwable
{
users.shutdown();
userRepository.shutdown();
}

@Override
public AuthSubject login( Map<String,Object> authToken ) throws InvalidAuthTokenException
public BasicAuthSubject login( Map<String,Object> authToken ) throws InvalidAuthTokenException
{
String username = AuthToken.safeCast( AuthToken.PRINCIPAL, authToken );
String password = AuthToken.safeCast( AuthToken.CREDENTIALS, authToken );

User user = users.getUserByName( username );
User user = userRepository.getUserByName( username );
AuthenticationResult result = AuthenticationResult.FAILURE;
if ( user != null )
{
Expand All @@ -111,30 +112,34 @@ public AuthSubject login( Map<String,Object> authToken ) throws InvalidAuthToken
public User newUser( String username, String initialPassword, boolean requirePasswordChange ) throws IOException,
InvalidArgumentsException
{
assertValidName( username );
assertValidUsername( username );

passwordPolicy.validatePassword( initialPassword );

User user = new User.Builder()
.withName( username )
.withCredentials( Credential.forPassword( initialPassword ) )
.withRequiredPasswordChange( requirePasswordChange )
.build();
users.create( user );
userRepository.create( user );

return user;
}

@Override
public boolean deleteUser( String username ) throws IOException
public boolean deleteUser( String username ) throws IOException, InvalidArgumentsException
{
User user = users.getUserByName( username );
return user != null && users.delete( user );
User user = getUser( username );
return user != null && userRepository.delete( user );
}

@Override
public User getUser( String username ) throws InvalidArgumentsException
{
User user = users.getUserByName( username );
User user = userRepository.getUserByName( username );
if ( user == null )
{
throw new InvalidArgumentsException( "User '" + username + "' does not exist!" );
throw new InvalidArgumentsException( "User '" + username + "' does not exist." );
}
return user;
}
Expand All @@ -156,11 +161,7 @@ public void setPassword( AuthSubject authSubject, String username, String passwo
public void setUserPassword( String username, String password ) throws IOException,
InvalidArgumentsException
{
User existingUser = users.getUserByName( username );
if ( existingUser == null )
{
throw new InvalidArgumentsException( "User '" + username + "' does not exist" );
}
User existingUser = getUser( username );

passwordPolicy.validatePassword( password );

Expand All @@ -175,19 +176,31 @@ public void setUserPassword( String username, String password ) throws IOExcepti
.withCredentials( Credential.forPassword( password ) )
.withRequiredPasswordChange( false )
.build();
users.update( existingUser, updatedUser );
userRepository.update( existingUser, updatedUser );
} catch ( ConcurrentModificationException e )
{
// try again
setUserPassword( username, password );
}
}

private void assertValidName( String name ) throws InvalidArgumentsException
@Override
public Set<String> getAllUsernames()
{
if ( !users.isValidUsername( name ) )
return userRepository.getAllUsernames();
}

private void assertValidUsername( String name ) throws InvalidArgumentsException
{
if ( name.isEmpty() )
{
throw new InvalidArgumentsException( "The provided user name is empty." );
}
if ( !userRepository.isValidUsername( name ) )
{
throw new InvalidArgumentsException( "User name contains illegal characters. Please use simple ascii characters and numbers." );
throw new InvalidArgumentsException(
"User name '" + name +
"' contains illegal characters. Use simple ascii characters and numbers." );
}
}

Expand Down
Expand Up @@ -97,6 +97,11 @@ public void setPassword( String password ) throws IOException, InvalidArgumentsE
}
}

public BasicAuthManager getAuthManager()
{
return authManager;
}

public boolean doesUsernameMatch( String username )
{
return user.name().equals( username );
Expand Down Expand Up @@ -135,6 +140,6 @@ public AuthorizationViolationException onViolation( String msg )
@Override
public String name()
{
return accessMode.name();
return user.name();
}
}
Expand Up @@ -20,6 +20,7 @@
package org.neo4j.server.security.auth;

import java.io.IOException;
import java.util.Set;

import org.neo4j.kernel.api.security.exception.InvalidArgumentsException;

Expand All @@ -33,4 +34,6 @@ User newUser( String username, String initialPassword, boolean requirePasswordCh
User getUser( String username ) throws InvalidArgumentsException;

void setUserPassword( String username, String password ) throws IOException, InvalidArgumentsException;

Set<String> getAllUsernames();
}

0 comments on commit a224b4c

Please sign in to comment.