added option to skip failed authenticators

This commit is contained in:
Sebastian Sdorra
2014-02-16 15:41:48 +01:00
parent cb3ad8f19b
commit 7d198a2d95
5 changed files with 160 additions and 60 deletions

View File

@@ -233,17 +233,6 @@ public class ScmConfiguration
return baseUrl; return baseUrl;
} }
/**
* Returns the realm description.
*
*
* @return realm description
*/
public String getRealmDescription()
{
return realmDescription;
}
/** /**
* Returns the date format for the user interface. This format is a * Returns the date format for the user interface. This format is a
* JavaScript date format, from the library moment.js. * JavaScript date format, from the library moment.js.
@@ -375,6 +364,17 @@ public class ScmConfiguration
return proxyUser; return proxyUser;
} }
/**
* Returns the realm description.
*
*
* @return realm description
*/
public String getRealmDescription()
{
return realmDescription;
}
/** /**
* Returns the servername of the SCM-Manager host. * Returns the servername of the SCM-Manager host.
* *
@@ -482,6 +482,19 @@ public class ScmConfiguration
return forceBaseUrl; return forceBaseUrl;
} }
/**
* Method description
*
*
* @return
*
* @since 1.36
*/
public boolean isSkipFailedAuthenticators()
{
return skipFailedAuthenticators;
}
//~--- set methods ---------------------------------------------------------- //~--- set methods ----------------------------------------------------------
/** /**
@@ -529,17 +542,6 @@ public class ScmConfiguration
this.baseUrl = baseUrl; this.baseUrl = baseUrl;
} }
/**
* Sets the realm description.
*
*
* @param realmDescription
*/
public void setRealmDescription(String realmDescription)
{
this.realmDescription = realmDescription;
}
/** /**
* Sets the date format for the ui. * Sets the date format for the ui.
* *
@@ -733,6 +735,17 @@ public class ScmConfiguration
this.proxyUser = proxyUser; this.proxyUser = proxyUser;
} }
/**
* Sets the realm description.
*
*
* @param realmDescription
*/
public void setRealmDescription(String realmDescription)
{
this.realmDescription = realmDescription;
}
/** /**
* Method description * Method description
* *
@@ -745,6 +758,19 @@ public class ScmConfiguration
this.servername = servername; this.servername = servername;
} }
/**
* Method description
*
*
* @param skipFailedAuthenticators
*
* @since 1.36
*/
public void setSkipFailedAuthenticators(boolean skipFailedAuthenticators)
{
this.skipFailedAuthenticators = skipFailedAuthenticators;
}
/** /**
* Method description * Method description
* *
@@ -790,21 +816,6 @@ public class ScmConfiguration
@XmlElement(name = "login-attempt-limit") @XmlElement(name = "login-attempt-limit")
private int loginAttemptLimit = -1; private int loginAttemptLimit = -1;
/**
* Login attempt timeout.
*
* @since 1.34
*/
@XmlElement(name = "login-attempt-limit-timeout")
private long loginAttemptLimitTimeout = TimeUnit.MINUTES.toSeconds(5l);
/** Field description */
private boolean enableProxy = false;
/** Field description */
@XmlElement(name = "plugin-url")
private String pluginUrl = DEFAULT_PLUGINURL;
/** glob patterns for urls which are excluded from proxy */ /** glob patterns for urls which are excluded from proxy */
@XmlElement(name = "proxy-excludes") @XmlElement(name = "proxy-excludes")
@XmlJavaTypeAdapter(XmlSetStringAdapter.class) @XmlJavaTypeAdapter(XmlSetStringAdapter.class)
@@ -825,10 +836,33 @@ public class ScmConfiguration
/** @deprecated use {@link #baseUrl} */ /** @deprecated use {@link #baseUrl} */
private String servername = "localhost"; private String servername = "localhost";
/**
* Skip failed authenticators.
*
* @since 1.36
*/
@XmlElement(name = "skip-failed-authenticators")
private boolean skipFailedAuthenticators = false;
/** Field description */
@XmlElement(name = "plugin-url")
private String pluginUrl = DEFAULT_PLUGINURL;
/**
* Login attempt timeout.
*
* @since 1.34
*/
@XmlElement(name = "login-attempt-limit-timeout")
private long loginAttemptLimitTimeout = TimeUnit.MINUTES.toSeconds(5l);
/** @deprecated use {@link #baseUrl} and {@link #forceBaseUrl} */ /** @deprecated use {@link #baseUrl} and {@link #forceBaseUrl} */
@Deprecated @Deprecated
private boolean enableSSL = false; private boolean enableSSL = false;
/** Field description */
private boolean enableProxy = false;
/** @deprecated use {@link #baseUrl} */ /** @deprecated use {@link #baseUrl} */
@Deprecated @Deprecated
private boolean enablePortForward = false; private boolean enablePortForward = false;
@@ -837,6 +871,13 @@ public class ScmConfiguration
@Deprecated @Deprecated
private int sslPort = 8181; private int sslPort = 8181;
/**
*
* Authentication realm for basic authentication.
*
*/
private String realmDescription = HttpUtil.AUTHENTICATION_REALM;
/** Configuration change listeners */ /** Configuration change listeners */
@XmlTransient @XmlTransient
private Set<ConfigChangedListener> listeners = private Set<ConfigChangedListener> listeners =
@@ -848,13 +889,6 @@ public class ScmConfiguration
/** Field description */ /** Field description */
private boolean disableGroupingGrid = false; private boolean disableGroupingGrid = false;
/**
*
* Authentication realm for basic authentication.
*
*/
private String realmDescription = HttpUtil.AUTHENTICATION_REALM;
/** /**
* JavaScript date format from moment.js * JavaScript date format from moment.js
* @see <a href="http://momentjs.com/docs/#/parsing/" target="_blank">http://momentjs.com/docs/#/parsing/</a> * @see <a href="http://momentjs.com/docs/#/parsing/" target="_blank">http://momentjs.com/docs/#/parsing/</a>

View File

@@ -47,6 +47,7 @@ import org.slf4j.LoggerFactory;
import sonia.scm.SCMContextProvider; import sonia.scm.SCMContextProvider;
import sonia.scm.cache.Cache; import sonia.scm.cache.Cache;
import sonia.scm.cache.CacheManager; import sonia.scm.cache.CacheManager;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.security.EncryptionHandler; import sonia.scm.security.EncryptionHandler;
import sonia.scm.user.User; import sonia.scm.user.User;
import sonia.scm.user.UserManager; import sonia.scm.user.UserManager;
@@ -87,21 +88,24 @@ public class ChainAuthenticatonManager extends AbstractAuthenticationManager
* *
* *
* *
*
* @param configuration
* @param userManager * @param userManager
* @param authenticationHandlerSet * @param authenticationHandlerSet
* @param encryptionHandler * @param encryptionHandler
* @param cacheManager * @param cacheManager
* @param authenticationListenerProvider
* @param authenticationListeners * @param authenticationListeners
*/ */
@Inject @Inject
public ChainAuthenticatonManager(UserManager userManager, public ChainAuthenticatonManager(ScmConfiguration configuration,
UserManager userManager,
Set<AuthenticationHandler> authenticationHandlerSet, Set<AuthenticationHandler> authenticationHandlerSet,
EncryptionHandler encryptionHandler, CacheManager cacheManager, EncryptionHandler encryptionHandler, CacheManager cacheManager,
Set<AuthenticationListener> authenticationListeners) Set<AuthenticationListener> authenticationListeners)
{ {
AssertUtil.assertIsNotEmpty(authenticationHandlerSet); AssertUtil.assertIsNotEmpty(authenticationHandlerSet);
AssertUtil.assertIsNotNull(cacheManager); AssertUtil.assertIsNotNull(cacheManager);
this.configuration = configuration;
this.authenticationHandlers = sort(userManager, authenticationHandlerSet); this.authenticationHandlers = sort(userManager, authenticationHandlerSet);
this.encryptionHandler = encryptionHandler; this.encryptionHandler = encryptionHandler;
this.cache = cacheManager.getCache(String.class, this.cache = cacheManager.getCache(String.class,
@@ -200,6 +204,22 @@ public class ChainAuthenticatonManager extends AbstractAuthenticationManager
} }
} }
/**
* Method description
*
*
* @param result
*
* @return
*/
boolean stopChain(AuthenticationResult result)
{
return (result != null) && (result.getState() != null)
&& (result.getState().isSuccessfully()
|| ((result.getState() == AuthenticationState.FAILED)
&&!configuration.isSkipFailedAuthenticators()));
}
/** /**
* Method description * Method description
* *
@@ -240,9 +260,7 @@ public class ChainAuthenticatonManager extends AbstractAuthenticationManager
authenticator.getClass().getName(), result); authenticator.getClass().getName(), result);
} }
if ((result != null) && (result.getState() != null) if (stopChain(result))
&& (result.getState().isSuccessfully()
|| (result.getState() == AuthenticationState.FAILED)))
{ {
if (result.getState().isSuccessfully() && (result.getUser() != null)) if (result.getState().isSuccessfully() && (result.getUser() != null))
{ {
@@ -378,11 +396,14 @@ public class ChainAuthenticatonManager extends AbstractAuthenticationManager
//~--- fields --------------------------------------------------------------- //~--- fields ---------------------------------------------------------------
/** Field description */ /** Field description */
private List<AuthenticationHandler> authenticationHandlers; private final List<AuthenticationHandler> authenticationHandlers;
/** Field description */ /** Field description */
private Cache<String, AuthenticationCacheValue> cache; private final Cache<String, AuthenticationCacheValue> cache;
/** Field description */ /** Field description */
private EncryptionHandler encryptionHandler; private final ScmConfiguration configuration;
/** Field description */
private final EncryptionHandler encryptionHandler;
} }

View File

@@ -51,6 +51,7 @@ Sonia.config.ScmConfigPanel = Ext.extend(Sonia.config.ConfigPanel,{
errorSubmitMsgText: 'Could not submit config.', errorSubmitMsgText: 'Could not submit config.',
// TODO i18n // TODO i18n
skipFailedAuthenticatorsText: 'Skip failed authenticators',
loginAttemptLimitText: 'Login Attempt Limit', loginAttemptLimitText: 'Login Attempt Limit',
loginAttemptLimitTimeoutText: 'Login Attempt Limit Timeout', loginAttemptLimitTimeoutText: 'Login Attempt Limit Timeout',
@@ -85,6 +86,8 @@ Sonia.config.ScmConfigPanel = Ext.extend(Sonia.config.ConfigPanel,{
adminUsersHelpText: 'Comma seperated list of users with admin permissions.', adminUsersHelpText: 'Comma seperated list of users with admin permissions.',
// TODO i18n // TODO i18n
skipFailedAuthenticatorsHelpText: 'Do not stop the authentication chain, \n\
if an authenticator finds the user but fails to authenticate the user.',
loginAttemptLimitHelpText: 'Maximum allowed login attempts. Use -1 to disable the login attempt limit.', loginAttemptLimitHelpText: 'Maximum allowed login attempts. Use -1 to disable the login attempt limit.',
loginAttemptLimitTimeoutHelpText: 'Timeout in seconds for users which are temporary disabled,\ loginAttemptLimitTimeoutHelpText: 'Timeout in seconds for users which are temporary disabled,\
because of too many failed login attempts.', because of too many failed login attempts.',
@@ -157,6 +160,12 @@ Sonia.config.ScmConfigPanel = Ext.extend(Sonia.config.ConfigPanel,{
name: 'anonymousAccessEnabled', name: 'anonymousAccessEnabled',
inputValue: 'true', inputValue: 'true',
helpText: this.allowAnonymousAccessHelpText helpText: this.allowAnonymousAccessHelpText
},{
xtype: 'checkbox',
fieldLabel: this.skipFailedAuthenticatorsText,
name: 'skip-failed-authenticators',
inputValue: 'true',
helpText: this.skipFailedAuthenticatorsHelpText
},{ },{
xtype: 'numberfield', xtype: 'numberfield',
fieldLabel: this.loginAttemptLimitText, fieldLabel: this.loginAttemptLimitText,

View File

@@ -40,7 +40,7 @@ if (Ext.form.VTypes){
passwordText: 'Die Passwörter stimmen nicht überein!', passwordText: 'Die Passwörter stimmen nicht überein!',
nameTest: 'Der Name ist invalid.', nameTest: 'Der Name ist invalid.',
usernameText: 'Der Benutzername ist invalid.', usernameText: 'Der Benutzername ist invalid.',
repositoryNameText: 'Der Name des Repositorys ist ungültig.', repositoryNameText: 'Der Name des Repositorys ist ungültig.'
}); });
} }
@@ -349,6 +349,10 @@ if (Sonia.config.ScmConfigPanel){
adminGroupsHelpText: 'Komma getrennte Liste von Gruppen mit Administrationsrechten.', adminGroupsHelpText: 'Komma getrennte Liste von Gruppen mit Administrationsrechten.',
adminUsersHelpText: 'Komma getrennte Liste von Benutzern mit Administrationsrechten.', adminUsersHelpText: 'Komma getrennte Liste von Benutzern mit Administrationsrechten.',
skipFailedAuthenticatorsText: 'Überspringe fehlgeschlagene Authentifizierer',
skipFailedAuthenticatorsHelpText: 'Setzt die Authentifizierungs-Kette fort,\n\
auch wenn ein ein Authentifizierer einen Benutzer gefunden hat,\n\
diesen aber nicht Authentifizieren kann.',
loginAttemptLimitText: 'Login Attempt Limit', loginAttemptLimitText: 'Login Attempt Limit',
loginAttemptLimitTimeoutText: 'Login Attempt Limit Timeout', loginAttemptLimitTimeoutText: 'Login Attempt Limit Timeout',
loginAttemptLimitHelpText: 'Maximale Anzahl gescheiterte Loginversuche. Der Wert -1 deaktiviert die Begrenzung.', loginAttemptLimitHelpText: 'Maximale Anzahl gescheiterte Loginversuche. Der Wert -1 deaktiviert die Begrenzung.',

View File

@@ -42,6 +42,7 @@ import org.junit.Test;
import sonia.scm.AbstractTestBase; import sonia.scm.AbstractTestBase;
import sonia.scm.SCMContextProvider; import sonia.scm.SCMContextProvider;
import sonia.scm.cache.MapCacheManager; import sonia.scm.cache.MapCacheManager;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.security.MessageDigestEncryptionHandler; import sonia.scm.security.MessageDigestEncryptionHandler;
import sonia.scm.user.User; import sonia.scm.user.User;
import sonia.scm.user.UserManager; import sonia.scm.user.UserManager;
@@ -137,7 +138,7 @@ public class ChainAuthenticationManagerTest extends AbstractTestBase
SingleUserAuthenticaionHandler a2 = SingleUserAuthenticaionHandler a2 =
new SingleUserAuthenticaionHandler("a2", trillian); new SingleUserAuthenticaionHandler("a2", trillian);
manager = createManager("a2", a1, a2); manager = createManager("a2", false, a1, a2);
AuthenticationResult result = manager.authenticate(request, response, AuthenticationResult result = manager.authenticate(request, response,
trillian.getName(), "trillian123"); trillian.getName(), "trillian123");
@@ -147,6 +148,24 @@ public class ChainAuthenticationManagerTest extends AbstractTestBase
assertEquals("a2", result.getUser().getType()); assertEquals("a2", result.getUser().getType());
} }
/**
* Method description
*
*/
@Test
public void testStopChain()
{
ChainAuthenticatonManager cam = createManager("", false);
assertTrue(cam.stopChain(new AuthenticationResult(perfect)));
assertTrue(cam.stopChain(AuthenticationResult.FAILED));
assertFalse(cam.stopChain(AuthenticationResult.NOT_FOUND));
cam = createManager("", true);
assertTrue(cam.stopChain(new AuthenticationResult(perfect)));
assertFalse(cam.stopChain(AuthenticationResult.FAILED));
assertFalse(cam.stopChain(AuthenticationResult.NOT_FOUND));
}
/** /**
* Method description * Method description
* *
@@ -199,7 +218,7 @@ public class ChainAuthenticationManagerTest extends AbstractTestBase
trillian = UserTestData.createTrillian(); trillian = UserTestData.createTrillian();
trillian.setPassword("trillian123"); trillian.setPassword("trillian123");
return createManager("", return createManager("", false,
new SingleUserAuthenticaionHandler("perfectsType", perfect), new SingleUserAuthenticaionHandler("perfectsType", perfect),
new SingleUserAuthenticaionHandler("trilliansType", trillian)); new SingleUserAuthenticaionHandler("trilliansType", trillian));
} }
@@ -209,20 +228,33 @@ public class ChainAuthenticationManagerTest extends AbstractTestBase
* *
* *
* @param defaultType * @param defaultType
* @param skipFailedAuthenticators
* @param handlers * @param handlers
* *
* @return * @return
*/ */
private ChainAuthenticatonManager createManager(String defaultType, private ChainAuthenticatonManager createManager(String defaultType,
AuthenticationHandler... handlers) boolean skipFailedAuthenticators, AuthenticationHandler... handlers)
{ {
if ( handlers == null || handlers.length == 0 ){
//J-
handlers = new AuthenticationHandler[]{
new SingleUserAuthenticaionHandler("perfectsType", perfect),
new SingleUserAuthenticaionHandler("trilliansType", trillian)
};
//J+
}
ScmConfiguration configuration = new ScmConfiguration();
configuration.setSkipFailedAuthenticators(skipFailedAuthenticators);
Set<AuthenticationHandler> handlerSet = ImmutableSet.copyOf(handlers); Set<AuthenticationHandler> handlerSet = ImmutableSet.copyOf(handlers);
UserManager userManager = mock(UserManager.class); UserManager userManager = mock(UserManager.class);
when(userManager.getDefaultType()).thenReturn(defaultType); when(userManager.getDefaultType()).thenReturn(defaultType);
manager = new ChainAuthenticatonManager(userManager, handlerSet, manager = new ChainAuthenticatonManager(configuration, userManager,
new MessageDigestEncryptionHandler(), new MapCacheManager(), handlerSet, new MessageDigestEncryptionHandler(), new MapCacheManager(),
Collections.EMPTY_SET); Collections.EMPTY_SET);
manager.init(contextProvider); manager.init(contextProvider);
@@ -328,10 +360,10 @@ public class ChainAuthenticationManagerTest extends AbstractTestBase
//~--- fields ------------------------------------------------------------- //~--- fields -------------------------------------------------------------
/** Field description */ /** Field description */
private String type; private final String type;
/** Field description */ /** Field description */
private User user; private final User user;
} }