Skip to content

Commit

Permalink
Exposed setting to control whether successful authentications are log…
Browse files Browse the repository at this point in the history
…ged or not
  • Loading branch information
fickludd committed Sep 12, 2016
1 parent 756df3e commit 0c8bddc
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 12 deletions.
Expand Up @@ -22,14 +22,18 @@
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.ForkJoinPool;
import java.util.function.Consumer;

import org.neo4j.concurrent.AsyncEventSender;
import org.neo4j.io.fs.FileSystemAbstraction;
import org.neo4j.kernel.api.security.AuthSubject;
import org.neo4j.kernel.configuration.Config;
import org.neo4j.logging.FormattedLog;
import org.neo4j.logging.Log;
import org.neo4j.logging.Logger;
import org.neo4j.logging.async.AsyncLog;
import org.neo4j.logging.async.AsyncLogEvent;

import static org.neo4j.helpers.Strings.escape;
import static org.neo4j.io.file.Files.createOrOpenAsOuputStream;
Expand All @@ -49,7 +53,7 @@ public SecurityLog( Log log )
inner = log;
}

private static Log createLog( Config config, FileSystemAbstraction fileSystem )
private static AsyncLog createLog( Config config, FileSystemAbstraction fileSystem )
{
FormattedLog.Builder builder = FormattedLog.withUTCTimeZone();
File logFile = config.get( security_log_filename );
Expand All @@ -62,7 +66,14 @@ private static Log createLog( Config config, FileSystemAbstraction fileSystem )
{
throw new AssertionError( "File not possible to create", e );
}
return builder.toOutputStream( ouputStream );
return new AsyncLog( new AsyncEventSender<AsyncLogEvent>()
{
@Override
public void send( AsyncLogEvent event )
{
ForkJoinPool.commonPool().execute( event::process );
}
}, builder.toOutputStream( ouputStream ) );
}

private static String withSubject( AuthSubject subject, String msg )
Expand Down
Expand Up @@ -27,6 +27,7 @@
import org.neo4j.graphdb.factory.GraphDatabaseSettings;
import org.neo4j.kernel.impl.store.id.IdType;

import static org.neo4j.kernel.configuration.Settings.BOOLEAN;
import static org.neo4j.kernel.configuration.Settings.PATH;
import static org.neo4j.kernel.configuration.Settings.derivedSetting;
import static org.neo4j.kernel.configuration.Settings.list;
Expand All @@ -52,4 +53,8 @@ public class EnterpriseEditionSettings
( logs ) -> new File( logs, "security.log" ),
PATH );

@Description( "Set to log successful authentication events." )
public static final Setting<Boolean> security_log_successful_authentication =
setting("dbms.security.log_successful_authentication", BOOLEAN, "true" );

}
Expand Up @@ -34,6 +34,7 @@
import org.neo4j.kernel.configuration.Config;
import org.neo4j.kernel.impl.util.JobScheduler;
import org.neo4j.kernel.impl.enterprise.SecurityLog;
import org.neo4j.kernel.impl.enterprise.configuration.EnterpriseEditionSettings;
import org.neo4j.logging.Log;
import org.neo4j.logging.LogProvider;
import org.neo4j.server.security.auth.AuthenticationStrategy;
Expand Down Expand Up @@ -68,8 +69,7 @@ public AuthManager newInstance( Config config, LogProvider logProvider, Log secu
SecurityLog castedSecurityLog = (SecurityLog) securityLog;

// We always create the internal realm as it is our only UserManager implementation
InternalFlatFileRealm internalRealm = createInternalRealm( config, logProvider, fileSystem,
jobScheduler, castedSecurityLog );
InternalFlatFileRealm internalRealm = createInternalRealm( config, logProvider, fileSystem, jobScheduler );

if ( config.get( SecuritySettings.internal_authentication_enabled ) ||
config.get( SecuritySettings.internal_authorization_enabled ) )
Expand All @@ -94,11 +94,11 @@ public AuthManager newInstance( Config config, LogProvider logProvider, Log secu

return new MultiRealmAuthManager( internalRealm, realms,
new ShiroCaffeineCache.Manager( Ticker.systemTicker(), ttl, maxCapacity ),
castedSecurityLog );
castedSecurityLog, config.get( EnterpriseEditionSettings.security_log_successful_authentication ) );
}

private static InternalFlatFileRealm createInternalRealm( Config config, LogProvider logProvider,
FileSystemAbstraction fileSystem, JobScheduler jobScheduler, Log securityLog )
FileSystemAbstraction fileSystem, JobScheduler jobScheduler )
{
// Resolve auth store and roles file names
File authStoreDir = config.get( DatabaseManagementSystemSettings.auth_store_directory );
Expand Down
Expand Up @@ -50,16 +50,18 @@ class MultiRealmAuthManager implements EnterpriseAuthManager, UserManagerSupplie
private final DefaultSecurityManager securityManager;
private final CacheManager cacheManager;
private final SecurityLog securityLog;
private final boolean logSuccessfulLogin;

MultiRealmAuthManager( EnterpriseUserManager userManager, Collection<Realm> realms, CacheManager cacheManager,
SecurityLog securityLog )
SecurityLog securityLog, boolean logSuccessfulLogin )
{
this.userManager = userManager;
this.realms = realms;
this.cacheManager = cacheManager;

securityManager = new DefaultSecurityManager( realms );
this.securityLog = securityLog;
this.logSuccessfulLogin = logSuccessfulLogin;
securityManager.setSubjectFactory( new ShiroSubjectFactory() );
((ModularRealmAuthenticator) securityManager.getAuthenticator())
.setAuthenticationStrategy( new ShiroAuthenticationStrategy() );
Expand Down Expand Up @@ -93,7 +95,10 @@ public EnterpriseAuthSubject login( Map<String,Object> authToken ) throws Invali
try
{
subject = new StandardEnterpriseAuthSubject( this, (ShiroSubject) securityManager.login( null, token ) );
securityLog.info( subject, "logged in" );
if ( logSuccessfulLogin )
{
securityLog.info( subject, "logged in" );
}
}
catch ( UnsupportedTokenException e )
{
Expand Down
Expand Up @@ -61,6 +61,8 @@
public class MultiRealmAuthManagerTest
{
private InMemoryUserRepository users;
private InMemoryRoleRepository roles;
private PasswordPolicy passwordPolicy;
private AuthenticationStrategy authStrategy;
private MultiRealmAuthManager manager;
private EnterpriseUserManager userManager;
Expand All @@ -70,19 +72,29 @@ public class MultiRealmAuthManagerTest
public void setUp() throws Throwable
{
users = new InMemoryUserRepository();
roles = new InMemoryRoleRepository();
passwordPolicy = mock( PasswordPolicy.class );
authStrategy = mock( AuthenticationStrategy.class );
logProvider = new AssertableLogProvider();

manager = createAuthManager( roles, passwordPolicy, true );
userManager = manager.getUserManager();
}

private MultiRealmAuthManager createAuthManager( InMemoryRoleRepository roles, PasswordPolicy passwordPolicy, boolean
logSuccessfulAuthentications ) throws Throwable
{
Log log = logProvider.getLog( this.getClass() );

InternalFlatFileRealm internalFlatFileRealm =
new InternalFlatFileRealm( users, new InMemoryRoleRepository(), mock( PasswordPolicy.class ),
authStrategy, mock( JobScheduler.class ) );

manager = new MultiRealmAuthManager( internalFlatFileRealm, Collections.singleton( internalFlatFileRealm ),
new MemoryConstrainedCacheManager(), new SecurityLog( log ) );
new MemoryConstrainedCacheManager(), new SecurityLog( log ), logSuccessfulAuthentications );

manager.init();
userManager = manager.getUserManager();
return manager;
}

@After
Expand Down Expand Up @@ -123,6 +135,25 @@ public void shouldFindAndAuthenticateUserSuccessfully() throws Throwable
logProvider.assertExactly( info( "[jake]: logged in" ) );
}

@Test
public void shouldNotLogAuthenticationIfFlagSaysNo() throws Throwable
{
// Given
manager.shutdown();
manager = createAuthManager( roles, passwordPolicy, false );

users.create( newUser( "jake", "abc123" , false ) );
manager.start();
setMockAuthenticationStrategyResult( "jake", "abc123", AuthenticationResult.SUCCESS );

// When
AuthenticationResult result = manager.login( authToken( "jake", "abc123" ) ).getAuthenticationResult();

// Then
assertThat( result, equalTo( AuthenticationResult.SUCCESS ) );
logProvider.assertNone( info( "[jake]: logged in" ) );
}

@Test
public void shouldReturnTooManyAttemptsWhenThatIsAppropriate() throws Throwable
{
Expand Down Expand Up @@ -591,12 +622,12 @@ private AssertableLogProvider.LogMatcher info( String message )

private AssertableLogProvider.LogMatcher info( String message, String... arguments )
{
return inLog( this.getClass() ).info( message, arguments );
return inLog( this.getClass() ).info( message, (Object[]) arguments );
}

private AssertableLogProvider.LogMatcher error( String message, String... arguments )
{
return inLog( this.getClass() ).error( message, arguments );
return inLog( this.getClass() ).error( message, (Object[]) arguments );
}

private User newUser( String userName, String password, boolean pwdChange )
Expand Down

0 comments on commit 0c8bddc

Please sign in to comment.