diff --git a/scm-core/src/main/java/sonia/scm/group/GroupNames.java b/scm-core/src/main/java/sonia/scm/group/GroupNames.java index acd75a0319..43ca462d32 100644 --- a/scm-core/src/main/java/sonia/scm/group/GroupNames.java +++ b/scm-core/src/main/java/sonia/scm/group/GroupNames.java @@ -35,6 +35,7 @@ package sonia.scm.group; //~--- non-JDK imports -------------------------------------------------------- +import com.google.common.base.Joiner; import com.google.common.base.Objects; import com.google.common.collect.Lists; @@ -156,6 +157,18 @@ public final class GroupNames implements Serializable, Iterable return collection.iterator(); } + /** + * Method description + * + * + * @return + */ + @Override + public String toString() + { + return Joiner.on(", ").join(collection); + } + //~--- get methods ---------------------------------------------------------- /** @@ -172,5 +185,5 @@ public final class GroupNames implements Serializable, Iterable //~--- fields --------------------------------------------------------------- /** Field description */ - private Collection collection; + private final Collection collection; } diff --git a/scm-core/src/main/java/sonia/scm/security/EncryptionException.java b/scm-core/src/main/java/sonia/scm/security/EncryptionException.java deleted file mode 100644 index 3109297d68..0000000000 --- a/scm-core/src/main/java/sonia/scm/security/EncryptionException.java +++ /dev/null @@ -1,90 +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; - -/** - * - * @author Sebastian Sdorra - */ -public class EncryptionException extends RuntimeException -{ - - /** Field description */ - private static final long serialVersionUID = -3733681356044140444L; - - //~--- constructors --------------------------------------------------------- - - /** - * Constructs ... - * - */ - public EncryptionException() - { - super(); - } - - /** - * Constructs ... - * - * - * @param message - */ - public EncryptionException(String message) - { - super(message); - } - - /** - * Constructs ... - * - * - * @param cause - */ - public EncryptionException(Throwable cause) - { - super(cause); - } - - /** - * Constructs ... - * - * - * @param message - * @param cause - */ - public EncryptionException(String message, Throwable cause) - { - super(message, cause); - } -} diff --git a/scm-core/src/main/java/sonia/scm/security/EncryptionHandler.java b/scm-core/src/main/java/sonia/scm/security/EncryptionHandler.java deleted file mode 100644 index a326f223af..0000000000 --- a/scm-core/src/main/java/sonia/scm/security/EncryptionHandler.java +++ /dev/null @@ -1,52 +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; - -/** - * - * @author Sebastian Sdorra - */ -public interface EncryptionHandler -{ - - /** - * Method description - * - * - * @param value - * - * @return - */ - public String encrypt(String value); -} diff --git a/scm-core/src/main/java/sonia/scm/security/LoginAttemptHandler.java b/scm-core/src/main/java/sonia/scm/security/LoginAttemptHandler.java deleted file mode 100644 index 9d44c3cfe7..0000000000 --- a/scm-core/src/main/java/sonia/scm/security/LoginAttemptHandler.java +++ /dev/null @@ -1,87 +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 org.apache.shiro.authc.AuthenticationException; -import org.apache.shiro.authc.AuthenticationToken; - -import sonia.scm.web.security.AuthenticationResult; - -/** - * Login attempt handler. - * - * @author Sebastian Sdorra - * @since 1.34 - */ -public interface LoginAttemptHandler -{ - - /** - * This method is called before the authentication procedure is invoked. - * - * - * @param token authentication token - * - * @throws AuthenticationException - */ - public void beforeAuthentication(AuthenticationToken token) - throws AuthenticationException; - - /** - * Handle successful authentication. - * - * - * @param token authentication token - * @param result successful authentication result - * - * @throws AuthenticationException - */ - public void onSuccessfulAuthentication(AuthenticationToken token, - AuthenticationResult result) - throws AuthenticationException; - - /** - * Handle unsuccessful authentication. - * - * - * @param token authentication token - * @param result unsuccessful authentication result - * - * @throws AuthenticationException - */ - public void onUnsuccessfulAuthentication(AuthenticationToken token, - AuthenticationResult result) - throws AuthenticationException; -} diff --git a/scm-core/src/main/java/sonia/scm/security/MessageDigestEncryptionHandler.java b/scm-core/src/main/java/sonia/scm/security/MessageDigestEncryptionHandler.java deleted file mode 100644 index 2176f6fde1..0000000000 --- a/scm-core/src/main/java/sonia/scm/security/MessageDigestEncryptionHandler.java +++ /dev/null @@ -1,145 +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 sonia.scm.util.AssertUtil; -import sonia.scm.util.Util; - -//~--- JDK imports ------------------------------------------------------------ - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -/** - * - * @author Sebastian Sdorra - */ -public class MessageDigestEncryptionHandler implements EncryptionHandler -{ - - /** Field description */ - public static final String DEFAULT_DIGEST = "SHA-1"; - - //~--- constructors --------------------------------------------------------- - - /** - * Constructs ... - * - */ - public MessageDigestEncryptionHandler() - { - this.digest = DEFAULT_DIGEST; - } - - /** - * Constructs ... - * - * - * @param digest - */ - public MessageDigestEncryptionHandler(String digest) - { - this.digest = digest; - } - - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - * - * @param value - * - * @return - */ - @Override - public String encrypt(String value) - { - String result = null; - - try - { - AssertUtil.assertIsNotEmpty(value); - - MessageDigest messageDigest = MessageDigest.getInstance(digest); - - result = encrypt(messageDigest, value); - } - catch (NoSuchAlgorithmException ex) - { - throw new EncryptionException(ex); - } - - return result; - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @return - */ - public String getDigest() - { - return digest; - } - - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - * - * @param messageDigest - * @param value - * - * @return - */ - private String encrypt(MessageDigest messageDigest, String value) - { - messageDigest.reset(); - messageDigest.update(value.getBytes()); - - return Util.toString(messageDigest.digest()); - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private String digest; -} diff --git a/scm-core/src/main/java/sonia/scm/security/PasswordHandler.java b/scm-core/src/main/java/sonia/scm/security/PasswordHandler.java deleted file mode 100644 index dde208339a..0000000000 --- a/scm-core/src/main/java/sonia/scm/security/PasswordHandler.java +++ /dev/null @@ -1,69 +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 sonia.scm.PasswordAware; - -/** - * Checks if a plain text password matches against a encrypted one and updates - * password objects. - * - * @author Sebastian Sdorra - * @since 1.13 - */ -public interface PasswordHandler -{ - - /** - * Returns true if the given password - * is the same as store in the password object. - * - * - * @param passwordAware object that holds the encrypted password - * @param password plain text password to check - * - * @return true if the password matches the encrypted one - */ - public boolean matches(PasswordAware passwordAware, String password); - - /** - * Updates the password of the given password object. - * - * - * @param passwordAware object to update - * @param password plain text password - */ - public void updatePassword(PasswordAware passwordAware, String password); -} diff --git a/scm-core/src/main/java/sonia/scm/util/HttpUtil.java b/scm-core/src/main/java/sonia/scm/util/HttpUtil.java index abe7fdd089..236e3f8435 100644 --- a/scm-core/src/main/java/sonia/scm/util/HttpUtil.java +++ b/scm-core/src/main/java/sonia/scm/util/HttpUtil.java @@ -73,6 +73,18 @@ public final class HttpUtil /** Field description */ public static final String ENCODING = "UTF-8"; + /** + * authorization header + * @since 2.0.0 + */ + public static final String HEADER_AUTHORIZATION = "Authorization"; + + /** + * Basic authorization scheme + * @since 2.0.0 + */ + public static final String AUTHORIZATION_SCHEME_BASIC = "Basic"; + /** * location header * @since 1.43 diff --git a/scm-core/src/main/java/sonia/scm/web/security/AbstractAuthenticationManager.java b/scm-core/src/main/java/sonia/scm/web/security/AbstractAuthenticationManager.java deleted file mode 100644 index 0fd3bc11df..0000000000 --- a/scm-core/src/main/java/sonia/scm/web/security/AbstractAuthenticationManager.java +++ /dev/null @@ -1,66 +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 sonia.scm.event.ScmEventBus; -import sonia.scm.user.User; - -//~--- JDK imports ------------------------------------------------------------ - - -/** - * Abstract base class for {@link AuthenticationManager} implementations. This - * class implements the listener methods of the {@link AuthenticationManager} - * interface. - * - * @author Sebastian Sdorra - */ -public abstract class AbstractAuthenticationManager - implements AuthenticationManager -{ - - /** - * - * Send a {@link AuthenticationEvent} to the {@link ScmEventBus}. - * - * @param user successful authenticated user - */ - protected void fireAuthenticationEvent(User user) - { - ScmEventBus.getInstance().post(new AuthenticationEvent(user)); - } -} diff --git a/scm-core/src/main/java/sonia/scm/web/security/AuthenticationEvent.java b/scm-core/src/main/java/sonia/scm/web/security/AuthenticationEvent.java deleted file mode 100644 index 06be4a76c5..0000000000 --- a/scm-core/src/main/java/sonia/scm/web/security/AuthenticationEvent.java +++ /dev/null @@ -1,137 +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.base.Objects; - -import sonia.scm.event.Event; -import sonia.scm.user.User; - -/** - * The authentication event is called after a successful authentication. - * - * @author Sebastian Sdorra - * @since 1.27 - */ -@Event -public class AuthenticationEvent -{ - - /** - * Constructs a new authentication event. - * - * - * @param user successful authenticated user - */ - public AuthenticationEvent(User user) - { - this.user = user; - } - - //~--- methods -------------------------------------------------------------- - - /** - * {@inheritDoc} - * - * - * @param obj - * - * @return - */ - @Override - public boolean equals(Object obj) - { - if (obj == null) - { - return false; - } - - if (getClass() != obj.getClass()) - { - return false; - } - - final AuthenticationEvent other = (AuthenticationEvent) obj; - - return Objects.equal(user, other.user); - } - - /** - * {@inheritDoc} - * - * - * @return - */ - @Override - public int hashCode() - { - return Objects.hashCode(user); - } - - /** - * {@inheritDoc} - * - * - * @return - */ - @Override - public String toString() - { - - // J+ - return Objects.toStringHelper(this).add("user", user).toString(); - - // J+ - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Returns the successful authenticated user. - * - * - * @return successful authenticated user - */ - public User getUser() - { - return user; - } - - //~--- fields --------------------------------------------------------------- - - /** successful authenticated user */ - private User user; -} diff --git a/scm-core/src/main/java/sonia/scm/web/security/AuthenticationHandler.java b/scm-core/src/main/java/sonia/scm/web/security/AuthenticationHandler.java deleted file mode 100644 index 228221195e..0000000000 --- a/scm-core/src/main/java/sonia/scm/web/security/AuthenticationHandler.java +++ /dev/null @@ -1,70 +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 sonia.scm.Initable; -import sonia.scm.TypedObject; -import sonia.scm.plugin.ExtensionPoint; - -//~--- JDK imports ------------------------------------------------------------ - -import java.io.Closeable; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -/** - * - * @author Sebastian Sdorra - */ -@ExtensionPoint -public interface AuthenticationHandler extends Initable, Closeable, TypedObject -{ - - /** - * Method description - * - * - * @param request - * @param response - * @param username - * @param password - * - * @return - */ - public AuthenticationResult authenticate(HttpServletRequest request, - HttpServletResponse response, String username, String password); -} diff --git a/scm-core/src/main/java/sonia/scm/web/security/AuthenticationManager.java b/scm-core/src/main/java/sonia/scm/web/security/AuthenticationManager.java deleted file mode 100644 index 4960a020e8..0000000000 --- a/scm-core/src/main/java/sonia/scm/web/security/AuthenticationManager.java +++ /dev/null @@ -1,68 +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 sonia.scm.Initable; - -//~--- JDK imports ------------------------------------------------------------ - -import java.io.Closeable; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -/** - * - * @author Sebastian Sdorra - */ -public interface AuthenticationManager - extends Initable, Closeable -{ - - /** - * Method description - * - * - * @param request - * @param response - * @param username - * @param password - * - * @return - */ - public AuthenticationResult authenticate(HttpServletRequest request, - HttpServletResponse response, String username, String password); -} diff --git a/scm-core/src/main/java/sonia/scm/web/security/AuthenticationResult.java b/scm-core/src/main/java/sonia/scm/web/security/AuthenticationResult.java deleted file mode 100644 index baab3901df..0000000000 --- a/scm-core/src/main/java/sonia/scm/web/security/AuthenticationResult.java +++ /dev/null @@ -1,231 +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 sonia.scm.user.User; - -//~--- JDK imports ------------------------------------------------------------ - -import java.io.Serializable; - -import java.util.Collection; - -/** - * - * @author Sebastian Sdorra - */ -public class AuthenticationResult implements Serializable -{ - - /** Field description */ - public static final AuthenticationResult NOT_FOUND = - new AuthenticationResult(AuthenticationState.NOT_FOUND); - - /** Field description */ - public static final AuthenticationResult FAILED = - new AuthenticationResult(AuthenticationState.FAILED); - - /** Field description */ - private static final long serialVersionUID = -1318200822398893598L; - - //~--- constructors --------------------------------------------------------- - - /** - * Constructs ... - * - * - * @param state - */ - public AuthenticationResult(AuthenticationState state) - { - this.state = state; - } - - /** - * Constructs ... - * - * - * - * @param user - */ - public AuthenticationResult(User user) - { - this.user = user; - this.state = AuthenticationState.SUCCESS; - } - - /** - * Constructs ... - * - * - * @param user - * @param state - */ - public AuthenticationResult(User user, AuthenticationState state) - { - this.user = user; - this.state = state; - } - - /** - * Constructs ... - * - * - * - * @param user - * @param groups - */ - public AuthenticationResult(User user, Collection groups) - { - this.user = user; - this.groups = groups; - this.state = AuthenticationState.SUCCESS; - } - - /** - * Constructs ... - * - * - * @param user - * @param groups - * @param state - */ - public AuthenticationResult(User user, Collection groups, - AuthenticationState state) - { - this.user = user; - this.groups = groups; - this.state = state; - } - - //~--- methods -------------------------------------------------------------- - - /** - * Method description - * - * - * @return - */ - @Override - public String toString() - { - StringBuilder out = new StringBuilder("user: "); - - if (user != null) - { - out.append(user.getName()); - } - else - { - out.append("null"); - } - - return out.append(", state: ").append(state.toString()).toString(); - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @return - */ - public Collection getGroups() - { - return groups; - } - - /** - * Method description - * - * - * @return - */ - public AuthenticationState getState() - { - return state; - } - - /** - * Method description - * - * - * @return - */ - public User getUser() - { - return user; - } - - /** - * Method description - * - * - * @return - */ - public boolean isCacheable() - { - return cacheable; - } - - //~--- set methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @param cacheable - */ - public void setCacheable(boolean cacheable) - { - this.cacheable = cacheable; - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private boolean cacheable = true; - - /** Field description */ - private Collection groups; - - /** Field description */ - private AuthenticationState state; - - /** Field description */ - private User user; -} diff --git a/scm-core/src/main/java/sonia/scm/web/security/AuthenticationState.java b/scm-core/src/main/java/sonia/scm/web/security/AuthenticationState.java deleted file mode 100644 index 277c0edb5a..0000000000 --- a/scm-core/src/main/java/sonia/scm/web/security/AuthenticationState.java +++ /dev/null @@ -1,72 +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; - -/** - * - * @author Sebastian Sdorra - */ -public enum AuthenticationState -{ - SUCCESS(true), NOT_FOUND(false), FAILED(false); - - /** - * Constructs ... - * - * - * @param successfully - */ - private AuthenticationState(boolean successfully) - { - this.successfully = successfully; - } - - //~--- get methods ---------------------------------------------------------- - - /** - * Method description - * - * - * @return - */ - public boolean isSuccessfully() - { - return successfully; - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private boolean successfully = false; -} diff --git a/scm-core/src/main/resources/sonia/scm/config/admin-account.xml b/scm-core/src/main/resources/sonia/scm/config/admin-account.xml index 3185b8b2e0..980545d8bd 100644 --- a/scm-core/src/main/resources/sonia/scm/config/admin-account.xml +++ b/scm-core/src/main/resources/sonia/scm/config/admin-account.xml @@ -35,7 +35,7 @@ scmadmin SCM Administrator scm-admin@scm-manager.org - ff8f5c593a01f9fcd3ed48b09a4b013e8d8f3be7 + $shiro1$SHA-512$8192$$yrNahBVDa4Gz+y5gat4msdjyvjtHlVE+N5nTl4WIDhtBFwhSIib13mKJt1sWmVqgHDWi3VwX7fkdkJ2+WToTbw== true xml diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServlet.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServlet.java index 63992c2e71..69c321df06 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServlet.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/web/HgCGIServlet.java @@ -35,6 +35,7 @@ package sonia.scm.web; //~--- non-JDK imports -------------------------------------------------------- +import com.google.common.base.Strings; import com.google.inject.Inject; import com.google.inject.Singleton; @@ -51,13 +52,17 @@ import sonia.scm.repository.HgRepositoryHandler; import sonia.scm.repository.Repository; import sonia.scm.repository.RepositoryProvider; import sonia.scm.repository.RepositoryRequestListenerUtil; +import sonia.scm.security.CipherUtil; import sonia.scm.util.AssertUtil; +import sonia.scm.util.HttpUtil; import sonia.scm.web.cgi.CGIExecutor; import sonia.scm.web.cgi.CGIExecutorFactory; import sonia.scm.web.cgi.EnvList; //~--- JDK imports ------------------------------------------------------------ +import com.sun.jersey.core.util.Base64; + import java.io.File; import java.io.IOException; @@ -86,6 +91,9 @@ public class HgCGIServlet extends HttpServlet /** Field description */ public static final String ENV_SESSION_PREFIX = "SCM_"; + /** Field description */ + private static final String SCM_CREDENTIALS = "SCM_CREDENTIALS"; + /** Field description */ private static final long serialVersionUID = -3492811300905099810L; @@ -183,6 +191,39 @@ public class HgCGIServlet extends HttpServlet } } + /** + * Method description + * + * + * @param env + * @param request + */ + private void addCredentials(EnvList env, HttpServletRequest request) + { + String authorization = request.getHeader(HttpUtil.HEADER_AUTHORIZATION); + + if (!Strings.isNullOrEmpty(authorization)) + { + if (authorization.startsWith(HttpUtil.AUTHORIZATION_SCHEME_BASIC)) + { + String encodedUserInfo = + authorization.substring( + HttpUtil.AUTHORIZATION_SCHEME_BASIC.length()).trim(); + String userInfo = Base64.base64Decode(encodedUserInfo); + + env.set(SCM_CREDENTIALS, CipherUtil.getInstance().encode(userInfo)); + } + else + { + logger.warn("unknow authentication scheme used"); + } + } + else + { + logger.trace("no authorization header found"); + } + } + /** * Method description * @@ -259,6 +300,7 @@ public class HgCGIServlet extends HttpServlet executor.getEnvironment().set(ENV_REPOSITORY_NAME, name); executor.getEnvironment().set(ENV_REPOSITORY_PATH, directory.getAbsolutePath()); + // add hook environment //J- HgEnvironment.prepareEnvironment( @@ -269,6 +311,9 @@ public class HgCGIServlet extends HttpServlet ); //J+ + addCredentials(executor.getEnvironment(), request); + + // unused ??? HttpSession session = request.getSession(); if (session != null) diff --git a/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java b/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java index 7f02b1ac16..bcdb7d1549 100644 --- a/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java +++ b/scm-webapp/src/main/java/sonia/scm/ScmContextListener.java @@ -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)); diff --git a/scm-webapp/src/main/java/sonia/scm/ScmSecurityModule.java b/scm-webapp/src/main/java/sonia/scm/ScmSecurityModule.java index 4821d4baf9..db7cd4b1a6 100644 --- a/scm-webapp/src/main/java/sonia/scm/ScmSecurityModule.java +++ b/scm-webapp/src/main/java/sonia/scm/ScmSecurityModule.java @@ -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; + } } diff --git a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java index e56428746b..66a12cc1a9 100644 --- a/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java +++ b/scm-webapp/src/main/java/sonia/scm/ScmServletModule.java @@ -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); diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/ChangePasswordResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/ChangePasswordResource.java index 121729f65b..90743126bc 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/ChangePasswordResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/ChangePasswordResource.java @@ -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; } diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/UserResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/UserResource.java index ad264fd0a8..42bfe702c7 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/UserResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/UserResource.java @@ -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 * * * @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 if (Util.isNotEmpty(password)) { - user.setPassword(encryptionHandler.encrypt(password)); + user.setPassword(passwordService.encryptPassword(password)); } } //~--- fields --------------------------------------------------------------- /** Field description */ - private EncryptionHandler encryptionHandler; + private PasswordService passwordService; } diff --git a/scm-webapp/src/main/java/sonia/scm/security/ConfigurableLoginAttemptHandler.java b/scm-webapp/src/main/java/sonia/scm/security/ConfigurableLoginAttemptHandler.java deleted file mode 100644 index 18bfbf375b..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/security/ConfigurableLoginAttemptHandler.java +++ /dev/null @@ -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 attempts = - new ConcurrentHashMap(); - - /** Field description */ - private final ScmConfiguration configuration; -} diff --git a/scm-webapp/src/main/java/sonia/scm/security/DefaultRealm.java b/scm-webapp/src/main/java/sonia/scm/security/DefaultRealm.java new file mode 100644 index 0000000000..141dcc3373 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/security/DefaultRealm.java @@ -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 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; +} diff --git a/scm-webapp/src/main/java/sonia/scm/security/ScmRealm.java b/scm-webapp/src/main/java/sonia/scm/security/ScmRealm.java deleted file mode 100644 index 68a58cb124..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/security/ScmRealm.java +++ /dev/null @@ -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 requestProvider, - Provider 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 authenticate(HttpServletRequest request, String password, - AuthenticationResult ar) - { - Set 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 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 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 createGroupSet(AuthenticationResult ar) - { - Set groupSet = Sets.newHashSet(); - - // add group for all authenticated users - groupSet.add(GroupNames.AUTHENTICATED); - - // load external groups - Collection 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 groupSet) - { - Collection 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 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 groups) - { - boolean result = false; - Set adminUsers = configuration.getAdminUsers(); - - if (adminUsers != null) - { - result = adminUsers.contains(user.getName()); - } - - if (!result) - { - Set 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 requestProvider; - - /** Field description */ - private final Provider responseProvider; - - /** Field description */ - private final UserDAO userDAO; - - /** Field description */ - private final UserManager userManager; -} diff --git a/scm-webapp/src/main/java/sonia/scm/web/security/ChainAuthenticatonManager.java b/scm-webapp/src/main/java/sonia/scm/web/security/ChainAuthenticatonManager.java deleted file mode 100644 index eeade3f1a2..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/web/security/ChainAuthenticatonManager.java +++ /dev/null @@ -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 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 sort(UserManager userManager, - Set authenticationHandlerSet) - { - List 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 authenticationHandlers; - - /** authentication cache */ - private final Cache cache; - - /** Field description */ - private final ScmConfiguration configuration; - - /** encryption handler */ - private final EncryptionHandler encryptionHandler; -} diff --git a/scm-webapp/src/main/java/sonia/scm/web/security/DefaultAdministrationContext.java b/scm-webapp/src/main/java/sonia/scm/web/security/DefaultAdministrationContext.java index 37c7abd41d..46c6c5de00 100644 --- a/scm-webapp/src/main/java/sonia/scm/web/security/DefaultAdministrationContext.java +++ b/scm-webapp/src/main/java/sonia/scm/web/security/DefaultAdministrationContext.java @@ -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; } diff --git a/scm-webapp/src/main/java/sonia/scm/web/security/DefaultAuthenticationHandler.java b/scm-webapp/src/main/java/sonia/scm/web/security/DefaultAuthenticationHandler.java deleted file mode 100644 index 466de681ec..0000000000 --- a/scm-webapp/src/main/java/sonia/scm/web/security/DefaultAuthenticationHandler.java +++ /dev/null @@ -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; -} diff --git a/scm-webapp/src/test/java/sonia/scm/it/DeactivatedUserITCase.java b/scm-webapp/src/test/java/sonia/scm/it/DeactivatedUserITCase.java index 68fe13b201..a8585d208c 100644 --- a/scm-webapp/src/test/java/sonia/scm/it/DeactivatedUserITCase.java +++ b/scm-webapp/src/test/java/sonia/scm/it/DeactivatedUserITCase.java @@ -50,6 +50,7 @@ import static sonia.scm.it.IntegrationTestUtil.*; import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.ClientResponse; import com.sun.jersey.api.client.WebResource; +import javax.servlet.http.HttpServletResponse; import javax.ws.rs.core.MediaType; @@ -126,9 +127,8 @@ public class DeactivatedUserITCase Client client = createClient(); ClientResponse response = authenticate(client, slarti.getName(), "slart123"); - assertNotNull(response); - assertEquals(401, response.getStatus()); + assertEquals(HttpServletResponse.SC_FORBIDDEN, response.getStatus()); } //~--- fields --------------------------------------------------------------- diff --git a/scm-webapp/src/test/java/sonia/scm/security/ConfigurableLoginAttemptHandlerTest.java b/scm-webapp/src/test/java/sonia/scm/security/ConfigurableLoginAttemptHandlerTest.java deleted file mode 100644 index ea1fb7a6f2..0000000000 --- a/scm-webapp/src/test/java/sonia/scm/security/ConfigurableLoginAttemptHandlerTest.java +++ /dev/null @@ -1,139 +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 org.apache.shiro.authc.ExcessiveAttemptsException; -import org.apache.shiro.authc.UsernamePasswordToken; - -import org.junit.Test; - -import sonia.scm.config.ScmConfiguration; -import sonia.scm.web.security.AuthenticationResult; -import sonia.scm.web.security.AuthenticationState; - -//~--- JDK imports ------------------------------------------------------------ - -import java.util.concurrent.TimeUnit; - -/** - * - * @author Sebastian Sdorra - */ -public class ConfigurableLoginAttemptHandlerTest -{ - - /** - * Method description - * - */ - @Test(expected = ExcessiveAttemptsException.class) - public void testLoginAttemptLimitReached() - { - LoginAttemptHandler handler = createHandler(2, 2); - UsernamePasswordToken token = new UsernamePasswordToken("hansolo", "hobbo"); - - handler.beforeAuthentication(token); - handler.onUnsuccessfulAuthentication(token, AuthenticationResult.FAILED); - handler.beforeAuthentication(token); - handler.onUnsuccessfulAuthentication(token, AuthenticationResult.FAILED); - handler.beforeAuthentication(token); - } - - /** - * Method description - * - * - * @throws InterruptedException - */ - @Test - public void testLoginAttemptLimitTimeout() throws InterruptedException - { - LoginAttemptHandler handler = createHandler(2, 1); - UsernamePasswordToken token = new UsernamePasswordToken("hansolo", "hobbo"); - - handler.beforeAuthentication(token); - handler.onUnsuccessfulAuthentication(token, AuthenticationResult.FAILED); - handler.beforeAuthentication(token); - handler.onUnsuccessfulAuthentication(token, AuthenticationResult.FAILED); - Thread.currentThread().sleep(TimeUnit.MILLISECONDS.toMillis(1200l)); - handler.beforeAuthentication(token); - } - - /** - * Method description - * - * - * @throws InterruptedException - */ - @Test - public void testLoginAttemptResetOnSuccess() throws InterruptedException - { - LoginAttemptHandler handler = createHandler(2, 1); - UsernamePasswordToken token = new UsernamePasswordToken("hansolo", "hobbo"); - - handler.beforeAuthentication(token); - handler.onUnsuccessfulAuthentication(token, AuthenticationResult.FAILED); - handler.beforeAuthentication(token); - handler.onUnsuccessfulAuthentication(token, AuthenticationResult.FAILED); - - handler.onSuccessfulAuthentication(token, - new AuthenticationResult(AuthenticationState.SUCCESS)); - - handler.beforeAuthentication(token); - handler.onUnsuccessfulAuthentication(token, AuthenticationResult.FAILED); - handler.beforeAuthentication(token); - handler.onUnsuccessfulAuthentication(token, AuthenticationResult.FAILED); - } - - /** - * Method description - * - * - * @param loginAttemptLimit - * @param loginAttemptLimitTimeout - * - * @return - */ - private LoginAttemptHandler createHandler(int loginAttemptLimit, - long loginAttemptLimitTimeout) - { - ScmConfiguration configuration = new ScmConfiguration(); - - configuration.setLoginAttemptLimit(loginAttemptLimit); - configuration.setLoginAttemptLimitTimeout(loginAttemptLimitTimeout); - - return new ConfigurableLoginAttemptHandler(configuration); - } -} diff --git a/scm-webapp/src/test/java/sonia/scm/security/DefaultRealmTest.java b/scm-webapp/src/test/java/sonia/scm/security/DefaultRealmTest.java new file mode 100644 index 0000000000..40aa4ae179 --- /dev/null +++ b/scm-webapp/src/test/java/sonia/scm/security/DefaultRealmTest.java @@ -0,0 +1,308 @@ +/** + * 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.collect.Lists; + +import org.apache.shiro.authc.AuthenticationInfo; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authc.DisabledAccountException; +import org.apache.shiro.authc.IncorrectCredentialsException; +import org.apache.shiro.authc.UnknownAccountException; +import org.apache.shiro.authc.UsernamePasswordToken; +import org.apache.shiro.authc.credential.DefaultPasswordService; +import org.apache.shiro.crypto.hash.DefaultHashService; +import org.apache.shiro.subject.PrincipalCollection; +import org.apache.shiro.subject.SimplePrincipalCollection; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +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 sonia.scm.user.UserTestData; + +import static org.hamcrest.Matchers.*; + +import static org.junit.Assert.*; + +import static org.mockito.Mockito.*; + +//~--- JDK imports ------------------------------------------------------------ + +import java.util.List; + +/** + * + * @author Sebastian Sdorra + */ +@RunWith(MockitoJUnitRunner.class) +public class DefaultRealmTest +{ + + /** + * Method description + * + */ + @Test(expected = DisabledAccountException.class) + public void testDisabledAccount() + { + User user = UserTestData.createMarvin(); + + user.setActive(false); + + UsernamePasswordToken token = daoUser(user, "secret"); + + realm.getAuthenticationInfo(token); + } + + /** + * Method description + * + */ + @Test + public void testGetAuthorizationInfo() + { + SimplePrincipalCollection col = new SimplePrincipalCollection(); + + realm.doGetAuthorizationInfo(col); + verify(collector, times(1)).collect(col); + } + + /** + * Method description + * + */ + @Test + public void testGroupCollection() + { + User user = UserTestData.createTrillian(); + //J- + List groups = Lists.newArrayList( + new Group(DefaultRealm.REALM, "scm", user.getName()), + new Group(DefaultRealm.REALM, "developers", "perfect") + ); + //J+ + + when(groupDAO.getAll()).thenReturn(groups); + + UsernamePasswordToken token = daoUser(user, "secret"); + AuthenticationInfo info = realm.getAuthenticationInfo(token); + GroupNames groupNames = info.getPrincipals().oneByType(GroupNames.class); + + assertNotNull(groupNames); + assertThat(groupNames.getCollection(), hasSize(2)); + assertThat(groupNames, hasItems("scm", GroupNames.AUTHENTICATED)); + } + + /** + * Method description + * + */ + @Test + public void testSimpleAuthentication() + { + User user = UserTestData.createTrillian(); + UsernamePasswordToken token = daoUser(user, "secret"); + AuthenticationInfo info = realm.getAuthenticationInfo(token); + + assertNotNull(info); + + PrincipalCollection collection = info.getPrincipals(); + + assertEquals(token.getUsername(), collection.getPrimaryPrincipal()); + assertThat(collection.getRealmNames(), hasSize(1)); + assertThat(collection.getRealmNames(), hasItem(DefaultRealm.REALM)); + assertEquals(user, collection.oneByType(User.class)); + + GroupNames groups = collection.oneByType(GroupNames.class); + + assertNotNull(groups); + assertThat(groups.getCollection(), hasSize(1)); + assertThat(groups.getCollection(), hasItem(GroupNames.AUTHENTICATED)); + } + + /** + * Method description + * + */ + @Test(expected = UnknownAccountException.class) + public void testUnknownAccount() + { + realm.getAuthenticationInfo(new UsernamePasswordToken("tricia", "secret")); + } + + /** + * Method description + * + */ + @Test(expected = IllegalArgumentException.class) + public void testWithoutUsername() + { + realm.getAuthenticationInfo(new UsernamePasswordToken(null, "secret")); + } + + /** + * Method description + * + */ + @Test(expected = IncorrectCredentialsException.class) + public void testWrongCredentials() + { + UsernamePasswordToken token = daoUser(UserTestData.createDent(), "secret"); + + token.setPassword("secret123".toCharArray()); + realm.getAuthenticationInfo(token); + } + + /** + * Method description + * + */ + @Test(expected = IllegalArgumentException.class) + public void testWrongToken() + { + realm.getAuthenticationInfo(new OtherAuthenticationToken()); + } + + //~--- set methods ---------------------------------------------------------- + + /** + * Method description + * + */ + @Before + public void setUp() + { + service = new DefaultPasswordService(); + + DefaultHashService hashService = new DefaultHashService(); + + // use a small number of iterations for faster test execution + hashService.setHashIterations(512); + service.setHashService(hashService); + realm = new DefaultRealm(service, collector, userDAO, groupDAO); + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param user + * @param password + * + * @return + */ + private UsernamePasswordToken daoUser(User user, String password) + { + user.setPassword(service.encryptPassword(password)); + when(userDAO.get(user.getName())).thenReturn(user); + + return new UsernamePasswordToken(user.getName(), password); + } + + //~--- inner classes -------------------------------------------------------- + + /** + * Class description + * + * + * @version Enter version here..., 14/12/13 + * @author Enter your name here... + */ + private static class OtherAuthenticationToken implements AuthenticationToken + { + + /** Field description */ + private static final long serialVersionUID = 8891352342377018022L; + + //~--- get methods -------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + @Override + public Object getCredentials() + { + throw new UnsupportedOperationException("Not supported yet."); // To change body of generated methods, choose Tools | Templates. + } + + /** + * Method description + * + * + * @return + */ + @Override + public Object getPrincipal() + { + throw new UnsupportedOperationException("Not supported yet."); // To change body of generated methods, choose Tools | Templates. + } + } + + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + @Mock + private AuthorizationCollector collector; + + /** Field description */ + @Mock + private GroupDAO groupDAO; + + /** Field description */ + private DefaultRealm realm; + + /** Field description */ + private DefaultPasswordService service; + + /** Field description */ + @Mock + private UserDAO userDAO; +} diff --git a/scm-webapp/src/test/java/sonia/scm/security/ScmRealmTest.java b/scm-webapp/src/test/java/sonia/scm/security/ScmRealmTest.java deleted file mode 100644 index c1534aff48..0000000000 --- a/scm-webapp/src/test/java/sonia/scm/security/ScmRealmTest.java +++ /dev/null @@ -1,627 +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.Predicate; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.inject.Provider; - -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.UnknownAccountException; -import org.apache.shiro.authc.UsernamePasswordToken; -import org.apache.shiro.authz.AuthorizationInfo; -import org.apache.shiro.authz.Permission; -import org.apache.shiro.subject.PrincipalCollection; - -import org.junit.Test; - -import org.mockito.Mockito; - -import sonia.scm.cache.CacheManager; -import sonia.scm.cache.MapCacheManager; -import sonia.scm.config.ScmConfiguration; -import sonia.scm.group.Group; -import sonia.scm.group.GroupManager; -import sonia.scm.group.GroupNames; -import sonia.scm.repository.PermissionType; -import sonia.scm.repository.Repository; -import sonia.scm.repository.RepositoryDAO; -import sonia.scm.repository.RepositoryTestData; -import sonia.scm.user.User; -import sonia.scm.user.UserDAO; -import sonia.scm.user.UserManager; -import sonia.scm.user.UserTestData; -import sonia.scm.web.security.AuthenticationManager; -import sonia.scm.web.security.AuthenticationResult; -import sonia.scm.web.security.AuthenticationState; - -import static org.hamcrest.Matchers.*; - -import static org.junit.Assert.*; - -import static org.mockito.Mockito.*; - -//~--- JDK imports ------------------------------------------------------------ - -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.concurrent.atomic.AtomicLong; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; - -/** - * - * @author Sebastian Sdorra - */ -public class ScmRealmTest -{ - - /** - * Method description - * - */ - @Test(expected = UnknownAccountException.class) - public void testAuthenticationWithUknownUser() - { - User trillian = createSampleUser(); - ScmRealm realm = createRealm(trillian); - - realm.getAuthenticationInfo(token("marvin", trillian.getPassword())); - } - - /** - * Method description - * - */ - @Test - public void testAuthorizationAdminPermissions() - { - User trillian = createSampleUser(); - - trillian.setAdmin(true); - - AuthorizationInfo ai = authorizationInfo(trillian); - Collection permissions = ai.getObjectPermissions(); - - assertNotNull(permissions); - assertFalse(permissions.isEmpty()); - assertEquals(1, permissions.size()); - //J- - assertTrue( - permissions.contains(new RepositoryPermission("*", PermissionType.OWNER)) - ); - //J+ - } - - /** - * Method description - * - */ - @Test - public void testAuthorizationAdminRoles() - { - User trillian = createSampleUser(); - - trillian.setAdmin(true); - - AuthorizationInfo aci = authorizationInfo(trillian); - Collection roles = aci.getRoles(); - - assertNotNull(roles); - assertEquals(2, roles.size()); - assertTrue(roles.contains(Role.ADMIN)); - assertTrue(roles.contains(Role.USER)); - } - - /** - * Method description - * - */ - @Test - public void testAuthorizationDefaultUserPermissions() - { - User trillian = createSampleUser(); - - AuthorizationInfo ai = authorizationInfo(trillian); - Collection permissions = ai.getObjectPermissions(); - - assertNotNull(permissions); - assertTrue(permissions.isEmpty()); - } - - /** - * Method description - * - */ - @Test - public void testAuthorizationGroupPermissions() - { - User trillian = createSampleUser(); - - String g1 = "g1"; - String g2 = "g2"; - Group g3 = new Group("xml", "g3"); - Group g4 = new Group("xml", "g4"); - - Repository r1 = RepositoryTestData.create42Puzzle(); - - prepareRepo(r1, g1, PermissionType.READ, true); - - Repository r2 = RepositoryTestData.createHappyVerticalPeopleTransporter(); - - prepareRepo(r2, g2, PermissionType.WRITE, true); - - Repository r3 = RepositoryTestData.createHeartOfGold(); - - prepareRepo(r3, g3, PermissionType.OWNER); - - Repository r4 = RepositoryTestData.createRestaurantAtTheEndOfTheUniverse(); - - Set repositories = ImmutableSet.of(r1, r2, r3, r4); - ScmRealm realm = createRealm(trillian, ImmutableSet.of(g1, g2), - ImmutableSet.of(g3, g4), repositories); - AuthenticationInfo aui = realm.getAuthenticationInfo(token(trillian)); - AuthorizationInfo aci = realm.doGetAuthorizationInfo(aui.getPrincipals()); - - Collection permissions = aci.getObjectPermissions(); - - assertNotNull(permissions); - assertFalse(permissions.isEmpty()); - assertEquals(3, permissions.size()); - containPermission(permissions, r1, PermissionType.READ); - containPermission(permissions, r2, PermissionType.WRITE); - containPermission(permissions, r3, PermissionType.OWNER); - } - - /** - * Method description - * - */ - @Test - public void testAuthorizationUserPermissions() - { - User trillian = createSampleUser(); - Repository r1 = RepositoryTestData.create42Puzzle(); - - prepareRepo(r1, trillian, PermissionType.READ); - - Repository r2 = RepositoryTestData.createHappyVerticalPeopleTransporter(); - - prepareRepo(r2, trillian, PermissionType.WRITE); - - Repository r3 = RepositoryTestData.createHeartOfGold(); - - prepareRepo(r3, trillian, PermissionType.OWNER); - - Repository r4 = RepositoryTestData.createRestaurantAtTheEndOfTheUniverse(); - - Set repositories = ImmutableSet.of(r1, r2, r3, r4); - - ScmRealm realm = createRealm(trillian, null, null, repositories); - AuthenticationInfo aui = realm.getAuthenticationInfo(token(trillian)); - AuthorizationInfo aci = realm.doGetAuthorizationInfo(aui.getPrincipals()); - Collection permissions = aci.getObjectPermissions(); - - assertNotNull(permissions); - assertFalse(permissions.isEmpty()); - assertEquals(3, permissions.size()); - containPermission(permissions, r1, PermissionType.READ); - containPermission(permissions, r2, PermissionType.WRITE); - containPermission(permissions, r3, PermissionType.OWNER); - } - - /** - * Method description - * - */ - @Test - public void testAuthorizationUserRoles() - { - AuthorizationInfo aci = authorizationInfo(createSampleUser()); - Collection roles = aci.getRoles(); - - assertNotNull(roles); - assertEquals(1, roles.size()); - assertEquals(Role.USER, roles.iterator().next()); - } - - /** - * Method description - * - */ - @Test(expected = AccountException.class) - public void testFailedAuthentication() - { - User trillian = createSampleUser(); - ScmRealm realm = createRealm(trillian); - - realm.getAuthenticationInfo(token(trillian.getId(), "hobbo")); - } - - /** - * Method description - * - */ - @Test - public void testSimpleAuthentication() - { - User trillian = createSampleUser(); - - //J- - ScmRealm realm = createRealm( - trillian, - ImmutableSet.of("g1", "g2"), - ImmutableSet.of(new Group("xml", "g3"), new Group("xml", "g4")), - null - ); - //J+ - AuthenticationInfo ai = realm.getAuthenticationInfo(token(trillian)); - - assertNotNull(ai); - - PrincipalCollection collection = ai.getPrincipals(); - - assertNotNull(collection); - assertFalse(collection.isEmpty()); - - assertEquals(trillian.getId(), collection.getPrimaryPrincipal()); - assertEquals(trillian, collection.oneByType(User.class)); - - GroupNames groups = collection.oneByType(GroupNames.class); - - assertNotNull(groups); - assertFalse(groups.getCollection().isEmpty()); - assertEquals(5, groups.getCollection().size()); - //J- - assertThat(groups, - containsInAnyOrder("g1", "g2", "g3", "g4", GroupNames.AUTHENTICATED) - ); - //J+ - } - - /** - * Method description - * - * - * @param user - * - * @return - */ - private AuthorizationInfo authorizationInfo(User user) - { - ScmRealm realm = createRealm(user); - AuthenticationInfo aui = realm.getAuthenticationInfo(token(user)); - AuthorizationInfo aci = realm.doGetAuthorizationInfo(aui.getPrincipals()); - - assertNotNull(aci); - - return aci; - } - - /** - * Method description - * - * - * @param permissions - * @param repository - * @param type - */ - private void containPermission(Collection permissions, - Repository repository, PermissionType type) - { - assertTrue( - permissions.contains(new RepositoryPermission(repository.getId(), type))); - } - - /** - * Method description - * - * - * @param user - * - * @return - */ - private ScmRealm createRealm(User user) - { - return createRealm(user, null, null, null); - } - - /** - * Method description - * - * - * - * @param user - * @param authenticationGroups - * @param dbGroups - * @param repositories - * @return - */ - @SuppressWarnings("unchecked") - private ScmRealm createRealm(User user, - Collection authenticationGroups, Collection dbGroups, - Collection repositories) - { - UserManager userManager = mock(UserManager.class); - GroupManager groupManager = mock(GroupManager.class); - - if (dbGroups != null) - { - when(groupManager.getGroupsForMember(user.getId())).thenReturn(dbGroups); - } - - RepositoryDAO repositoryDAO = mock(RepositoryDAO.class); - - if (repositories != null) - { - when(repositoryDAO.getAll()).thenReturn(repositories); - } - - UserDAO userDAO = mock(UserDAO.class); - - when(userDAO.get(user.getId())).thenReturn(user); - - HttpSession session = mock(HttpSession.class); - - final HttpServletRequest request = mock(HttpServletRequest.class); - - when(request.getSession(true)).thenReturn(session); - - Provider requestProvider = - new Provider() - { - - @Override - public HttpServletRequest get() - { - return request; - } - }; - - final HttpServletResponse response = mock(HttpServletResponse.class); - Provider responseProvider = - new Provider() - { - - @Override - public HttpServletResponse get() - { - return response; - } - }; - - //J- - AuthenticationManager authManager = mock(AuthenticationManager.class); - - when( - authManager.authenticate( - eq(requestProvider.get()), - eq(responseProvider.get()), - eq(user.getId()), - eq(user.getPassword()) - ) - ).thenReturn( - new AuthenticationResult(user, authenticationGroups, AuthenticationState.SUCCESS) - ); - - when( - authManager.authenticate( - eq(requestProvider.get()), - eq(responseProvider.get()), - eq(user.getId()), - argThat( - not(user.getPassword()) - ) - ) - ).thenReturn( - AuthenticationResult.FAILED - ); - - when( - authManager.authenticate( - eq(requestProvider.get()), - eq(responseProvider.get()), - argThat( - not(user.getName()) - ), - anyString() - ) - ).thenReturn( - AuthenticationResult.NOT_FOUND - ); - - SecuritySystem securitySystem = mock(SecuritySystem.class); - when( - securitySystem.getPermissions(Mockito.any(Predicate.class)) - ).thenReturn( - Collections.EMPTY_LIST - ); - - CacheManager cacheManager = new MapCacheManager(); - - AuthorizationCollector collector = new AuthorizationCollector( - cacheManager, - repositoryDAO, - securitySystem, - new RepositoryPermissionResolver() - ); - - LoginAttemptHandler dummyLoginAttemptHandler = new LoginAttemptHandler() - { - @Override - public void beforeAuthentication(AuthenticationToken token) - throws AuthenticationException {} - - @Override - public void onSuccessfulAuthentication(AuthenticationToken token, - AuthenticationResult result) throws AuthenticationException {} - - @Override - public void onUnsuccessfulAuthentication(AuthenticationToken token, - AuthenticationResult result) throws AuthenticationException {} - }; - - return new ScmRealm( - new ScmConfiguration(), - dummyLoginAttemptHandler, - collector, - // cacheManager, - userManager, - groupManager, - userDAO, - authManager, - null, - requestProvider, - responseProvider - ); - //J+ - } - - /** - * Method description - * - * - * @return - */ - private User createSampleUser() - { - User trillian = UserTestData.createTrillian(); - - trillian.setPassword("moppo123"); - - return trillian; - } - - /** - * Method description - * - * - * @return - */ - private String id() - { - return String.valueOf(counter.incrementAndGet()); - } - - /** - * Method description - * - * - * @param repository - * @param user - * @param type - */ - private void prepareRepo(Repository repository, User user, - PermissionType type) - { - prepareRepo(repository, user.getId(), type, false); - } - - /** - * Method description - * - * - * @param repository - * @param group - * @param type - */ - private void prepareRepo(Repository repository, Group group, - PermissionType type) - { - prepareRepo(repository, group.getId(), type, true); - } - - /** - * Method description - * - * - * @param repository - * @param name - * @param type - * @param groupPermission - */ - private void prepareRepo(Repository repository, String name, - PermissionType type, boolean groupPermission) - { - repository.setId(id()); - - List permissions = - ImmutableList.of(new sonia.scm.repository.Permission(name, type, - groupPermission)); - - repository.setPermissions(permissions); - } - - /** - * Method description - * - * - * @param user - * - * @return - */ - private AuthenticationToken token(User user) - { - return new UsernamePasswordToken(user.getId(), user.getPassword()); - } - - /** - * Method description - * - * - * @param username - * @param password - * - * @return - */ - private AuthenticationToken token(String username, String password) - { - return new UsernamePasswordToken(username, password); - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private AtomicLong counter = new AtomicLong(); -} diff --git a/scm-webapp/src/test/java/sonia/scm/web/security/ChainAuthenticationManagerTest.java b/scm-webapp/src/test/java/sonia/scm/web/security/ChainAuthenticationManagerTest.java deleted file mode 100644 index 1fa0ff384a..0000000000 --- a/scm-webapp/src/test/java/sonia/scm/web/security/ChainAuthenticationManagerTest.java +++ /dev/null @@ -1,384 +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.collect.ImmutableSet; - -import org.junit.Test; - -import sonia.scm.AbstractTestBase; -import sonia.scm.SCMContextProvider; -import sonia.scm.cache.MapCacheManager; -import sonia.scm.config.ScmConfiguration; -import sonia.scm.security.MessageDigestEncryptionHandler; -import sonia.scm.user.User; -import sonia.scm.user.UserManager; -import sonia.scm.user.UserTestData; -import sonia.scm.util.MockUtil; - -import static org.junit.Assert.*; - -import static org.mockito.Mockito.*; - -//~--- JDK imports ------------------------------------------------------------ - -import java.io.IOException; - -import java.util.Set; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -/** - * - * @author Sebastian Sdorra - */ -public class ChainAuthenticationManagerTest extends AbstractTestBase -{ - - /** - * Method description - * - */ - @Test - public void testAuthenticateFailed() - { - manager = createManager(); - - AuthenticationResult result = manager.authenticate(request, response, - trillian.getName(), "trillian"); - - assertNull(result); - } - - /** - * Method description - * - */ - @Test - public void testAuthenticateNotFound() - { - manager = createManager(); - - AuthenticationResult result = manager.authenticate(request, response, - "dent", "trillian"); - - assertNull(result); - } - - /** - * Method description - * - */ - @Test - public void testAuthenticateSuccess() - { - manager = createManager(); - - AuthenticationResult result = manager.authenticate(request, response, - trillian.getName(), "trillian123"); - - assertNotNull(result); - assertUserEquals(trillian, result.getUser()); - assertEquals("trilliansType", result.getUser().getType()); - result = manager.authenticate(request, response, perfect.getName(), - "perfect123"); - assertNotNull(perfect); - assertUserEquals(perfect, result.getUser()); - assertEquals("perfectsType", result.getUser().getType()); - } - - /** - * Method description - * - */ - @Test - public void testAuthenticationOrder() - { - User trillian = UserTestData.createTrillian(); - - trillian.setPassword("trillian123"); - - SingleUserAuthenticaionHandler a1 = - new SingleUserAuthenticaionHandler("a1", trillian); - SingleUserAuthenticaionHandler a2 = - new SingleUserAuthenticaionHandler("a2", trillian); - - manager = createManager("a2", false, a1, a2); - - AuthenticationResult result = manager.authenticate(request, response, - trillian.getName(), "trillian123"); - - assertNotNull(result); - assertEquals(AuthenticationState.SUCCESS, result.getState()); - 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 - * - * - * @throws Exception - */ - @Override - protected void postSetUp() throws Exception - { - request = MockUtil.getHttpServletRequest(); - response = MockUtil.getHttpServletResponse(); - } - - /** - * Method description - * - * - * @throws Exception - */ - @Override - protected void preTearDown() throws Exception - { - manager.close(); - } - - /** - * Method description - * - * - * @param user - * @param other - */ - private void assertUserEquals(User user, User other) - { - assertEquals(user.getName(), other.getName()); - assertEquals(user.getDisplayName(), other.getDisplayName()); - assertEquals(user.getMail(), other.getMail()); - } - - /** - * Method description - * - * - * @return - */ - private ChainAuthenticatonManager createManager() - { - perfect = UserTestData.createPerfect(); - perfect.setPassword("perfect123"); - trillian = UserTestData.createTrillian(); - trillian.setPassword("trillian123"); - - return createManager("", false, - new SingleUserAuthenticaionHandler("perfectsType", perfect), - new SingleUserAuthenticaionHandler("trilliansType", trillian)); - } - - /** - * Method description - * - * - * @param defaultType - * @param skipFailedAuthenticators - * @param handlers - * - * @return - */ - private ChainAuthenticatonManager createManager(String defaultType, - 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 handlerSet = ImmutableSet.copyOf(handlers); - - UserManager userManager = mock(UserManager.class); - - when(userManager.getDefaultType()).thenReturn(defaultType); - manager = new ChainAuthenticatonManager(configuration, userManager, - handlerSet, new MessageDigestEncryptionHandler(), new MapCacheManager()); - manager.init(contextProvider); - - return manager; - } - - //~--- inner classes -------------------------------------------------------- - - /** - * Class description - * - * - * @version Enter version here..., 2010-12-07 - * @author Sebastian Sdorra - */ - private static class SingleUserAuthenticaionHandler - implements AuthenticationHandler - { - - /** - * Constructs ... - * - * - * @param type - * @param user - */ - public SingleUserAuthenticaionHandler(String type, User user) - { - this.type = type; - this.user = user; - } - - //~--- 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; - - if (username.equals(user.getName())) - { - if (password.equals(user.getPassword())) - { - result = new AuthenticationResult(user.clone()); - } - else - { - result = AuthenticationResult.FAILED; - } - } - else - { - result = AuthenticationResult.NOT_FOUND; - } - - return result; - } - - /** - * Method description - * - * - * @throws IOException - */ - @Override - public void close() throws IOException {} - - /** - * Method description - * - * - * @param context - */ - @Override - public void init(SCMContextProvider context) {} - - //~--- get methods -------------------------------------------------------- - - /** - * Method description - * - * - * @return - */ - @Override - public String getType() - { - return type; - } - - //~--- fields ------------------------------------------------------------- - - /** Field description */ - private final String type; - - /** Field description */ - private final User user; - } - - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private ChainAuthenticatonManager manager; - - /** Field description */ - private User perfect; - - /** Field description */ - private HttpServletRequest request; - - /** Field description */ - private HttpServletResponse response; - - /** Field description */ - private User trillian; -} diff --git a/scm-webapp/src/test/java/sonia/scm/web/security/DefaultAuthenticationHandlerTest.java b/scm-webapp/src/test/java/sonia/scm/web/security/DefaultAuthenticationHandlerTest.java deleted file mode 100644 index 5eb661f884..0000000000 --- a/scm-webapp/src/test/java/sonia/scm/web/security/DefaultAuthenticationHandlerTest.java +++ /dev/null @@ -1,172 +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 org.junit.Test; - -import sonia.scm.AbstractTestBase; -import sonia.scm.security.EncryptionHandler; -import sonia.scm.security.MessageDigestEncryptionHandler; -import sonia.scm.store.JAXBStoreFactory; -import sonia.scm.store.StoreFactory; -import sonia.scm.user.DefaultUserManager; -import sonia.scm.user.User; -import sonia.scm.user.UserTestData; -import sonia.scm.user.xml.XmlUserDAO; -import sonia.scm.util.MockUtil; - -import static org.junit.Assert.*; - -//~--- JDK imports ------------------------------------------------------------ - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -/** - * - * @author Sebastian Sdorra - */ -public class DefaultAuthenticationHandlerTest extends AbstractTestBase -{ - - /** - * Method description - * - */ - @Test - public void testAuthenticateFailed() - { - AuthenticationResult result = handler.authenticate(request, reponse, - slarti.getName(), "otherPWD"); - - assertNotNull(result); - assertTrue(result.getState() == AuthenticationState.FAILED); - assertNull(result.getUser()); - } - - /** - * Method description - * - */ - @Test - public void testAuthenticateNotFound() - { - AuthenticationResult result = handler.authenticate(request, reponse, - "notSlarti", "otherPWD"); - - assertNotNull(result); - assertTrue(result.getState() == AuthenticationState.NOT_FOUND); - assertNull(result.getUser()); - } - - /** - * Method description - * - */ - @Test - public void testAuthenticateSuccess() - { - AuthenticationResult result = handler.authenticate(request, reponse, - slarti.getName(), "slartisPWD"); - - assertNotNull(result); - assertTrue(result.getState() == AuthenticationState.SUCCESS); - assertNotNull(result.getUser()); - assertEquals(slarti.getName(), result.getUser().getName()); - assertEquals(slarti.getDisplayName(), result.getUser().getDisplayName()); - assertEquals(slarti.getMail(), result.getUser().getMail()); - } - - /** - * Method description - * - * - * @throws Exception - */ - @Override - protected void postSetUp() throws Exception - { - EncryptionHandler enc = new MessageDigestEncryptionHandler(); - - slarti = UserTestData.createSlarti(); - slarti.setPassword(enc.encrypt("slartisPWD")); - - StoreFactory storeFactory = new JAXBStoreFactory(); - - storeFactory.init(contextProvider); - - XmlUserDAO userDAO = new XmlUserDAO(storeFactory); - - setSubject(MockUtil.createAdminSubject()); - - DefaultUserManager userManager = new DefaultUserManager(userDAO); - - userManager.init(contextProvider); - userManager.create(slarti); - clearSubject(); - - handler = new DefaultAuthenticationHandler(userManager, enc); - handler.init(contextProvider); - request = MockUtil.getHttpServletRequest(); - reponse = MockUtil.getHttpServletResponse(); - } - - /** - * Method description - * - * - * @throws Exception - */ - @Override - protected void preTearDown() throws Exception - { - handler.close(); - } - - //~--- fields --------------------------------------------------------------- - - /** Field description */ - private DefaultAuthenticationHandler handler; - - /** Field description */ - private HttpServletResponse reponse; - - /** Field description */ - private HttpServletRequest request; - - /** Field description */ - private User slarti; -}