Skip to content

Commit

Permalink
Changed how authmanager is loaded
Browse files Browse the repository at this point in the history
In embedded scenarios `auth_enabled` will default to `false` and load a no op
authentication manager. In server scenarios `auth_enabled` will default to
`true`.
  • Loading branch information
pontusmelke committed Feb 25, 2016
1 parent 201ca50 commit 0c9597f
Show file tree
Hide file tree
Showing 16 changed files with 311 additions and 177 deletions.
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
import org.neo4j.kernel.lifecycle.Lifecycle; import org.neo4j.kernel.lifecycle.Lifecycle;
import org.neo4j.kernel.monitoring.Monitors; import org.neo4j.kernel.monitoring.Monitors;
import org.neo4j.logging.Log; import org.neo4j.logging.Log;
import org.neo4j.server.security.auth.AuthManager; import org.neo4j.server.security.auth.BasicAuthManager;
import org.neo4j.udc.UsageData; import org.neo4j.udc.UsageData;


import static org.neo4j.bolt.BoltKernelExtension.EncryptionLevel.OPTIONAL; import static org.neo4j.bolt.BoltKernelExtension.EncryptionLevel.OPTIONAL;
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -25,19 +25,19 @@
import org.neo4j.kernel.api.exceptions.Status; import org.neo4j.kernel.api.exceptions.Status;
import org.neo4j.logging.Log; import org.neo4j.logging.Log;
import org.neo4j.logging.LogProvider; import org.neo4j.logging.LogProvider;
import org.neo4j.server.security.auth.AuthManager; import org.neo4j.server.security.auth.BasicAuthManager;


/** /**
* Performs basic authentication with user name and password. * Performs basic authentication with user name and password.
*/ */
public class BasicAuthentication implements Authentication public class BasicAuthentication implements Authentication
{ {
private final AuthManager authManager; private final BasicAuthManager authManager;
private final static String SCHEME = "basic"; private final static String SCHEME = "basic";
private final Log log; private final Log log;




public BasicAuthentication( AuthManager authManager, LogProvider logProvider ) public BasicAuthentication( BasicAuthManager authManager, LogProvider logProvider )
{ {
this.authManager = authManager; this.authManager = authManager;
this.log = logProvider.getLog( getClass() ); this.log = logProvider.getLog( getClass() );
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
import org.neo4j.kernel.impl.query.QueryExecutionEngine; import org.neo4j.kernel.impl.query.QueryExecutionEngine;
import org.neo4j.kernel.lifecycle.LifeSupport; import org.neo4j.kernel.lifecycle.LifeSupport;
import org.neo4j.kernel.lifecycle.LifecycleAdapter; import org.neo4j.kernel.lifecycle.LifecycleAdapter;
import org.neo4j.server.security.auth.AuthManager; import org.neo4j.server.security.auth.BasicAuthManager;
import org.neo4j.udc.UsageData; import org.neo4j.udc.UsageData;


/** /**
Expand Down Expand Up @@ -102,7 +102,7 @@ private Authentication authentication( DependencyResolver dependencyResolver )


if ( config.get( GraphDatabaseSettings.auth_enabled ) ) if ( config.get( GraphDatabaseSettings.auth_enabled ) )
{ {
return new BasicAuthentication( dependencyResolver.resolveDependency( AuthManager.class ), logging.getUserLogProvider() ); return new BasicAuthentication( dependencyResolver.resolveDependency( BasicAuthManager.class ), logging.getUserLogProvider() );
} }
else else
{ {
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
import org.neo4j.kernel.api.exceptions.Status; import org.neo4j.kernel.api.exceptions.Status;
import org.neo4j.logging.Log; import org.neo4j.logging.Log;
import org.neo4j.logging.LogProvider; import org.neo4j.logging.LogProvider;
import org.neo4j.server.security.auth.AuthManager;
import org.neo4j.server.security.auth.AuthenticationResult; import org.neo4j.server.security.auth.AuthenticationResult;
import org.neo4j.server.security.auth.BasicAuthManager;


import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.anyString;
Expand All @@ -48,7 +48,7 @@ public class BasicAuthenticationTest
public void shouldNotDoAnythingOnSuccess() throws AuthenticationException public void shouldNotDoAnythingOnSuccess() throws AuthenticationException
{ {
// Given // Given
AuthManager manager = mock( AuthManager.class ); BasicAuthManager manager = mock( BasicAuthManager.class );
BasicAuthentication authentication = new BasicAuthentication( manager, mock( LogProvider.class ) ); BasicAuthentication authentication = new BasicAuthentication( manager, mock( LogProvider.class ) );
when( manager.authenticate( anyString(), anyString() ) ).thenReturn( AuthenticationResult.SUCCESS ); when( manager.authenticate( anyString(), anyString() ) ).thenReturn( AuthenticationResult.SUCCESS );


Expand All @@ -62,7 +62,7 @@ public void shouldNotDoAnythingOnSuccess() throws AuthenticationException
public void shouldThrowAndLogOnFailure() throws AuthenticationException public void shouldThrowAndLogOnFailure() throws AuthenticationException
{ {
// Given // Given
AuthManager manager = mock( AuthManager.class ); BasicAuthManager manager = mock( BasicAuthManager.class );
Log log = mock( Log.class ); Log log = mock( Log.class );
LogProvider logProvider = mock( LogProvider.class ); LogProvider logProvider = mock( LogProvider.class );
when( logProvider.getLog( BasicAuthentication.class ) ).thenReturn( log ); when( logProvider.getLog( BasicAuthentication.class ) ).thenReturn( log );
Expand All @@ -85,7 +85,7 @@ public void shouldThrowAndLogOnFailure() throws AuthenticationException
public void shouldIndicateThatCredentialsExpired() throws AuthenticationException public void shouldIndicateThatCredentialsExpired() throws AuthenticationException
{ {
// Given // Given
AuthManager manager = mock( AuthManager.class ); BasicAuthManager manager = mock( BasicAuthManager.class );
BasicAuthentication authentication = new BasicAuthentication( manager, mock( LogProvider.class ) ); BasicAuthentication authentication = new BasicAuthentication( manager, mock( LogProvider.class ) );
when( manager.authenticate( anyString(), anyString() ) ) when( manager.authenticate( anyString(), anyString() ) )
.thenReturn( AuthenticationResult.PASSWORD_CHANGE_REQUIRED ); .thenReturn( AuthenticationResult.PASSWORD_CHANGE_REQUIRED );
Expand All @@ -103,7 +103,7 @@ public void shouldIndicateThatCredentialsExpired() throws AuthenticationExceptio
public void shouldFailWhenTooManyAttempts() throws AuthenticationException public void shouldFailWhenTooManyAttempts() throws AuthenticationException
{ {
// Given // Given
AuthManager manager = mock( AuthManager.class ); BasicAuthManager manager = mock( BasicAuthManager.class );
BasicAuthentication authentication = new BasicAuthentication( manager, mock( LogProvider.class ) ); BasicAuthentication authentication = new BasicAuthentication( manager, mock( LogProvider.class ) );
when( manager.authenticate( anyString(), anyString() ) ).thenReturn( AuthenticationResult.TOO_MANY_ATTEMPTS ); when( manager.authenticate( anyString(), anyString() ) ).thenReturn( AuthenticationResult.TOO_MANY_ATTEMPTS );


Expand All @@ -120,7 +120,7 @@ public void shouldFailWhenTooManyAttempts() throws AuthenticationException
public void shouldBeAbleToUpdateCredentials() throws AuthenticationException public void shouldBeAbleToUpdateCredentials() throws AuthenticationException
{ {
// Given // Given
AuthManager manager = mock( AuthManager.class ); BasicAuthManager manager = mock( BasicAuthManager.class );
BasicAuthentication authentication = new BasicAuthentication( manager, mock( LogProvider.class ) ); BasicAuthentication authentication = new BasicAuthentication( manager, mock( LogProvider.class ) );
when( manager.authenticate( anyString(), anyString() ) ).thenReturn( AuthenticationResult.SUCCESS ); when( manager.authenticate( anyString(), anyString() ) ).thenReturn( AuthenticationResult.SUCCESS );


Expand All @@ -135,7 +135,7 @@ public void shouldBeAbleToUpdateCredentials() throws AuthenticationException
public void shouldBeAbleToUpdateExpiredCredentials() throws AuthenticationException public void shouldBeAbleToUpdateExpiredCredentials() throws AuthenticationException
{ {
// Given // Given
AuthManager manager = mock( AuthManager.class ); BasicAuthManager manager = mock( BasicAuthManager.class );
BasicAuthentication authentication = new BasicAuthentication( manager, mock( LogProvider.class ) ); BasicAuthentication authentication = new BasicAuthentication( manager, mock( LogProvider.class ) );
when( manager.authenticate( anyString(), anyString() ) ) when( manager.authenticate( anyString(), anyString() ) )
.thenReturn( AuthenticationResult.PASSWORD_CHANGE_REQUIRED ); .thenReturn( AuthenticationResult.PASSWORD_CHANGE_REQUIRED );
Expand All @@ -151,7 +151,7 @@ public void shouldBeAbleToUpdateExpiredCredentials() throws AuthenticationExcept
public void shouldNotBeAbleToUpdateCredentialsIfOldCredentialsAreInvalid() throws AuthenticationException public void shouldNotBeAbleToUpdateCredentialsIfOldCredentialsAreInvalid() throws AuthenticationException
{ {
// Given // Given
AuthManager manager = mock( AuthManager.class ); BasicAuthManager manager = mock( BasicAuthManager.class );
BasicAuthentication authentication = new BasicAuthentication( manager, mock( LogProvider.class ) ); BasicAuthentication authentication = new BasicAuthentication( manager, mock( LogProvider.class ) );
when( manager.authenticate( anyString(), anyString() ) ).thenReturn( AuthenticationResult.FAILURE ); when( manager.authenticate( anyString(), anyString() ) ).thenReturn( AuthenticationResult.FAILURE );


Expand All @@ -170,7 +170,7 @@ public void shouldNotBeAbleToUpdateCredentialsIfOldCredentialsAreInvalid() throw
public void shouldFailOnUnknownScheme() throws AuthenticationException public void shouldFailOnUnknownScheme() throws AuthenticationException
{ {
// Given // Given
AuthManager manager = mock( AuthManager.class ); BasicAuthManager manager = mock( BasicAuthManager.class );
BasicAuthentication authentication = new BasicAuthentication( manager, mock( LogProvider.class ) ); BasicAuthentication authentication = new BasicAuthentication( manager, mock( LogProvider.class ) );
when( manager.authenticate( anyString(), anyString() ) ).thenReturn( AuthenticationResult.SUCCESS ); when( manager.authenticate( anyString(), anyString() ) ).thenReturn( AuthenticationResult.SUCCESS );


Expand All @@ -187,7 +187,7 @@ public void shouldFailOnUnknownScheme() throws AuthenticationException
public void shouldFailOnMalformedToken() throws AuthenticationException public void shouldFailOnMalformedToken() throws AuthenticationException
{ {
// Given // Given
AuthManager manager = mock( AuthManager.class ); BasicAuthManager manager = mock( BasicAuthManager.class );
BasicAuthentication authentication = new BasicAuthentication( manager, mock( LogProvider.class ) ); BasicAuthentication authentication = new BasicAuthentication( manager, mock( LogProvider.class ) );
when( manager.authenticate( anyString(), anyString() ) ).thenReturn( AuthenticationResult.SUCCESS ); when( manager.authenticate( anyString(), anyString() ) ).thenReturn( AuthenticationResult.SUCCESS );


Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ private static String defaultPageCacheMemory()
"10000" ); "10000" );


@Description("Enable auth requirement to access Neo4j.") @Description("Enable auth requirement to access Neo4j.")
public static final Setting<Boolean> auth_enabled = setting( "dbms.security.auth_enabled", BOOLEAN, "true" ); public static final Setting<Boolean> auth_enabled = setting( "dbms.security.auth_enabled", BOOLEAN, "false" );


@Internal @Internal
public static final Setting<File> auth_store = setting("dbms.security.auth_store.location", PATH, "data/dbms/auth"); public static final Setting<File> auth_store = setting("dbms.security.auth_store.location", PATH, "data/dbms/auth");
Expand Down
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import org.neo4j.kernel.lifecycle.LifeSupport; import org.neo4j.kernel.lifecycle.LifeSupport;
import org.neo4j.logging.LogProvider; import org.neo4j.logging.LogProvider;
import org.neo4j.server.security.auth.AuthManager; import org.neo4j.server.security.auth.AuthManager;
import org.neo4j.server.security.auth.BasicAuthManager;
import org.neo4j.server.security.auth.FileUserRepository; import org.neo4j.server.security.auth.FileUserRepository;
import org.neo4j.udc.UsageData; import org.neo4j.udc.UsageData;
import org.neo4j.udc.UsageDataKeys; import org.neo4j.udc.UsageDataKeys;
Expand Down Expand Up @@ -95,8 +96,16 @@ protected void publishEditionInfo( UsageData sysInfo, DatabaseInfo databaseInfo,
protected AuthManager createAuthManager(Config config, LifeSupport life, LogProvider logProvider) protected AuthManager createAuthManager(Config config, LifeSupport life, LogProvider logProvider)
{ {


FileUserRepository users = life.add( new FileUserRepository( config.get( GraphDatabaseSettings.auth_store ).toPath(), logProvider ) ); boolean authEnabled = config.get( GraphDatabaseSettings.auth_enabled );
if ( authEnabled)
{
FileUserRepository users = life.add( new FileUserRepository( config.get( GraphDatabaseSettings.auth_store ).toPath(), logProvider ) );
return life.add(new BasicAuthManager( users, systemUTC(), true ));
}
else
{
return AuthManager.NO_AUTH;
}


return life.add(new AuthManager( users, systemUTC(), config.get( GraphDatabaseSettings.auth_enabled )));
} }
} }
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -20,137 +20,99 @@
package org.neo4j.server.security.auth; package org.neo4j.server.security.auth;


import java.io.IOException; import java.io.IOException;
import java.time.Clock;


import org.neo4j.kernel.lifecycle.LifecycleAdapter;
import org.neo4j.server.security.auth.exception.ConcurrentModificationException;
import org.neo4j.server.security.auth.exception.IllegalUsernameException; import org.neo4j.server.security.auth.exception.IllegalUsernameException;


/** /**
* Manages server authentication and authorization. * An AuthManager is used to do basic authentication and user management.
* <p>
* Through the AuthManager you can create, update and delete users, and authenticate using credentials.
*/ */
public class AuthManager extends LifecycleAdapter public interface AuthManager
{ {
private final AuthenticationStrategy authStrategy;
private final UserRepository users;
private final boolean authEnabled;


public AuthManager( UserRepository users, AuthenticationStrategy authStrategy, boolean authEnabled ) /**
* Authenticate a username and password
* @param username The name of the user
* @param password The password of the user
*/
AuthenticationResult authenticate( String username, String password );

/**
* Create a new user with the provided credentials.
* @param username The name of the user.
* @param initialPassword The initial password.
* @param requirePasswordChange Does the user need to change the initial password.
* @return A new user with the provided credentials.
* @throws IOException If user can't be serialized to disk.
* @throws IllegalUsernameException If the username is invalid.
*/
User newUser( String username, String initialPassword, boolean requirePasswordChange ) throws IOException,
IllegalUsernameException;

/**
* Delete the given user
* @param username the name of the user to delete.
* @return <tt>true</tt> is user was deleted otherwise <tt>false</tt>
* @throws IOException
*/
boolean deleteUser( String username ) throws IOException;

/**
* Retrieves the user with the provided user name.
* @param username The name of the user to retrieve.
* @return The stored user with the given user name.
*/
User getUser( String username );

/**
* Set the password of the provided user.
* @param username The name of the user whose password should be set.
* @param password The new password for the user.
* @return User with updated credentials
* @throws IOException
*/
User setPassword( String username, String password ) throws IOException;

/**
* Implementation that does no authentication.
*/
AuthManager NO_AUTH = new AuthManager()
{ {
this.users = users; @Override
this.authStrategy = authStrategy; public AuthenticationResult authenticate( String username, String password )
this.authEnabled = authEnabled;
}

public AuthManager( UserRepository users, AuthenticationStrategy authStrategy )
{
this( users, authStrategy, true );
}

public AuthManager( UserRepository users, Clock clock, boolean authEnabled )
{
this( users, new RateLimitedAuthenticationStrategy( clock, 3 ), authEnabled );
}

@Override
public void start() throws Throwable
{
if ( authEnabled && users.numberOfUsers() == 0 )
{ {
newUser( "neo4j", "neo4j", true ); return AuthenticationResult.SUCCESS;
} }
}


public AuthenticationResult authenticate( String username, String password ) @Override
{ public User newUser( String username, String initialPassword, boolean requirePasswordChange )
assertAuthEnabled(); throws IOException, IllegalUsernameException
User user = users.findByName( username );
if ( user == null )
{ {
return AuthenticationResult.FAILURE; return new User.Builder( )
} .withName( username )
AuthenticationResult result = authStrategy.authenticate( user, password ); .withCredentials( Credential.forPassword( initialPassword ) )
if ( result != AuthenticationResult.SUCCESS ) .withRequiredPasswordChange( requirePasswordChange )
{ .build();
return result;
}
if ( user.passwordChangeRequired() )
{
return AuthenticationResult.PASSWORD_CHANGE_REQUIRED;
} }
return AuthenticationResult.SUCCESS;
}

public User newUser( String username, String initialPassword, boolean requirePasswordChange ) throws IOException, IllegalUsernameException
{
assertAuthEnabled();
assertValidName( username );
User user = new User.Builder()
.withName( username )
.withCredentials( Credential.forPassword( initialPassword ) )
.withRequiredPasswordChange( requirePasswordChange )
.build();
users.create( user );
return user;
}


public boolean deleteUser( String username ) throws IOException @Override
{ public boolean deleteUser( String username ) throws IOException
assertAuthEnabled();
User user = users.findByName( username );
return user != null && users.delete( user );
}

public User getUser( String username )
{
assertAuthEnabled();
return users.findByName( username );
}

public User setPassword( String username, String password ) throws IOException
{
assertAuthEnabled();
User existingUser = users.findByName( username );
if ( existingUser == null )
{ {
return null; return true;
} }


if ( existingUser.credentials().matchesPassword( password ) ) @Override
public User getUser( String username )
{ {
return existingUser; return new User.Builder().withName( username ).build();
} }


try @Override
public User setPassword( String username, String password ) throws IOException
{ {
User updatedUser = existingUser.augment() return new User.Builder( )
.withName( username )
.withCredentials( Credential.forPassword( password ) ) .withCredentials( Credential.forPassword( password ) )
.withRequiredPasswordChange( false )
.build(); .build();
users.update( existingUser, updatedUser );
return updatedUser;
} catch ( ConcurrentModificationException e )
{
// try again
return setPassword( username, password );
}
}

private void assertAuthEnabled()
{
if ( !authEnabled )
{
throw new IllegalStateException( "Auth not enabled" );
}
}

private void assertValidName( String name )
{
if ( !users.isValidName( name ) )
{
throw new IllegalArgumentException( "User name contains illegal characters. Please use simple ascii characters and numbers." );
} }
} };
} }
Loading

0 comments on commit 0c9597f

Please sign in to comment.