Skip to content

Commit

Permalink
Log authentication failures
Browse files Browse the repository at this point in the history
  • Loading branch information
pontusmelke committed Feb 21, 2016
1 parent d29fae5 commit 27ca8dd
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 38 deletions.
Expand Up @@ -23,6 +23,8 @@
import java.util.Map;

import org.neo4j.kernel.api.exceptions.Status;
import org.neo4j.logging.Log;
import org.neo4j.logging.LogProvider;
import org.neo4j.server.security.auth.AuthManager;

/**
Expand All @@ -32,11 +34,13 @@ public class BasicAuthentication implements Authentication
{
private final AuthManager authManager;
private final static String SCHEME = "basic";
private final Log log;


public BasicAuthentication( AuthManager authManager )
public BasicAuthentication( AuthManager authManager, LogProvider logProvider )
{
this.authManager = authManager;
this.log = logProvider.getLog( getClass() );
}

@Override
Expand Down Expand Up @@ -70,9 +74,8 @@ private void authenticate( String user, String password ) throws AuthenticationE
throw new AuthenticationException( Status.Security.CredentialsExpired );
case TOO_MANY_ATTEMPTS:
throw new AuthenticationException( Status.Security.AuthenticationRateLimit );
case FAILURE:
throw new AuthenticationException( Status.Security.AuthenticationFailed );
default:
log.warn( "Failed authentication attempt for '%s'", user);
throw new AuthenticationException( Status.Security.AuthenticationFailed );
}
}
Expand Down
Expand Up @@ -99,7 +99,7 @@ private Authentication authentication( DependencyResolver dependencyResolver )

if ( config.get( GraphDatabaseSettings.auth_enabled ) )
{
return new BasicAuthentication( dependencyResolver.resolveDependency( AuthManager.class ) );
return new BasicAuthentication( dependencyResolver.resolveDependency( AuthManager.class ), logging.getUserLogProvider() );
}
else
{
Expand Down
Expand Up @@ -27,12 +27,15 @@
import org.junit.rules.ExpectedException;

import org.neo4j.kernel.api.exceptions.Status;
import org.neo4j.logging.Log;
import org.neo4j.logging.LogProvider;
import org.neo4j.server.security.auth.AuthManager;
import org.neo4j.server.security.auth.AuthenticationResult;

import static java.util.Collections.singletonList;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.neo4j.helpers.collection.MapUtil.map;

Expand All @@ -46,21 +49,24 @@ public void shouldNotDoAnythingOnSuccess() throws AuthenticationException
{
// Given
AuthManager manager = mock( AuthManager.class );
BasicAuthentication authentication = new BasicAuthentication( manager );
BasicAuthentication authentication = new BasicAuthentication( manager, mock( LogProvider.class ) );
when( manager.authenticate( anyString(), anyString() ) ).thenReturn( AuthenticationResult.SUCCESS );

//Expect nothing

// When
authentication.authenticate( map("scheme", "basic", "principal", "bob", "credentials", "secret") );
authentication.authenticate( map( "scheme", "basic", "principal", "bob", "credentials", "secret" ) );
}

@Test
public void shouldThrowOnFailure() throws AuthenticationException
public void shouldThrowAndLogOnFailure() throws AuthenticationException
{
// Given
AuthManager manager = mock( AuthManager.class );
BasicAuthentication authentication = new BasicAuthentication( manager );
Log log = mock( Log.class );
LogProvider logProvider = mock( LogProvider.class );
when( logProvider.getLog( BasicAuthentication.class ) ).thenReturn( log );
BasicAuthentication authentication = new BasicAuthentication( manager, logProvider );
when( manager.authenticate( anyString(), anyString() ) ).thenReturn( AuthenticationResult.FAILURE );

// Expect
Expand All @@ -69,32 +75,36 @@ public void shouldThrowOnFailure() throws AuthenticationException
exception.expectMessage( "The client provided an incorrect username and/or password." );

// When
authentication.authenticate( map("scheme", "basic", "principal", "bob", "credentials", "secret") );
authentication.authenticate( map( "scheme", "basic", "principal", "bob", "credentials", "secret" ) );

//Then
verify( log ).warn( "Failed authentication attempt for 'bob')" );
}

@Test
public void shouldIndicateThatCredentialsExpired() throws AuthenticationException
{
// Given
AuthManager manager = mock( AuthManager.class );
BasicAuthentication authentication = new BasicAuthentication( manager );
when( manager.authenticate( anyString(), anyString() ) ).thenReturn( AuthenticationResult.PASSWORD_CHANGE_REQUIRED );
BasicAuthentication authentication = new BasicAuthentication( manager, mock( LogProvider.class ) );
when( manager.authenticate( anyString(), anyString() ) )
.thenReturn( AuthenticationResult.PASSWORD_CHANGE_REQUIRED );

// Expect
exception.expect( AuthenticationException.class );
exception.expect( hasStatus( Status.Security.CredentialsExpired ) );
exception.expectMessage( "The credentials have expired and needs to be updated." );

// When
authentication.authenticate( map("scheme", "basic", "principal", "bob", "credentials", "secret") );
authentication.authenticate( map( "scheme", "basic", "principal", "bob", "credentials", "secret" ) );
}

@Test
public void shouldFailWhenTooManyAttempts() throws AuthenticationException
{
// Given
AuthManager manager = mock( AuthManager.class );
BasicAuthentication authentication = new BasicAuthentication( manager );
BasicAuthentication authentication = new BasicAuthentication( manager, mock( LogProvider.class ) );
when( manager.authenticate( anyString(), anyString() ) ).thenReturn( AuthenticationResult.TOO_MANY_ATTEMPTS );

// Expect
Expand All @@ -103,45 +113,46 @@ public void shouldFailWhenTooManyAttempts() throws AuthenticationException
exception.expectMessage( "The client has provided incorrect authentication details too many times in a row." );

// When
authentication.authenticate( map("scheme", "basic", "principal", "bob", "credentials", "secret") );
authentication.authenticate( map( "scheme", "basic", "principal", "bob", "credentials", "secret" ) );
}

@Test
public void shouldBeAbleToUpdateCredentials() throws AuthenticationException
{
// Given
AuthManager manager = mock( AuthManager.class );
BasicAuthentication authentication = new BasicAuthentication( manager );
BasicAuthentication authentication = new BasicAuthentication( manager, mock( LogProvider.class ) );
when( manager.authenticate( anyString(), anyString() ) ).thenReturn( AuthenticationResult.SUCCESS );

//Expect nothing

// When
authentication.authenticate( map("scheme", "basic", "principal", "bob", "credentials", "secret",
"new-credentials", "secret2") );
authentication.authenticate( map( "scheme", "basic", "principal", "bob", "credentials", "secret",
"new-credentials", "secret2" ) );
}

@Test
public void shouldBeAbleToUpdateExpiredCredentials() throws AuthenticationException
{
// Given
AuthManager manager = mock( AuthManager.class );
BasicAuthentication authentication = new BasicAuthentication( manager );
when( manager.authenticate( anyString(), anyString() ) ).thenReturn( AuthenticationResult.PASSWORD_CHANGE_REQUIRED );
BasicAuthentication authentication = new BasicAuthentication( manager, mock( LogProvider.class ) );
when( manager.authenticate( anyString(), anyString() ) )
.thenReturn( AuthenticationResult.PASSWORD_CHANGE_REQUIRED );

//Expect nothing

// When
authentication.authenticate( map("scheme", "basic", "principal", "bob", "credentials", "secret",
"new-credentials", "secret2") );
authentication.authenticate( map( "scheme", "basic", "principal", "bob", "credentials", "secret",
"new-credentials", "secret2" ) );
}

@Test
public void shouldNotBeAbleToUpdateCredentialsIfOldCredentialsAreInvalid() throws AuthenticationException
{
// Given
AuthManager manager = mock( AuthManager.class );
BasicAuthentication authentication = new BasicAuthentication( manager );
BasicAuthentication authentication = new BasicAuthentication( manager, mock( LogProvider.class ) );
when( manager.authenticate( anyString(), anyString() ) ).thenReturn( AuthenticationResult.FAILURE );

// Expect
Expand All @@ -151,16 +162,16 @@ public void shouldNotBeAbleToUpdateCredentialsIfOldCredentialsAreInvalid() throw

// When
// When
authentication.authenticate( map("scheme", "basic", "principal", "bob", "credentials", "secret",
"new-credentials", "secret2") );
authentication.authenticate( map( "scheme", "basic", "principal", "bob", "credentials", "secret",
"new-credentials", "secret2" ) );
}

@Test
public void shouldFailOnUnknownScheme() throws AuthenticationException
{
// Given
AuthManager manager = mock( AuthManager.class );
BasicAuthentication authentication = new BasicAuthentication( manager );
BasicAuthentication authentication = new BasicAuthentication( manager, mock( LogProvider.class ) );
when( manager.authenticate( anyString(), anyString() ) ).thenReturn( AuthenticationResult.SUCCESS );

// Expect
Expand All @@ -169,27 +180,29 @@ public void shouldFailOnUnknownScheme() throws AuthenticationException
exception.expectMessage( "Authorization token must contain: 'scheme : basic'" );

// When
authentication.authenticate( map("scheme", "UNKNOWN", "principal", "bob", "credentials", "secret") );
authentication.authenticate( map( "scheme", "UNKNOWN", "principal", "bob", "credentials", "secret" ) );
}

@Test
public void shouldFailOnMalformedToken() throws AuthenticationException
{
// Given
AuthManager manager = mock( AuthManager.class );
BasicAuthentication authentication = new BasicAuthentication( manager );
BasicAuthentication authentication = new BasicAuthentication( manager, mock( LogProvider.class ) );
when( manager.authenticate( anyString(), anyString() ) ).thenReturn( AuthenticationResult.SUCCESS );

// Expect
exception.expect( AuthenticationException.class );
exception.expect( hasStatus( Status.Security.AuthenticationFailed ) );
exception.expectMessage( "The value associated with the key `principal` must be a String but was: SingletonList" );
exception.expectMessage(
"The value associated with the key `principal` must be a String but was: SingletonList" );

// When
authentication.authenticate( map("scheme", "basic", "principal", singletonList( "bob" ), "credentials", "secret") );
authentication
.authenticate( map( "scheme", "basic", "principal", singletonList( "bob" ), "credentials", "secret" ) );
}

private HasStatus hasStatus(Status status)
private HasStatus hasStatus( Status status )
{
return new HasStatus( status );
}
Expand All @@ -198,25 +211,29 @@ static class HasStatus extends TypeSafeMatcher<Status.HasStatus>
{
private Status status;

public HasStatus(Status status) {
public HasStatus( Status status )
{
this.status = status;
}

@Override
protected boolean matchesSafely(Status.HasStatus item) {
protected boolean matchesSafely( Status.HasStatus item )
{
return item.status() == status;
}

@Override
public void describeTo(Description description) {
description.appendText("expects status ")
.appendValue(status);
public void describeTo( Description description )
{
description.appendText( "expects status " )
.appendValue( status );
}

@Override
protected void describeMismatchSafely(Status.HasStatus item, Description mismatchDescription) {
mismatchDescription.appendText("was ")
.appendValue(item.status());
protected void describeMismatchSafely( Status.HasStatus item, Description mismatchDescription )
{
mismatchDescription.appendText( "was " )
.appendValue( item.status() );
}
}

Expand Down

0 comments on commit 27ca8dd

Please sign in to comment.