mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-09 06:55:47 +01:00
replace scm-manager 1.x security api with apache shiro and use PasswordService for stronger password hashes
This commit is contained in:
@@ -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<String>
|
||||
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<String>
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private Collection<String> collection;
|
||||
private final Collection<String> collection;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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<String> groups)
|
||||
{
|
||||
this.user = user;
|
||||
this.groups = groups;
|
||||
this.state = AuthenticationState.SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param user
|
||||
* @param groups
|
||||
* @param state
|
||||
*/
|
||||
public AuthenticationResult(User user, Collection<String> 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<String> 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<String> groups;
|
||||
|
||||
/** Field description */
|
||||
private AuthenticationState state;
|
||||
|
||||
/** Field description */
|
||||
private User user;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -35,7 +35,7 @@
|
||||
<name>scmadmin</name>
|
||||
<displayName>SCM Administrator</displayName>
|
||||
<mail>scm-admin@scm-manager.org</mail>
|
||||
<password>ff8f5c593a01f9fcd3ed48b09a4b013e8d8f3be7</password>
|
||||
<password>$shiro1$SHA-512$8192$$yrNahBVDa4Gz+y5gat4msdjyvjtHlVE+N5nTl4WIDhtBFwhSIib13mKJt1sWmVqgHDWi3VwX7fkdkJ2+WToTbw==</password>
|
||||
<admin>true</admin>
|
||||
<type>xml</type>
|
||||
</users>
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -39,11 +39,11 @@ import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.authc.credential.PasswordService;
|
||||
|
||||
import org.codehaus.enunciate.jaxrs.TypeHint;
|
||||
import org.codehaus.enunciate.modules.jersey.ExternallyManagedLifecycle;
|
||||
|
||||
import sonia.scm.security.EncryptionHandler;
|
||||
import sonia.scm.security.Role;
|
||||
import sonia.scm.user.User;
|
||||
import sonia.scm.user.UserException;
|
||||
@@ -95,15 +95,13 @@ public class UserResource extends AbstractManagerResource<User, UserException>
|
||||
*
|
||||
*
|
||||
* @param userManager
|
||||
* @param encryptionHandler
|
||||
* @param securityContextProvider
|
||||
* @param passwordService
|
||||
*/
|
||||
@Inject
|
||||
public UserResource(UserManager userManager,
|
||||
EncryptionHandler encryptionHandler)
|
||||
public UserResource(UserManager userManager, PasswordService passwordService)
|
||||
{
|
||||
super(userManager);
|
||||
this.encryptionHandler = encryptionHandler;
|
||||
this.passwordService = passwordService;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
@@ -386,12 +384,12 @@ public class UserResource extends AbstractManagerResource<User, UserException>
|
||||
|
||||
if (Util.isNotEmpty(password))
|
||||
{
|
||||
user.setPassword(encryptionHandler.encrypt(password));
|
||||
user.setPassword(passwordService.encryptPassword(password));
|
||||
}
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private EncryptionHandler encryptionHandler;
|
||||
private PasswordService passwordService;
|
||||
}
|
||||
|
||||
@@ -1,331 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2010, Sebastian Sdorra
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer. 2. Redistributions in
|
||||
* binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution. 3. Neither the name of SCM-Manager;
|
||||
* nor the names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* http://bitbucket.org/sdorra/scm-manager
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
package sonia.scm.security;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import org.apache.shiro.authc.AuthenticationException;
|
||||
import org.apache.shiro.authc.AuthenticationToken;
|
||||
import org.apache.shiro.authc.ExcessiveAttemptsException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.config.ScmConfiguration;
|
||||
import sonia.scm.web.security.AuthenticationResult;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
@Singleton
|
||||
public class ConfigurableLoginAttemptHandler implements LoginAttemptHandler
|
||||
{
|
||||
|
||||
/**
|
||||
* the logger for ConfigurableLoginAttemptHandler
|
||||
*/
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(ConfigurableLoginAttemptHandler.class);
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param configuration
|
||||
*/
|
||||
@Inject
|
||||
public ConfigurableLoginAttemptHandler(ScmConfiguration configuration)
|
||||
{
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param token
|
||||
*
|
||||
* @throws AuthenticationException
|
||||
*/
|
||||
@Override
|
||||
public void beforeAuthentication(AuthenticationToken token)
|
||||
throws AuthenticationException
|
||||
{
|
||||
if (isEnabled())
|
||||
{
|
||||
handleBeforeAuthentication(token);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.trace("LoginAttemptHandler is disabled");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param token
|
||||
* @param result
|
||||
*
|
||||
* @throws AuthenticationException
|
||||
*/
|
||||
@Override
|
||||
public void onSuccessfulAuthentication(AuthenticationToken token,
|
||||
AuthenticationResult result)
|
||||
throws AuthenticationException
|
||||
{
|
||||
if (isEnabled())
|
||||
{
|
||||
handleOnSuccessfulAuthentication(token);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.trace("LoginAttemptHandler is disabled");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param token
|
||||
* @param result
|
||||
*
|
||||
* @throws AuthenticationException
|
||||
*/
|
||||
@Override
|
||||
public void onUnsuccessfulAuthentication(AuthenticationToken token,
|
||||
AuthenticationResult result)
|
||||
throws AuthenticationException
|
||||
{
|
||||
if (isEnabled())
|
||||
{
|
||||
handleOnUnsuccessfulAuthentication(token);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.trace("LoginAttemptHandler is disabled");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param token
|
||||
*/
|
||||
private void handleBeforeAuthentication(AuthenticationToken token)
|
||||
{
|
||||
LoginAttempt attempt = getAttempt(token);
|
||||
long time = System.currentTimeMillis() - attempt.lastAttempt;
|
||||
|
||||
if (time > getLoginAttemptLimitTimeout())
|
||||
{
|
||||
logger.debug("reset login attempts for {}, because of time",
|
||||
token.getPrincipal());
|
||||
attempt.reset();
|
||||
}
|
||||
else if (attempt.counter >= configuration.getLoginAttemptLimit())
|
||||
{
|
||||
logger.warn("account {} is temporary locked, because of {}",
|
||||
token.getPrincipal(), attempt);
|
||||
attempt.increase();
|
||||
|
||||
throw new ExcessiveAttemptsException("account is temporary locked");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param token
|
||||
* @param result
|
||||
*
|
||||
* @throws AuthenticationException
|
||||
*/
|
||||
private void handleOnSuccessfulAuthentication(AuthenticationToken token)
|
||||
throws AuthenticationException
|
||||
{
|
||||
logger.debug("reset login attempts for {}, because of successful login",
|
||||
token.getPrincipal());
|
||||
getAttempt(token).reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param token
|
||||
* @param result
|
||||
*
|
||||
* @throws AuthenticationException
|
||||
*/
|
||||
private void handleOnUnsuccessfulAuthentication(AuthenticationToken token)
|
||||
throws AuthenticationException
|
||||
{
|
||||
logger.debug("increase failed login attempts for {}", token.getPrincipal());
|
||||
|
||||
LoginAttempt attempt = getAttempt(token);
|
||||
|
||||
attempt.increase();
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param token
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private LoginAttempt getAttempt(AuthenticationToken token)
|
||||
{
|
||||
LoginAttempt freshAttempt = new LoginAttempt();
|
||||
LoginAttempt attempt = attempts.putIfAbsent(token.getPrincipal(),
|
||||
freshAttempt);
|
||||
|
||||
if (attempt == null)
|
||||
{
|
||||
attempt = freshAttempt;
|
||||
}
|
||||
|
||||
return attempt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private long getLoginAttemptLimitTimeout()
|
||||
{
|
||||
return TimeUnit.SECONDS.toMillis(
|
||||
configuration.getLoginAttemptLimitTimeout());
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private boolean isEnabled()
|
||||
{
|
||||
return (configuration.getLoginAttemptLimit() > 0)
|
||||
&& (configuration.getLoginAttemptLimitTimeout() > 0l);
|
||||
}
|
||||
|
||||
//~--- inner classes --------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Login attempt
|
||||
*/
|
||||
private static class LoginAttempt
|
||||
{
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
//J-
|
||||
return Objects.toStringHelper(this)
|
||||
.add("counter", counter)
|
||||
.add("lastAttempt", lastAttempt)
|
||||
.toString();
|
||||
//J+
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*/
|
||||
synchronized void increase()
|
||||
{
|
||||
counter++;
|
||||
lastAttempt = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*/
|
||||
synchronized void reset()
|
||||
{
|
||||
lastAttempt = -1l;
|
||||
counter = 0;
|
||||
}
|
||||
|
||||
//~--- fields -------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private int counter = 0;
|
||||
|
||||
/** Field description */
|
||||
private long lastAttempt = -1l;
|
||||
}
|
||||
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private final ConcurrentMap<Object, LoginAttempt> attempts =
|
||||
new ConcurrentHashMap<Object, LoginAttempt>();
|
||||
|
||||
/** Field description */
|
||||
private final ScmConfiguration configuration;
|
||||
}
|
||||
228
scm-webapp/src/main/java/sonia/scm/security/DefaultRealm.java
Normal file
228
scm-webapp/src/main/java/sonia/scm/security/DefaultRealm.java
Normal file
@@ -0,0 +1,228 @@
|
||||
/**
|
||||
* Copyright (c) 2014, Sebastian Sdorra All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer. 2. Redistributions in
|
||||
* binary form must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution. 3. Neither the name of SCM-Manager;
|
||||
* nor the names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* http://bitbucket.org/sdorra/scm-manager
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
package sonia.scm.security;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.ImmutableSet.Builder;
|
||||
|
||||
import org.apache.shiro.authc.AuthenticationException;
|
||||
import org.apache.shiro.authc.AuthenticationInfo;
|
||||
import org.apache.shiro.authc.AuthenticationToken;
|
||||
import org.apache.shiro.authc.DisabledAccountException;
|
||||
import org.apache.shiro.authc.SimpleAuthenticationInfo;
|
||||
import org.apache.shiro.authc.UnknownAccountException;
|
||||
import org.apache.shiro.authc.UsernamePasswordToken;
|
||||
import org.apache.shiro.authc.credential.PasswordMatcher;
|
||||
import org.apache.shiro.authc.credential.PasswordService;
|
||||
import org.apache.shiro.authz.AuthorizationInfo;
|
||||
import org.apache.shiro.realm.AuthorizingRealm;
|
||||
import org.apache.shiro.subject.PrincipalCollection;
|
||||
import org.apache.shiro.subject.SimplePrincipalCollection;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.group.Group;
|
||||
import sonia.scm.group.GroupDAO;
|
||||
import sonia.scm.group.GroupNames;
|
||||
import sonia.scm.user.User;
|
||||
import sonia.scm.user.UserDAO;
|
||||
|
||||
import static com.google.common.base.Preconditions.*;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@Singleton
|
||||
public class DefaultRealm extends AuthorizingRealm
|
||||
{
|
||||
|
||||
/** Field description */
|
||||
@VisibleForTesting
|
||||
static final String REALM = "DefaultRealm";
|
||||
|
||||
/**
|
||||
* the logger for DefaultRealm
|
||||
*/
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(DefaultRealm.class);
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param service
|
||||
* @param collector
|
||||
* @param userDAO
|
||||
* @param groupDAO
|
||||
*/
|
||||
@Inject
|
||||
public DefaultRealm(PasswordService service,
|
||||
AuthorizationCollector collector, UserDAO userDAO, GroupDAO groupDAO)
|
||||
{
|
||||
this.collector = collector;
|
||||
this.userDAO = userDAO;
|
||||
this.groupDAO = groupDAO;
|
||||
|
||||
PasswordMatcher matcher = new PasswordMatcher();
|
||||
|
||||
matcher.setPasswordService(service);
|
||||
setCredentialsMatcher(matcher);
|
||||
setAuthenticationTokenClass(UsernamePasswordToken.class);
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param token
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws AuthenticationException
|
||||
*/
|
||||
@Override
|
||||
protected AuthenticationInfo doGetAuthenticationInfo(
|
||||
AuthenticationToken token)
|
||||
throws AuthenticationException
|
||||
{
|
||||
checkArgument(token instanceof UsernamePasswordToken, "%s is required",
|
||||
UsernamePasswordToken.class);
|
||||
|
||||
UsernamePasswordToken upt = (UsernamePasswordToken) token;
|
||||
String principal = upt.getUsername();
|
||||
|
||||
checkArgument(!Strings.isNullOrEmpty(principal), "username is required");
|
||||
|
||||
logger.debug("try to authenticate {}", principal);
|
||||
|
||||
User user = userDAO.get(principal);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
//J-
|
||||
throw new UnknownAccountException(
|
||||
String.format("unknown account %s", principal)
|
||||
);
|
||||
//J+
|
||||
}
|
||||
|
||||
if (!user.isActive())
|
||||
{
|
||||
//J-
|
||||
throw new DisabledAccountException(
|
||||
String.format("account %s is disabled", principal)
|
||||
);
|
||||
//J+
|
||||
}
|
||||
|
||||
SimplePrincipalCollection collection = new SimplePrincipalCollection();
|
||||
|
||||
collection.add(principal, REALM);
|
||||
collection.add(user, REALM);
|
||||
collection.add(collectGroups(principal), REALM);
|
||||
|
||||
return new SimpleAuthenticationInfo(collection, user.getPassword());
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param principals
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
protected AuthorizationInfo doGetAuthorizationInfo(
|
||||
PrincipalCollection principals)
|
||||
{
|
||||
return collector.collect(principals);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param principal
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private GroupNames collectGroups(String principal)
|
||||
{
|
||||
Builder<String> builder = ImmutableSet.builder();
|
||||
|
||||
builder.add(GroupNames.AUTHENTICATED);
|
||||
|
||||
for (Group group : groupDAO.getAll())
|
||||
{
|
||||
if (group.isMember(principal))
|
||||
{
|
||||
builder.add(group.getName());
|
||||
}
|
||||
}
|
||||
|
||||
GroupNames groups = new GroupNames(builder.build());
|
||||
|
||||
logger.debug("collected following groups for principal {}: {}", principal,
|
||||
groups);
|
||||
|
||||
return groups;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private final AuthorizationCollector collector;
|
||||
|
||||
/** Field description */
|
||||
private final GroupDAO groupDAO;
|
||||
|
||||
/** Field description */
|
||||
private final UserDAO userDAO;
|
||||
}
|
||||
@@ -1,566 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2010, Sebastian Sdorra
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of SCM-Manager; nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* http://bitbucket.org/sdorra/scm-manager
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
package sonia.scm.security;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Provider;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import org.apache.shiro.authc.AccountException;
|
||||
import org.apache.shiro.authc.AuthenticationException;
|
||||
import org.apache.shiro.authc.AuthenticationInfo;
|
||||
import org.apache.shiro.authc.AuthenticationToken;
|
||||
import org.apache.shiro.authc.DisabledAccountException;
|
||||
import org.apache.shiro.authc.SimpleAuthenticationInfo;
|
||||
import org.apache.shiro.authc.UnknownAccountException;
|
||||
import org.apache.shiro.authc.UsernamePasswordToken;
|
||||
import org.apache.shiro.authc.pam.UnsupportedTokenException;
|
||||
import org.apache.shiro.authz.AuthorizationInfo;
|
||||
import org.apache.shiro.realm.AuthorizingRealm;
|
||||
import org.apache.shiro.subject.PrincipalCollection;
|
||||
import org.apache.shiro.subject.SimplePrincipalCollection;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.HandlerEventType;
|
||||
import sonia.scm.config.ScmConfiguration;
|
||||
import sonia.scm.group.Group;
|
||||
import sonia.scm.group.GroupManager;
|
||||
import sonia.scm.group.GroupNames;
|
||||
import sonia.scm.repository.RepositoryManager;
|
||||
import sonia.scm.user.User;
|
||||
import sonia.scm.user.UserDAO;
|
||||
import sonia.scm.user.UserEventHack;
|
||||
import sonia.scm.user.UserException;
|
||||
import sonia.scm.user.UserManager;
|
||||
import sonia.scm.util.Util;
|
||||
import sonia.scm.web.security.AuthenticationManager;
|
||||
import sonia.scm.web.security.AuthenticationResult;
|
||||
import sonia.scm.web.security.AuthenticationState;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
@Singleton
|
||||
public class ScmRealm extends AuthorizingRealm
|
||||
{
|
||||
|
||||
/** Field description */
|
||||
public static final String NAME = "scm";
|
||||
|
||||
/** Field description */
|
||||
private static final String SCM_CREDENTIALS = "SCM_CREDENTIALS";
|
||||
|
||||
/**
|
||||
* the logger for ScmRealm
|
||||
*/
|
||||
private static final Logger logger = LoggerFactory.getLogger(ScmRealm.class);
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
* @param configuration
|
||||
* @param loginAttemptHandler
|
||||
* @param collector
|
||||
* @param userManager
|
||||
* @param groupManager
|
||||
* @param userDAO
|
||||
* @param authenticator
|
||||
* @param manager
|
||||
* @param requestProvider
|
||||
* @param responseProvider
|
||||
*/
|
||||
@Inject
|
||||
public ScmRealm(ScmConfiguration configuration,
|
||||
LoginAttemptHandler loginAttemptHandler, AuthorizationCollector collector,
|
||||
UserManager userManager, GroupManager groupManager, UserDAO userDAO,
|
||||
AuthenticationManager authenticator, RepositoryManager manager,
|
||||
Provider<HttpServletRequest> requestProvider,
|
||||
Provider<HttpServletResponse> responseProvider)
|
||||
{
|
||||
this.configuration = configuration;
|
||||
this.loginAttemptHandler = loginAttemptHandler;
|
||||
this.collector = collector;
|
||||
this.userManager = userManager;
|
||||
this.groupManager = groupManager;
|
||||
this.userDAO = userDAO;
|
||||
this.authenticator = authenticator;
|
||||
this.requestProvider = requestProvider;
|
||||
this.responseProvider = responseProvider;
|
||||
|
||||
// set token class
|
||||
setAuthenticationTokenClass(UsernamePasswordToken.class);
|
||||
|
||||
// use own custom caching
|
||||
setCachingEnabled(false);
|
||||
setAuthenticationCachingEnabled(false);
|
||||
setAuthorizationCachingEnabled(false);
|
||||
|
||||
// set components
|
||||
setPermissionResolver(new RepositoryPermissionResolver());
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param authToken
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws AuthenticationException
|
||||
*/
|
||||
@Override
|
||||
protected AuthenticationInfo doGetAuthenticationInfo(
|
||||
AuthenticationToken authToken)
|
||||
throws AuthenticationException
|
||||
{
|
||||
if (!(authToken instanceof UsernamePasswordToken))
|
||||
{
|
||||
throw new UnsupportedTokenException("ScmAuthenticationToken is required");
|
||||
}
|
||||
|
||||
loginAttemptHandler.beforeAuthentication(authToken);
|
||||
|
||||
UsernamePasswordToken token = (UsernamePasswordToken) authToken;
|
||||
|
||||
AuthenticationInfo info = null;
|
||||
AuthenticationResult result =
|
||||
authenticator.authenticate(requestProvider.get(), responseProvider.get(),
|
||||
token.getUsername(), new String(token.getPassword()));
|
||||
|
||||
if ((result != null) && (AuthenticationState.SUCCESS == result.getState()))
|
||||
{
|
||||
loginAttemptHandler.onSuccessfulAuthentication(authToken, result);
|
||||
info = createAuthenticationInfo(token, result);
|
||||
}
|
||||
else if ((result != null)
|
||||
&& (AuthenticationState.NOT_FOUND == result.getState()))
|
||||
{
|
||||
throw new UnknownAccountException(
|
||||
"unknown account ".concat(token.getUsername()));
|
||||
}
|
||||
else
|
||||
{
|
||||
loginAttemptHandler.onUnsuccessfulAuthentication(authToken, result);
|
||||
|
||||
throw new AccountException("authentication failed");
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param principals
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
protected AuthorizationInfo doGetAuthorizationInfo(
|
||||
PrincipalCollection principals)
|
||||
{
|
||||
return collector.collect(principals);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param request
|
||||
* @param password
|
||||
* @param ar
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private Set<String> authenticate(HttpServletRequest request, String password,
|
||||
AuthenticationResult ar)
|
||||
{
|
||||
Set<String> groupSet = null;
|
||||
User user = ar.getUser();
|
||||
|
||||
try
|
||||
{
|
||||
groupSet = createGroupSet(ar);
|
||||
|
||||
// check for admin user
|
||||
checkForAuthenticatedAdmin(user, groupSet);
|
||||
|
||||
// store user
|
||||
User dbUser = userDAO.get(user.getName());
|
||||
|
||||
if (dbUser != null)
|
||||
{
|
||||
checkDBForAdmin(user, dbUser);
|
||||
checkDBForActive(user, dbUser);
|
||||
}
|
||||
|
||||
// create new user
|
||||
else if (user.isValid())
|
||||
{
|
||||
user.setCreationDate(System.currentTimeMillis());
|
||||
|
||||
// TODO find a better way
|
||||
UserEventHack.fireEvent(userManager, HandlerEventType.BEFORE_CREATE,
|
||||
user);
|
||||
userDAO.add(user);
|
||||
UserEventHack.fireEvent(userManager, HandlerEventType.CREATE, user);
|
||||
}
|
||||
else if (logger.isErrorEnabled())
|
||||
{
|
||||
logger.error("could not create user {}, beacause it is not valid",
|
||||
user.getName());
|
||||
}
|
||||
|
||||
if (user.isActive())
|
||||
{
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logGroups(user, groupSet);
|
||||
}
|
||||
|
||||
// store encrypted credentials in session
|
||||
String credentials = user.getName();
|
||||
|
||||
if (Util.isNotEmpty(password))
|
||||
{
|
||||
credentials = credentials.concat(":").concat(password);
|
||||
}
|
||||
|
||||
credentials = CipherUtil.getInstance().encode(credentials);
|
||||
request.getSession(true).setAttribute(SCM_CREDENTIALS, credentials);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
String msg = "user ".concat(user.getName()).concat(" is deactivated");
|
||||
|
||||
if (logger.isWarnEnabled())
|
||||
{
|
||||
logger.warn(msg);
|
||||
}
|
||||
|
||||
throw new DisabledAccountException(msg);
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.error("authentication failed", ex);
|
||||
|
||||
throw new AuthenticationException("authentication failed", ex);
|
||||
}
|
||||
|
||||
return groupSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param user
|
||||
* @param dbUser
|
||||
*/
|
||||
private void checkDBForActive(User user, User dbUser)
|
||||
{
|
||||
|
||||
// user is deactivated by database
|
||||
if (!dbUser.isActive())
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("user {} is marked as deactivated by local database",
|
||||
user.getName());
|
||||
}
|
||||
|
||||
user.setActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param user
|
||||
* @param dbUser
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws UserException
|
||||
*/
|
||||
private void checkDBForAdmin(User user, User dbUser)
|
||||
throws UserException, IOException
|
||||
{
|
||||
|
||||
// if database user is an admin, set admin for the current user
|
||||
if (dbUser.isAdmin())
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("user {} of type {} is marked as admin by local database",
|
||||
user.getName(), user.getType());
|
||||
}
|
||||
|
||||
user.setAdmin(true);
|
||||
}
|
||||
|
||||
// modify existing user, copy properties except password and admin
|
||||
if (user.copyProperties(dbUser, false))
|
||||
{
|
||||
user.setLastModified(System.currentTimeMillis());
|
||||
UserEventHack.fireEvent(userManager, HandlerEventType.BEFORE_MODIFY, user);
|
||||
userDAO.modify(user);
|
||||
UserEventHack.fireEvent(userManager, HandlerEventType.MODIFY, user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param user
|
||||
* @param groupSet
|
||||
*/
|
||||
private void checkForAuthenticatedAdmin(User user, Set<String> groupSet)
|
||||
{
|
||||
if (!user.isAdmin())
|
||||
{
|
||||
user.setAdmin(isAdmin(user, groupSet));
|
||||
|
||||
if (logger.isDebugEnabled() && user.isAdmin())
|
||||
{
|
||||
logger.debug("user {} is marked as admin by configuration",
|
||||
user.getName());
|
||||
}
|
||||
}
|
||||
else if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("authenticator {} marked user {} as admin", user.getType(),
|
||||
user.getName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param token
|
||||
* @param result
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private AuthenticationInfo createAuthenticationInfo(
|
||||
UsernamePasswordToken token, AuthenticationResult result)
|
||||
{
|
||||
User user = result.getUser();
|
||||
Collection<String> groups = authenticate(requestProvider.get(),
|
||||
new String(token.getPassword()), result);
|
||||
|
||||
SimplePrincipalCollection collection = new SimplePrincipalCollection();
|
||||
|
||||
/*
|
||||
* the first (primary) principal should be a unique identifier
|
||||
*/
|
||||
collection.add(user.getId(), NAME);
|
||||
collection.add(user, NAME);
|
||||
collection.add(new GroupNames(groups), NAME);
|
||||
|
||||
return new SimpleAuthenticationInfo(collection, token.getPassword());
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param ar
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private Set<String> createGroupSet(AuthenticationResult ar)
|
||||
{
|
||||
Set<String> groupSet = Sets.newHashSet();
|
||||
|
||||
// add group for all authenticated users
|
||||
groupSet.add(GroupNames.AUTHENTICATED);
|
||||
|
||||
// load external groups
|
||||
Collection<String> extGroups = ar.getGroups();
|
||||
|
||||
if (extGroups != null)
|
||||
{
|
||||
groupSet.addAll(extGroups);
|
||||
}
|
||||
|
||||
// load internal groups
|
||||
loadGroups(ar.getUser(), groupSet);
|
||||
|
||||
return groupSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param user
|
||||
* @param groupSet
|
||||
*/
|
||||
private void loadGroups(User user, Set<String> groupSet)
|
||||
{
|
||||
Collection<Group> groupCollection =
|
||||
groupManager.getGroupsForMember(user.getName());
|
||||
|
||||
if (groupCollection != null)
|
||||
{
|
||||
for (Group group : groupCollection)
|
||||
{
|
||||
groupSet.add(group.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param user
|
||||
* @param groups
|
||||
*/
|
||||
private void logGroups(User user, Set<String> groups)
|
||||
{
|
||||
StringBuilder msg = new StringBuilder("user ");
|
||||
|
||||
msg.append(user.getName());
|
||||
|
||||
if (Util.isNotEmpty(groups))
|
||||
{
|
||||
msg.append(" is member of ");
|
||||
|
||||
Joiner.on(", ").appendTo(msg, groups);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.append(" is not a member of a group");
|
||||
}
|
||||
|
||||
logger.debug(msg.toString());
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param user
|
||||
* @param groups
|
||||
* @return
|
||||
*/
|
||||
private boolean isAdmin(User user, Collection<String> groups)
|
||||
{
|
||||
boolean result = false;
|
||||
Set<String> adminUsers = configuration.getAdminUsers();
|
||||
|
||||
if (adminUsers != null)
|
||||
{
|
||||
result = adminUsers.contains(user.getName());
|
||||
}
|
||||
|
||||
if (!result)
|
||||
{
|
||||
Set<String> adminGroups = configuration.getAdminGroups();
|
||||
|
||||
if (adminGroups != null)
|
||||
{
|
||||
result = Util.containsOne(adminGroups, groups);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private final AuthenticationManager authenticator;
|
||||
|
||||
/** Field description */
|
||||
private final AuthorizationCollector collector;
|
||||
|
||||
/** Field description */
|
||||
private final ScmConfiguration configuration;
|
||||
|
||||
/** Field description */
|
||||
private final GroupManager groupManager;
|
||||
|
||||
/** Field description */
|
||||
private final LoginAttemptHandler loginAttemptHandler;
|
||||
|
||||
/** Field description */
|
||||
private final Provider<HttpServletRequest> requestProvider;
|
||||
|
||||
/** Field description */
|
||||
private final Provider<HttpServletResponse> responseProvider;
|
||||
|
||||
/** Field description */
|
||||
private final UserDAO userDAO;
|
||||
|
||||
/** Field description */
|
||||
private final UserManager userManager;
|
||||
}
|
||||
@@ -1,398 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2010, Sebastian Sdorra
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of SCM-Manager; nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* http://bitbucket.org/sdorra/scm-manager
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
package sonia.scm.web.security;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.SCMContextProvider;
|
||||
import sonia.scm.cache.Cache;
|
||||
import sonia.scm.cache.CacheManager;
|
||||
import sonia.scm.config.ScmConfiguration;
|
||||
import sonia.scm.security.EncryptionHandler;
|
||||
import sonia.scm.user.User;
|
||||
import sonia.scm.user.UserManager;
|
||||
import sonia.scm.util.AssertUtil;
|
||||
import sonia.scm.util.IOUtil;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
@Singleton
|
||||
public class ChainAuthenticatonManager extends AbstractAuthenticationManager
|
||||
{
|
||||
|
||||
/** Field description */
|
||||
public static final String CACHE_NAME = "sonia.cache.auth";
|
||||
|
||||
/** the logger for ChainAuthenticatonManager */
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(ChainAuthenticatonManager.class);
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
* @param configuration
|
||||
* @param userManager
|
||||
* @param authenticationHandlerSet
|
||||
* @param encryptionHandler
|
||||
* @param cacheManager
|
||||
*/
|
||||
@Inject
|
||||
public ChainAuthenticatonManager(ScmConfiguration configuration,
|
||||
UserManager userManager,
|
||||
Set<AuthenticationHandler> authenticationHandlerSet,
|
||||
EncryptionHandler encryptionHandler, CacheManager cacheManager)
|
||||
{
|
||||
AssertUtil.assertIsNotEmpty(authenticationHandlerSet);
|
||||
AssertUtil.assertIsNotNull(cacheManager);
|
||||
this.configuration = configuration;
|
||||
this.authenticationHandlers = sort(userManager, authenticationHandlerSet);
|
||||
this.encryptionHandler = encryptionHandler;
|
||||
this.cache = cacheManager.getCache(CACHE_NAME);
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @param username
|
||||
* @param password
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public AuthenticationResult authenticate(HttpServletRequest request,
|
||||
HttpServletResponse response, String username, String password)
|
||||
{
|
||||
AssertUtil.assertIsNotEmpty(username);
|
||||
AssertUtil.assertIsNotEmpty(password);
|
||||
|
||||
String encryptedPassword = encryptionHandler.encrypt(password);
|
||||
AuthenticationResult ar = getCached(username, encryptedPassword);
|
||||
|
||||
if (ar == null)
|
||||
{
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("no authentication result for user {} found in cache",
|
||||
username);
|
||||
}
|
||||
|
||||
ar = doAuthentication(request, response, username, password);
|
||||
|
||||
if ((ar != null) && ar.isCacheable())
|
||||
{
|
||||
cache.put(username,
|
||||
new AuthenticationCacheValue(ar, encryptedPassword));
|
||||
}
|
||||
}
|
||||
else if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("authenticate {} via cache", username);
|
||||
}
|
||||
|
||||
return ar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException
|
||||
{
|
||||
for (AuthenticationHandler authenticator : authenticationHandlers)
|
||||
{
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("close authenticator {}", authenticator.getClass());
|
||||
}
|
||||
|
||||
IOUtil.close(authenticator);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param context
|
||||
*/
|
||||
@Override
|
||||
public void init(SCMContextProvider context)
|
||||
{
|
||||
for (AuthenticationHandler authenticator : authenticationHandlers)
|
||||
{
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("initialize authenticator {}", authenticator.getClass());
|
||||
}
|
||||
|
||||
authenticator.init(context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param result
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean stopChain(AuthenticationResult result)
|
||||
{
|
||||
return (result != null) && (result.getState() != null)
|
||||
&& (result.getState().isSuccessfully()
|
||||
|| ((result.getState() == AuthenticationState.FAILED)
|
||||
&&!configuration.isSkipFailedAuthenticators()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param request
|
||||
* @param response
|
||||
* @param username
|
||||
* @param password
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private AuthenticationResult doAuthentication(HttpServletRequest request,
|
||||
HttpServletResponse response, String username, String password)
|
||||
{
|
||||
AuthenticationResult ar = null;
|
||||
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("start authentication chain for user {}", username);
|
||||
}
|
||||
|
||||
for (AuthenticationHandler authenticator : authenticationHandlers)
|
||||
{
|
||||
if (logger.isTraceEnabled())
|
||||
{
|
||||
logger.trace("check authenticator {} for user {}",
|
||||
authenticator.getClass(), username);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
AuthenticationResult result = authenticator.authenticate(request,
|
||||
response, username, password);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("authenticator {} ends with result, {}",
|
||||
authenticator.getClass().getName(), result);
|
||||
}
|
||||
|
||||
if (stopChain(result))
|
||||
{
|
||||
if (result.getState().isSuccessfully() && (result.getUser() != null))
|
||||
{
|
||||
User user = result.getUser();
|
||||
|
||||
user.setType(authenticator.getType());
|
||||
ar = result;
|
||||
|
||||
// notify authentication listeners
|
||||
fireAuthenticationEvent(user);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.error(
|
||||
"error durring authentication process of ".concat(
|
||||
authenticator.getClass().getName()), ex);
|
||||
}
|
||||
}
|
||||
|
||||
return ar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param userManager
|
||||
* @param authenticationHandlerSet
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@VisibleForTesting
|
||||
private List<AuthenticationHandler> sort(UserManager userManager,
|
||||
Set<AuthenticationHandler> authenticationHandlerSet)
|
||||
{
|
||||
List<AuthenticationHandler> handlers =
|
||||
Lists.newArrayListWithCapacity(authenticationHandlerSet.size());
|
||||
|
||||
String first = Strings.nullToEmpty(userManager.getDefaultType());
|
||||
|
||||
for (AuthenticationHandler handler : authenticationHandlerSet)
|
||||
{
|
||||
if (first.equals(handler.getType()))
|
||||
{
|
||||
handlers.add(0, handler);
|
||||
}
|
||||
else
|
||||
{
|
||||
handlers.add(handler);
|
||||
}
|
||||
}
|
||||
|
||||
return handlers;
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
* @param username
|
||||
* @param encryptedPassword
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private AuthenticationResult getCached(String username,
|
||||
String encryptedPassword)
|
||||
{
|
||||
AuthenticationResult result = null;
|
||||
AuthenticationCacheValue value = cache.get(username);
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
String cachedPassword = value.password;
|
||||
|
||||
if (cachedPassword.equals(encryptedPassword))
|
||||
{
|
||||
result = value.authenticationResult;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//~--- inner classes --------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Class description
|
||||
*
|
||||
*
|
||||
* @version Enter version here..., 2011-01-15
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
private static class AuthenticationCacheValue implements Serializable
|
||||
{
|
||||
|
||||
/** Field description */
|
||||
private static final long serialVersionUID = 2201116145941277549L;
|
||||
|
||||
//~--- constructors -------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param ar
|
||||
* @param password
|
||||
*/
|
||||
public AuthenticationCacheValue(AuthenticationResult ar, String password)
|
||||
{
|
||||
this.authenticationResult =
|
||||
new AuthenticationResult(ar.getUser().clone(), ar.getGroups(),
|
||||
ar.getState());
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
//~--- fields -------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private AuthenticationResult authenticationResult;
|
||||
|
||||
/** Field description */
|
||||
private String password;
|
||||
}
|
||||
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** authentication handlers */
|
||||
private final List<AuthenticationHandler> authenticationHandlers;
|
||||
|
||||
/** authentication cache */
|
||||
private final Cache<String, AuthenticationCacheValue> cache;
|
||||
|
||||
/** Field description */
|
||||
private final ScmConfiguration configuration;
|
||||
|
||||
/** encryption handler */
|
||||
private final EncryptionHandler encryptionHandler;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 ---------------------------------------------------------------
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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<Group> 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;
|
||||
}
|
||||
@@ -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<Permission> 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<String> 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<Permission> 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<Repository> 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<Permission> 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<Repository> 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<Permission> 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<String> 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<Permission> 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<String> authenticationGroups, Collection<Group> dbGroups,
|
||||
Collection<Repository> 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<HttpServletRequest> requestProvider =
|
||||
new Provider<HttpServletRequest>()
|
||||
{
|
||||
|
||||
@Override
|
||||
public HttpServletRequest get()
|
||||
{
|
||||
return request;
|
||||
}
|
||||
};
|
||||
|
||||
final HttpServletResponse response = mock(HttpServletResponse.class);
|
||||
Provider<HttpServletResponse> responseProvider =
|
||||
new Provider<HttpServletResponse>()
|
||||
{
|
||||
|
||||
@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<sonia.scm.repository.Permission> 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();
|
||||
}
|
||||
@@ -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<AuthenticationHandler> 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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user