Skip to content

Commit

Permalink
Fail on missing configured auth plugin
Browse files Browse the repository at this point in the history
 - if plugin authn/authr enabled but no respective auth
 plugin is found during service loading. The server will
 fail to start with an appropriate error message.
  • Loading branch information
fickludd committed Nov 1, 2016
1 parent ad8d780 commit a8d2e2d
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 41 deletions.
Expand Up @@ -177,7 +177,7 @@ public DependencySatisfier dependencySatisfier()
} ); } );
return; return;
} }
catch ( KernelException e ) catch ( Exception e )
{ {
String errorMessage = "Failed to load security module."; String errorMessage = "Failed to load security module.";
log.error( errorMessage ); log.error( errorMessage );
Expand Down
Expand Up @@ -28,6 +28,7 @@
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;


import org.neo4j.commandline.admin.security.SetDefaultAdminCommand; import org.neo4j.commandline.admin.security.SetDefaultAdminCommand;
import org.neo4j.dbms.DatabaseManagementSystemSettings; import org.neo4j.dbms.DatabaseManagementSystemSettings;
Expand Down Expand Up @@ -58,6 +59,7 @@
import org.neo4j.server.security.enterprise.log.SecurityLog; import org.neo4j.server.security.enterprise.log.SecurityLog;
import org.neo4j.time.Clocks; import org.neo4j.time.Clocks;


import static java.lang.String.format;
import static org.neo4j.kernel.api.proc.Context.SECURITY_CONTEXT; import static org.neo4j.kernel.api.proc.Context.SECURITY_CONTEXT;


@Service.Implementation( SecurityModule.class ) @Service.Implementation( SecurityModule.class )
Expand Down Expand Up @@ -147,16 +149,18 @@ public EnterpriseAuthAndUserManager newAuthManager( Config config, LogProvider l
realms.add( new LdapRealm( config, securityLog, secureHasher ) ); realms.add( new LdapRealm( config, securityLog, secureHasher ) );
} }


// Load plugin realms if we have any // Load plugin realms if a plugin provider in configured
realms.addAll( createPluginRealms( config, securityLog, secureHasher ) ); if ( configuredRealms.stream().anyMatch( ( r ) -> r.startsWith( SecuritySettings.PLUGIN_REALM_NAME_PREFIX ) ) )
{
realms.addAll( createPluginRealms( config, securityLog, secureHasher, configuredRealms ) );
}


// Select the active realms in the order they are configured // Select the active realms in the order they are configured
List<Realm> orderedActiveRealms = selectOrderedActiveRealms( configuredRealms, realms ); List<Realm> orderedActiveRealms = selectOrderedActiveRealms( configuredRealms, realms );


if ( orderedActiveRealms.isEmpty() ) if ( orderedActiveRealms.isEmpty() )
{ {
String message = "Illegal configuration: No valid security realm is active."; throw new IllegalArgumentException( "Illegal configuration: No valid auth provider is active." );
throw new IllegalArgumentException( message );
} }


return new MultiRealmAuthManager( internalRealm, orderedActiveRealms, createCacheManager( config ), return new MultiRealmAuthManager( internalRealm, orderedActiveRealms, createCacheManager( config ),
Expand Down Expand Up @@ -203,9 +207,10 @@ private static CacheManager createCacheManager( Config config )
return new ShiroCaffeineCache.Manager( Ticker.systemTicker(), ttl, maxCapacity ); return new ShiroCaffeineCache.Manager( Ticker.systemTicker(), ttl, maxCapacity );
} }


private static List<Realm> createPluginRealms( Config config, SecurityLog securityLog, SecureHasher secureHasher ) private static List<PluginRealm> createPluginRealms(
Config config, SecurityLog securityLog, SecureHasher secureHasher, List<String> configuredRealms )
{ {
List<Realm> realms = new ArrayList<>(); List<PluginRealm> availablePluginRealms = new ArrayList<>();
Set<Class> excludedClasses = new HashSet<>(); Set<Class> excludedClasses = new HashSet<>();


Boolean pluginAuthenticationEnabled = config.get( SecuritySettings.plugin_authentication_enabled ); Boolean pluginAuthenticationEnabled = config.get( SecuritySettings.plugin_authentication_enabled );
Expand All @@ -220,7 +225,7 @@ private static List<Realm> createPluginRealms( Config config, SecurityLog securi
{ {
PluginRealm pluginRealm = PluginRealm pluginRealm =
new PluginRealm( plugin, config, securityLog, Clocks.systemClock(), secureHasher ); new PluginRealm( plugin, config, securityLog, Clocks.systemClock(), secureHasher );
realms.add( pluginRealm ); availablePluginRealms.add( pluginRealm );
} }
} }


Expand Down Expand Up @@ -248,7 +253,7 @@ private static List<Realm> createPluginRealms( Config config, SecurityLog securi
pluginRealm = pluginRealm =
new PluginRealm( plugin, null, config, securityLog, Clocks.systemClock(), secureHasher ); new PluginRealm( plugin, null, config, securityLog, Clocks.systemClock(), secureHasher );
} }
realms.add( pluginRealm ); availablePluginRealms.add( pluginRealm );
} }
} }


Expand All @@ -263,11 +268,30 @@ private static List<Realm> createPluginRealms( Config config, SecurityLog securi
{ {
PluginRealm pluginRealm = PluginRealm pluginRealm =
new PluginRealm( null, plugin, config, securityLog, Clocks.systemClock(), secureHasher ); new PluginRealm( null, plugin, config, securityLog, Clocks.systemClock(), secureHasher );
realms.add( pluginRealm ); availablePluginRealms.add( pluginRealm );
} }
} }
} }


List<PluginRealm> realms =
availablePluginRealms.stream()
.filter( realm -> configuredRealms.contains( realm.getName() ) )
.collect( Collectors.toList() );
boolean missingAuthenticationRealm = pluginAuthenticationEnabled &&
!realms.stream().anyMatch( PluginRealm::canAuthenticate );
boolean missingAuthorizingRealm = pluginAuthorizationEnabled &&
!realms.stream().anyMatch( PluginRealm::canAuthorize );

if ( missingAuthenticationRealm || missingAuthorizingRealm )
{
String missingProvider =
( missingAuthenticationRealm && missingAuthorizingRealm ) ? "authentication or authorization" :
( missingAuthenticationRealm ) ? "authentication" : "authorization";

throw new IllegalArgumentException( format( "Illegal configuration: No plugin %s provider loaded even " +
"though required by configuration.", missingProvider ) );
}

return realms; return realms;
} }


Expand Down
Expand Up @@ -40,19 +40,19 @@
import org.neo4j.kernel.internal.Version; import org.neo4j.kernel.internal.Version;
import org.neo4j.logging.Log; import org.neo4j.logging.Log;
import org.neo4j.server.security.enterprise.auth.PredefinedRolesBuilder; import org.neo4j.server.security.enterprise.auth.PredefinedRolesBuilder;
import org.neo4j.server.security.enterprise.auth.RealmLifecycle;
import org.neo4j.server.security.enterprise.auth.SecureHasher; import org.neo4j.server.security.enterprise.auth.SecureHasher;
import org.neo4j.server.security.enterprise.auth.ShiroAuthToken; import org.neo4j.server.security.enterprise.auth.ShiroAuthToken;
import org.neo4j.server.security.enterprise.auth.ShiroAuthorizationInfoProvider; import org.neo4j.server.security.enterprise.auth.ShiroAuthorizationInfoProvider;
import org.neo4j.server.security.enterprise.auth.plugin.api.AuthToken;
import org.neo4j.server.security.enterprise.auth.plugin.api.AuthProviderOperations; import org.neo4j.server.security.enterprise.auth.plugin.api.AuthProviderOperations;
import org.neo4j.server.security.enterprise.auth.plugin.api.AuthToken;
import org.neo4j.server.security.enterprise.auth.plugin.api.AuthorizationExpiredException; import org.neo4j.server.security.enterprise.auth.plugin.api.AuthorizationExpiredException;
import org.neo4j.server.security.enterprise.auth.plugin.spi.AuthInfo; import org.neo4j.server.security.enterprise.auth.plugin.spi.AuthInfo;
import org.neo4j.server.security.enterprise.auth.plugin.spi.AuthPlugin; import org.neo4j.server.security.enterprise.auth.plugin.spi.AuthPlugin;
import org.neo4j.server.security.enterprise.auth.plugin.spi.AuthenticationPlugin; import org.neo4j.server.security.enterprise.auth.plugin.spi.AuthenticationPlugin;
import org.neo4j.server.security.enterprise.auth.plugin.spi.AuthorizationPlugin; import org.neo4j.server.security.enterprise.auth.plugin.spi.AuthorizationPlugin;
import org.neo4j.server.security.enterprise.auth.plugin.spi.CustomCacheableAuthenticationInfo; import org.neo4j.server.security.enterprise.auth.plugin.spi.CustomCacheableAuthenticationInfo;
import org.neo4j.server.security.enterprise.log.SecurityLog; import org.neo4j.server.security.enterprise.log.SecurityLog;
import org.neo4j.server.security.enterprise.auth.RealmLifecycle;


import static org.neo4j.server.security.enterprise.configuration.SecuritySettings.PLUGIN_REALM_NAME_PREFIX; import static org.neo4j.server.security.enterprise.configuration.SecuritySettings.PLUGIN_REALM_NAME_PREFIX;


Expand Down Expand Up @@ -224,6 +224,22 @@ private void cacheAuthorizationInfo( PluginAuthInfo authInfo )
authorizationCache.put( key, authInfo ); authorizationCache.put( key, authInfo );
} }


public boolean canAuthenticate()
{
return authPlugin != null || authenticationPlugin !=null;
}

public boolean canAuthorize()
{
return authPlugin != null || authorizationPlugin != null;
}

@Override
public AuthorizationInfo getAuthorizationInfoSnapshot( PrincipalCollection principalCollection )
{
return getAuthorizationInfo( principalCollection );
}

@Override @Override
protected Object getAuthorizationCacheKey( PrincipalCollection principals ) protected Object getAuthorizationCacheKey( PrincipalCollection principals )
{ {
Expand Down Expand Up @@ -331,12 +347,6 @@ private static CustomCacheableAuthenticationInfo.CredentialsMatcher getCustomCre
return null; return null;
} }


@Override
public AuthorizationInfo getAuthorizationInfoSnapshot( PrincipalCollection principalCollection )
{
return getAuthorizationInfo( principalCollection );
}

private class CredentialsMatcher implements org.apache.shiro.authc.credential.CredentialsMatcher private class CredentialsMatcher implements org.apache.shiro.authc.credential.CredentialsMatcher
{ {
@Override @Override
Expand Down
Expand Up @@ -26,16 +26,13 @@
import java.util.Arrays; import java.util.Arrays;


import org.neo4j.kernel.configuration.Config; import org.neo4j.kernel.configuration.Config;
import org.neo4j.server.security.enterprise.configuration.SecuritySettings;
import org.neo4j.server.security.enterprise.log.SecurityLog;
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.enterprise.configuration.SecuritySettings;
import org.neo4j.server.security.enterprise.log.SecurityLog;


import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.contains;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;


public class EnterpriseSecurityModuleTest public class EnterpriseSecurityModuleTest
Expand All @@ -52,21 +49,20 @@ public void shouldFailOnIllegalRealmNameConfiguration()
Log mockLog = mock( Log.class ); Log mockLog = mock( Log.class );
when( mockLogProvider.getLog( anyString() ) ).thenReturn( mockLog ); when( mockLogProvider.getLog( anyString() ) ).thenReturn( mockLog );
when( mockLog.isDebugEnabled() ).thenReturn( true ); when( mockLog.isDebugEnabled() ).thenReturn( true );
when( config.get( SecuritySettings.native_authentication_enabled ) ).thenReturn( true ); when( config.get( SecuritySettings.native_authentication_enabled ) ).thenReturn( true );
when( config.get( SecuritySettings.native_authorization_enabled ) ).thenReturn( true ); when( config.get( SecuritySettings.native_authorization_enabled ) ).thenReturn( true );
when( config.get( SecuritySettings.ldap_authentication_enabled ) ).thenReturn( true ); when( config.get( SecuritySettings.ldap_authentication_enabled ) ).thenReturn( true );
when( config.get( SecuritySettings.ldap_authorization_enabled ) ).thenReturn( true ); when( config.get( SecuritySettings.ldap_authorization_enabled ) ).thenReturn( true );
when( config.get( SecuritySettings.plugin_authentication_enabled ) ).thenReturn( true ); when( config.get( SecuritySettings.plugin_authentication_enabled ) ).thenReturn( false );
when( config.get( SecuritySettings.plugin_authorization_enabled ) ).thenReturn( true ); when( config.get( SecuritySettings.plugin_authorization_enabled ) ).thenReturn( false );
when( config.get( SecuritySettings.auth_providers ) ).thenReturn( Arrays.asList( "this-realm-does-not-exist" ) ); when( config.get( SecuritySettings.auth_providers ) ).thenReturn( Arrays.asList( "this-realm-does-not-exist" ) );

// Then
thrown.expect( IllegalArgumentException.class ); thrown.expect( IllegalArgumentException.class );
thrown.expectMessage( "Illegal configuration: No valid auth provider is active." );


// When // When
new EnterpriseSecurityModule().newAuthManager( config, mockLogProvider, mock( SecurityLog.class), null, null ); new EnterpriseSecurityModule().newAuthManager( config, mockLogProvider, mock( SecurityLog.class), null, null );

// Then
verify( mockLog, atLeastOnce() ).debug( anyString(),
contains( "Illegal configuration: No valid security realm is active." ), anyString() );
} }


@Test @Test
Expand All @@ -78,24 +74,57 @@ public void shouldFailOnIllegalAdvancedRealmConfiguration()
Log mockLog = mock( Log.class ); Log mockLog = mock( Log.class );
when( mockLogProvider.getLog( anyString() ) ).thenReturn( mockLog ); when( mockLogProvider.getLog( anyString() ) ).thenReturn( mockLog );
when( mockLog.isDebugEnabled() ).thenReturn( true ); when( mockLog.isDebugEnabled() ).thenReturn( true );
when( config.get( SecuritySettings.native_authentication_enabled ) ).thenReturn( false ); when( config.get( SecuritySettings.native_authentication_enabled ) ).thenReturn( false );
when( config.get( SecuritySettings.native_authorization_enabled ) ).thenReturn( false ); when( config.get( SecuritySettings.native_authorization_enabled ) ).thenReturn( false );
when( config.get( SecuritySettings.ldap_authentication_enabled ) ).thenReturn( false ); when( config.get( SecuritySettings.ldap_authentication_enabled ) ).thenReturn( false );
when( config.get( SecuritySettings.ldap_authorization_enabled ) ).thenReturn( false ); when( config.get( SecuritySettings.ldap_authorization_enabled ) ).thenReturn( false );
when( config.get( SecuritySettings.plugin_authentication_enabled ) ).thenReturn( true ); when( config.get( SecuritySettings.plugin_authentication_enabled ) ).thenReturn( true );
when( config.get( SecuritySettings.plugin_authorization_enabled ) ).thenReturn( true ); when( config.get( SecuritySettings.plugin_authorization_enabled ) ).thenReturn( true );
when( config.get( SecuritySettings.auth_providers ) ).thenReturn( when( config.get( SecuritySettings.auth_providers ) ).thenReturn(
Arrays.asList( Arrays.asList(
SecuritySettings.NATIVE_REALM_NAME, SecuritySettings.NATIVE_REALM_NAME,
SecuritySettings.LDAP_REALM_NAME ) SecuritySettings.LDAP_REALM_NAME )
); );

// Then
thrown.expect( IllegalArgumentException.class ); thrown.expect( IllegalArgumentException.class );
thrown.expectMessage( "Illegal configuration: No valid auth provider is active." );


// When // When
new EnterpriseSecurityModule().newAuthManager( config, mockLogProvider, mock( SecurityLog.class), null, null ); new EnterpriseSecurityModule().newAuthManager( config, mockLogProvider, mock( SecurityLog.class), null, null );
}

@Test
public void shouldFailOnNotLoadedPluginAuthProvider()
{
// Given
Config config = mock( Config.class );
LogProvider mockLogProvider = mock( LogProvider.class );
Log mockLog = mock( Log.class );
when( mockLogProvider.getLog( anyString() ) ).thenReturn( mockLog );
when( mockLog.isDebugEnabled() ).thenReturn( true );
when( config.get( SecuritySettings.auth_cache_ttl ) ).thenReturn( 0L );
when( config.get( SecuritySettings.auth_cache_max_capacity ) ).thenReturn( 10 );
when( config.get( SecuritySettings.security_log_successful_authentication ) ).thenReturn( false );

when( config.get( SecuritySettings.native_authentication_enabled ) ).thenReturn( false );
when( config.get( SecuritySettings.native_authorization_enabled ) ).thenReturn( false );
when( config.get( SecuritySettings.ldap_authentication_enabled ) ).thenReturn( false );
when( config.get( SecuritySettings.ldap_authorization_enabled ) ).thenReturn( false );
when( config.get( SecuritySettings.plugin_authentication_enabled ) ).thenReturn( true );
when( config.get( SecuritySettings.plugin_authorization_enabled ) ).thenReturn( true );
when( config.get( SecuritySettings.auth_providers ) ).thenReturn(
Arrays.asList(
SecuritySettings.PLUGIN_REALM_NAME_PREFIX + "TestAuthenticationPlugin",
SecuritySettings.PLUGIN_REALM_NAME_PREFIX + "IllConfiguredAuthorizationPlugin"
) );


// Then // Then
verify( mockLog, atLeastOnce() ).debug( anyString(), thrown.expect( IllegalArgumentException.class );
contains( "Illegal configuration: No valid security realm is active." ), anyString() ); thrown.expectMessage( "Illegal configuration: No plugin authorization provider loaded even though required by " +
"configuration." );

// When
new EnterpriseSecurityModule().newAuthManager( config, mockLogProvider, mock( SecurityLog.class), null, null );
} }
} }

0 comments on commit a8d2e2d

Please sign in to comment.