#781 process all kinds authorization relevant event and produce AuthorizationChangedEvent

This commit is contained in:
Sebastian Sdorra
2017-02-26 13:19:35 +01:00
parent b6a49570cf
commit acc3ff791b
10 changed files with 718 additions and 377 deletions

View File

@@ -0,0 +1,89 @@
/**
* 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;
import sonia.scm.event.Event;
/**
* This type of event is fired whenever a authorization relevant data changes. This event
* is especially useful for cache invalidation.
*
* @author Sebastian Sdorra
* @since 1.52
*/
@Event
public final class AuthorizationChangedEvent {
private final String nameOfAffectedUser;
private AuthorizationChangedEvent(String nameOfAffectedUser) {
this.nameOfAffectedUser = nameOfAffectedUser;
}
/**
* Returns {@code true} if every user is affected by this data change.
*
* @return {@code true} if every user is affected
*/
public boolean isEveryUserAffected(){
return nameOfAffectedUser != null;
}
/**
* Returns the name of the user which is affected by this event.
*
* @return name of affected user
*/
public String getNameOfAffectedUser(){
return nameOfAffectedUser;
}
/**
* Creates a new event which affects every user.
*
* @return new event for every user
*/
public static AuthorizationChangedEvent createForEveryUser() {
return new AuthorizationChangedEvent(null);
}
/**
* Create a new event which affect a single user.
*
* @param nameOfAffectedUser name of affected user
*
* @return new event for a single user
*/
public static AuthorizationChangedEvent createForUser(String nameOfAffectedUser) {
return new AuthorizationChangedEvent(nameOfAffectedUser);
}
}

View File

@@ -166,6 +166,7 @@ import sonia.scm.net.ahc.JsonContentTransformer;
import sonia.scm.net.ahc.XmlContentTransformer; import sonia.scm.net.ahc.XmlContentTransformer;
import sonia.scm.schedule.QuartzScheduler; import sonia.scm.schedule.QuartzScheduler;
import sonia.scm.schedule.Scheduler; import sonia.scm.schedule.Scheduler;
import sonia.scm.security.AuthorizationChangedEventProducer;
import sonia.scm.security.XsrfProtectionFilter; import sonia.scm.security.XsrfProtectionFilter;
import sonia.scm.web.UserAgentParser; import sonia.scm.web.UserAgentParser;
@@ -300,6 +301,7 @@ public class ScmServletModule extends ServletModule
pluginLoader.processExtensions(binder()); pluginLoader.processExtensions(binder());
// bind security stuff // bind security stuff
bind(AuthorizationChangedEventProducer.class);
bind(PermissionResolver.class, RepositoryPermissionResolver.class); bind(PermissionResolver.class, RepositoryPermissionResolver.class);
bind(AuthenticationManager.class, ChainAuthenticatonManager.class); bind(AuthenticationManager.class, ChainAuthenticatonManager.class);
bind(SecurityContext.class).to(BasicSecurityContext.class); bind(SecurityContext.class).to(BasicSecurityContext.class);
@@ -310,6 +312,7 @@ public class ScmServletModule extends ServletModule
// bind cache // bind cache
bind(CacheManager.class, GuavaCacheManager.class); bind(CacheManager.class, GuavaCacheManager.class);
bind(org.apache.shiro.cache.CacheManager.class, GuavaCacheManager.class);
// bind dao // bind dao
bind(GroupDAO.class, XmlGroupDAO.class); bind(GroupDAO.class, XmlGroupDAO.class);
@@ -386,8 +389,7 @@ public class ScmServletModule extends ServletModule
filter(PATTERN_ALL).through(BaseUrlFilter.class); filter(PATTERN_ALL).through(BaseUrlFilter.class);
filter(PATTERN_ALL).through(AutoLoginFilter.class); filter(PATTERN_ALL).through(AutoLoginFilter.class);
filterRegex(RESOURCE_REGEX).through(GZipFilter.class); filterRegex(RESOURCE_REGEX).through(GZipFilter.class);
filter(PATTERN_RESTAPI, filter(PATTERN_RESTAPI, PATTERN_DEBUG).through(ApiBasicAuthenticationFilter.class);
PATTERN_DEBUG).through(ApiBasicAuthenticationFilter.class);
filter(PATTERN_RESTAPI, PATTERN_DEBUG).through(SecurityFilter.class); filter(PATTERN_RESTAPI, PATTERN_DEBUG).through(SecurityFilter.class);
filter(PATTERN_CONFIG, PATTERN_ADMIN).through(AdminSecurityFilter.class); filter(PATTERN_CONFIG, PATTERN_ADMIN).through(AdminSecurityFilter.class);
@@ -434,11 +436,7 @@ public class ScmServletModule extends ServletModule
UriExtensionsConfig.class.getName()); UriExtensionsConfig.class.getName());
String restPath = getRestPackages(); String restPath = getRestPackages();
logger.info("configure jersey with package path: {}", restPath);
if (logger.isInfoEnabled())
{
logger.info("configure jersey with package path: {}", restPath);
}
params.put(PackagesResourceConfig.PROPERTY_PACKAGES, restPath); params.put(PackagesResourceConfig.PROPERTY_PACKAGES, restPath);
serve(PATTERN_RESTAPI).with(GuiceContainer.class, params); serve(PATTERN_RESTAPI).with(GuiceContainer.class, params);

View File

@@ -93,7 +93,7 @@ public class AuthenticationInfoCollector {
synchronizer.synchronize(user, groups); synchronizer.synchronize(user, groups);
if (isUserIsDisabled(user)) { if (isUserDisabled(user)) {
throwAccountIsDisabledExceptionAndLog(user.getName()); throwAccountIsDisabledExceptionAndLog(user.getName());
} }
@@ -112,7 +112,7 @@ public class AuthenticationInfoCollector {
return collection; return collection;
} }
private boolean isUserIsDisabled(User user) { private boolean isUserDisabled(User user) {
return !user.isActive(); return !user.isActive();
} }

View File

@@ -0,0 +1,271 @@
/**
* 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;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.eventbus.Subscribe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.EagerSingleton;
import sonia.scm.ModificationHandlerEvent;
import sonia.scm.event.HandlerEventBase;
import sonia.scm.event.ScmEventBus;
import sonia.scm.group.Group;
import sonia.scm.group.GroupEvent;
import sonia.scm.group.GroupModificationEvent;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryEvent;
import sonia.scm.repository.RepositoryModificationEvent;
import sonia.scm.user.User;
import sonia.scm.user.UserEvent;
import sonia.scm.user.UserModificationEvent;
/**
* Receives all kinds of events, which affects authorization relevant data and fires an
* {@link AuthorizationChangedEvent} if authorization data has changed.
*
* @author Sebastian Sdorra
* @since 1.52
*/
@EagerSingleton
public class AuthorizationChangedEventProducer {
/**
* the logger for AuthorizationChangedEventProducer
*/
private static final Logger logger = LoggerFactory.getLogger(AuthorizationChangedEventProducer.class);
/**
* Constructs a new instance.
*/
public AuthorizationChangedEventProducer() {
}
/**
* Invalidates the cache of a user which was modified. The cache entries for the user will be invalidated for the
* following reasons:
* <ul>
* <li>Admin or Active flag was modified.</li>
* <li>New user created, for the case of old cache values</li>
* <li>User deleted</li>
* </ul>
*
* @param event user event
*/
@Subscribe
public void onEvent(UserEvent event) {
if (event.getEventType().isPost()) {
if (isModificationEvent(event)) {
handleUserModificationEvent((UserModificationEvent) event);
} else {
handleUserEvent(event);
}
}
}
private boolean isModificationEvent(HandlerEventBase<?> event) {
return event instanceof ModificationHandlerEvent;
}
private void handleUserEvent(UserEvent event) {
String username = event.getItem().getName();
logger.debug(
"fire authorization changed event for user {}, because of user {} event", username, event.getEventType()
);
fireEventForUser(username);
}
private void handleUserModificationEvent(UserModificationEvent event) {
String username = event.getItem().getId();
User beforeModification = event.getItemBeforeModification();
if (isAuthorizationDataModified(event.getItem(), beforeModification)) {
logger.debug(
"fire authorization changed event for user {}, because of a authorization relevant field has changed",
username
);
fireEventForUser(username);
} else {
logger.debug(
"authorization changed event for user {} is not fired, because no authorization relevant field has changed",
username
);
}
}
private boolean isAuthorizationDataModified(User user, User beforeModification) {
return user.isAdmin() != beforeModification.isAdmin() || user.isActive() != beforeModification.isActive();
}
private void fireEventForUser(String username) {
sendEvent(AuthorizationChangedEvent.createForUser(username));
}
/**
* Invalidates the whole cache, if a repository has changed. The cache get cleared for one of the following reasons:
* <ul>
* <li>New repository created</li>
* <li>Repository was removed</li>
* <li>Archived, Public readable or permission field of the repository was modified</li>
* </ul>
*
* @param event repository event
*/
@Subscribe
public void onEvent(RepositoryEvent event) {
if (event.getEventType().isPost()) {
if (isModificationEvent(event)) {
handleRepositoryModificationEvent((RepositoryModificationEvent) event);
} else {
handleRepositoryEvent(event);
}
}
}
private void handleRepositoryModificationEvent(RepositoryModificationEvent event) {
Repository repository = event.getItem();
if (isAuthorizationDataModified(repository, event.getItemBeforeModification())) {
logger.debug(
"fire authorization changed event, because a relevant field of repository {} has changed", repository.getName()
);
fireEventForEveryUser();
} else {
logger.debug(
"authorization changed event is not fired, because non relevant field of repository {} has changed",
repository.getName()
);
}
}
private boolean isAuthorizationDataModified(Repository repository, Repository beforeModification) {
return repository.isArchived() != beforeModification.isArchived()
|| repository.isPublicReadable() != beforeModification.isPublicReadable()
|| ! repository.getPermissions().equals(beforeModification.getPermissions());
}
private void fireEventForEveryUser() {
sendEvent(AuthorizationChangedEvent.createForEveryUser());
}
private void handleRepositoryEvent(RepositoryEvent event){
logger.debug(
"fire authorization changed event, because of received {} event for repository {}",
event.getEventType(), event.getItem().getName()
);
fireEventForEveryUser();
}
/**
* Invalidates the whole cache if a group permission has changed and invalidates the cached entries of a user, if a
* user permission has changed.
*
* @param event permission event
*/
@Subscribe
public void onEvent(StoredAssignedPermissionEvent event) {
if (event.getEventType().isPost()) {
StoredAssignedPermission permission = event.getPermission();
if (permission.isGroupPermission()) {
handleGroupPermissionChange(permission);
} else {
handleUserPermissionChange(permission);
}
}
}
private void handleGroupPermissionChange(StoredAssignedPermission permission) {
logger.debug(
"fire authorization changed event, because global group permission {} has changed",
permission.getId()
);
fireEventForEveryUser();
}
private void handleUserPermissionChange(StoredAssignedPermission permission) {
logger.debug(
"fire authorization changed event for user {}, because permission {} has changed",
permission.getName(), permission.getId()
);
fireEventForUser(permission.getName());
}
/**
* Invalidates the whole cache, if a group has changed. The cache get cleared for one of the following reasons:
* <ul>
* <li>New group created</li>
* <li>Group was removed</li>
* <li>Group members was modified</li>
* </ul>
*
* @param event group event
*/
@Subscribe
public void onEvent(GroupEvent event) {
if (event.getEventType().isPost()) {
if (isModificationEvent(event)) {
handleGroupModificationEvent((GroupModificationEvent) event);
} else {
handleGroupEvent(event);
}
}
}
private void handleGroupModificationEvent(GroupModificationEvent event) {
Group group = event.getItem();
if (isAuthorizationDataModified(group, event.getItemBeforeModification())) {
logger.debug("fire authorization changed event, because group {} has changed", group.getId());
fireEventForEveryUser();
} else {
logger.debug(
"authorization changed event is not fired, because non relevant field of group {} has changed",
group.getId()
);
}
}
private boolean isAuthorizationDataModified(Group group, Group beforeModification) {
return !group.getMembers().equals(beforeModification.getMembers());
}
private void handleGroupEvent(GroupEvent event){
logger.debug(
"fire authorization changed event, because of received group event {} for group {}",
event.getEventType(),
event.getItem().getId()
);
fireEventForEveryUser();
}
@VisibleForTesting
protected void sendEvent(AuthorizationChangedEvent event) {
ScmEventBus.getInstance().post(event);
}
}

View File

@@ -57,14 +57,11 @@ import org.slf4j.LoggerFactory;
import sonia.scm.cache.Cache; import sonia.scm.cache.Cache;
import sonia.scm.cache.CacheManager; import sonia.scm.cache.CacheManager;
import sonia.scm.group.GroupEvent;
import sonia.scm.group.GroupNames; import sonia.scm.group.GroupNames;
import sonia.scm.repository.PermissionType; import sonia.scm.repository.PermissionType;
import sonia.scm.repository.Repository; import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryDAO; import sonia.scm.repository.RepositoryDAO;
import sonia.scm.repository.RepositoryEvent;
import sonia.scm.user.User; import sonia.scm.user.User;
import sonia.scm.user.UserEvent;
import sonia.scm.util.Util; import sonia.scm.util.Util;
//~--- JDK imports ------------------------------------------------------------ //~--- JDK imports ------------------------------------------------------------
@@ -72,10 +69,6 @@ import sonia.scm.util.Util;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import sonia.scm.Filter; import sonia.scm.Filter;
import sonia.scm.group.Group;
import sonia.scm.group.GroupModificationEvent;
import sonia.scm.repository.RepositoryModificationEvent;
import sonia.scm.user.UserModificationEvent;
/** /**
* *
@@ -98,7 +91,7 @@ public class AuthorizationCollector
LoggerFactory.getLogger(AuthorizationCollector.class); LoggerFactory.getLogger(AuthorizationCollector.class);
//~--- constructors --------------------------------------------------------- //~--- constructors ---------------------------------------------------------
/** /**
* Constructs ... * Constructs ...
* *
@@ -114,8 +107,7 @@ public class AuthorizationCollector
RepositoryDAO repositoryDAO, SecuritySystem securitySystem, RepositoryDAO repositoryDAO, SecuritySystem securitySystem,
PermissionResolver resolver) PermissionResolver resolver)
{ {
this.cache = cacheManager.getCache(CacheKey.class, AuthorizationInfo.class, this.cache = cacheManager.getCache(CacheKey.class, AuthorizationInfo.class, CACHE_NAME);
CACHE_NAME);
this.repositoryDAO = repositoryDAO; this.repositoryDAO = repositoryDAO;
this.securitySystem = securitySystem; this.securitySystem = securitySystem;
this.resolver = resolver; this.resolver = resolver;
@@ -146,189 +138,9 @@ public class AuthorizationCollector
return authorizationInfo; return authorizationInfo;
} }
/**
* Invalidates the cache of a user which was modified. The cache entries for the user will be invalidated for the
* following reasons:
* <ul>
* <li>Admin or Active flag was modified.</li>
* <li>New user created, for the case of old cache values</li>
* <li>User deleted</li>
* </ul>
*
* @param event user event
*/
@Subscribe
public void onEvent(UserEvent event)
{
if (event.getEventType().isPost())
{
User user = event.getItem();
String username = user.getId();
if (event instanceof UserModificationEvent)
{
User beforeModification = ((UserModificationEvent) event).getItemBeforeModification();
if (shouldCacheBeCleared(user, beforeModification))
{
logger.debug("invalidate cache of user {}, because of a permission relevant field has changed", username);
invalidateUserCache(username);
}
else
{
logger.debug("cache of user {} is not invalidated, because no permission relevant field has changed", username);
}
}
else
{
logger.debug("invalidate cache of user {}, because of user {} event", username, event.getEventType());
invalidateUserCache(username);
}
}
}
private boolean shouldCacheBeCleared(User user, User beforeModification)
{
return user.isAdmin() != beforeModification.isAdmin() || user.isActive() != beforeModification.isActive();
}
private void invalidateUserCache(final String username)
{
cache.removeAll(new Filter<CacheKey>()
{
@Override
public boolean accept(CacheKey item)
{
return username.equalsIgnoreCase(item.username);
}
});
}
/**
* Invalidates the whole cache, if a repository has changed. The cache get cleared for one of the following reasons:
* <ul>
* <li>New repository created</li>
* <li>Repository was removed</li>
* <li>Archived, Public readable or permission field of the repository was modified</li>
* </ul>
*
* @param event repository event
*/
@Subscribe
public void onEvent(RepositoryEvent event)
{
if (event.getEventType().isPost())
{
Repository repository = event.getItem();
if (event instanceof RepositoryModificationEvent)
{
Repository beforeModification = ((RepositoryModificationEvent) event).getItemBeforeModification();
if (shouldCacheBeCleared(repository, beforeModification))
{
logger.debug("clear cache, because a relevant field of repository {} has changed", repository.getName());
cache.clear();
}
else
{
logger.debug(
"cache is not invalidated, because non relevant field of repository {} has changed",
repository.getName()
);
}
}
else
{
logger.debug("clear cache, received {} event of repository {}", event.getEventType(), repository.getName());
cache.clear();
}
}
}
private boolean shouldCacheBeCleared(Repository repository, Repository beforeModification)
{
return repository.isArchived() != beforeModification.isArchived()
|| repository.isPublicReadable() != beforeModification.isPublicReadable()
|| ! repository.getPermissions().equals(beforeModification.getPermissions());
}
/**
* Invalidates the whole cache if a group permission has changed and invalidates the cached entries of a user, if a
* user permission has changed.
*
*
* @param event permission event
*/
@Subscribe
public void onEvent(StoredAssignedPermissionEvent event)
{
if (event.getEventType().isPost())
{
StoredAssignedPermission permission = event.getPermission();
if (permission.isGroupPermission())
{
logger.debug("clear cache, because global group permission {} has changed", permission.getId());
cache.clear();
}
else
{
logger.debug(
"clear cache of user {}, because permission {} has changed",
permission.getName(), event.getPermission().getId()
);
invalidateUserCache(permission.getName());
}
}
}
/**
* Invalidates the whole cache, if a group has changed. The cache get cleared for one of the following reasons:
* <ul>
* <li>New group created</li>
* <li>Group was removed</li>
* <li>Group members was modified</li>
* </ul>
*
* @param event group event
*/
@Subscribe
public void onEvent(GroupEvent event)
{
if (event.getEventType().isPost())
{
Group group = event.getItem();
if (event instanceof GroupModificationEvent)
{
Group beforeModification = ((GroupModificationEvent) event).getItemBeforeModification();
if (shouldCacheBeCleared(group, beforeModification))
{
logger.debug("clear cache, because group {} has changed", group.getId());
cache.clear();
}
else
{
logger.debug(
"cache is not invalidated, because non relevant field of group {} has changed",
group.getId()
);
}
}
else
{
logger.debug("clear cache, received group event {} for group {}", event.getEventType(), group.getId());
cache.clear();
}
}
}
private boolean shouldCacheBeCleared(Group group, Group beforeModification)
{
return !group.getMembers().equals(beforeModification.getMembers());
}
/** /**
* Method description * Method description
* *
*
*
* @param principals * @param principals
* *
* @return * @return
@@ -567,6 +379,30 @@ public class AuthorizationCollector
|| ((!perm.isGroupPermission()) && user.getName().equals(perm.getName())); || ((!perm.isGroupPermission()) && user.getName().equals(perm.getName()));
//J+ //J+
} }
@Subscribe
public void invalidateCache(AuthorizationChangedEvent event) {
if (event.isEveryUserAffected()) {
invalidateUserCache(event.getNameOfAffectedUser());
} else {
invalidateCache();
}
}
private void invalidateUserCache(final String username) {
logger.info("invalidate cache for user {}, because of a received authorization event", username);
cache.removeAll(new Filter<CacheKey>() {
@Override
public boolean accept(CacheKey item) {
return username.equalsIgnoreCase(item.username);
}
});
}
private void invalidateCache() {
logger.info("invalidate cache, because of a received authorization event");
cache.clear();
}
//~--- inner classes -------------------------------------------------------- //~--- inner classes --------------------------------------------------------

View File

@@ -35,6 +35,7 @@ package sonia.scm.security;
//~--- non-JDK imports -------------------------------------------------------- //~--- non-JDK imports --------------------------------------------------------
import com.google.common.eventbus.Subscribe;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
@@ -46,9 +47,11 @@ import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.pam.UnsupportedTokenException; import org.apache.shiro.authc.pam.UnsupportedTokenException;
import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.web.security.AuthenticationResult; import sonia.scm.web.security.AuthenticationResult;
import sonia.scm.web.security.AuthenticationState; import sonia.scm.web.security.AuthenticationState;
@@ -63,6 +66,11 @@ import sonia.scm.web.security.AuthenticationState;
@Singleton @Singleton
public class ScmRealm extends AuthorizingRealm { public class ScmRealm extends AuthorizingRealm {
/**
* the logger for ScmRealm
*/
private static final Logger logger = LoggerFactory.getLogger(ScmRealm.class);
public static final String NAME = "scm"; public static final String NAME = "scm";
@@ -74,32 +82,35 @@ public class ScmRealm extends AuthorizingRealm {
/** /**
* Constructs a new scm realm. * Constructs a new scm realm.
* *
* @param cacheManager cache manager
* @param authenticator authenticator facade * @param authenticator authenticator facade
* @param loginAttemptHandler login attempt handler * @param loginAttemptHandler login attempt handler
* @param authcCollector authentication info collector * @param authcCollector authentication info collector
* @param authzCollector authorization collector * @param authzCollector authorization collector
*/ */
@Inject @Inject
public ScmRealm(AuthenticatorFacade authenticator, LoginAttemptHandler loginAttemptHandler, public ScmRealm(CacheManager cacheManager,
AuthenticatorFacade authenticator, LoginAttemptHandler loginAttemptHandler,
AuthenticationInfoCollector authcCollector, AuthorizationCollector authzCollector) { AuthenticationInfoCollector authcCollector, AuthorizationCollector authzCollector) {
super(cacheManager);
this.authenticator = authenticator; this.authenticator = authenticator;
this.loginAttemptHandler = loginAttemptHandler; this.loginAttemptHandler = loginAttemptHandler;
this.authcCollector = authcCollector; this.authcCollector = authcCollector;
this.authzCollector = authzCollector; this.authzCollector = authzCollector;
// set token class // set token class
setAuthenticationTokenClass(UsernamePasswordToken.class); setAuthenticationTokenClass(UsernamePasswordToken.class);
// use own custom caching
setCachingEnabled(false);
setAuthenticationCachingEnabled(false);
setAuthorizationCachingEnabled(false);
// set components // set components
setPermissionResolver(new RepositoryPermissionResolver()); setPermissionResolver(new RepositoryPermissionResolver());
} }
@Override
protected Object getAuthorizationCacheKey(PrincipalCollection principals) {
return principals.getPrimaryPrincipal();
}
@Override @Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authToken) throws AuthenticationException { protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authToken) throws AuthenticationException {
UsernamePasswordToken token = castToken(authToken); UsernamePasswordToken token = castToken(authToken);
@@ -138,4 +149,23 @@ public class ScmRealm extends AuthorizingRealm {
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals){ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals){
return authzCollector.collect(principals); return authzCollector.collect(principals);
} }
@Subscribe
public void invalidateCache(AuthorizationChangedEvent event) {
if (event.isEveryUserAffected()) {
invalidateUserCache(event.getNameOfAffectedUser());
} else {
invalidateCache();
}
}
private void invalidateUserCache(final String username) {
logger.info("invalidate cache for user {}, because of a received authorization event", username);
getAuthorizationCache().remove(username);
}
private void invalidateCache() {
logger.info("invalidate cache, because of a received authorization event");
getAuthorizationCache().clear();
}
} }

View File

@@ -72,8 +72,11 @@ import sonia.scm.security.SecuritySystem;
import sonia.scm.user.UserTestData; import sonia.scm.user.UserTestData;
/** /**
* * Performance test for {@link RepositoryManager#getAll()}.
*
* @see <a href="https://goo.gl/PD1AeM">Issue 781</a>
* @author Sebastian Sdorra * @author Sebastian Sdorra
* @since 1.52
*/ */
@RunWith(MockitoJUnitRunner.class) @RunWith(MockitoJUnitRunner.class)
public class DefaultRepositoryManagerPerfTest { public class DefaultRepositoryManagerPerfTest {
@@ -105,6 +108,9 @@ public class DefaultRepositoryManagerPerfTest {
private AuthorizationCollector authzCollector; private AuthorizationCollector authzCollector;
/**
* Setup object under test.
*/
@Before @Before
public void setUpObjectUnderTest(){ public void setUpObjectUnderTest(){
when(repositoryHandler.getType()).thenReturn(new Type(REPOSITORY_TYPE, REPOSITORY_TYPE)); when(repositoryHandler.getType()).thenReturn(new Type(REPOSITORY_TYPE, REPOSITORY_TYPE));
@@ -132,11 +138,17 @@ public class DefaultRepositoryManagerPerfTest {
ThreadContext.bind(securityManager); ThreadContext.bind(securityManager);
} }
/**
* Tear down test objects.
*/
@After @After
public void tearDown(){ public void tearDown(){
ThreadContext.unbindSecurityManager(); ThreadContext.unbindSecurityManager();
} }
/**
* Start performance test and ensure that the timeout is not reached.
*/
@Test(timeout = 6000l) @Test(timeout = 6000l)
public void perfTestGetAll(){ public void perfTestGetAll(){
SecurityUtils.getSubject().login(new UsernamePasswordToken("trillian", "secret")); SecurityUtils.getSubject().login(new UsernamePasswordToken("trillian", "secret"));

View File

@@ -0,0 +1,256 @@
/**
* 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;
import com.google.common.collect.Lists;
import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.Before;
import sonia.scm.HandlerEvent;
import sonia.scm.group.Group;
import sonia.scm.group.GroupEvent;
import sonia.scm.group.GroupModificationEvent;
import sonia.scm.repository.PermissionType;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryEvent;
import sonia.scm.repository.RepositoryModificationEvent;
import sonia.scm.repository.RepositoryTestData;
import sonia.scm.user.User;
import sonia.scm.user.UserEvent;
import sonia.scm.user.UserModificationEvent;
import sonia.scm.user.UserTestData;
/**
* Unit tests for {@link AuthorizationChangedEventProducer}.
*
* @author Sebastian Sdorra
*/
public class AuthorizationChangedEventProducerTest {
private StoringAuthorizationChangedEventProducer producer;
@Before
public void setUpProducer() {
producer = new StoringAuthorizationChangedEventProducer();
}
/**
* Tests {@link AuthorizationChangedEventProducer#onEvent(sonia.scm.user.UserEvent)}.
*/
@Test
public void testOnUserEvent()
{
User user = UserTestData.createDent();
producer.onEvent(new UserEvent(user, HandlerEvent.BEFORE_CREATE));
assertEventIsNotFired();
producer.onEvent(new UserEvent(user, HandlerEvent.CREATE));
assertUserEventIsFired("dent");
}
private void assertEventIsNotFired(){
assertNull(producer.event);
}
private void assertUserEventIsFired(String username){
assertNotNull(producer.event);
assertTrue(producer.event.isEveryUserAffected());
assertEquals(username, producer.event.getNameOfAffectedUser());
}
/**
* Tests {@link AuthorizationChangedEventProducer#onEvent(sonia.scm.user.UserEvent)} with modified user.
*/
@Test
public void testOnUserModificationEvent()
{
User user = UserTestData.createDent();
User userModified = UserTestData.createDent();
userModified.setDisplayName("Super Dent");
producer.onEvent(new UserModificationEvent(userModified, user, HandlerEvent.BEFORE_CREATE));
assertEventIsNotFired();
producer.onEvent(new UserModificationEvent(userModified, user, HandlerEvent.CREATE));
assertEventIsNotFired();
userModified.setAdmin(true);
producer.onEvent(new UserModificationEvent(userModified, user, HandlerEvent.BEFORE_CREATE));
assertEventIsNotFired();
producer.onEvent(new UserModificationEvent(userModified, user, HandlerEvent.CREATE));
assertUserEventIsFired("dent");
}
/**
* Tests {@link AuthorizationChangedEventProducer#onEvent(sonia.scm.group.GroupEvent)}.
*/
@Test
public void testOnGroupEvent()
{
Group group = new Group("xml", "base");
producer.onEvent(new GroupEvent(group, HandlerEvent.BEFORE_CREATE));
assertEventIsNotFired();
producer.onEvent(new GroupEvent(group, HandlerEvent.CREATE));
assertGlobalEventIsFired();
}
private void assertGlobalEventIsFired(){
assertNotNull(producer.event);
assertFalse(producer.event.isEveryUserAffected());
}
/**
* Tests {@link AuthorizationChangedEventProducer#onEvent(sonia.scm.group.GroupEvent)} with modified groups.
*/
@Test
public void testOnGroupModificationEvent()
{
Group group = new Group("xml", "base");
Group modifiedGroup = new Group("xml", "base");
producer.onEvent(new GroupModificationEvent(modifiedGroup, group, HandlerEvent.BEFORE_MODIFY));
assertEventIsNotFired();
producer.onEvent(new GroupModificationEvent(modifiedGroup, group, HandlerEvent.MODIFY));
assertEventIsNotFired();
modifiedGroup.add("test");
producer.onEvent(new GroupModificationEvent(modifiedGroup, group, HandlerEvent.MODIFY));
assertGlobalEventIsFired();
}
/**
* Tests {@link AuthorizationChangedEventProducer#onEvent(sonia.scm.repository.RepositoryEvent)}.
*/
@Test
public void testOnRepositoryEvent()
{
Repository repository = RepositoryTestData.createHeartOfGold();
producer.onEvent(new RepositoryEvent(repository, HandlerEvent.BEFORE_CREATE));
assertEventIsNotFired();
producer.onEvent(new RepositoryEvent(repository, HandlerEvent.CREATE));
assertGlobalEventIsFired();
}
/**
* Tests {@link AuthorizationChangedEventProducer#onEvent(sonia.scm.repository.RepositoryEvent)} with modified
* repository.
*/
@Test
public void testOnRepositoryModificationEvent()
{
Repository repositoryModified = RepositoryTestData.createHeartOfGold();
repositoryModified.setName("test123");
repositoryModified.setPermissions(Lists.newArrayList(new sonia.scm.repository.Permission("test")));
Repository repository = RepositoryTestData.createHeartOfGold();
repository.setPermissions(Lists.newArrayList(new sonia.scm.repository.Permission("test")));
producer.onEvent(new RepositoryModificationEvent(repositoryModified, repository, HandlerEvent.BEFORE_CREATE));
assertEventIsNotFired();
producer.onEvent(new RepositoryModificationEvent(repositoryModified, repository, HandlerEvent.CREATE));
assertEventIsNotFired();
repositoryModified.setPermissions(Lists.newArrayList(new sonia.scm.repository.Permission("test")));
producer.onEvent(new RepositoryModificationEvent(repositoryModified, repository, HandlerEvent.CREATE));
assertEventIsNotFired();
repositoryModified.setPermissions(Lists.newArrayList(new sonia.scm.repository.Permission("test123")));
producer.onEvent(new RepositoryModificationEvent(repositoryModified, repository, HandlerEvent.CREATE));
assertGlobalEventIsFired();
resetStoredEvent();
repositoryModified.setPermissions(
Lists.newArrayList(new sonia.scm.repository.Permission("test", PermissionType.READ, true))
);
producer.onEvent(new RepositoryModificationEvent(repositoryModified, repository, HandlerEvent.CREATE));
assertGlobalEventIsFired();
resetStoredEvent();
repositoryModified.setPermissions(
Lists.newArrayList(new sonia.scm.repository.Permission("test", PermissionType.WRITE))
);
producer.onEvent(new RepositoryModificationEvent(repositoryModified, repository, HandlerEvent.CREATE));
assertGlobalEventIsFired();
}
private void resetStoredEvent(){
producer.event = null;
}
/**
* Tests {@link AuthorizationChangedEventProducer#onEvent(sonia.scm.security.StoredAssignedPermissionEvent)}.
*/
@Test
public void testOnStoredAssignedPermissionEvent()
{
StoredAssignedPermission groupPermission = new StoredAssignedPermission(
"123", new AssignedPermission("_authenticated", true, "repository:read:*")
);
producer.onEvent(new StoredAssignedPermissionEvent(HandlerEvent.BEFORE_CREATE, groupPermission));
assertEventIsNotFired();
producer.onEvent(new StoredAssignedPermissionEvent(HandlerEvent.CREATE, groupPermission));
assertGlobalEventIsFired();
resetStoredEvent();
StoredAssignedPermission userPermission = new StoredAssignedPermission(
"123", new AssignedPermission("trillian", false, "repository:read:*")
);
producer.onEvent(new StoredAssignedPermissionEvent(HandlerEvent.BEFORE_CREATE, userPermission));
assertEventIsNotFired();
resetStoredEvent();
producer.onEvent(new StoredAssignedPermissionEvent(HandlerEvent.CREATE, userPermission));
assertUserEventIsFired("trillian");
}
private static class StoringAuthorizationChangedEventProducer extends AuthorizationChangedEventProducer {
private AuthorizationChangedEvent event;
@Override
protected void sendEvent(AuthorizationChangedEvent event) {
this.event = event;
}
}
}

View File

@@ -54,22 +54,14 @@ import static org.junit.Assert.*;
import org.junit.Rule; import org.junit.Rule;
import org.mockito.runners.MockitoJUnitRunner; import org.mockito.runners.MockitoJUnitRunner;
import sonia.scm.Filter; import sonia.scm.Filter;
import sonia.scm.HandlerEvent;
import sonia.scm.cache.Cache; import sonia.scm.cache.Cache;
import sonia.scm.cache.CacheManager; import sonia.scm.cache.CacheManager;
import sonia.scm.group.Group;
import sonia.scm.group.GroupEvent;
import sonia.scm.group.GroupModificationEvent;
import sonia.scm.group.GroupNames; import sonia.scm.group.GroupNames;
import sonia.scm.repository.PermissionType; import sonia.scm.repository.PermissionType;
import sonia.scm.repository.Repository; import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryDAO; import sonia.scm.repository.RepositoryDAO;
import sonia.scm.repository.RepositoryEvent;
import sonia.scm.repository.RepositoryModificationEvent;
import sonia.scm.repository.RepositoryTestData; import sonia.scm.repository.RepositoryTestData;
import sonia.scm.user.User; import sonia.scm.user.User;
import sonia.scm.user.UserEvent;
import sonia.scm.user.UserModificationEvent;
import sonia.scm.user.UserTestData; import sonia.scm.user.UserTestData;
/** /**
@@ -85,7 +77,7 @@ public class AuthorizationCollectorTest {
@Mock @Mock
private CacheManager cacheManager; private CacheManager cacheManager;
@Mock @Mock
private RepositoryDAO repositoryDAO; private RepositoryDAO repositoryDAO;
@@ -111,160 +103,6 @@ public class AuthorizationCollectorTest {
collector = new AuthorizationCollector(cacheManager, repositoryDAO, securitySystem, resolver); collector = new AuthorizationCollector(cacheManager, repositoryDAO, securitySystem, resolver);
} }
/**
* Tests {@link AuthorizationCollector#onEvent(sonia.scm.user.UserEvent)}.
*/
@Test
public void testOnUserEvent()
{
User user = UserTestData.createDent();
collector.onEvent(new UserEvent(user, HandlerEvent.BEFORE_CREATE));
verify(cache, never()).clear();
collector.onEvent(new UserEvent(user, HandlerEvent.CREATE));
verify(cache).removeAll(Mockito.any(Filter.class));
}
/**
* Tests {@link AuthorizationCollector#onEvent(sonia.scm.user.UserEvent)} with modified user.
*/
@Test
public void testOnUserModificationEvent()
{
User user = UserTestData.createDent();
User userModified = UserTestData.createDent();
userModified.setDisplayName("Super Dent");
collector.onEvent(new UserModificationEvent(userModified, user, HandlerEvent.BEFORE_CREATE));
verify(cache, never()).removeAll(Mockito.any(Filter.class));
collector.onEvent(new UserModificationEvent(userModified, user, HandlerEvent.CREATE));
verify(cache, never()).removeAll(Mockito.any(Filter.class));
userModified.setAdmin(true);
collector.onEvent(new UserModificationEvent(userModified, user, HandlerEvent.BEFORE_CREATE));
verify(cache, never()).removeAll(Mockito.any(Filter.class));
collector.onEvent(new UserModificationEvent(userModified, user, HandlerEvent.CREATE));
verify(cache).removeAll(Mockito.any(Filter.class));
}
/**
* Tests {@link AuthorizationCollector#onEvent(sonia.scm.group.GroupEvent)}.
*/
@Test
public void testOnGroupEvent()
{
Group group = new Group("xml", "base");
collector.onEvent(new GroupEvent(group, HandlerEvent.BEFORE_CREATE));
verify(cache, never()).clear();
collector.onEvent(new GroupEvent(group, HandlerEvent.CREATE));
verify(cache).clear();
}
/**
* Tests {@link AuthorizationCollector#onEvent(sonia.scm.group.GroupEvent)} with modified groups.
*/
@Test
public void testOnGroupModificationEvent()
{
Group group = new Group("xml", "base");
Group modifiedGroup = new Group("xml", "base");
collector.onEvent(new GroupModificationEvent(modifiedGroup, group, HandlerEvent.BEFORE_MODIFY));
verify(cache, never()).clear();
collector.onEvent(new GroupModificationEvent(modifiedGroup, group, HandlerEvent.MODIFY));
verify(cache, never()).clear();
modifiedGroup.add("test");
collector.onEvent(new GroupModificationEvent(modifiedGroup, group, HandlerEvent.MODIFY));
verify(cache).clear();
}
/**
* Tests {@link AuthorizationCollector#onEvent(sonia.scm.repository.RepositoryEvent)}.
*/
@Test
public void testOnRepositoryEvent()
{
Repository repository = RepositoryTestData.createHeartOfGold();
collector.onEvent(new RepositoryEvent(repository, HandlerEvent.BEFORE_CREATE));
verify(cache, never()).clear();
collector.onEvent(new RepositoryEvent(repository, HandlerEvent.CREATE));
verify(cache).clear();
}
/**
* Tests {@link AuthorizationCollector#onEvent(sonia.scm.repository.RepositoryEvent)} with modified repository.
*/
@Test
public void testOnRepositoryModificationEvent()
{
Repository repositoryModified = RepositoryTestData.createHeartOfGold();
repositoryModified.setName("test123");
repositoryModified.setPermissions(Lists.newArrayList(new sonia.scm.repository.Permission("test")));
Repository repository = RepositoryTestData.createHeartOfGold();
repository.setPermissions(Lists.newArrayList(new sonia.scm.repository.Permission("test")));
collector.onEvent(new RepositoryModificationEvent(repositoryModified, repository, HandlerEvent.BEFORE_CREATE));
verify(cache, never()).clear();
collector.onEvent(new RepositoryModificationEvent(repositoryModified, repository, HandlerEvent.CREATE));
verify(cache, never()).clear();
repositoryModified.setPermissions(Lists.newArrayList(new sonia.scm.repository.Permission("test")));
collector.onEvent(new RepositoryModificationEvent(repositoryModified, repository, HandlerEvent.CREATE));
verify(cache, never()).clear();
repositoryModified.setPermissions(Lists.newArrayList(new sonia.scm.repository.Permission("test123")));
collector.onEvent(new RepositoryModificationEvent(repositoryModified, repository, HandlerEvent.CREATE));
verify(cache).clear();
repositoryModified.setPermissions(
Lists.newArrayList(new sonia.scm.repository.Permission("test", PermissionType.READ, true))
);
collector.onEvent(new RepositoryModificationEvent(repositoryModified, repository, HandlerEvent.CREATE));
verify(cache, times(2)).clear();
repositoryModified.setPermissions(
Lists.newArrayList(new sonia.scm.repository.Permission("test", PermissionType.WRITE))
);
collector.onEvent(new RepositoryModificationEvent(repositoryModified, repository, HandlerEvent.CREATE));
verify(cache, times(3)).clear();
}
/**
* Tests {@link AuthorizationCollector#onEvent(sonia.scm.security.StoredAssignedPermissionEvent)}.
*/
@Test
public void testOnStoredAssignedPermissionEvent()
{
StoredAssignedPermission groupPermission = new StoredAssignedPermission(
"123", new AssignedPermission("_authenticated", true, "repository:read:*")
);
collector.onEvent(new StoredAssignedPermissionEvent(HandlerEvent.BEFORE_CREATE, groupPermission));
verify(cache, never()).clear();
collector.onEvent(new StoredAssignedPermissionEvent(HandlerEvent.CREATE, groupPermission));
verify(cache).clear();
StoredAssignedPermission userPermission = new StoredAssignedPermission(
"123", new AssignedPermission("trillian", false, "repository:read:*")
);
collector.onEvent(new StoredAssignedPermissionEvent(HandlerEvent.BEFORE_CREATE, userPermission));
verify(cache, never()).removeAll(Mockito.any(Filter.class));
verify(cache).clear();
collector.onEvent(new StoredAssignedPermissionEvent(HandlerEvent.CREATE, userPermission));
verify(cache).removeAll(Mockito.any(Filter.class));
verify(cache).clear();
}
/** /**
* Tests {@link AuthorizationCollector#collect()} without user role. * Tests {@link AuthorizationCollector#collect()} without user role.
*/ */
@@ -402,8 +240,7 @@ public class AuthorizationCollectorTest {
assertThat(authInfo.getObjectPermissions(), containsInAnyOrder(wp1, wp2)); assertThat(authInfo.getObjectPermissions(), containsInAnyOrder(wp1, wp2));
} }
private void authenticate(User user, String group, String... groups) private void authenticate(User user, String group, String... groups) {
{
SimplePrincipalCollection spc = new SimplePrincipalCollection(); SimplePrincipalCollection spc = new SimplePrincipalCollection();
spc.add(user.getName(), "unit"); spc.add(user.getName(), "unit");
spc.add(user, "unit"); spc.add(user, "unit");
@@ -412,4 +249,16 @@ public class AuthorizationCollectorTest {
shiro.setSubject(subject); shiro.setSubject(subject);
} }
/**
* Tests {@link AuthorizationCollector#invalidateCache(sonia.scm.security.AuthorizationChangedEvent)}.
*/
@Test
public void testInvalidateCache() {
collector.invalidateCache(AuthorizationChangedEvent.createForEveryUser());
verify(cache).clear();
collector.invalidateCache(AuthorizationChangedEvent.createForUser("dent"));
verify(cache).removeAll(Mockito.any(Filter.class));
}
} }

View File

@@ -54,8 +54,6 @@ import org.junit.Test;
import org.mockito.Mockito; import org.mockito.Mockito;
import sonia.scm.cache.CacheManager;
import sonia.scm.cache.MapCacheManager;
import sonia.scm.config.ScmConfiguration; import sonia.scm.config.ScmConfiguration;
import sonia.scm.group.Group; import sonia.scm.group.Group;
import sonia.scm.group.GroupManager; import sonia.scm.group.GroupManager;
@@ -89,6 +87,7 @@ import java.util.concurrent.atomic.AtomicLong;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
import sonia.scm.cache.GuavaCacheManager;
/** /**
* *
@@ -477,7 +476,7 @@ public class ScmRealmTest
Collections.EMPTY_LIST Collections.EMPTY_LIST
); );
CacheManager cacheManager = new MapCacheManager(); GuavaCacheManager cacheManager = new GuavaCacheManager();
AdminDetector adminSelector = new AdminDetector(new ScmConfiguration()); AdminDetector adminSelector = new AdminDetector(new ScmConfiguration());
LocalDatabaseSynchronizer synchronizer = new LocalDatabaseSynchronizer( LocalDatabaseSynchronizer synchronizer = new LocalDatabaseSynchronizer(
@@ -494,7 +493,7 @@ public class ScmRealmTest
); );
AuthorizationCollector authzCollector = new AuthorizationCollector( AuthorizationCollector authzCollector = new AuthorizationCollector(
cacheManager, cacheManager,
repositoryDAO, repositoryDAO,
securitySystem, securitySystem,
new RepositoryPermissionResolver() new RepositoryPermissionResolver()
@@ -516,6 +515,7 @@ public class ScmRealmTest
}; };
return new ScmRealm( return new ScmRealm(
cacheManager,
new AuthenticatorFacade(authManager, requestProvider, responseProvider), new AuthenticatorFacade(authManager, requestProvider, responseProvider),
dummyLoginAttemptHandler, dummyLoginAttemptHandler,
authcCollector, authcCollector,