mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-02-22 22:50:11 +01:00
replace scm-manager 1.x security api with apache shiro and use PasswordService for stronger password hashes
This commit is contained in:
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user