mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-02-22 22:50:11 +01:00
* prepare license-maven-plugin for license migration * added license mapping for tsx files and added some more excludes * Changeover to MIT license * Fix build problems * Delete old remaining licenses * Add more exclude path for license checker * Rename included netbeans license, add exclude .m2/repository/ * Specify .m2 exclude because not only repository/, also wrapper/ must match * Add .cache/ exclude for license check * Modify formatting of license in java classes to comply with convention and IDE * Add IntelliJ documentation for license configuration * Update CHANGELOG.md * Exclude tmp/workspace/ dir for license check * Edit README.md Co-authored-by: Sebastian Sdorra <sebastian.sdorra@cloudogu.com>
377 lines
12 KiB
Java
377 lines
12 KiB
Java
/*
|
|
* MIT License
|
|
*
|
|
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
package sonia.scm.security;
|
|
|
|
//~--- non-JDK imports --------------------------------------------------------
|
|
|
|
import com.google.common.collect.Collections2;
|
|
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.authz.AuthorizationInfo;
|
|
import org.apache.shiro.authz.Permission;
|
|
import org.apache.shiro.authz.SimpleAuthorizationInfo;
|
|
import org.apache.shiro.authz.permission.WildcardPermissionResolver;
|
|
import org.apache.shiro.crypto.hash.DefaultHashService;
|
|
import org.apache.shiro.subject.PrincipalCollection;
|
|
import org.apache.shiro.subject.SimplePrincipalCollection;
|
|
import org.hamcrest.Matchers;
|
|
import org.junit.Before;
|
|
import org.junit.Test;
|
|
import org.junit.runner.RunWith;
|
|
import org.mockito.InjectMocks;
|
|
import org.mockito.Mock;
|
|
import org.mockito.junit.MockitoJUnitRunner;
|
|
import sonia.scm.group.GroupDAO;
|
|
import sonia.scm.user.User;
|
|
import sonia.scm.user.UserDAO;
|
|
import sonia.scm.user.UserTestData;
|
|
|
|
import java.util.HashSet;
|
|
import java.util.Set;
|
|
|
|
import static org.hamcrest.Matchers.allOf;
|
|
import static org.hamcrest.Matchers.contains;
|
|
import static org.hamcrest.Matchers.containsInAnyOrder;
|
|
import static org.hamcrest.Matchers.hasItem;
|
|
import static org.hamcrest.Matchers.hasSize;
|
|
import static org.hamcrest.Matchers.is;
|
|
import static org.hamcrest.Matchers.not;
|
|
import static org.hamcrest.Matchers.nullValue;
|
|
import static org.junit.Assert.assertEquals;
|
|
import static org.junit.Assert.assertNotNull;
|
|
import static org.junit.Assert.assertThat;
|
|
import static org.mockito.Mockito.times;
|
|
import static org.mockito.Mockito.verify;
|
|
import static org.mockito.Mockito.when;
|
|
|
|
//~--- JDK imports ------------------------------------------------------------
|
|
|
|
/**
|
|
*
|
|
* @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);
|
|
}
|
|
|
|
/**
|
|
* Tests {@link DefaultRealm#doGetAuthorizationInfo(PrincipalCollection)} without scope.
|
|
*/
|
|
@Test
|
|
public void testGetAuthorizationInfoWithoutScope(){
|
|
SimplePrincipalCollection col = new SimplePrincipalCollection();
|
|
|
|
SimpleAuthorizationInfo collectorsAuthz = new SimpleAuthorizationInfo();
|
|
collectorsAuthz.addStringPermission("repository:*");
|
|
when(collector.collect(col)).thenReturn(collectorsAuthz);
|
|
|
|
AuthorizationInfo realmsAutz = realm.doGetAuthorizationInfo(col);
|
|
assertThat(realmsAutz.getObjectPermissions(), is(nullValue()));
|
|
assertThat(realmsAutz.getStringPermissions(), Matchers.contains("repository:*"));
|
|
}
|
|
|
|
@Test
|
|
public void testGetAuthorizationInfoWithMultipleAuthorizationCollectors(){
|
|
SimplePrincipalCollection col = new SimplePrincipalCollection();
|
|
col.add(Scope.empty(), DefaultRealm.REALM);
|
|
|
|
SimpleAuthorizationInfo collectedFromDefault = new SimpleAuthorizationInfo();
|
|
collectedFromDefault.addStringPermission("repository:*");
|
|
when(collector.collect(col)).thenReturn(collectedFromDefault);
|
|
|
|
SimpleAuthorizationInfo collectedFromSecond = new SimpleAuthorizationInfo();
|
|
collectedFromSecond.addStringPermission("user:*");
|
|
collectedFromSecond.addRole("awesome");
|
|
|
|
AuthorizationCollector secondCollector = principalCollection -> collectedFromSecond;
|
|
authorizationCollectors.add(secondCollector);
|
|
|
|
SimpleAuthorizationInfo collectedFromThird = new SimpleAuthorizationInfo();
|
|
Permission permission = p -> false;
|
|
collectedFromThird.addObjectPermission(permission);
|
|
collectedFromThird.addRole("awesome");
|
|
|
|
AuthorizationCollector thirdCollector = principalCollection -> collectedFromThird;
|
|
authorizationCollectors.add(thirdCollector);
|
|
|
|
AuthorizationInfo realmsAuthz = realm.doGetAuthorizationInfo(col);
|
|
assertThat(realmsAuthz.getObjectPermissions(), contains(permission));
|
|
assertThat(realmsAuthz.getStringPermissions(), containsInAnyOrder("repository:*", "user:*"));
|
|
assertThat(realmsAuthz.getRoles(), Matchers.contains("awesome"));
|
|
}
|
|
|
|
/**
|
|
* Tests {@link DefaultRealm#doGetAuthorizationInfo(PrincipalCollection)} with empty scope.
|
|
*/
|
|
@Test
|
|
public void testGetAuthorizationInfoWithEmptyScope(){
|
|
SimplePrincipalCollection col = new SimplePrincipalCollection();
|
|
col.add(Scope.empty(), DefaultRealm.REALM);
|
|
|
|
SimpleAuthorizationInfo collectorsAuthz = new SimpleAuthorizationInfo();
|
|
collectorsAuthz.addStringPermission("repository:*");
|
|
when(collector.collect(col)).thenReturn(collectorsAuthz);
|
|
|
|
AuthorizationInfo realmsAutz = realm.doGetAuthorizationInfo(col);
|
|
assertThat(realmsAutz.getObjectPermissions(), is(nullValue()));
|
|
assertThat(realmsAutz.getStringPermissions(), Matchers.contains("repository:*"));
|
|
}
|
|
|
|
/**
|
|
* Tests {@link DefaultRealm#doGetAuthorizationInfo(PrincipalCollection)} with scope.
|
|
*/
|
|
@Test
|
|
public void testGetAuthorizationInfoWithScope(){
|
|
SimplePrincipalCollection col = new SimplePrincipalCollection();
|
|
col.add(Scope.valueOf("user:*:me"), DefaultRealm.REALM);
|
|
|
|
SimpleAuthorizationInfo collectorsAuthz = new SimpleAuthorizationInfo();
|
|
collectorsAuthz.addStringPermission("repository:*");
|
|
collectorsAuthz.addStringPermission("user:*:me");
|
|
when(collector.collect(col)).thenReturn(collectorsAuthz);
|
|
|
|
AuthorizationInfo realmsAutz = realm.doGetAuthorizationInfo(col);
|
|
assertThat(
|
|
Collections2.transform(realmsAutz.getObjectPermissions(), Permission::toString),
|
|
allOf(
|
|
Matchers.contains("user:*:me"),
|
|
not(Matchers.contains("repository:*"))
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* 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));
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
|
|
authorizationCollectors = new HashSet<>();
|
|
authorizationCollectors.add(collector);
|
|
|
|
realm = new DefaultRealm(service, authorizationCollectors, helperFactory);
|
|
|
|
// set permission resolver
|
|
realm.setPermissionResolver(new WildcardPermissionResolver());
|
|
}
|
|
|
|
//~--- 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 DefaultAuthorizationCollector collector;
|
|
|
|
private Set<AuthorizationCollector> authorizationCollectors;
|
|
|
|
@Mock
|
|
private LoginAttemptHandler loginAttemptHandler;
|
|
|
|
@Mock
|
|
private GroupDAO groupDAO;
|
|
|
|
@Mock
|
|
private UserDAO userDAO;
|
|
|
|
@InjectMocks
|
|
private DAORealmHelperFactory helperFactory;
|
|
|
|
/** Field description */
|
|
private DefaultRealm realm;
|
|
|
|
/** Field description */
|
|
private DefaultPasswordService service;
|
|
}
|