replace scm-manager 1.x security api with apache shiro and use PasswordService for stronger password hashes

This commit is contained in:
Sebastian Sdorra
2014-12-14 12:26:03 +01:00
parent 876f501644
commit 4fa8e6e88a
32 changed files with 661 additions and 3974 deletions

View File

@@ -53,7 +53,6 @@ import sonia.scm.store.StoreFactory;
import sonia.scm.upgrade.UpgradeManager;
import sonia.scm.user.UserManager;
import sonia.scm.util.IOUtil;
import sonia.scm.web.security.AuthenticationManager;
//~--- JDK imports ------------------------------------------------------------
@@ -100,9 +99,6 @@ public class ScmContextListener extends GuiceServletContextListener
// close RepositoryManager
IOUtil.close(globalInjector.getInstance(RepositoryManager.class));
// close Authenticator
IOUtil.close(globalInjector.getInstance(AuthenticationManager.class));
// close GroupManager
IOUtil.close(globalInjector.getInstance(GroupManager.class));

View File

@@ -37,9 +37,12 @@ package sonia.scm;
import com.google.inject.name.Names;
import org.apache.shiro.authc.credential.DefaultPasswordService;
import org.apache.shiro.authc.credential.PasswordService;
import org.apache.shiro.crypto.hash.DefaultHashService;
import org.apache.shiro.guice.web.ShiroWebModule;
import sonia.scm.security.ScmRealm;
import sonia.scm.security.DefaultRealm;
import static org.apache.shiro.guice.web.ShiroWebModule.ROLES;
@@ -54,6 +57,11 @@ import javax.servlet.ServletContext;
public class ScmSecurityModule extends ShiroWebModule
{
/** Field description */
private static final int ITERATIONS = 8192;
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
@@ -75,9 +83,13 @@ public class ScmSecurityModule extends ShiroWebModule
@SuppressWarnings("unchecked")
protected void configureShiroWeb()
{
bind(PasswordService.class).toInstance(createPasswordService());
// expose password service to global injector
expose(PasswordService.class);
// bind realm
bindRealm().to(ScmRealm.class);
bindRealm().to(DefaultRealm.class);
// bind constant
bindConstant().annotatedWith(Names.named("shiro.loginUrl")).to(
@@ -86,4 +98,21 @@ public class ScmSecurityModule extends ShiroWebModule
// disable access to mustache resources
addFilterChain("/**.mustache", config(ROLES, "nobody"));
}
/**
* Creates a {@link PasswordService} with a smaller size of iteration, because
* large iterations will slow down subversion.
*
* @return instance of {@link PasswordService}
*/
private PasswordService createPasswordService()
{
DefaultPasswordService passwordService = new DefaultPasswordService();
DefaultHashService hashService = new DefaultHashService();
hashService.setHashIterations(ITERATIONS);
passwordService.setHashService(hashService);
return passwordService;
}
}

View File

@@ -88,13 +88,9 @@ import sonia.scm.resources.ResourceManager;
import sonia.scm.resources.ScriptResourceServlet;
import sonia.scm.security.CipherHandler;
import sonia.scm.security.CipherUtil;
import sonia.scm.security.ConfigurableLoginAttemptHandler;
import sonia.scm.security.DefaultKeyGenerator;
import sonia.scm.security.DefaultSecuritySystem;
import sonia.scm.security.EncryptionHandler;
import sonia.scm.security.KeyGenerator;
import sonia.scm.security.LoginAttemptHandler;
import sonia.scm.security.MessageDigestEncryptionHandler;
import sonia.scm.security.RepositoryPermissionResolver;
import sonia.scm.security.SecuritySystem;
import sonia.scm.store.BlobStoreFactory;
@@ -127,8 +123,6 @@ import sonia.scm.web.filter.AutoLoginFilter;
import sonia.scm.web.filter.LoggingFilter;
import sonia.scm.web.security.AdministrationContext;
import sonia.scm.web.security.ApiBasicAuthenticationFilter;
import sonia.scm.web.security.AuthenticationManager;
import sonia.scm.web.security.ChainAuthenticatonManager;
import sonia.scm.web.security.DefaultAdministrationContext;
//~--- JDK imports ------------------------------------------------------------
@@ -269,7 +263,6 @@ public class ScmServletModule extends JerseyServletModule
// note CipherUtil uses an other generator
bind(KeyGenerator.class).to(DefaultKeyGenerator.class);
bind(CipherHandler.class).toInstance(cu.getCipherHandler());
bind(EncryptionHandler.class, MessageDigestEncryptionHandler.class);
bind(FileSystem.class, DefaultFileSystem.class);
// bind health check stuff
@@ -280,10 +273,8 @@ public class ScmServletModule extends JerseyServletModule
// bind security stuff
bind(PermissionResolver.class, RepositoryPermissionResolver.class);
bind(AuthenticationManager.class, ChainAuthenticatonManager.class);
bind(SecuritySystem.class).to(DefaultSecuritySystem.class);
bind(AdministrationContext.class, DefaultAdministrationContext.class);
bind(LoginAttemptHandler.class, ConfigurableLoginAttemptHandler.class);
// bind cache
bind(CacheManager.class, GuavaCacheManager.class);

View File

@@ -38,6 +38,7 @@ package sonia.scm.api.rest.resources;
import com.google.inject.Inject;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.credential.PasswordService;
import org.apache.shiro.subject.Subject;
import org.codehaus.enunciate.jaxrs.TypeHint;
@@ -47,7 +48,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.api.rest.RestActionResult;
import sonia.scm.security.EncryptionHandler;
import sonia.scm.security.Role;
import sonia.scm.security.ScmSecurityException;
import sonia.scm.user.User;
import sonia.scm.user.UserException;
@@ -65,7 +66,6 @@ import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import sonia.scm.security.Role;
/**
*
@@ -88,14 +88,13 @@ public class ChangePasswordResource
*
* @param userManager
* @param encryptionHandler
* @param securityContextProvider
*/
@Inject
public ChangePasswordResource(UserManager userManager,
EncryptionHandler encryptionHandler)
PasswordService encryptionHandler)
{
this.userManager = userManager;
this.encryptionHandler = encryptionHandler;
this.passwordService = encryptionHandler;
}
//~--- methods --------------------------------------------------------------
@@ -155,9 +154,9 @@ public class ChangePasswordResource
{
User dbUser = userManager.get(currentUser.getName());
if (encryptionHandler.encrypt(oldPassword).equals(dbUser.getPassword()))
if (passwordService.passwordsMatch(oldPassword, dbUser.getPassword()))
{
dbUser.setPassword(encryptionHandler.encrypt(newPassword));
dbUser.setPassword(passwordService.encryptPassword(newPassword));
userManager.modify(dbUser);
response = Response.ok(new RestActionResult(true)).build();
}
@@ -183,8 +182,8 @@ public class ChangePasswordResource
//~--- fields ---------------------------------------------------------------
/** Field description */
private EncryptionHandler encryptionHandler;
private final PasswordService passwordService;
/** Field description */
private UserManager userManager;
private final UserManager userManager;
}

View File

@@ -39,11 +39,11 @@ import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.credential.PasswordService;
import org.codehaus.enunciate.jaxrs.TypeHint;
import org.codehaus.enunciate.modules.jersey.ExternallyManagedLifecycle;
import sonia.scm.security.EncryptionHandler;
import sonia.scm.security.Role;
import sonia.scm.user.User;
import sonia.scm.user.UserException;
@@ -95,15 +95,13 @@ public class UserResource extends AbstractManagerResource<User, UserException>
*
*
* @param userManager
* @param encryptionHandler
* @param securityContextProvider
* @param passwordService
*/
@Inject
public UserResource(UserManager userManager,
EncryptionHandler encryptionHandler)
public UserResource(UserManager userManager, PasswordService passwordService)
{
super(userManager);
this.encryptionHandler = encryptionHandler;
this.passwordService = passwordService;
}
//~--- methods --------------------------------------------------------------
@@ -386,12 +384,12 @@ public class UserResource extends AbstractManagerResource<User, UserException>
if (Util.isNotEmpty(password))
{
user.setPassword(encryptionHandler.encrypt(password));
user.setPassword(passwordService.encryptPassword(password));
}
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private EncryptionHandler encryptionHandler;
private PasswordService passwordService;
}

View File

@@ -1,331 +0,0 @@
/**
* Copyright (c) 2010, Sebastian Sdorra
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. 2. Redistributions in
* binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution. 3. Neither the name of SCM-Manager;
* nor the names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.security;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.base.Objects;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.web.security.AuthenticationResult;
//~--- JDK imports ------------------------------------------------------------
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
/**
*
* @author Sebastian Sdorra
*/
@Singleton
public class ConfigurableLoginAttemptHandler implements LoginAttemptHandler
{
/**
* the logger for ConfigurableLoginAttemptHandler
*/
private static final Logger logger =
LoggerFactory.getLogger(ConfigurableLoginAttemptHandler.class);
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
*
* @param configuration
*/
@Inject
public ConfigurableLoginAttemptHandler(ScmConfiguration configuration)
{
this.configuration = configuration;
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param token
*
* @throws AuthenticationException
*/
@Override
public void beforeAuthentication(AuthenticationToken token)
throws AuthenticationException
{
if (isEnabled())
{
handleBeforeAuthentication(token);
}
else
{
logger.trace("LoginAttemptHandler is disabled");
}
}
/**
* Method description
*
*
* @param token
* @param result
*
* @throws AuthenticationException
*/
@Override
public void onSuccessfulAuthentication(AuthenticationToken token,
AuthenticationResult result)
throws AuthenticationException
{
if (isEnabled())
{
handleOnSuccessfulAuthentication(token);
}
else
{
logger.trace("LoginAttemptHandler is disabled");
}
}
/**
* Method description
*
*
* @param token
* @param result
*
* @throws AuthenticationException
*/
@Override
public void onUnsuccessfulAuthentication(AuthenticationToken token,
AuthenticationResult result)
throws AuthenticationException
{
if (isEnabled())
{
handleOnUnsuccessfulAuthentication(token);
}
else
{
logger.trace("LoginAttemptHandler is disabled");
}
}
/**
* Method description
*
*
* @param token
*/
private void handleBeforeAuthentication(AuthenticationToken token)
{
LoginAttempt attempt = getAttempt(token);
long time = System.currentTimeMillis() - attempt.lastAttempt;
if (time > getLoginAttemptLimitTimeout())
{
logger.debug("reset login attempts for {}, because of time",
token.getPrincipal());
attempt.reset();
}
else if (attempt.counter >= configuration.getLoginAttemptLimit())
{
logger.warn("account {} is temporary locked, because of {}",
token.getPrincipal(), attempt);
attempt.increase();
throw new ExcessiveAttemptsException("account is temporary locked");
}
}
/**
* Method description
*
*
* @param token
* @param result
*
* @throws AuthenticationException
*/
private void handleOnSuccessfulAuthentication(AuthenticationToken token)
throws AuthenticationException
{
logger.debug("reset login attempts for {}, because of successful login",
token.getPrincipal());
getAttempt(token).reset();
}
/**
* Method description
*
*
* @param token
* @param result
*
* @throws AuthenticationException
*/
private void handleOnUnsuccessfulAuthentication(AuthenticationToken token)
throws AuthenticationException
{
logger.debug("increase failed login attempts for {}", token.getPrincipal());
LoginAttempt attempt = getAttempt(token);
attempt.increase();
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @param token
*
* @return
*/
private LoginAttempt getAttempt(AuthenticationToken token)
{
LoginAttempt freshAttempt = new LoginAttempt();
LoginAttempt attempt = attempts.putIfAbsent(token.getPrincipal(),
freshAttempt);
if (attempt == null)
{
attempt = freshAttempt;
}
return attempt;
}
/**
* Method description
*
*
* @return
*/
private long getLoginAttemptLimitTimeout()
{
return TimeUnit.SECONDS.toMillis(
configuration.getLoginAttemptLimitTimeout());
}
/**
* Method description
*
*
* @return
*/
private boolean isEnabled()
{
return (configuration.getLoginAttemptLimit() > 0)
&& (configuration.getLoginAttemptLimitTimeout() > 0l);
}
//~--- inner classes --------------------------------------------------------
/**
* Login attempt
*/
private static class LoginAttempt
{
/**
* Method description
*
*
* @return
*/
@Override
public String toString()
{
//J-
return Objects.toStringHelper(this)
.add("counter", counter)
.add("lastAttempt", lastAttempt)
.toString();
//J+
}
/**
* Method description
*
*/
synchronized void increase()
{
counter++;
lastAttempt = System.currentTimeMillis();
}
/**
* Method description
*
*/
synchronized void reset()
{
lastAttempt = -1l;
counter = 0;
}
//~--- fields -------------------------------------------------------------
/** Field description */
private int counter = 0;
/** Field description */
private long lastAttempt = -1l;
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private final ConcurrentMap<Object, LoginAttempt> attempts =
new ConcurrentHashMap<Object, LoginAttempt>();
/** Field description */
private final ScmConfiguration configuration;
}

View File

@@ -0,0 +1,228 @@
/**
* Copyright (c) 2014, Sebastian Sdorra All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. 2. Redistributions in
* binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution. 3. Neither the name of SCM-Manager;
* nor the names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.security;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSet.Builder;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.PasswordMatcher;
import org.apache.shiro.authc.credential.PasswordService;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.group.Group;
import sonia.scm.group.GroupDAO;
import sonia.scm.group.GroupNames;
import sonia.scm.user.User;
import sonia.scm.user.UserDAO;
import static com.google.common.base.Preconditions.*;
//~--- JDK imports ------------------------------------------------------------
import javax.inject.Inject;
import javax.inject.Singleton;
/**
*
* @author Sebastian Sdorra
*
* @since 2.0.0
*/
@Singleton
public class DefaultRealm extends AuthorizingRealm
{
/** Field description */
@VisibleForTesting
static final String REALM = "DefaultRealm";
/**
* the logger for DefaultRealm
*/
private static final Logger logger =
LoggerFactory.getLogger(DefaultRealm.class);
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
*
* @param service
* @param collector
* @param userDAO
* @param groupDAO
*/
@Inject
public DefaultRealm(PasswordService service,
AuthorizationCollector collector, UserDAO userDAO, GroupDAO groupDAO)
{
this.collector = collector;
this.userDAO = userDAO;
this.groupDAO = groupDAO;
PasswordMatcher matcher = new PasswordMatcher();
matcher.setPasswordService(service);
setCredentialsMatcher(matcher);
setAuthenticationTokenClass(UsernamePasswordToken.class);
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param token
*
* @return
*
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token)
throws AuthenticationException
{
checkArgument(token instanceof UsernamePasswordToken, "%s is required",
UsernamePasswordToken.class);
UsernamePasswordToken upt = (UsernamePasswordToken) token;
String principal = upt.getUsername();
checkArgument(!Strings.isNullOrEmpty(principal), "username is required");
logger.debug("try to authenticate {}", principal);
User user = userDAO.get(principal);
if (user == null)
{
//J-
throw new UnknownAccountException(
String.format("unknown account %s", principal)
);
//J+
}
if (!user.isActive())
{
//J-
throw new DisabledAccountException(
String.format("account %s is disabled", principal)
);
//J+
}
SimplePrincipalCollection collection = new SimplePrincipalCollection();
collection.add(principal, REALM);
collection.add(user, REALM);
collection.add(collectGroups(principal), REALM);
return new SimpleAuthenticationInfo(collection, user.getPassword());
}
/**
* Method description
*
*
* @param principals
*
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals)
{
return collector.collect(principals);
}
/**
* Method description
*
*
* @param principal
*
* @return
*/
private GroupNames collectGroups(String principal)
{
Builder<String> builder = ImmutableSet.builder();
builder.add(GroupNames.AUTHENTICATED);
for (Group group : groupDAO.getAll())
{
if (group.isMember(principal))
{
builder.add(group.getName());
}
}
GroupNames groups = new GroupNames(builder.build());
logger.debug("collected following groups for principal {}: {}", principal,
groups);
return groups;
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private final AuthorizationCollector collector;
/** Field description */
private final GroupDAO groupDAO;
/** Field description */
private final UserDAO userDAO;
}

View File

@@ -1,566 +0,0 @@
/**
* Copyright (c) 2010, Sebastian Sdorra
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of SCM-Manager; nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.security;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.base.Joiner;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.apache.shiro.authc.AccountException;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.pam.UnsupportedTokenException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.HandlerEventType;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.group.Group;
import sonia.scm.group.GroupManager;
import sonia.scm.group.GroupNames;
import sonia.scm.repository.RepositoryManager;
import sonia.scm.user.User;
import sonia.scm.user.UserDAO;
import sonia.scm.user.UserEventHack;
import sonia.scm.user.UserException;
import sonia.scm.user.UserManager;
import sonia.scm.util.Util;
import sonia.scm.web.security.AuthenticationManager;
import sonia.scm.web.security.AuthenticationResult;
import sonia.scm.web.security.AuthenticationState;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.util.Collection;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author Sebastian Sdorra
*/
@Singleton
public class ScmRealm extends AuthorizingRealm
{
/** Field description */
public static final String NAME = "scm";
/** Field description */
private static final String SCM_CREDENTIALS = "SCM_CREDENTIALS";
/**
* the logger for ScmRealm
*/
private static final Logger logger = LoggerFactory.getLogger(ScmRealm.class);
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
* @param configuration
* @param loginAttemptHandler
* @param collector
* @param userManager
* @param groupManager
* @param userDAO
* @param authenticator
* @param manager
* @param requestProvider
* @param responseProvider
*/
@Inject
public ScmRealm(ScmConfiguration configuration,
LoginAttemptHandler loginAttemptHandler, AuthorizationCollector collector,
UserManager userManager, GroupManager groupManager, UserDAO userDAO,
AuthenticationManager authenticator, RepositoryManager manager,
Provider<HttpServletRequest> requestProvider,
Provider<HttpServletResponse> responseProvider)
{
this.configuration = configuration;
this.loginAttemptHandler = loginAttemptHandler;
this.collector = collector;
this.userManager = userManager;
this.groupManager = groupManager;
this.userDAO = userDAO;
this.authenticator = authenticator;
this.requestProvider = requestProvider;
this.responseProvider = responseProvider;
// set token class
setAuthenticationTokenClass(UsernamePasswordToken.class);
// use own custom caching
setCachingEnabled(false);
setAuthenticationCachingEnabled(false);
setAuthorizationCachingEnabled(false);
// set components
setPermissionResolver(new RepositoryPermissionResolver());
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
*
* @param authToken
*
* @return
*
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken authToken)
throws AuthenticationException
{
if (!(authToken instanceof UsernamePasswordToken))
{
throw new UnsupportedTokenException("ScmAuthenticationToken is required");
}
loginAttemptHandler.beforeAuthentication(authToken);
UsernamePasswordToken token = (UsernamePasswordToken) authToken;
AuthenticationInfo info = null;
AuthenticationResult result =
authenticator.authenticate(requestProvider.get(), responseProvider.get(),
token.getUsername(), new String(token.getPassword()));
if ((result != null) && (AuthenticationState.SUCCESS == result.getState()))
{
loginAttemptHandler.onSuccessfulAuthentication(authToken, result);
info = createAuthenticationInfo(token, result);
}
else if ((result != null)
&& (AuthenticationState.NOT_FOUND == result.getState()))
{
throw new UnknownAccountException(
"unknown account ".concat(token.getUsername()));
}
else
{
loginAttemptHandler.onUnsuccessfulAuthentication(authToken, result);
throw new AccountException("authentication failed");
}
return info;
}
/**
* Method description
*
*
* @param principals
*
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals)
{
return collector.collect(principals);
}
/**
* Method description
*
*
* @param request
* @param password
* @param ar
*
* @return
*/
private Set<String> authenticate(HttpServletRequest request, String password,
AuthenticationResult ar)
{
Set<String> groupSet = null;
User user = ar.getUser();
try
{
groupSet = createGroupSet(ar);
// check for admin user
checkForAuthenticatedAdmin(user, groupSet);
// store user
User dbUser = userDAO.get(user.getName());
if (dbUser != null)
{
checkDBForAdmin(user, dbUser);
checkDBForActive(user, dbUser);
}
// create new user
else if (user.isValid())
{
user.setCreationDate(System.currentTimeMillis());
// TODO find a better way
UserEventHack.fireEvent(userManager, HandlerEventType.BEFORE_CREATE,
user);
userDAO.add(user);
UserEventHack.fireEvent(userManager, HandlerEventType.CREATE, user);
}
else if (logger.isErrorEnabled())
{
logger.error("could not create user {}, beacause it is not valid",
user.getName());
}
if (user.isActive())
{
if (logger.isDebugEnabled())
{
logGroups(user, groupSet);
}
// store encrypted credentials in session
String credentials = user.getName();
if (Util.isNotEmpty(password))
{
credentials = credentials.concat(":").concat(password);
}
credentials = CipherUtil.getInstance().encode(credentials);
request.getSession(true).setAttribute(SCM_CREDENTIALS, credentials);
}
else
{
String msg = "user ".concat(user.getName()).concat(" is deactivated");
if (logger.isWarnEnabled())
{
logger.warn(msg);
}
throw new DisabledAccountException(msg);
}
}
catch (Exception ex)
{
logger.error("authentication failed", ex);
throw new AuthenticationException("authentication failed", ex);
}
return groupSet;
}
/**
* Method description
*
*
* @param user
* @param dbUser
*/
private void checkDBForActive(User user, User dbUser)
{
// user is deactivated by database
if (!dbUser.isActive())
{
if (logger.isDebugEnabled())
{
logger.debug("user {} is marked as deactivated by local database",
user.getName());
}
user.setActive(false);
}
}
/**
* Method description
*
*
* @param user
* @param dbUser
*
* @throws IOException
* @throws UserException
*/
private void checkDBForAdmin(User user, User dbUser)
throws UserException, IOException
{
// if database user is an admin, set admin for the current user
if (dbUser.isAdmin())
{
if (logger.isDebugEnabled())
{
logger.debug("user {} of type {} is marked as admin by local database",
user.getName(), user.getType());
}
user.setAdmin(true);
}
// modify existing user, copy properties except password and admin
if (user.copyProperties(dbUser, false))
{
user.setLastModified(System.currentTimeMillis());
UserEventHack.fireEvent(userManager, HandlerEventType.BEFORE_MODIFY, user);
userDAO.modify(user);
UserEventHack.fireEvent(userManager, HandlerEventType.MODIFY, user);
}
}
/**
* Method description
*
*
* @param user
* @param groupSet
*/
private void checkForAuthenticatedAdmin(User user, Set<String> groupSet)
{
if (!user.isAdmin())
{
user.setAdmin(isAdmin(user, groupSet));
if (logger.isDebugEnabled() && user.isAdmin())
{
logger.debug("user {} is marked as admin by configuration",
user.getName());
}
}
else if (logger.isDebugEnabled())
{
logger.debug("authenticator {} marked user {} as admin", user.getType(),
user.getName());
}
}
/**
* Method description
*
*
* @param token
* @param result
*
* @return
*/
private AuthenticationInfo createAuthenticationInfo(
UsernamePasswordToken token, AuthenticationResult result)
{
User user = result.getUser();
Collection<String> groups = authenticate(requestProvider.get(),
new String(token.getPassword()), result);
SimplePrincipalCollection collection = new SimplePrincipalCollection();
/*
* the first (primary) principal should be a unique identifier
*/
collection.add(user.getId(), NAME);
collection.add(user, NAME);
collection.add(new GroupNames(groups), NAME);
return new SimpleAuthenticationInfo(collection, token.getPassword());
}
/**
* Method description
*
*
* @param ar
*
* @return
*/
private Set<String> createGroupSet(AuthenticationResult ar)
{
Set<String> groupSet = Sets.newHashSet();
// add group for all authenticated users
groupSet.add(GroupNames.AUTHENTICATED);
// load external groups
Collection<String> extGroups = ar.getGroups();
if (extGroups != null)
{
groupSet.addAll(extGroups);
}
// load internal groups
loadGroups(ar.getUser(), groupSet);
return groupSet;
}
/**
* Method description
*
*
*
* @param user
* @param groupSet
*/
private void loadGroups(User user, Set<String> groupSet)
{
Collection<Group> groupCollection =
groupManager.getGroupsForMember(user.getName());
if (groupCollection != null)
{
for (Group group : groupCollection)
{
groupSet.add(group.getName());
}
}
}
/**
* Method description
*
*
* @param user
* @param groups
*/
private void logGroups(User user, Set<String> groups)
{
StringBuilder msg = new StringBuilder("user ");
msg.append(user.getName());
if (Util.isNotEmpty(groups))
{
msg.append(" is member of ");
Joiner.on(", ").appendTo(msg, groups);
}
else
{
msg.append(" is not a member of a group");
}
logger.debug(msg.toString());
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
*
*
* @param user
* @param groups
* @return
*/
private boolean isAdmin(User user, Collection<String> groups)
{
boolean result = false;
Set<String> adminUsers = configuration.getAdminUsers();
if (adminUsers != null)
{
result = adminUsers.contains(user.getName());
}
if (!result)
{
Set<String> adminGroups = configuration.getAdminGroups();
if (adminGroups != null)
{
result = Util.containsOne(adminGroups, groups);
}
}
return result;
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private final AuthenticationManager authenticator;
/** Field description */
private final AuthorizationCollector collector;
/** Field description */
private final ScmConfiguration configuration;
/** Field description */
private final GroupManager groupManager;
/** Field description */
private final LoginAttemptHandler loginAttemptHandler;
/** Field description */
private final Provider<HttpServletRequest> requestProvider;
/** Field description */
private final Provider<HttpServletResponse> responseProvider;
/** Field description */
private final UserDAO userDAO;
/** Field description */
private final UserManager userManager;
}

View File

@@ -1,398 +0,0 @@
/**
* Copyright (c) 2010, Sebastian Sdorra
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of SCM-Manager; nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.web.security;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.SCMContextProvider;
import sonia.scm.cache.Cache;
import sonia.scm.cache.CacheManager;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.security.EncryptionHandler;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import sonia.scm.util.AssertUtil;
import sonia.scm.util.IOUtil;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.io.Serializable;
import java.util.List;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author Sebastian Sdorra
*/
@Singleton
public class ChainAuthenticatonManager extends AbstractAuthenticationManager
{
/** Field description */
public static final String CACHE_NAME = "sonia.cache.auth";
/** the logger for ChainAuthenticatonManager */
private static final Logger logger =
LoggerFactory.getLogger(ChainAuthenticatonManager.class);
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
*
* @param configuration
* @param userManager
* @param authenticationHandlerSet
* @param encryptionHandler
* @param cacheManager
*/
@Inject
public ChainAuthenticatonManager(ScmConfiguration configuration,
UserManager userManager,
Set<AuthenticationHandler> authenticationHandlerSet,
EncryptionHandler encryptionHandler, CacheManager cacheManager)
{
AssertUtil.assertIsNotEmpty(authenticationHandlerSet);
AssertUtil.assertIsNotNull(cacheManager);
this.configuration = configuration;
this.authenticationHandlers = sort(userManager, authenticationHandlerSet);
this.encryptionHandler = encryptionHandler;
this.cache = cacheManager.getCache(CACHE_NAME);
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param request
* @param response
* @param username
* @param password
*
* @return
*/
@Override
public AuthenticationResult authenticate(HttpServletRequest request,
HttpServletResponse response, String username, String password)
{
AssertUtil.assertIsNotEmpty(username);
AssertUtil.assertIsNotEmpty(password);
String encryptedPassword = encryptionHandler.encrypt(password);
AuthenticationResult ar = getCached(username, encryptedPassword);
if (ar == null)
{
if (logger.isTraceEnabled())
{
logger.trace("no authentication result for user {} found in cache",
username);
}
ar = doAuthentication(request, response, username, password);
if ((ar != null) && ar.isCacheable())
{
cache.put(username,
new AuthenticationCacheValue(ar, encryptedPassword));
}
}
else if (logger.isDebugEnabled())
{
logger.debug("authenticate {} via cache", username);
}
return ar;
}
/**
* Method description
*
*
* @throws IOException
*/
@Override
public void close() throws IOException
{
for (AuthenticationHandler authenticator : authenticationHandlers)
{
if (logger.isTraceEnabled())
{
logger.trace("close authenticator {}", authenticator.getClass());
}
IOUtil.close(authenticator);
}
}
/**
* Method description
*
*
* @param context
*/
@Override
public void init(SCMContextProvider context)
{
for (AuthenticationHandler authenticator : authenticationHandlers)
{
if (logger.isTraceEnabled())
{
logger.trace("initialize authenticator {}", authenticator.getClass());
}
authenticator.init(context);
}
}
/**
* 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
*
*
* @param request
* @param response
* @param username
* @param password
*
* @return
*/
private AuthenticationResult doAuthentication(HttpServletRequest request,
HttpServletResponse response, String username, String password)
{
AuthenticationResult ar = null;
if (logger.isTraceEnabled())
{
logger.trace("start authentication chain for user {}", username);
}
for (AuthenticationHandler authenticator : authenticationHandlers)
{
if (logger.isTraceEnabled())
{
logger.trace("check authenticator {} for user {}",
authenticator.getClass(), username);
}
try
{
AuthenticationResult result = authenticator.authenticate(request,
response, username, password);
if (logger.isDebugEnabled())
{
logger.debug("authenticator {} ends with result, {}",
authenticator.getClass().getName(), result);
}
if (stopChain(result))
{
if (result.getState().isSuccessfully() && (result.getUser() != null))
{
User user = result.getUser();
user.setType(authenticator.getType());
ar = result;
// notify authentication listeners
fireAuthenticationEvent(user);
}
break;
}
}
catch (Exception ex)
{
logger.error(
"error durring authentication process of ".concat(
authenticator.getClass().getName()), ex);
}
}
return ar;
}
/**
* Method description
*
*
* @param userManager
* @param authenticationHandlerSet
*
* @return
*/
@VisibleForTesting
private List<AuthenticationHandler> sort(UserManager userManager,
Set<AuthenticationHandler> authenticationHandlerSet)
{
List<AuthenticationHandler> handlers =
Lists.newArrayListWithCapacity(authenticationHandlerSet.size());
String first = Strings.nullToEmpty(userManager.getDefaultType());
for (AuthenticationHandler handler : authenticationHandlerSet)
{
if (first.equals(handler.getType()))
{
handlers.add(0, handler);
}
else
{
handlers.add(handler);
}
}
return handlers;
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @param username
* @param encryptedPassword
*
* @return
*/
private AuthenticationResult getCached(String username,
String encryptedPassword)
{
AuthenticationResult result = null;
AuthenticationCacheValue value = cache.get(username);
if (value != null)
{
String cachedPassword = value.password;
if (cachedPassword.equals(encryptedPassword))
{
result = value.authenticationResult;
}
}
return result;
}
//~--- inner classes --------------------------------------------------------
/**
* Class description
*
*
* @version Enter version here..., 2011-01-15
* @author Sebastian Sdorra
*/
private static class AuthenticationCacheValue implements Serializable
{
/** Field description */
private static final long serialVersionUID = 2201116145941277549L;
//~--- constructors -------------------------------------------------------
/**
* Constructs ...
*
*
*
* @param ar
* @param password
*/
public AuthenticationCacheValue(AuthenticationResult ar, String password)
{
this.authenticationResult =
new AuthenticationResult(ar.getUser().clone(), ar.getGroups(),
ar.getState());
this.password = password;
}
//~--- fields -------------------------------------------------------------
/** Field description */
private AuthenticationResult authenticationResult;
/** Field description */
private String password;
}
//~--- fields ---------------------------------------------------------------
/** authentication handlers */
private final List<AuthenticationHandler> authenticationHandlers;
/** authentication cache */
private final Cache<String, AuthenticationCacheValue> cache;
/** Field description */
private final ScmConfiguration configuration;
/** encryption handler */
private final EncryptionHandler encryptionHandler;
}

View File

@@ -53,7 +53,6 @@ import org.slf4j.LoggerFactory;
import sonia.scm.SCMContext;
import sonia.scm.group.GroupNames;
import sonia.scm.security.Role;
import sonia.scm.security.ScmRealm;
import sonia.scm.user.User;
import sonia.scm.util.AssertUtil;
@@ -75,6 +74,9 @@ public class DefaultAdministrationContext implements AdministrationContext
public static final String SYSTEM_ACCOUNT =
"/sonia/scm/web/security/system-account.xml";
/** Field description */
private static final String REALM = "AdminRealm";
/** the logger for DefaultAdministrationContext */
private static final Logger logger =
LoggerFactory.getLogger(DefaultAdministrationContext.class);
@@ -169,9 +171,9 @@ public class DefaultAdministrationContext implements AdministrationContext
{
SimplePrincipalCollection collection = new SimplePrincipalCollection();
collection.add(adminUser.getId(), ScmRealm.NAME);
collection.add(adminUser, ScmRealm.NAME);
collection.add(new GroupNames(), ScmRealm.NAME);
collection.add(adminUser.getId(), REALM);
collection.add(adminUser, REALM);
collection.add(new GroupNames(), REALM);
return collection;
}

View File

@@ -1,230 +0,0 @@
/**
* Copyright (c) 2010, Sebastian Sdorra
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of SCM-Manager; nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.web.security;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.SCMContextProvider;
import sonia.scm.security.EncryptionHandler;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import sonia.scm.plugin.Extension;
/**
*
* @author Sebastian Sdorra
*/
@Extension
@Singleton
public class DefaultAuthenticationHandler implements AuthenticationHandler
{
/** Field description */
public static final String NAME_DIRECTORY = "users";
/** the logger for XmlAuthenticationHandler */
private static final Logger logger =
LoggerFactory.getLogger(DefaultAuthenticationHandler.class);
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
*
* @param userManager
* @param encryptionHandler
*/
@Inject
public DefaultAuthenticationHandler(UserManager userManager,
EncryptionHandler encryptionHandler)
{
this.userManager = userManager;
this.encryptionHandler = encryptionHandler;
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param request
* @param response
* @param username
* @param password
*
* @return
*/
@Override
public AuthenticationResult authenticate(HttpServletRequest request,
HttpServletResponse response, String username, String password)
{
AuthenticationResult result = null;
User user = userManager.get(username);
if (user != null)
{
if (userManager.getDefaultType().equals(user.getType()))
{
result = authenticate(user, username, password);
}
else
{
if (logger.isDebugEnabled())
{
logger.debug("{} is not an {} user", username,
userManager.getDefaultType());
}
result = AuthenticationResult.NOT_FOUND;
}
}
else
{
if (logger.isDebugEnabled())
{
logger.debug("could not find user {}", username);
}
result = AuthenticationResult.NOT_FOUND;
}
return result;
}
/**
* Method description
*
*
* @throws IOException
*/
@Override
public void close() throws IOException
{
// do nothing
}
/**
* Method description
*
*
* @param provider
*/
@Override
public void init(SCMContextProvider provider)
{
// do nothing
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
@Override
public String getType()
{
return userManager.getDefaultType();
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param user
* @param username
* @param password
*
* @return
*/
private AuthenticationResult authenticate(User user, String username,
String password)
{
AuthenticationResult result = null;
String encryptedPassword = encryptionHandler.encrypt(password);
if (!encryptedPassword.equalsIgnoreCase(user.getPassword()))
{
user = null;
if (logger.isDebugEnabled())
{
logger.debug("password for user {} is wrong", username);
}
result = AuthenticationResult.FAILED;
}
else
{
if (logger.isDebugEnabled())
{
logger.debug("user {} successfully prepared for login", username);
}
user.setPassword(null);
result = new AuthenticationResult(user, AuthenticationState.SUCCESS);
}
return result;
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private EncryptionHandler encryptionHandler;
/** Field description */
private UserManager userManager;
}