mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-10 15:35:49 +01:00
define AuthorizationCollector as extension point with multiple implmentations
This commit is contained in:
@@ -77,15 +77,13 @@ public final class ScmState
|
||||
* @param repositoryTypes available repository types
|
||||
* @param defaultUserType default user type
|
||||
* @param clientConfig client configuration
|
||||
* @param assignedPermission assigned permissions
|
||||
* @param availablePermissions list of available permissions
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public ScmState(String version, User user, Collection<String> groups,
|
||||
String token, Collection<RepositoryType> repositoryTypes, String defaultUserType,
|
||||
ScmClientConfig clientConfig, List<String> assignedPermission,
|
||||
Collection<PermissionDescriptor> availablePermissions)
|
||||
ScmClientConfig clientConfig, Collection<PermissionDescriptor> availablePermissions)
|
||||
{
|
||||
this.version = version;
|
||||
this.user = user;
|
||||
@@ -94,24 +92,11 @@ public final class ScmState
|
||||
this.repositoryTypes = repositoryTypes;
|
||||
this.clientConfig = clientConfig;
|
||||
this.defaultUserType = defaultUserType;
|
||||
this.assignedPermissions = assignedPermission;
|
||||
this.availablePermissions = availablePermissions;
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Return a list of assigned permissions.
|
||||
*
|
||||
*
|
||||
* @return list of assigned permissions
|
||||
* @since 1.31
|
||||
*/
|
||||
public List<String> getAssignedPermissions()
|
||||
{
|
||||
return assignedPermissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of available global permissions.
|
||||
*
|
||||
@@ -225,9 +210,6 @@ public final class ScmState
|
||||
/** authentication token */
|
||||
private String token;
|
||||
|
||||
/** Field description */
|
||||
private List<String> assignedPermissions;
|
||||
|
||||
/**
|
||||
* Avaliable global permission
|
||||
* @since 1.31
|
||||
|
||||
@@ -74,20 +74,17 @@ public final class ScmStateFactory
|
||||
* @param repositoryManger repository manager
|
||||
* @param userManager user manager
|
||||
* @param securitySystem security system
|
||||
* @param authorizationCollector authorization collector
|
||||
*/
|
||||
@Inject
|
||||
public ScmStateFactory(SCMContextProvider contextProvider,
|
||||
ScmConfiguration configuration, RepositoryManager repositoryManger,
|
||||
UserManager userManager, SecuritySystem securitySystem,
|
||||
AuthorizationCollector authorizationCollector)
|
||||
UserManager userManager, SecuritySystem securitySystem)
|
||||
{
|
||||
this.contextProvider = contextProvider;
|
||||
this.configuration = configuration;
|
||||
this.repositoryManger = repositoryManger;
|
||||
this.userManager = userManager;
|
||||
this.securitySystem = securitySystem;
|
||||
this.authorizationCollector = authorizationCollector;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
@@ -101,8 +98,7 @@ public final class ScmStateFactory
|
||||
@SuppressWarnings("unchecked")
|
||||
public ScmState createAnonymousState()
|
||||
{
|
||||
return createState(SCMContext.ANONYMOUS, Collections.EMPTY_LIST, null,
|
||||
Collections.EMPTY_LIST, Collections.EMPTY_LIST);
|
||||
return createState(SCMContext.ANONYMOUS, Collections.EMPTY_LIST, null, Collections.EMPTY_LIST);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -141,15 +137,11 @@ public final class ScmStateFactory
|
||||
ap = securitySystem.getAvailablePermissions();
|
||||
}
|
||||
|
||||
List<String> permissions =
|
||||
ImmutableList.copyOf(
|
||||
authorizationCollector.collect().getStringPermissions());
|
||||
|
||||
return createState(user, groups.getCollection(), token, permissions, ap);
|
||||
return createState(user, groups.getCollection(), token, ap);
|
||||
}
|
||||
|
||||
private ScmState createState(User user, Collection<String> groups,
|
||||
String token, List<String> assignedPermissions,
|
||||
String token,
|
||||
Collection<PermissionDescriptor> availablePermissions)
|
||||
{
|
||||
User u = user.clone();
|
||||
@@ -159,15 +151,11 @@ public final class ScmStateFactory
|
||||
|
||||
return new ScmState(contextProvider.getVersion(), u, groups, token,
|
||||
repositoryManger.getConfiguredTypes(), userManager.getDefaultType(),
|
||||
new ScmClientConfig(configuration), assignedPermissions,
|
||||
availablePermissions);
|
||||
new ScmClientConfig(configuration), availablePermissions);
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** authorization collector */
|
||||
private final AuthorizationCollector authorizationCollector;
|
||||
|
||||
/** configuration */
|
||||
private final ScmConfiguration configuration;
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ package sonia.scm.security;
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import org.apache.shiro.authz.AuthorizationInfo;
|
||||
import org.apache.shiro.subject.PrincipalCollection;
|
||||
import sonia.scm.plugin.ExtensionPoint;
|
||||
|
||||
/**
|
||||
@@ -42,15 +43,16 @@ import sonia.scm.plugin.ExtensionPoint;
|
||||
* @author Sebastian Sdorra
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@ExtensionPoint(multi = false)
|
||||
@ExtensionPoint
|
||||
public interface AuthorizationCollector
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns {@link AuthorizationInfo} for the authenticated user.
|
||||
*
|
||||
* @param principalCollection collected principals
|
||||
*
|
||||
* @return {@link AuthorizationInfo} for authenticated user
|
||||
*/
|
||||
public AuthorizationInfo collect();
|
||||
AuthorizationInfo collect(PrincipalCollection principalCollection);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ package sonia.scm.security;
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.github.legman.Subscribe;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -118,8 +119,8 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public AuthorizationInfo collect()
|
||||
@VisibleForTesting
|
||||
AuthorizationInfo collect()
|
||||
{
|
||||
AuthorizationInfo authorizationInfo;
|
||||
Subject subject = SecurityUtils.getSubject();
|
||||
@@ -143,6 +144,7 @@ public class DefaultAuthorizationCollector implements AuthorizationCollector
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public AuthorizationInfo collect(PrincipalCollection principals)
|
||||
{
|
||||
Preconditions.checkNotNull(principals, "principals parameter is required");
|
||||
|
||||
@@ -42,9 +42,11 @@ 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.authz.SimpleAuthorizationInfo;
|
||||
import org.apache.shiro.realm.AuthorizingRealm;
|
||||
import org.apache.shiro.subject.PrincipalCollection;
|
||||
|
||||
import org.apache.shiro.subject.SimplePrincipalCollection;
|
||||
import sonia.scm.group.GroupNames;
|
||||
import sonia.scm.plugin.Extension;
|
||||
|
||||
@@ -56,6 +58,8 @@ import javax.inject.Singleton;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Default authorizing realm.
|
||||
*
|
||||
@@ -85,14 +89,13 @@ public class DefaultRealm extends AuthorizingRealm
|
||||
*
|
||||
*
|
||||
* @param service
|
||||
* @param collector
|
||||
* @param authorizationCollectors
|
||||
* @param helperFactory
|
||||
*/
|
||||
@Inject
|
||||
public DefaultRealm(PasswordService service,
|
||||
DefaultAuthorizationCollector collector, DAORealmHelperFactory helperFactory)
|
||||
public DefaultRealm(PasswordService service, Set<AuthorizationCollector> authorizationCollectors, DAORealmHelperFactory helperFactory)
|
||||
{
|
||||
this.collector = collector;
|
||||
this.authorizationCollectors = authorizationCollectors;
|
||||
this.helper = helperFactory.create(REALM);
|
||||
|
||||
PasswordMatcher matcher = new PasswordMatcher();
|
||||
@@ -133,8 +136,7 @@ public class DefaultRealm extends AuthorizingRealm
|
||||
@Override
|
||||
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals)
|
||||
{
|
||||
AuthorizationInfo info = collector.collect(principals);
|
||||
|
||||
AuthorizationInfo info = collectors(principals);
|
||||
Scope scope = principals.oneByType(Scope.class);
|
||||
if (scope != null && ! scope.isEmpty()) {
|
||||
LOG.trace("filter permissions by scope {}", scope);
|
||||
@@ -144,13 +146,36 @@ public class DefaultRealm extends AuthorizingRealm
|
||||
}
|
||||
return filtered;
|
||||
} else if (LOG.isTraceEnabled()) {
|
||||
LOG.trace("principal does not contain scope informations, returning all permissions");
|
||||
LOG.trace("principal does not contain scope information, returning all permissions");
|
||||
log(principals, info, null);
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
private AuthorizationInfo collectors(PrincipalCollection principals) {
|
||||
SimpleAuthorizationInfo merged = new SimpleAuthorizationInfo();
|
||||
for (AuthorizationCollector collector : authorizationCollectors) {
|
||||
AuthorizationInfo authorizationInfo = collector.collect(principals);
|
||||
merge(merged, authorizationInfo);
|
||||
}
|
||||
return merged;
|
||||
}
|
||||
|
||||
private void merge(SimpleAuthorizationInfo merged, AuthorizationInfo authorizationInfo) {
|
||||
if (authorizationInfo != null) {
|
||||
if (authorizationInfo.getRoles() != null) {
|
||||
merged.addRoles(authorizationInfo.getRoles());
|
||||
}
|
||||
if (authorizationInfo.getObjectPermissions() != null) {
|
||||
merged.addObjectPermissions(authorizationInfo.getObjectPermissions());
|
||||
}
|
||||
if (authorizationInfo.getStringPermissions() != null) {
|
||||
merged.addStringPermissions(authorizationInfo.getStringPermissions());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void log( PrincipalCollection collection, AuthorizationInfo original, AuthorizationInfo filtered ) {
|
||||
StringBuilder buffer = new StringBuilder("authorization summary: ");
|
||||
|
||||
@@ -190,8 +215,8 @@ public class DefaultRealm extends AuthorizingRealm
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** default authorization collector */
|
||||
private final DefaultAuthorizationCollector collector;
|
||||
/** set of authorization collector */
|
||||
private final Set<AuthorizationCollector> authorizationCollectors;
|
||||
|
||||
/** realm helper */
|
||||
private final DAORealmHelper helper;
|
||||
|
||||
@@ -205,7 +205,7 @@ private long calculateAverage(List<Long> times) {
|
||||
|
||||
@Override
|
||||
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
|
||||
return authzCollector.collect();
|
||||
return authzCollector.collect(principals);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -71,7 +71,11 @@ import static org.mockito.Mockito.*;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.shiro.authz.AuthorizationInfo;
|
||||
import org.apache.shiro.authz.Permission;
|
||||
import org.apache.shiro.authz.SimpleAuthorizationInfo;
|
||||
@@ -132,6 +136,36 @@ public class DefaultRealmTest
|
||||
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.
|
||||
*/
|
||||
@@ -284,7 +318,11 @@ public class DefaultRealmTest
|
||||
// use a small number of iterations for faster test execution
|
||||
hashService.setHashIterations(512);
|
||||
service.setHashService(hashService);
|
||||
realm = new DefaultRealm(service, collector, helperFactory);
|
||||
|
||||
authorizationCollectors = new HashSet<>();
|
||||
authorizationCollectors.add(collector);
|
||||
|
||||
realm = new DefaultRealm(service, authorizationCollectors, helperFactory);
|
||||
|
||||
// set permission resolver
|
||||
realm.setPermissionResolver(new WildcardPermissionResolver());
|
||||
@@ -358,6 +396,8 @@ public class DefaultRealmTest
|
||||
@Mock
|
||||
private DefaultAuthorizationCollector collector;
|
||||
|
||||
private Set<AuthorizationCollector> authorizationCollectors;
|
||||
|
||||
@Mock
|
||||
private LoginAttemptHandler loginAttemptHandler;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user