From 440542f55e694449606039eaf7e0e0e9e5e5af48 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Sun, 26 Aug 2012 17:16:08 +0200 Subject: [PATCH] start implementation of shiro realm --- .../main/java/sonia/scm/security/Groups.java | 101 +++++ .../scm/security/RepositoryPermission.java | 163 ++++++++ .../RepositoryPermissionResolver.java | 144 +++++++ .../scm/security/ScmAuthenticationToken.java | 220 +++++++++++ .../java/sonia/scm/security/ScmRealm.java | 373 ++++++++++++++++++ 5 files changed, 1001 insertions(+) create mode 100644 scm-webapp/src/main/java/sonia/scm/security/Groups.java create mode 100644 scm-webapp/src/main/java/sonia/scm/security/RepositoryPermission.java create mode 100644 scm-webapp/src/main/java/sonia/scm/security/RepositoryPermissionResolver.java create mode 100644 scm-webapp/src/main/java/sonia/scm/security/ScmAuthenticationToken.java create mode 100644 scm-webapp/src/main/java/sonia/scm/security/ScmRealm.java diff --git a/scm-webapp/src/main/java/sonia/scm/security/Groups.java b/scm-webapp/src/main/java/sonia/scm/security/Groups.java new file mode 100644 index 0000000000..be4504f61a --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/security/Groups.java @@ -0,0 +1,101 @@ +/** + * 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; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.Serializable; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; + +/** + * + * @author Sebastian Sdorra + */ +public class Groups implements Serializable, Iterable +{ + + /** Field description */ + private static final long serialVersionUID = -4152799570939669716L; + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param groups + */ + public Groups(Collection groups) + { + this.groups = groups; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + @Override + public Iterator iterator() + { + return getGroups().iterator(); + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + public Collection getGroups() + { + if (groups == null) + { + groups = Collections.EMPTY_LIST; + } + + return groups; + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private Collection groups; +} diff --git a/scm-webapp/src/main/java/sonia/scm/security/RepositoryPermission.java b/scm-webapp/src/main/java/sonia/scm/security/RepositoryPermission.java new file mode 100644 index 0000000000..c4e8916d57 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/security/RepositoryPermission.java @@ -0,0 +1,163 @@ +/** + * 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 org.apache.shiro.authz.Permission; + +import sonia.scm.repository.PermissionType; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.Serializable; + +/** + * + * @author Sebastian Sdorra + */ +public class RepositoryPermission implements Permission, Serializable +{ + + /** Field description */ + private static final long serialVersionUID = 3832804235417228043L; + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param repositoryId + * @param permissionType + */ + public RepositoryPermission(String repositoryId, + PermissionType permissionType) + { + this.repositoryId = repositoryId; + this.permissionType = permissionType; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param obj + * + * @return + */ + @Override + public boolean equals(Object obj) + { + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final RepositoryPermission other = (RepositoryPermission) obj; + + return Objects.equal(repositoryId, other.repositoryId) + && Objects.equal(permissionType, other.permissionType); + } + + /** + * Method description + * + * + * @return + */ + @Override + public int hashCode() + { + return Objects.hashCode(repositoryId, permissionType); + } + + /** + * Method description + * + * + * @param p + * + * @return + */ + @Override + public boolean implies(Permission p) + { + boolean result = false; + + if (p instanceof RepositoryPermission) + { + RepositoryPermission rp = (RepositoryPermission) p; + + //J- + result = (repositoryId.equals("*") || repositoryId.equals(rp.repositoryId)) + && (permissionType.getValue() >= rp.permissionType.getValue()); + //J+ + } + + return result; + } + + /** + * Method description + * + * + * @return + */ + @Override + public String toString() + { + //J- + return Objects.toStringHelper(this) + .add("repositoryId", repositoryId) + .add("permissionType", permissionType) + .toString(); + //J+ + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private PermissionType permissionType; + + /** Field description */ + private String repositoryId; +} diff --git a/scm-webapp/src/main/java/sonia/scm/security/RepositoryPermissionResolver.java b/scm-webapp/src/main/java/sonia/scm/security/RepositoryPermissionResolver.java new file mode 100644 index 0000000000..9c217136f0 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/security/RepositoryPermissionResolver.java @@ -0,0 +1,144 @@ +/** + * 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.Splitter; + +import org.apache.shiro.authz.Permission; +import org.apache.shiro.authz.permission.PermissionResolver; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sonia.scm.repository.PermissionType; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.Iterator; + +/** + * + * @author Sebastian Sdorra + */ +public class RepositoryPermissionResolver implements PermissionResolver +{ + + /** Field description */ + private static final String TYPE_REPOSITORY = "repository"; + + /** + * the logger for RepositoryPermissionResolver + */ + private static final Logger logger = + LoggerFactory.getLogger(RepositoryPermissionResolver.class); + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param permissionString + * + * @return + */ + @Override + public Permission resolvePermission(String permissionString) + { + RepositoryPermission permission = null; + Iterator permissionIt = + Splitter.on(':').omitEmptyStrings().trimResults().split( + permissionString).iterator(); + + if (permissionIt.hasNext()) + { + String type = permissionIt.next(); + + if (type.equals(TYPE_REPOSITORY)) + { + permission = createRepositoryPermission(permissionIt); + } + else if (logger.isWarnEnabled()) + { + logger.warn("permission '{}' is not a repository permission", + permissionString); + } + } + + return permission; + } + + /** + * Method description + * + * + * @param permissionIt + * + * @return + */ + private RepositoryPermission createRepositoryPermission( + Iterator permissionIt) + { + RepositoryPermission permission = null; + + if (permissionIt.hasNext()) + { + String repositoryId = permissionIt.next(); + + if (permissionIt.hasNext()) + { + try + { + PermissionType type = PermissionType.valueOf(permissionIt.next()); + + permission = new RepositoryPermission(repositoryId, type); + } + catch (IllegalArgumentException ex) + { + logger.warn("type is not a valid permission type", ex); + } + } + else if (logger.isWarnEnabled()) + { + logger.warn("permission type is missing"); + } + } + else if (logger.isWarnEnabled()) + { + logger.warn("repository id and permission type is missing"); + } + + return permission; + } +} diff --git a/scm-webapp/src/main/java/sonia/scm/security/ScmAuthenticationToken.java b/scm-webapp/src/main/java/sonia/scm/security/ScmAuthenticationToken.java new file mode 100644 index 0000000000..a4969680a6 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/security/ScmAuthenticationToken.java @@ -0,0 +1,220 @@ +/** + * 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 org.apache.shiro.authc.AuthenticationToken; + +//~--- JDK imports ------------------------------------------------------------ + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * + * @author Sebastian Sdorra + */ +public class ScmAuthenticationToken implements AuthenticationToken +{ + + /** Field description */ + private static final long serialVersionUID = -3208692400029843828L; + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param request + * @param response + * @param username + * @param password + */ + public ScmAuthenticationToken(HttpServletRequest request, + HttpServletResponse response, String username, String password) + { + this.request = request; + this.response = response; + this.username = username; + this.password = password; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param obj + * + * @return + */ + @Override + public boolean equals(Object obj) + { + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final ScmAuthenticationToken other = (ScmAuthenticationToken) obj; + + return Objects.equal(request, other.request) + && Objects.equal(response, other.response) + && Objects.equal(username, other.username) + && Objects.equal(password, other.password); + } + + /** + * Method description + * + * + * @return + */ + @Override + public int hashCode() + { + return Objects.hashCode(request, response, username, password); + } + + /** + * Method description + * + * + * @return + */ + @Override + public String toString() + { + //J- + return Objects.toStringHelper(this) + .add("request", request) + .add("response", response) + .add("username", username) + .add("password", "xxx") + .toString(); + //J+ + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + @Override + public String getCredentials() + { + return password; + } + + /** + * Method description + * + * + * @return + */ + public String getPassword() + { + return password; + } + + /** + * Method description + * + * + * @return + */ + @Override + public String getPrincipal() + { + return username; + } + + /** + * Method description + * + * + * @return + */ + public HttpServletRequest getRequest() + { + return request; + } + + /** + * Method description + * + * + * @return + */ + public HttpServletResponse getResponse() + { + return response; + } + + /** + * Method description + * + * + * @return + */ + public String getUsername() + { + return username; + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private String password; + + /** Field description */ + private HttpServletRequest request; + + /** Field description */ + private HttpServletResponse response; + + /** Field description */ + private String username; +} diff --git a/scm-webapp/src/main/java/sonia/scm/security/ScmRealm.java b/scm-webapp/src/main/java/sonia/scm/security/ScmRealm.java new file mode 100644 index 0000000000..abb12f0957 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/security/ScmRealm.java @@ -0,0 +1,373 @@ +/** + * 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.collect.Lists; +import com.google.common.collect.Sets; + +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.SimpleAuthenticationInfo; +import org.apache.shiro.authc.UnknownAccountException; +import org.apache.shiro.authc.pam.UnsupportedTokenException; +import org.apache.shiro.authz.AuthorizationInfo; +import org.apache.shiro.authz.SimpleAuthorizationInfo; +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.HandlerEvent; +import sonia.scm.cache.Cache; +import sonia.scm.cache.CacheManager; +import sonia.scm.repository.Permission; +import sonia.scm.repository.Repository; +import sonia.scm.repository.RepositoryDAO; +import sonia.scm.repository.RepositoryListener; +import sonia.scm.repository.RepositoryManager; +import sonia.scm.user.User; +import sonia.scm.user.UserListener; +import sonia.scm.user.UserManager; +import sonia.scm.web.security.AuthenticationManager; +import sonia.scm.web.security.AuthenticationResult; +import sonia.scm.web.security.AuthenticationState; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +/** + * + * @author Sebastian Sdorra + */ +public class ScmRealm extends AuthorizingRealm + implements RepositoryListener, UserListener +{ + + /** Field description */ + private static final String CACHE_NAME = "sonia.cache.authorizing"; + + /** Field description */ + private static final String NAME = "scm"; + + /** Field description */ + private static final String ROLE_ADMIN = "admin"; + + /** Field description */ + private static final String ROLE_USER = "user"; + + /** + * the logger for ScmRealm + */ + private static final Logger logger = LoggerFactory.getLogger(ScmRealm.class); + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param cacheManager + * @param userManager + * @param repositoryManager + * @param repositoryDAO + * @param authenticator + */ + public ScmRealm(CacheManager cacheManager, UserManager userManager, + RepositoryManager repositoryManager, RepositoryDAO repositoryDAO, + AuthenticationManager authenticator) + { + this.repositoryDAO = repositoryDAO; + this.authenticator = authenticator; + this.cache = cacheManager.getCache(String.class, AuthorizationInfo.class, + CACHE_NAME); + setPermissionResolver(new RepositoryPermissionResolver()); + userManager.addListener(this); + repositoryManager.addListener(this); + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param repository + * @param event + */ + @Override + public void onEvent(Repository repository, HandlerEvent event) + { + if (logger.isDebugEnabled()) + { + logger.debug("clear cache, because repository {} has changed", + repository.getName()); + } + + cache.clear(); + } + + /** + * Method description + * + * + * @param user + * @param event + */ + @Override + public void onEvent(User user, HandlerEvent event) + { + if (logger.isDebugEnabled()) + { + logger.debug( + "clear cache of user {}, because user properties have changed", + user.getName()); + } + + cache.remove(user.getId()); + } + + /** + * Method description + * + * + * @param token + * + * @return + */ + @Override + public boolean supports(AuthenticationToken token) + { + return token instanceof ScmAuthenticationToken; + } + + /** + * Method description + * + * + * @param token + * + * @param authToken + * + * @return + * + * @throws AuthenticationException + */ + @Override + protected AuthenticationInfo doGetAuthenticationInfo( + AuthenticationToken authToken) + throws AuthenticationException + { + if ((authToken instanceof ScmAuthenticationToken)) + { + throw new UnsupportedTokenException("ScmAuthenticationToken is required"); + } + + ScmAuthenticationToken token = (ScmAuthenticationToken) authToken; + + AuthenticationInfo info = null; + AuthenticationResult result = + authenticator.authenticate(token.getRequest(), token.getResponse(), + token.getUsername(), token.getPassword()); + + if (result.getState() == AuthenticationState.SUCCESS) + { + info = createAuthenticationInfo(token, result); + } + else if (result.getState() == AuthenticationState.NOT_FOUND) + { + throw new UnknownAccountException( + "unknown account ".concat(token.getUsername())); + } + else + { + throw new AccountException("authentication failed"); + } + + return info; + } + + /** + * Method description + * + * + * @param principals + * + * @return + */ + @Override + protected AuthorizationInfo doGetAuthorizationInfo( + PrincipalCollection principals) + { + User user = principals.oneByType(User.class); + + AuthorizationInfo info = cache.get(user.getName()); + + if (info == null) + { + if (logger.isTraceEnabled()) + { + logger.trace("coullect AuthorizationInfo for user {}", user.getName()); + } + + Groups groups = principals.oneByType(Groups.class); + + info = createAuthorizationInfo(user, groups); + } + else if (logger.isDebugEnabled()) + { + logger.debug("retrieve AuthorizationInfo for user {} from cache", + user.getName()); + } + + return info; + } + + /** + * Method description + * + * + * @param user + * @param groups + * + * @return + */ + private List collectRepositoryPermissions( + User user, Collection groups) + { + List permissions = Lists.newArrayList(); + + for (Repository repository : repositoryDAO.getAll()) + { + List repositoryPermissions = repository.getPermissions(); + + for (Permission permission : repositoryPermissions) + { + if ((permission.isGroupPermission() + && groups.contains( + permission.getName())) || ((!permission.isGroupPermission()) + && user.getName().equals(permission.getName()))) + { + RepositoryPermission rp = + new RepositoryPermission(repository.getId(), permission.getType()); + + if (logger.isTraceEnabled()) + { + logger.trace("add repository permission {} for user {}", rp, + user.getName()); + } + + permissions.add(rp); + } + } + } + + return permissions; + } + + /** + * Method description + * + * + * @param token + * @param result + * + * @return + */ + private AuthenticationInfo createAuthenticationInfo( + ScmAuthenticationToken token, AuthenticationResult result) + { + User user = result.getUser(); + Collection groups = result.getGroups(); + + SimplePrincipalCollection collection = new SimplePrincipalCollection(); + + collection.add(user, NAME); + collection.add(groups, NAME); + + return new SimpleAuthenticationInfo(collection, token.getPassword()); + } + + /** + * Method description + * + * + * @param user + * @param groups + * + * @return + */ + private AuthorizationInfo createAuthorizationInfo(User user, Groups groups) + { + Set roles = Sets.newHashSet(); + + roles.add(ROLE_USER); + + if (user.isAdmin()) + { + if (logger.isDebugEnabled()) + { + logger.debug("grant admin role for user {}", user.getName()); + } + + roles.add(ROLE_ADMIN); + } + + SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles); + + info.addObjectPermissions(collectRepositoryPermissions(user, + groups.getGroups())); + + return info; + } + + + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private AuthenticationManager authenticator; + + /** Field description */ + private Cache cache; + + /** Field description */ + private RepositoryDAO repositoryDAO; +}