mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-12 00:15:44 +01:00
#781 process all kinds authorization relevant event and produce AuthorizationChangedEvent
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -166,6 +166,7 @@ import sonia.scm.net.ahc.JsonContentTransformer;
|
||||
import sonia.scm.net.ahc.XmlContentTransformer;
|
||||
import sonia.scm.schedule.QuartzScheduler;
|
||||
import sonia.scm.schedule.Scheduler;
|
||||
import sonia.scm.security.AuthorizationChangedEventProducer;
|
||||
import sonia.scm.security.XsrfProtectionFilter;
|
||||
import sonia.scm.web.UserAgentParser;
|
||||
|
||||
@@ -300,6 +301,7 @@ public class ScmServletModule extends ServletModule
|
||||
pluginLoader.processExtensions(binder());
|
||||
|
||||
// bind security stuff
|
||||
bind(AuthorizationChangedEventProducer.class);
|
||||
bind(PermissionResolver.class, RepositoryPermissionResolver.class);
|
||||
bind(AuthenticationManager.class, ChainAuthenticatonManager.class);
|
||||
bind(SecurityContext.class).to(BasicSecurityContext.class);
|
||||
@@ -310,6 +312,7 @@ public class ScmServletModule extends ServletModule
|
||||
|
||||
// bind cache
|
||||
bind(CacheManager.class, GuavaCacheManager.class);
|
||||
bind(org.apache.shiro.cache.CacheManager.class, GuavaCacheManager.class);
|
||||
|
||||
// bind dao
|
||||
bind(GroupDAO.class, XmlGroupDAO.class);
|
||||
@@ -386,8 +389,7 @@ public class ScmServletModule extends ServletModule
|
||||
filter(PATTERN_ALL).through(BaseUrlFilter.class);
|
||||
filter(PATTERN_ALL).through(AutoLoginFilter.class);
|
||||
filterRegex(RESOURCE_REGEX).through(GZipFilter.class);
|
||||
filter(PATTERN_RESTAPI,
|
||||
PATTERN_DEBUG).through(ApiBasicAuthenticationFilter.class);
|
||||
filter(PATTERN_RESTAPI, PATTERN_DEBUG).through(ApiBasicAuthenticationFilter.class);
|
||||
filter(PATTERN_RESTAPI, PATTERN_DEBUG).through(SecurityFilter.class);
|
||||
filter(PATTERN_CONFIG, PATTERN_ADMIN).through(AdminSecurityFilter.class);
|
||||
|
||||
@@ -434,11 +436,7 @@ public class ScmServletModule extends ServletModule
|
||||
UriExtensionsConfig.class.getName());
|
||||
|
||||
String restPath = getRestPackages();
|
||||
|
||||
if (logger.isInfoEnabled())
|
||||
{
|
||||
logger.info("configure jersey with package path: {}", restPath);
|
||||
}
|
||||
logger.info("configure jersey with package path: {}", restPath);
|
||||
|
||||
params.put(PackagesResourceConfig.PROPERTY_PACKAGES, restPath);
|
||||
serve(PATTERN_RESTAPI).with(GuiceContainer.class, params);
|
||||
|
||||
@@ -93,7 +93,7 @@ public class AuthenticationInfoCollector {
|
||||
|
||||
synchronizer.synchronize(user, groups);
|
||||
|
||||
if (isUserIsDisabled(user)) {
|
||||
if (isUserDisabled(user)) {
|
||||
throwAccountIsDisabledExceptionAndLog(user.getName());
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ public class AuthenticationInfoCollector {
|
||||
return collection;
|
||||
}
|
||||
|
||||
private boolean isUserIsDisabled(User user) {
|
||||
private boolean isUserDisabled(User user) {
|
||||
return !user.isActive();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -57,14 +57,11 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import sonia.scm.cache.Cache;
|
||||
import sonia.scm.cache.CacheManager;
|
||||
import sonia.scm.group.GroupEvent;
|
||||
import sonia.scm.group.GroupNames;
|
||||
import sonia.scm.repository.PermissionType;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryDAO;
|
||||
import sonia.scm.repository.RepositoryEvent;
|
||||
import sonia.scm.user.User;
|
||||
import sonia.scm.user.UserEvent;
|
||||
import sonia.scm.util.Util;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
@@ -72,10 +69,6 @@ import sonia.scm.util.Util;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import sonia.scm.Filter;
|
||||
import sonia.scm.group.Group;
|
||||
import sonia.scm.group.GroupModificationEvent;
|
||||
import sonia.scm.repository.RepositoryModificationEvent;
|
||||
import sonia.scm.user.UserModificationEvent;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -114,8 +107,7 @@ public class AuthorizationCollector
|
||||
RepositoryDAO repositoryDAO, SecuritySystem securitySystem,
|
||||
PermissionResolver resolver)
|
||||
{
|
||||
this.cache = cacheManager.getCache(CacheKey.class, AuthorizationInfo.class,
|
||||
CACHE_NAME);
|
||||
this.cache = cacheManager.getCache(CacheKey.class, AuthorizationInfo.class, CACHE_NAME);
|
||||
this.repositoryDAO = repositoryDAO;
|
||||
this.securitySystem = securitySystem;
|
||||
this.resolver = resolver;
|
||||
@@ -146,189 +138,9 @@ public class AuthorizationCollector
|
||||
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
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param principals
|
||||
*
|
||||
* @return
|
||||
@@ -568,6 +380,30 @@ public class AuthorizationCollector
|
||||
//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 --------------------------------------------------------
|
||||
|
||||
/**
|
||||
|
||||
@@ -35,6 +35,7 @@ package sonia.scm.security;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import com.google.inject.Inject;
|
||||
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.pam.UnsupportedTokenException;
|
||||
import org.apache.shiro.authz.AuthorizationInfo;
|
||||
import org.apache.shiro.cache.CacheManager;
|
||||
import org.apache.shiro.realm.AuthorizingRealm;
|
||||
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.AuthenticationState;
|
||||
|
||||
@@ -63,6 +66,11 @@ import sonia.scm.web.security.AuthenticationState;
|
||||
@Singleton
|
||||
public class ScmRealm extends AuthorizingRealm {
|
||||
|
||||
/**
|
||||
* the logger for ScmRealm
|
||||
*/
|
||||
private static final Logger logger = LoggerFactory.getLogger(ScmRealm.class);
|
||||
|
||||
public static final String NAME = "scm";
|
||||
|
||||
|
||||
@@ -74,32 +82,35 @@ public class ScmRealm extends AuthorizingRealm {
|
||||
/**
|
||||
* Constructs a new scm realm.
|
||||
*
|
||||
* @param cacheManager cache manager
|
||||
* @param authenticator authenticator facade
|
||||
* @param loginAttemptHandler login attempt handler
|
||||
* @param authcCollector authentication info collector
|
||||
* @param authzCollector authorization collector
|
||||
*/
|
||||
@Inject
|
||||
public ScmRealm(AuthenticatorFacade authenticator, LoginAttemptHandler loginAttemptHandler,
|
||||
public ScmRealm(CacheManager cacheManager,
|
||||
AuthenticatorFacade authenticator, LoginAttemptHandler loginAttemptHandler,
|
||||
AuthenticationInfoCollector authcCollector, AuthorizationCollector authzCollector) {
|
||||
super(cacheManager);
|
||||
|
||||
this.authenticator = authenticator;
|
||||
this.loginAttemptHandler = loginAttemptHandler;
|
||||
this.authcCollector = authcCollector;
|
||||
this.authzCollector = authzCollector;
|
||||
|
||||
|
||||
// set token class
|
||||
setAuthenticationTokenClass(UsernamePasswordToken.class);
|
||||
|
||||
// use own custom caching
|
||||
setCachingEnabled(false);
|
||||
setAuthenticationCachingEnabled(false);
|
||||
setAuthorizationCachingEnabled(false);
|
||||
|
||||
// set components
|
||||
setPermissionResolver(new RepositoryPermissionResolver());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getAuthorizationCacheKey(PrincipalCollection principals) {
|
||||
return principals.getPrimaryPrincipal();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authToken) throws AuthenticationException {
|
||||
UsernamePasswordToken token = castToken(authToken);
|
||||
@@ -138,4 +149,23 @@ public class ScmRealm extends AuthorizingRealm {
|
||||
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,8 +72,11 @@ import sonia.scm.security.SecuritySystem;
|
||||
import sonia.scm.user.UserTestData;
|
||||
|
||||
/**
|
||||
* Performance test for {@link RepositoryManager#getAll()}.
|
||||
*
|
||||
* @see <a href="https://goo.gl/PD1AeM">Issue 781</a>
|
||||
* @author Sebastian Sdorra
|
||||
* @since 1.52
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class DefaultRepositoryManagerPerfTest {
|
||||
@@ -105,6 +108,9 @@ public class DefaultRepositoryManagerPerfTest {
|
||||
|
||||
private AuthorizationCollector authzCollector;
|
||||
|
||||
/**
|
||||
* Setup object under test.
|
||||
*/
|
||||
@Before
|
||||
public void setUpObjectUnderTest(){
|
||||
when(repositoryHandler.getType()).thenReturn(new Type(REPOSITORY_TYPE, REPOSITORY_TYPE));
|
||||
@@ -132,11 +138,17 @@ public class DefaultRepositoryManagerPerfTest {
|
||||
ThreadContext.bind(securityManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tear down test objects.
|
||||
*/
|
||||
@After
|
||||
public void tearDown(){
|
||||
ThreadContext.unbindSecurityManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start performance test and ensure that the timeout is not reached.
|
||||
*/
|
||||
@Test(timeout = 6000l)
|
||||
public void perfTestGetAll(){
|
||||
SecurityUtils.getSubject().login(new UsernamePasswordToken("trillian", "secret"));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -54,22 +54,14 @@ import static org.junit.Assert.*;
|
||||
import org.junit.Rule;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import sonia.scm.Filter;
|
||||
import sonia.scm.HandlerEvent;
|
||||
import sonia.scm.cache.Cache;
|
||||
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.repository.PermissionType;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.RepositoryDAO;
|
||||
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;
|
||||
|
||||
/**
|
||||
@@ -111,160 +103,6 @@ public class AuthorizationCollectorTest {
|
||||
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.
|
||||
*/
|
||||
@@ -402,8 +240,7 @@ public class AuthorizationCollectorTest {
|
||||
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();
|
||||
spc.add(user.getName(), "unit");
|
||||
spc.add(user, "unit");
|
||||
@@ -412,4 +249,16 @@ public class AuthorizationCollectorTest {
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -54,8 +54,6 @@ 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;
|
||||
@@ -89,6 +87,7 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import sonia.scm.cache.GuavaCacheManager;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -477,7 +476,7 @@ public class ScmRealmTest
|
||||
Collections.EMPTY_LIST
|
||||
);
|
||||
|
||||
CacheManager cacheManager = new MapCacheManager();
|
||||
GuavaCacheManager cacheManager = new GuavaCacheManager();
|
||||
|
||||
AdminDetector adminSelector = new AdminDetector(new ScmConfiguration());
|
||||
LocalDatabaseSynchronizer synchronizer = new LocalDatabaseSynchronizer(
|
||||
@@ -516,6 +515,7 @@ public class ScmRealmTest
|
||||
};
|
||||
|
||||
return new ScmRealm(
|
||||
cacheManager,
|
||||
new AuthenticatorFacade(authManager, requestProvider, responseProvider),
|
||||
dummyLoginAttemptHandler,
|
||||
authcCollector,
|
||||
|
||||
Reference in New Issue
Block a user