merge with branch apache-shiro

This commit is contained in:
Sebastian Sdorra
2012-10-16 07:08:28 +02:00
82 changed files with 3376 additions and 1317 deletions

View File

@@ -35,11 +35,14 @@ package sonia.scm;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.collect.Lists;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.servlet.GuiceServletContextListener;
import org.apache.shiro.guice.web.ShiroWebModule;
import sonia.scm.cache.CacheManager;
import sonia.scm.group.GroupManager;
import sonia.scm.plugin.DefaultPluginLoader;
@@ -49,13 +52,12 @@ import sonia.scm.store.StoreFactory;
import sonia.scm.user.UserManager;
import sonia.scm.util.IOUtil;
import sonia.scm.web.security.AuthenticationManager;
import sonia.scm.web.security.LocalSecurityContextHolder;
//~--- JDK imports ------------------------------------------------------------
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
/**
@@ -95,13 +97,10 @@ public class ScmContextListener extends GuiceServletContextListener
// close CacheManager
IOUtil.close(globalInjector.getInstance(CacheManager.class));
// remove thread local store
globalInjector.getInstance(LocalSecurityContextHolder.class).destroy();
// call destroy event
globalInjector.getInstance(
ServletContextListenerHolder.class).contextDestroyed(
servletContextEvent);
ServletContextListenerHolder.class).contextDestroyed(
servletContextEvent);
}
super.contextDestroyed(servletContextEvent);
@@ -116,6 +115,8 @@ public class ScmContextListener extends GuiceServletContextListener
@Override
public void contextInitialized(ServletContextEvent servletContextEvent)
{
this.servletContext = servletContextEvent.getServletContext();
if (SCMContext.getContext().getStartupError() == null)
{
ScmUpgradeHandler upgradeHandler = new ScmUpgradeHandler();
@@ -133,8 +134,8 @@ public class ScmContextListener extends GuiceServletContextListener
if ((globalInjector != null) &&!startupError)
{
globalInjector.getInstance(
ServletContextListenerHolder.class).contextInitialized(
servletContextEvent);
ServletContextListenerHolder.class).contextInitialized(
servletContextEvent);
}
}
@@ -155,7 +156,7 @@ public class ScmContextListener extends GuiceServletContextListener
}
else
{
globalInjector = getDefaultInjector();
globalInjector = getDefaultInjector(servletContext);
}
return globalInjector;
@@ -165,9 +166,11 @@ public class ScmContextListener extends GuiceServletContextListener
* Method description
*
*
*
* @param servletContext
* @return
*/
private Injector getDefaultInjector()
private Injector getDefaultInjector(ServletContext servletContext)
{
PluginLoader pluginLoader = new DefaultPluginLoader();
BindingExtensionProcessor bindExtProcessor =
@@ -178,42 +181,16 @@ public class ScmContextListener extends GuiceServletContextListener
ClassOverrides overrides = ClassOverrides.findOverrides();
ScmServletModule main = new ScmServletModule(pluginLoader,
bindExtProcessor, overrides);
List<Module> moduleList = new ArrayList<Module>();
List<Module> moduleList = Lists.newArrayList();
moduleList.add(new ScmInitializerModule());
moduleList.add(ShiroWebModule.guiceFilterModule());
moduleList.add(main);
moduleList.add(new ScmSecurityModule(servletContext));
moduleList.addAll(bindExtProcessor.getModuleSet());
moduleList.addAll(overrides.getModules());
moduleList.add(0, main);
Injector injector = Guice.createInjector(moduleList);
SCMContextProvider context = SCMContext.getContext();
// init StoreFactory
injector.getInstance(StoreFactory.class).init(context);
// init RepositoryManager
RepositoryManager repositoryManager =
injector.getInstance(RepositoryManager.class);
repositoryManager.addHooks(bindExtProcessor.getHooks());
repositoryManager.init(context);
// init UserManager
UserManager userManager = injector.getInstance(UserManager.class);
userManager.init(context);
// init GroupManager
GroupManager groupManager = injector.getInstance(GroupManager.class);
groupManager.init(context);
// init Authenticator
AuthenticationManager authenticationManager =
injector.getInstance(AuthenticationManager.class);
authenticationManager.init(context);
return injector;
return Guice.createInjector(moduleList);
}
/**
@@ -232,6 +209,9 @@ public class ScmContextListener extends GuiceServletContextListener
/** Field description */
private Injector globalInjector;
/** Field description */
private ServletContext servletContext;
/** Field description */
private boolean startupError = false;
}

View File

@@ -30,72 +30,86 @@
*/
package sonia.scm.web.security;
package sonia.scm;
//~--- non-JDK imports --------------------------------------------------------
import sonia.scm.user.User;
import com.google.inject.AbstractModule;
import com.google.inject.TypeLiteral;
import com.google.inject.matcher.AbstractMatcher;
import com.google.inject.matcher.Matcher;
import com.google.inject.spi.InjectionListener;
import com.google.inject.spi.TypeEncounter;
import com.google.inject.spi.TypeListener;
//~--- JDK imports ------------------------------------------------------------
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author Sebastian Sdorra
*/
public class AdministrationSecurityContext implements WebSecurityContext
public class ScmInitializerModule extends AbstractModule
{
/**
* Constructs ...
*
*
* @param user
* the logger for ScmInitializerModule
*/
public AdministrationSecurityContext(User user)
{
this.user = user;
}
private static final Logger logger =
LoggerFactory.getLogger(ScmInitializerModule.class);
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param request
* @param response
* @param username
* @param password
*
* @return
*/
@Override
public User authenticate(HttpServletRequest request,
HttpServletResponse response, String username,
String password)
protected void configure()
{
throw new UnsupportedOperationException("Not supported yet.");
bindListener(isSubtypeOf(Initable.class), new TypeListener()
{
@Override
public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter)
{
encounter.register(new InjectionListener<I>()
{
@Override
public void afterInjection(Object i)
{
if (logger.isTraceEnabled())
{
logger.trace("initialize initable {}", i.getClass());
}
Initable initable = (Initable) i;
initable.init(SCMContext.getContext());
}
});
}
});
}
/**
* Method description
*
*
* @param request
* @param response
* @param subtype
* @param supertype
*
* @return
*/
@Override
public void logout(HttpServletRequest request, HttpServletResponse response)
private boolean typeIsSubtypeOf(TypeLiteral<?> subtype,
TypeLiteral<?> supertype)
{
throw new UnsupportedOperationException("Not supported yet.");
// First check that raw types are compatible
// Then check that generic types are compatible! HOW????
return (subtype.equals(supertype)
|| (supertype.getRawType().isAssignableFrom(subtype.getRawType())
&& supertype.equals(subtype.getSupertype(supertype.getRawType()))));
}
//~--- get methods ----------------------------------------------------------
@@ -104,43 +118,32 @@ public class AdministrationSecurityContext implements WebSecurityContext
* Method description
*
*
* @param supertype
*
* @return
*/
@Override
public Collection<String> getGroups()
private Matcher<TypeLiteral<?>> isSubtypeOf(final Class<?> supertype)
{
return groups;
return isSubtypeOf(TypeLiteral.get(supertype));
}
/**
* Method description
*
*
* @return
*/
@Override
public User getUser()
{
return user;
}
/**
* Method description
*
* @param supertype
*
* @return
*/
@Override
public boolean isAuthenticated()
private Matcher<TypeLiteral<?>> isSubtypeOf(final TypeLiteral<?> supertype)
{
return true;
return new AbstractMatcher<TypeLiteral<?>>()
{
@Override
public boolean matches(TypeLiteral<?> type)
{
return typeIsSubtypeOf(type, supertype);
}
};
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private List<String> groups = new ArrayList<String>();
/** Field description */
private User user;
}

View File

@@ -30,67 +30,45 @@
*/
package sonia.scm;
package sonia.scm.web.security;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Singleton;
import org.apache.shiro.guice.web.ShiroWebModule;
import sonia.scm.security.ScmRealm;
//~--- JDK imports ------------------------------------------------------------
import javax.servlet.ServletContext;
/**
*
* @author Sebastian Sdorra
*/
@Singleton
public class LocalSecurityContextHolder
public class ScmSecurityModule extends ShiroWebModule
{
/**
* Method description
* Constructs ...
*
*
* @param servletContext
*/
public void destroy()
ScmSecurityModule(ServletContext servletContext)
{
store.remove();
store = null;
super(servletContext);
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*/
public void remove()
@Override
protected void configureShiroWeb()
{
store.remove();
bindRealm().to(ScmRealm.class);
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
public WebSecurityContext get()
{
return store.get();
}
//~--- set methods ----------------------------------------------------------
/**
* Method description
*
*
* @param value
*/
public void set(WebSecurityContext value)
{
store.set(value);
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private ThreadLocal<WebSecurityContext> store =
new ThreadLocal<WebSecurityContext>();
}

View File

@@ -116,8 +116,6 @@ import sonia.scm.web.security.AuthenticationManager;
import sonia.scm.web.security.BasicSecurityContext;
import sonia.scm.web.security.ChainAuthenticatonManager;
import sonia.scm.web.security.DefaultAdministrationContext;
import sonia.scm.web.security.LocalSecurityContextHolder;
import sonia.scm.web.security.SecurityContextProvider;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
@@ -264,11 +262,8 @@ public class ScmServletModule extends ServletModule
// bind security stuff
bind(AuthenticationManager.class, ChainAuthenticatonManager.class);
bind(LocalSecurityContextHolder.class);
bind(WebSecurityContext.class).annotatedWith(Names.named("userSession")).to(
BasicSecurityContext.class);
bind(SecurityContext.class).toProvider(SecurityContextProvider.class);
bind(WebSecurityContext.class).toProvider(SecurityContextProvider.class);
bind(SecurityContext.class).to(BasicSecurityContext.class);
bind(WebSecurityContext.class).to(BasicSecurityContext.class);
bind(AdministrationContext.class, DefaultAdministrationContext.class);
// bind security cache

View File

@@ -36,9 +36,13 @@ package sonia.scm.api.rest.resources;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.codehaus.enunciate.jaxrs.TypeHint;
import org.codehaus.enunciate.modules.jersey.ExternallyManagedLifecycle;
@@ -50,13 +54,17 @@ import sonia.scm.SCMContextProvider;
import sonia.scm.ScmClientConfig;
import sonia.scm.ScmState;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.group.GroupNames;
import sonia.scm.repository.RepositoryManager;
import sonia.scm.security.Tokens;
import sonia.scm.user.User;
import sonia.scm.user.UserManager;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
import java.util.Collection;
import java.util.Collections;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -98,16 +106,14 @@ public class AuthenticationResource
* @param securityContextProvider
*/
@Inject
public AuthenticationResource(
SCMContextProvider contextProvider, ScmConfiguration configuration,
RepositoryManager repositoryManger, UserManager userManager,
Provider<WebSecurityContext> securityContextProvider)
public AuthenticationResource(SCMContextProvider contextProvider,
ScmConfiguration configuration, RepositoryManager repositoryManger,
UserManager userManager)
{
this.contextProvider = contextProvider;
this.configuration = configuration;
this.repositoryManger = repositoryManger;
this.userManager = userManager;
this.securityContextProvider = securityContextProvider;
}
//~--- methods --------------------------------------------------------------
@@ -132,21 +138,30 @@ public class AuthenticationResource
@Path("login")
@TypeHint(ScmState.class)
public ScmState authenticate(@Context HttpServletRequest request,
@Context HttpServletResponse response,
@FormParam("username") String username,
@FormParam("password") String password)
@FormParam("username") String username,
@FormParam("password") String password)
{
ScmState state = null;
WebSecurityContext securityContext = securityContextProvider.get();
User user = securityContext.authenticate(request, response, username,
password);
if ((user != null) &&!SCMContext.USER_ANONYMOUS.equals(user.getName()))
Subject subject = SecurityUtils.getSubject();
try
{
state = createState(securityContext);
subject.login(Tokens.createAuthenticationToken(request, username,
password));
state = createState(subject);
}
else
catch (AuthenticationException ex)
{
if (logger.isTraceEnabled())
{
logger.trace("authentication failed for user ".concat(username), ex);
}
else if (logger.isWarnEnabled())
{
logger.warn("authentication failed for user {}", username);
}
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}
@@ -171,20 +186,18 @@ public class AuthenticationResource
@Path("logout")
@TypeHint(ScmState.class)
public Response logout(@Context HttpServletRequest request,
@Context HttpServletResponse response)
@Context HttpServletResponse response)
{
WebSecurityContext securityContext = securityContextProvider.get();
Subject subject = SecurityUtils.getSubject();
securityContext.logout(request, response);
subject.logout();
Response resp = null;
User user = securityContext.getUser();
if (user != null)
if (configuration.isAnonymousAccessEnabled())
{
ScmState state = createState(securityContext);
resp = Response.ok(state).build();
resp = Response.ok(createAnonymousState()).build();
}
else
{
@@ -238,20 +251,24 @@ public class AuthenticationResource
public Response getState(@Context HttpServletRequest request)
{
Response response = null;
ScmState state = null;
WebSecurityContext securityContext = securityContextProvider.get();
User user = securityContext.getUser();
Subject subject = SecurityUtils.getSubject();
if (user != null)
if (subject.isAuthenticated())
{
if (logger.isDebugEnabled())
{
logger.debug("return state for user {}", user.getName());
logger.debug("return state for user {}", subject.getPrincipal());
}
state = createState(securityContext);
ScmState state = createState(subject);
response = Response.ok(state).build();
}
else if (configuration.isAnonymousAccessEnabled())
{
response = Response.ok(createAnonymousState()).build();
}
else
{
response = Response.status(Response.Status.UNAUTHORIZED).build();
@@ -262,20 +279,50 @@ public class AuthenticationResource
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @return
*/
private ScmState createAnonymousState()
{
return createState(SCMContext.ANONYMOUS, Collections.EMPTY_LIST);
}
/**
* Method description
*
*
* @param securityContext
*
* @param subject
*
* @return
*/
private ScmState createState(WebSecurityContext securityContext)
private ScmState createState(Subject subject)
{
return new ScmState(contextProvider, securityContext,
repositoryManger.getConfiguredTypes(),
userManager.getDefaultType(),
new ScmClientConfig(configuration));
PrincipalCollection collection = subject.getPrincipals();
User user = collection.oneByType(User.class);
GroupNames groups = collection.oneByType(GroupNames.class);
return createState(user, groups.getCollection());
}
/**
* Method description
*
*
* @param user
* @param groups
*
* @return
*/
private ScmState createState(User user, Collection<String> groups)
{
return new ScmState(contextProvider, user, groups,
repositoryManger.getConfiguredTypes(), userManager.getDefaultType(),
new ScmClientConfig(configuration));
}
//~--- fields ---------------------------------------------------------------
@@ -289,9 +336,6 @@ public class AuthenticationResource
/** Field description */
private RepositoryManager repositoryManger;
/** Field description */
private Provider<WebSecurityContext> securityContextProvider;
/** Field description */
private UserManager userManager;
}

View File

@@ -36,7 +36,9 @@ package sonia.scm.api.rest.resources;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Provider;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.codehaus.enunciate.jaxrs.TypeHint;
import org.codehaus.enunciate.modules.jersey.ExternallyManagedLifecycle;
@@ -46,11 +48,11 @@ import org.slf4j.LoggerFactory;
import sonia.scm.api.rest.RestActionResult;
import sonia.scm.security.EncryptionHandler;
import sonia.scm.security.ScmSecurityException;
import sonia.scm.user.User;
import sonia.scm.user.UserException;
import sonia.scm.user.UserManager;
import sonia.scm.util.AssertUtil;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
@@ -88,13 +90,11 @@ public class ChangePasswordResource
* @param securityContextProvider
*/
@Inject
public ChangePasswordResource(
UserManager userManager, EncryptionHandler encryptionHandler,
Provider<WebSecurityContext> securityContextProvider)
public ChangePasswordResource(UserManager userManager,
EncryptionHandler encryptionHandler)
{
this.userManager = userManager;
this.encryptionHandler = encryptionHandler;
this.securityContextProvider = securityContextProvider;
}
//~--- methods --------------------------------------------------------------
@@ -121,8 +121,8 @@ public class ChangePasswordResource
@TypeHint(RestActionResult.class)
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public Response changePassword(@FormParam("old-password") String oldPassword,
@FormParam("new-password") String newPassword)
throws UserException, IOException
@FormParam("new-password") String newPassword)
throws UserException, IOException
{
AssertUtil.assertIsNotEmpty(oldPassword);
AssertUtil.assertIsNotEmpty(newPassword);
@@ -135,8 +135,14 @@ public class ChangePasswordResource
}
Response response = null;
WebSecurityContext securityContext = securityContextProvider.get();
User currentUser = securityContext.getUser();
Subject subject = SecurityUtils.getSubject();
if (!subject.isAuthenticated())
{
throw new ScmSecurityException("user is not authenticated");
}
User currentUser = subject.getPrincipals().oneByType(User.class);
if (logger.isInfoEnabled())
{
@@ -178,9 +184,6 @@ public class ChangePasswordResource
/** Field description */
private EncryptionHandler encryptionHandler;
/** Field description */
private Provider<WebSecurityContext> securityContextProvider;
/** Field description */
private UserManager userManager;
}

View File

@@ -36,15 +36,17 @@ package sonia.scm.api.rest.resources;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.codehaus.enunciate.modules.jersey.ExternallyManagedLifecycle;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.security.Role;
import sonia.scm.security.ScmSecurityException;
import sonia.scm.util.ScmConfigurationUtil;
import sonia.scm.util.SecurityUtil;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
@@ -76,11 +78,8 @@ public class ConfigurationResource
* @param securityContextProvider
*/
@Inject
public ConfigurationResource(
Provider<WebSecurityContext> securityContextProvider,
ScmConfiguration configuration)
public ConfigurationResource(ScmConfiguration configuration)
{
this.securityContextProvider = securityContextProvider;
this.configuration = configuration;
}
@@ -98,7 +97,7 @@ public class ConfigurationResource
{
Response response = null;
if (SecurityUtil.isAdmin(securityContextProvider))
if (SecurityUtils.getSubject().hasRole(Role.ADMIN))
{
response = Response.ok(configuration).build();
}
@@ -124,9 +123,17 @@ public class ConfigurationResource
@POST
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public Response setConfig(@Context UriInfo uriInfo,
ScmConfiguration newConfig)
ScmConfiguration newConfig)
{
SecurityUtil.assertIsAdmin(securityContextProvider);
// TODO replace by checkRole
Subject subject = SecurityUtils.getSubject();
if (!subject.hasRole(Role.ADMIN))
{
throw new ScmSecurityException("admin privileges required");
}
configuration.load(newConfig);
synchronized (ScmConfiguration.class)
@@ -141,7 +148,4 @@ public class ConfigurationResource
/** Field description */
public ScmConfiguration configuration;
/** Field description */
private Provider<WebSecurityContext> securityContextProvider;
}

View File

@@ -39,14 +39,15 @@ import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.apache.shiro.SecurityUtils;
import org.codehaus.enunciate.jaxrs.TypeHint;
import org.codehaus.enunciate.modules.jersey.ExternallyManagedLifecycle;
import sonia.scm.group.Group;
import sonia.scm.group.GroupException;
import sonia.scm.group.GroupManager;
import sonia.scm.util.SecurityUtil;
import sonia.scm.web.security.WebSecurityContext;
import sonia.scm.security.Role;
//~--- JDK imports ------------------------------------------------------------
@@ -77,7 +78,7 @@ import javax.ws.rs.core.UriInfo;
@Singleton
@ExternallyManagedLifecycle
public class GroupResource
extends AbstractManagerResource<Group, GroupException>
extends AbstractManagerResource<Group, GroupException>
{
/** Field description */
@@ -94,11 +95,9 @@ public class GroupResource
* @param groupManager
*/
@Inject
public GroupResource(Provider<WebSecurityContext> securityContextProvider,
GroupManager groupManager)
public GroupResource(GroupManager groupManager)
{
super(groupManager);
this.securityContextProvider = securityContextProvider;
}
//~--- methods --------------------------------------------------------------
@@ -172,7 +171,7 @@ public class GroupResource
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Override
public Response update(@Context UriInfo uriInfo,
@PathParam("id") String name, Group group)
@PathParam("id") String name, Group group)
{
return super.update(uriInfo, name, group);
}
@@ -205,7 +204,7 @@ public class GroupResource
{
Response response = null;
if (SecurityUtil.isAdmin(securityContextProvider))
if (SecurityUtils.getSubject().hasRole(Role.ADMIN))
{
response = super.get(request, id);
}
@@ -243,7 +242,7 @@ public class GroupResource
public Response getAll(@Context Request request, @DefaultValue("0")
@QueryParam("start") int start, @DefaultValue("-1")
@QueryParam("limit") int limit, @QueryParam("sortby") String sortby,
@DefaultValue("false")
@DefaultValue("false")
@QueryParam("desc") boolean desc)
{
return super.getAll(request, start, limit, sortby, desc);
@@ -261,7 +260,7 @@ public class GroupResource
*/
@Override
protected GenericEntity<Collection<Group>> createGenericEntity(
Collection<Group> items)
Collection<Group> items)
{
return new GenericEntity<Collection<Group>>(items) {}
;
@@ -294,9 +293,4 @@ public class GroupResource
{
return PATH_PART;
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private Provider<WebSecurityContext> securityContextProvider;
}

View File

@@ -30,12 +30,12 @@
*/
package sonia.scm.api.rest.resources;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.codehaus.enunciate.jaxrs.TypeHint;
@@ -50,7 +50,6 @@ import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryHandler;
import sonia.scm.repository.RepositoryManager;
import sonia.scm.util.SecurityUtil;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
@@ -93,12 +92,9 @@ public class RepositoryImportResource
* @param securityContextProvider
*/
@Inject
public RepositoryImportResource(
RepositoryManager manager,
Provider<WebSecurityContext> securityContextProvider)
public RepositoryImportResource(RepositoryManager manager)
{
this.manager = manager;
this.securityContextProvider = securityContextProvider;
}
//~--- methods --------------------------------------------------------------
@@ -116,9 +112,9 @@ public class RepositoryImportResource
@TypeHint(Repository[].class)
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public GenericEntity<List<Repository>> importRepositories(
@PathParam("type") String type)
@PathParam("type") String type)
{
SecurityUtil.assertIsAdmin(securityContextProvider);
SecurityUtil.assertIsAdmin();
List<Repository> repositories = new ArrayList<Repository>();
RepositoryHandler handler = manager.getHandler(type);
@@ -143,7 +139,7 @@ public class RepositoryImportResource
else if (logger.isWarnEnabled())
{
logger.warn("could not find imported repository {}",
repositoryName);
repositoryName);
}
}
}
@@ -175,7 +171,7 @@ public class RepositoryImportResource
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public GenericEntity<List<Type>> getImportableTypes()
{
SecurityUtil.assertIsAdmin(securityContextProvider);
SecurityUtil.assertIsAdmin();
List<Type> types = new ArrayList<Type>();
Collection<Type> handlerTypes = manager.getTypes();
@@ -202,7 +198,7 @@ public class RepositoryImportResource
else if (logger.isInfoEnabled())
{
logger.info("{} handler does not support import of repositories",
t.getName());
t.getName());
}
}
}
@@ -220,7 +216,4 @@ public class RepositoryImportResource
/** Field description */
private RepositoryManager manager;
/** Field description */
private Provider<WebSecurityContext> securityContextProvider;
}

View File

@@ -38,9 +38,10 @@ package sonia.scm.api.rest.resources;
import com.google.common.base.Strings;
import com.google.common.io.Closeables;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.apache.shiro.SecurityUtils;
import org.codehaus.enunciate.jaxrs.TypeHint;
import org.codehaus.enunciate.modules.jersey.ExternallyManagedLifecycle;
@@ -55,7 +56,6 @@ import sonia.scm.repository.Changeset;
import sonia.scm.repository.ChangesetPagingResult;
import sonia.scm.repository.Permission;
import sonia.scm.repository.PermissionType;
import sonia.scm.repository.PermissionUtil;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryException;
import sonia.scm.repository.RepositoryIsNotArchivedException;
@@ -71,10 +71,10 @@ import sonia.scm.repository.api.DiffCommandBuilder;
import sonia.scm.repository.api.LogCommandBuilder;
import sonia.scm.repository.api.RepositoryService;
import sonia.scm.repository.api.RepositoryServiceFactory;
import sonia.scm.security.RepositoryPermission;
import sonia.scm.security.ScmSecurityException;
import sonia.scm.util.AssertUtil;
import sonia.scm.util.Util;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
@@ -137,14 +137,12 @@ public class RepositoryResource
@Inject
public RepositoryResource(ScmConfiguration configuration,
RepositoryManager repositoryManager,
Provider<WebSecurityContext> securityContextProvider,
RepositoryServiceFactory servicefactory)
{
super(repositoryManager);
this.configuration = configuration;
this.repositoryManager = repositoryManager;
this.servicefactory = servicefactory;
this.securityContextProvider = securityContextProvider;
setDisableCache(false);
}
@@ -1091,8 +1089,9 @@ public class RepositoryResource
*/
private boolean isOwner(Repository repository)
{
return PermissionUtil.hasPermission(repository, securityContextProvider,
PermissionType.OWNER);
return SecurityUtils.getSubject().isPermitted(
new RepositoryPermission(repository, PermissionType.OWNER));
}
//~--- fields ---------------------------------------------------------------
@@ -1103,9 +1102,6 @@ public class RepositoryResource
/** Field description */
private RepositoryManager repositoryManager;
/** Field description */
private Provider<WebSecurityContext> securityContextProvider;
/** Field description */
private RepositoryServiceFactory servicefactory;
}

View File

@@ -37,7 +37,6 @@ package sonia.scm.api.rest.resources;
import com.google.common.base.Function;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.codehaus.enunciate.modules.jersey.ExternallyManagedLifecycle;
@@ -54,7 +53,6 @@ import sonia.scm.search.SearchResults;
import sonia.scm.user.User;
import sonia.scm.user.UserListener;
import sonia.scm.user.UserManager;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
@@ -92,9 +90,8 @@ public class SearchResource implements UserListener, GroupListener
* @param cacheManager
*/
@Inject
public SearchResource(Provider<WebSecurityContext> securityContextProvider,
UserManager userManager, GroupManager groupManager,
CacheManager cacheManager)
public SearchResource(UserManager userManager, GroupManager groupManager,
CacheManager cacheManager)
{
// create user searchhandler
@@ -103,8 +100,7 @@ public class SearchResource implements UserListener, GroupListener
Cache<String, SearchResults> userCache =
cacheManager.getCache(String.class, SearchResults.class, CACHE_USER);
this.userSearchHandler = new SearchHandler<User>(securityContextProvider,
userCache, userManager);
this.userSearchHandler = new SearchHandler<User>(userCache, userManager);
// create group searchhandler
groupManager.addListener(this);
@@ -112,8 +108,8 @@ public class SearchResource implements UserListener, GroupListener
Cache<String, SearchResults> groupCache =
cacheManager.getCache(String.class, SearchResults.class, CACHE_GROUP);
this.groupSearchHandler = new SearchHandler<Group>(securityContextProvider,
groupCache, groupManager);
this.groupSearchHandler = new SearchHandler<Group>(groupCache,
groupManager);
}
//~--- methods --------------------------------------------------------------
@@ -162,7 +158,7 @@ public class SearchResource implements UserListener, GroupListener
public SearchResults searchGroups(@QueryParam("query") String queryString)
{
return groupSearchHandler.search(queryString,
new Function<Group, SearchResult>()
new Function<Group, SearchResult>()
{
@Override
public SearchResult apply(Group group)
@@ -198,7 +194,7 @@ public class SearchResource implements UserListener, GroupListener
public SearchResults searchUsers(@QueryParam("query") String queryString)
{
return userSearchHandler.search(queryString,
new Function<User, SearchResult>()
new Function<User, SearchResult>()
{
@Override
public SearchResult apply(User user)

View File

@@ -39,6 +39,9 @@ import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.inject.Inject;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.codehaus.enunciate.modules.jersey.ExternallyManagedLifecycle;
import sonia.scm.SCMContextProvider;
@@ -47,10 +50,10 @@ import sonia.scm.config.ScmConfiguration;
import sonia.scm.plugin.PluginManager;
import sonia.scm.repository.RepositoryHandler;
import sonia.scm.repository.RepositoryManager;
import sonia.scm.security.Role;
import sonia.scm.security.ScmSecurityException;
import sonia.scm.store.StoreFactory;
import sonia.scm.util.SecurityUtil;
import sonia.scm.util.SystemUtil;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
@@ -96,12 +99,10 @@ public class SupportResource
* @param repositoryManager
*/
@Inject
public SupportResource(WebSecurityContext securityContext,
SCMContextProvider context, ScmConfiguration configuration,
PluginManager pluginManager, StoreFactory storeFactory,
RepositoryManager repositoryManager)
public SupportResource(SCMContextProvider context,
ScmConfiguration configuration, PluginManager pluginManager,
StoreFactory storeFactory, RepositoryManager repositoryManager)
{
this.securityContext = securityContext;
this.context = context;
this.configuration = configuration;
this.pluginManager = pluginManager;
@@ -123,7 +124,12 @@ public class SupportResource
@Produces(MediaType.TEXT_HTML)
public Viewable getSupport() throws IOException
{
SecurityUtil.assertIsAdmin(securityContext);
Subject subject = SecurityUtils.getSubject();
if (!subject.hasRole(Role.ADMIN))
{
throw new ScmSecurityException("admin privileges required");
}
Map<String, Object> env = Maps.newHashMap();
@@ -445,9 +451,6 @@ public class SupportResource
/** Field description */
private RepositoryManager repositoryManager;
/** Field description */
private WebSecurityContext securityContext;
/** Field description */
private Class<?> storeFactoryClass;
}

View File

@@ -36,20 +36,20 @@ package sonia.scm.api.rest.resources;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.apache.shiro.SecurityUtils;
import org.codehaus.enunciate.jaxrs.TypeHint;
import org.codehaus.enunciate.modules.jersey.ExternallyManagedLifecycle;
import sonia.scm.security.EncryptionHandler;
import sonia.scm.security.Role;
import sonia.scm.user.User;
import sonia.scm.user.UserException;
import sonia.scm.user.UserManager;
import sonia.scm.util.AssertUtil;
import sonia.scm.util.SecurityUtil;
import sonia.scm.util.Util;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
@@ -100,12 +100,10 @@ public class UserResource extends AbstractManagerResource<User, UserException>
*/
@Inject
public UserResource(UserManager userManager,
EncryptionHandler encryptionHandler,
Provider<WebSecurityContext> securityContextProvider)
EncryptionHandler encryptionHandler)
{
super(userManager);
this.encryptionHandler = encryptionHandler;
this.securityContextProvider = securityContextProvider;
}
//~--- methods --------------------------------------------------------------
@@ -179,7 +177,7 @@ public class UserResource extends AbstractManagerResource<User, UserException>
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Override
public Response update(@Context UriInfo uriInfo,
@PathParam("id") String name, User user)
@PathParam("id") String name, User user)
{
return super.update(uriInfo, name, user);
}
@@ -212,7 +210,7 @@ public class UserResource extends AbstractManagerResource<User, UserException>
{
Response response = null;
if (SecurityUtil.isAdmin(securityContextProvider))
if (SecurityUtils.getSubject().hasRole(Role.ADMIN))
{
response = super.get(request, id);
}
@@ -250,7 +248,7 @@ public class UserResource extends AbstractManagerResource<User, UserException>
public Response getAll(@Context Request request, @DefaultValue("0")
@QueryParam("start") int start, @DefaultValue("-1")
@QueryParam("limit") int limit, @QueryParam("sortby") String sortby,
@DefaultValue("false")
@DefaultValue("false")
@QueryParam("desc") boolean desc)
{
return super.getAll(request, start, limit, sortby, desc);
@@ -268,7 +266,7 @@ public class UserResource extends AbstractManagerResource<User, UserException>
*/
@Override
protected GenericEntity<Collection<User>> createGenericEntity(
Collection<User> items)
Collection<User> items)
{
return new GenericEntity<Collection<User>>(items) {}
;
@@ -396,7 +394,4 @@ public class UserResource extends AbstractManagerResource<User, UserException>
/** Field description */
private EncryptionHandler encryptionHandler;
/** Field description */
private Provider<WebSecurityContext> securityContextProvider;
}

View File

@@ -36,11 +36,12 @@ package sonia.scm.filter;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import sonia.scm.util.SecurityUtil;
import sonia.scm.web.security.WebSecurityContext;
import org.apache.shiro.subject.Subject;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.security.Role;
/**
*
@@ -54,13 +55,12 @@ public class AdminSecurityFilter extends SecurityFilter
* Constructs ...
*
*
* @param securityContextProvider
* @param configuration
*/
@Inject
public AdminSecurityFilter(
Provider<WebSecurityContext> securityContextProvider)
public AdminSecurityFilter(ScmConfiguration configuration)
{
super(securityContextProvider);
super(configuration);
}
//~--- get methods ----------------------------------------------------------
@@ -71,11 +71,13 @@ public class AdminSecurityFilter extends SecurityFilter
*
* @param securityContext
*
* @param subject
*
* @return
*/
@Override
protected boolean hasPermission(WebSecurityContext securityContext)
protected boolean hasPermission(Subject subject)
{
return SecurityUtil.isAdmin(securityContext);
return subject.hasRole(Role.ADMIN);
}
}

View File

@@ -36,12 +36,16 @@ package sonia.scm.filter;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import sonia.scm.SCMContext;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.user.User;
import sonia.scm.web.filter.HttpFilter;
import sonia.scm.web.filter.SecurityHttpServletRequestWrapper;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
@@ -69,12 +73,12 @@ public class SecurityFilter extends HttpFilter
* Constructs ...
*
*
* @param securityContextProvider
* @param configuration
*/
@Inject
public SecurityFilter(Provider<WebSecurityContext> securityContextProvider)
public SecurityFilter(ScmConfiguration configuration)
{
this.securityContextProvider = securityContextProvider;
this.configuration = configuration;
}
//~--- methods --------------------------------------------------------------
@@ -92,40 +96,37 @@ public class SecurityFilter extends HttpFilter
*/
@Override
protected void doFilter(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)
throws IOException, ServletException
HttpServletResponse response, FilterChain chain)
throws IOException, ServletException
{
WebSecurityContext securityContext = securityContextProvider.get();
Subject subject = SecurityUtils.getSubject();
if (securityContext != null)
String uri =
request.getRequestURI().substring(request.getContextPath().length());
if (!uri.startsWith(URL_AUTHENTICATION))
{
String uri =
request.getRequestURI().substring(request.getContextPath().length());
if (!uri.startsWith(URL_AUTHENTICATION))
if (hasPermission(subject))
{
if (hasPermission(securityContext))
{
chain.doFilter(new SecurityHttpServletRequestWrapper(request,
securityContext.getUser()), response);
}
else if (securityContext.isAuthenticated())
{
response.sendError(HttpServletResponse.SC_FORBIDDEN);
}
else
{
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
chain.doFilter(new SecurityHttpServletRequestWrapper(request,
getUser(subject)), response);
}
else if (subject.isAuthenticated())
{
response.sendError(HttpServletResponse.SC_FORBIDDEN);
}
else if (configuration.isAnonymousAccessEnabled())
{
response.sendError(HttpServletResponse.SC_FORBIDDEN);
}
else
{
chain.doFilter(request, response);
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
}
else
{
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
chain.doFilter(request, response);
}
}
@@ -135,17 +136,42 @@ public class SecurityFilter extends HttpFilter
* Method description
*
*
* @param securityContext
* @param subject
*
* @return
*/
protected boolean hasPermission(WebSecurityContext securityContext)
protected boolean hasPermission(Subject subject)
{
return securityContext.isAuthenticated();
return ((configuration != null)
&& configuration.isAnonymousAccessEnabled()) || subject.isAuthenticated();
}
/**
* Method description
*
*
* @param subject
*
* @return
*/
private User getUser(Subject subject)
{
User user = null;
if (subject.isAuthenticated())
{
user = subject.getPrincipals().oneByType(User.class);
}
else
{
user = SCMContext.ANONYMOUS;
}
return user;
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private Provider<WebSecurityContext> securityContextProvider;
private ScmConfiguration configuration;
}

View File

@@ -47,7 +47,6 @@ import sonia.scm.SCMContextProvider;
import sonia.scm.TransformFilter;
import sonia.scm.search.SearchRequest;
import sonia.scm.search.SearchUtil;
import sonia.scm.security.SecurityContext;
import sonia.scm.util.CollectionAppender;
import sonia.scm.util.SecurityUtil;
import sonia.scm.util.Util;
@@ -87,11 +86,9 @@ public class DefaultGroupManager extends AbstractGroupManager
* @param groupListenerProvider
*/
@Inject
public DefaultGroupManager(Provider<SecurityContext> securityContextProvider,
GroupDAO groupDAO,
public DefaultGroupManager(GroupDAO groupDAO,
Provider<Set<GroupListener>> groupListenerProvider)
{
this.securityContextProvider = securityContextProvider;
this.groupDAO = groupDAO;
this.groupListenerProvider = groupListenerProvider;
}
@@ -136,7 +133,7 @@ public class DefaultGroupManager extends AbstractGroupManager
group.getType());
}
SecurityUtil.assertIsAdmin(securityContextProvider);
SecurityUtil.assertIsAdmin();
if (groupDAO.contains(group.getName()))
{
@@ -167,7 +164,7 @@ public class DefaultGroupManager extends AbstractGroupManager
group.getType());
}
SecurityUtil.assertIsAdmin(securityContextProvider);
SecurityUtil.assertIsAdmin();
String name = group.getName();
@@ -218,7 +215,7 @@ public class DefaultGroupManager extends AbstractGroupManager
group.getType());
}
SecurityUtil.assertIsAdmin(securityContextProvider);
SecurityUtil.assertIsAdmin();
String name = group.getName();
@@ -253,7 +250,7 @@ public class DefaultGroupManager extends AbstractGroupManager
group.getType());
}
SecurityUtil.assertIsAdmin(securityContextProvider);
SecurityUtil.assertIsAdmin();
Group fresh = groupDAO.get(group.getName());
@@ -346,7 +343,7 @@ public class DefaultGroupManager extends AbstractGroupManager
@Override
public Collection<Group> getAll(Comparator<Group> comparator)
{
SecurityUtil.assertIsAdmin(securityContextProvider);
SecurityUtil.assertIsAdmin();
List<Group> groups = new ArrayList<Group>();
@@ -378,7 +375,7 @@ public class DefaultGroupManager extends AbstractGroupManager
public Collection<Group> getAll(Comparator<Group> comparator, int start,
int limit)
{
SecurityUtil.assertIsAdmin(securityContextProvider);
SecurityUtil.assertIsAdmin();
return Util.createSubCollection(groupDAO.getAll(), comparator,
new CollectionAppender<Group>()
@@ -449,7 +446,4 @@ public class DefaultGroupManager extends AbstractGroupManager
/** Field description */
private Provider<Set<GroupListener>> groupListenerProvider;
/** Field description */
private Provider<SecurityContext> securityContextProvider;
}

View File

@@ -53,7 +53,6 @@ import sonia.scm.cache.CacheManager;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.io.ZipUnArchiver;
import sonia.scm.net.HttpClient;
import sonia.scm.security.SecurityContext;
import sonia.scm.util.AssertUtil;
import sonia.scm.util.IOUtil;
import sonia.scm.util.SecurityUtil;
@@ -123,12 +122,10 @@ public class DefaultPluginManager
*/
@Inject
public DefaultPluginManager(SCMContextProvider context,
Provider<SecurityContext> securityContextProvicer,
ScmConfiguration configuration, PluginLoader pluginLoader,
CacheManager cacheManager, Provider<HttpClient> clientProvider)
{
this.context = context;
this.securityContextProvicer = securityContextProvicer;
this.configuration = configuration;
this.cache = cacheManager.getCache(String.class, PluginCenter.class,
CACHE_NAME);
@@ -196,7 +193,7 @@ public class DefaultPluginManager
@Override
public void install(String id)
{
SecurityUtil.assertIsAdmin(securityContextProvicer);
SecurityUtil.assertIsAdmin();
PluginCenter center = getPluginCenter();
@@ -230,7 +227,7 @@ public class DefaultPluginManager
@Override
public void installPackage(InputStream packageStream) throws IOException
{
SecurityUtil.assertIsAdmin(securityContextProvicer);
SecurityUtil.assertIsAdmin();
File tempDirectory = Files.createTempDir();
@@ -276,7 +273,7 @@ public class DefaultPluginManager
@Override
public void uninstall(String id)
{
SecurityUtil.assertIsAdmin(securityContextProvicer);
SecurityUtil.assertIsAdmin();
Plugin plugin = installedPlugins.get(id);
@@ -320,7 +317,7 @@ public class DefaultPluginManager
@Override
public void update(String id)
{
SecurityUtil.assertIsAdmin(securityContextProvicer);
SecurityUtil.assertIsAdmin();
String[] idParts = id.split(":");
String groupId = idParts[0];
@@ -364,7 +361,7 @@ public class DefaultPluginManager
@Override
public PluginInformation get(String id)
{
SecurityUtil.assertIsAdmin(securityContextProvicer);
SecurityUtil.assertIsAdmin();
PluginInformation result = null;
@@ -393,7 +390,7 @@ public class DefaultPluginManager
public Set<PluginInformation> get(PluginFilter filter)
{
AssertUtil.assertIsNotNull(filter);
SecurityUtil.assertIsAdmin(securityContextProvicer);
SecurityUtil.assertIsAdmin();
Set<PluginInformation> infoSet = new HashSet<PluginInformation>();
@@ -412,7 +409,7 @@ public class DefaultPluginManager
@Override
public Collection<PluginInformation> getAll()
{
SecurityUtil.assertIsAdmin(securityContextProvicer);
SecurityUtil.assertIsAdmin();
Set<PluginInformation> infoSet = getInstalled();
@@ -430,7 +427,7 @@ public class DefaultPluginManager
@Override
public Collection<PluginInformation> getAvailable()
{
SecurityUtil.assertIsAdmin(securityContextProvicer);
SecurityUtil.assertIsAdmin();
Set<PluginInformation> availablePlugins = new HashSet<PluginInformation>();
Set<PluginInformation> centerPlugins = getPluginCenter().getPlugins();
@@ -455,7 +452,7 @@ public class DefaultPluginManager
@Override
public Set<PluginInformation> getAvailableUpdates()
{
SecurityUtil.assertIsAdmin(securityContextProvicer);
SecurityUtil.assertIsAdmin();
return get(FILTER_UPDATES);
}
@@ -469,7 +466,7 @@ public class DefaultPluginManager
@Override
public Set<PluginInformation> getInstalled()
{
SecurityUtil.assertIsAdmin(securityContextProvicer);
SecurityUtil.assertIsAdmin();
Set<PluginInformation> infoSet = new LinkedHashSet<PluginInformation>();
@@ -765,9 +762,6 @@ public class DefaultPluginManager
/** Field description */
private AetherPluginHandler pluginHandler;
/** Field description */
private Provider<SecurityContext> securityContextProvicer;
/** Field description */
private Unmarshaller unmarshaller;
}

View File

@@ -36,10 +36,15 @@ package sonia.scm.repository;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.concurrent.SubjectAwareExecutorService;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -55,15 +60,12 @@ import sonia.scm.util.AssertUtil;
import sonia.scm.util.CollectionAppender;
import sonia.scm.util.HttpUtil;
import sonia.scm.util.IOUtil;
import sonia.scm.util.SecurityUtil;
import sonia.scm.util.Util;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
@@ -94,10 +96,6 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager
/**
* Constructs ...
*
*
*
*
*
* @param configuration
* @param contextProvider
* @param keyGenerator
@@ -110,18 +108,18 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager
@Inject
public DefaultRepositoryManager(ScmConfiguration configuration,
SCMContextProvider contextProvider, KeyGenerator keyGenerator,
Provider<WebSecurityContext> securityContextProvider,
RepositoryDAO repositoryDAO, Set<RepositoryHandler> handlerSet,
Provider<Set<RepositoryListener>> repositoryListenersProvider,
Provider<Set<RepositoryHook>> repositoryHooksProvider)
{
this.configuration = configuration;
this.securityContextProvider = securityContextProvider;
this.keyGenerator = keyGenerator;
this.repositoryDAO = repositoryDAO;
this.repositoryListenersProvider = repositoryListenersProvider;
this.repositoryHooksProvider = repositoryHooksProvider;
this.executorService = Executors.newCachedThreadPool();
this.executorService =
new SubjectAwareExecutorService(Executors.newCachedThreadPool());
handlerMap = new HashMap<String, RepositoryHandler>();
types = new HashSet<Type>();
@@ -169,7 +167,7 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager
repository.getType());
}
SecurityUtil.assertIsAdmin(securityContextProvider);
assertIsAdmin();
AssertUtil.assertIsValid(repository);
if (repositoryDAO.contains(repository))
@@ -475,7 +473,7 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager
@Override
public Collection<Repository> getAll(Comparator<Repository> comparator)
{
List<Repository> repositories = new ArrayList<Repository>();
List<Repository> repositories = Lists.newArrayList();
for (Repository repository : repositoryDAO.getAll())
{
@@ -603,7 +601,7 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager
@Override
public Collection<Type> getConfiguredTypes()
{
List<Type> validTypes = new ArrayList<Type>();
List<Type> validTypes = Lists.newArrayList();
for (RepositoryHandler handler : handlerMap.values())
{
@@ -867,25 +865,44 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager
/**
* Method description
*
*/
private void assertIsAdmin()
{
if (!SecurityUtils.getSubject().hasRole("admin"))
{
throw new ScmSecurityException("admin role is required");
}
}
/**
* TODO use {@link Subject#checkPermission(org.apache.shiro.authz.Permission)}
* in version 2.x.
*
*
* @param repository
*/
private void assertIsOwner(Repository repository)
{
PermissionUtil.assertPermission(repository, securityContextProvider,
PermissionType.OWNER);
if (!isPermitted(repository, PermissionType.OWNER))
{
throw new ScmSecurityException(
"owner permission is required, access denied");
}
}
/**
* Method description
*
* TODO use {@link Subject#checkPermission(org.apache.shiro.authz.Permission)}
* in version 2.x.
*
* @param repository
*/
private void assertIsReader(Repository repository)
{
PermissionUtil.assertPermission(repository, securityContextProvider,
PermissionType.READ);
if (!isReader(repository))
{
throw new ScmSecurityException(
"reader permission is required, access denied");
}
}
//~--- get methods ----------------------------------------------------------
@@ -944,6 +961,20 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager
return result;
}
/**
* Method description
*
*
* @param repository
* @param type
*
* @return
*/
private boolean isPermitted(Repository repository, PermissionType type)
{
return PermissionUtil.hasPermission(configuration, repository, type);
}
/**
* Method description
*
@@ -954,8 +985,7 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager
*/
private boolean isReader(Repository repository)
{
return PermissionUtil.hasPermission(repository, securityContextProvider,
PermissionType.READ);
return isPermitted(repository, PermissionType.READ);
}
//~--- fields ---------------------------------------------------------------
@@ -981,9 +1011,6 @@ public class DefaultRepositoryManager extends AbstractRepositoryManager
/** Field description */
private Provider<Set<RepositoryListener>> repositoryListenersProvider;
/** Field description */
private Provider<WebSecurityContext> securityContextProvider;
/** Field description */
private Set<Type> types;
}

View File

@@ -37,15 +37,16 @@ package sonia.scm.search;
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.google.inject.Provider;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.cache.Cache;
import sonia.scm.util.SecurityUtil;
import sonia.scm.security.ScmSecurityException;
import sonia.scm.util.Util;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
@@ -77,11 +78,10 @@ public class SearchHandler<T>
* @param cache
* @param searchable
*/
public SearchHandler(Provider<WebSecurityContext> securityContextProvider,
Cache<String, SearchResults> cache,
Searchable<T> searchable)
public SearchHandler(Cache<String, SearchResults> cache,
Searchable<T> searchable)
{
this.securityContextProvider = securityContextProvider;
this.cache = cache;
this.searchable = searchable;
}
@@ -107,9 +107,14 @@ public class SearchHandler<T>
* @return
*/
public SearchResults search(String queryString,
Function<T, SearchResult> function)
Function<T, SearchResult> function)
{
SecurityUtil.assertIsNotAnonymous(securityContextProvider);
Subject subject = SecurityUtils.getSubject();
if (!subject.isAuthenticated())
{
throw new ScmSecurityException("Authentication is required");
}
if (Util.isEmpty(queryString))
{
@@ -202,9 +207,6 @@ public class SearchHandler<T>
/** Field description */
protected Searchable<T> searchable;
/** Field description */
protected Provider<WebSecurityContext> securityContextProvider;
/** Field description */
private int maxResults = 5;

View File

@@ -0,0 +1,149 @@
/**
* Copyright (c) 2010, Sebastian Sdorra
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of SCM-Manager; nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.security;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.base.Splitter;
import org.apache.shiro.authz.permission.PermissionResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.repository.PermissionType;
//~--- JDK imports ------------------------------------------------------------
import java.util.Iterator;
import java.util.Locale;
/**
*
* @author Sebastian Sdorra
*/
public class RepositoryPermissionResolver implements PermissionResolver
{
/** Field description */
private static final String TYPE_REPOSITORY = "repository";
/**
* the logger for RepositoryPermissionResolver
*/
private static final Logger logger =
LoggerFactory.getLogger(RepositoryPermissionResolver.class);
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param permissionString
*
* @return
*/
@Override
public RepositoryPermission resolvePermission(String permissionString)
{
RepositoryPermission permission = null;
Iterator<String> permissionIt =
Splitter.on(':').omitEmptyStrings().trimResults().split(
permissionString).iterator();
if (permissionIt.hasNext())
{
String type = permissionIt.next();
if (type.equals(TYPE_REPOSITORY))
{
permission = createRepositoryPermission(permissionIt);
}
else if (logger.isWarnEnabled())
{
logger.warn("permission '{}' is not a repository permission",
permissionString);
}
}
return permission;
}
/**
* Method description
*
*
* @param permissionIt
*
* @return
*/
private RepositoryPermission createRepositoryPermission(
Iterator<String> permissionIt)
{
RepositoryPermission permission = null;
if (permissionIt.hasNext())
{
String repositoryId = permissionIt.next();
if (permissionIt.hasNext())
{
try
{
String typeString = permissionIt.next();
typeString = typeString.trim().toUpperCase(Locale.ENGLISH);
PermissionType type = PermissionType.valueOf(typeString);
permission = new RepositoryPermission(repositoryId, type);
}
catch (IllegalArgumentException ex)
{
logger.warn("type is not a valid permission type", ex);
}
}
else if (logger.isWarnEnabled())
{
logger.warn("permission type is missing");
}
}
else if (logger.isWarnEnabled())
{
logger.warn("repository id and permission type is missing");
}
return permission;
}
}

View File

@@ -0,0 +1,777 @@
/**
* Copyright (c) 2010, Sebastian Sdorra
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of SCM-Manager; nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.security;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.apache.shiro.authc.AccountException;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.DisabledAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
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.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.HandlerEvent;
import sonia.scm.cache.Cache;
import sonia.scm.cache.CacheManager;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.group.Group;
import sonia.scm.group.GroupManager;
import sonia.scm.group.GroupNames;
import sonia.scm.repository.Permission;
import sonia.scm.repository.PermissionType;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryDAO;
import sonia.scm.repository.RepositoryListener;
import sonia.scm.repository.RepositoryManager;
import sonia.scm.user.User;
import sonia.scm.user.UserDAO;
import sonia.scm.user.UserEventHack;
import sonia.scm.user.UserException;
import sonia.scm.user.UserListener;
import sonia.scm.user.UserManager;
import sonia.scm.util.Util;
import sonia.scm.web.security.AuthenticationManager;
import sonia.scm.web.security.AuthenticationResult;
import sonia.scm.web.security.AuthenticationState;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author Sebastian Sdorra
*/
@Singleton
public class ScmRealm extends AuthorizingRealm
implements RepositoryListener, UserListener
{
/** Field description */
public static final String NAME = "scm";
/** Field description */
private static final String CACHE_NAME = "sonia.cache.authorizing";
/** Field description */
private static final String SCM_CREDENTIALS = "SCM_CREDENTIALS";
/**
* the logger for ScmRealm
*/
private static final Logger logger = LoggerFactory.getLogger(ScmRealm.class);
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
*
*
* @param configuration
* @param cacheManager
* @param userManager
* @param groupManager
* @param repositoryManager
* @param repositoryDAO
* @param userDAO
* @param authenticator
* @param requestProvider
* @param responseProvider
*/
@Inject
public ScmRealm(ScmConfiguration configuration, CacheManager cacheManager,
UserManager userManager, GroupManager groupManager,
RepositoryManager repositoryManager, RepositoryDAO repositoryDAO,
UserDAO userDAO, AuthenticationManager authenticator,
Provider<HttpServletRequest> requestProvider,
Provider<HttpServletResponse> responseProvider)
{
this.configuration = configuration;
this.userManager = userManager;
this.groupManager = groupManager;
this.repositoryDAO = repositoryDAO;
this.userDAO = userDAO;
this.authenticator = authenticator;
this.requestProvider = requestProvider;
this.responseProvider = responseProvider;
// init cache
this.cache = cacheManager.getCache(String.class, AuthorizationInfo.class,
CACHE_NAME);
// set token class
setAuthenticationTokenClass(UsernamePasswordToken.class);
// use own custom caching
setCachingEnabled(false);
setAuthenticationCachingEnabled(false);
setAuthorizationCachingEnabled(false);
// set components
setPermissionResolver(new RepositoryPermissionResolver());
// add listeners for caching
userManager.addListener(this);
repositoryManager.addListener(this);
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param repository
* @param event
*/
@Override
public void onEvent(Repository repository, HandlerEvent event)
{
if (event.isPost())
{
if (logger.isDebugEnabled())
{
logger.debug("clear cache, because repository {} has changed",
repository.getName());
}
cache.clear();
}
}
/**
* Method description
*
*
* @param user
* @param event
*/
@Override
public void onEvent(User user, HandlerEvent event)
{
if (event.isPost())
{
if (logger.isDebugEnabled())
{
logger.debug(
"clear cache of user {}, because user properties have changed",
user.getName());
}
cache.remove(user.getId());
}
}
/**
* Method description
*
*
* @param token
*
* @param authToken
*
* @return
*
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken authToken)
throws AuthenticationException
{
if (!(authToken instanceof UsernamePasswordToken))
{
throw new UnsupportedTokenException("ScmAuthenticationToken is required");
}
UsernamePasswordToken token = (UsernamePasswordToken) authToken;
AuthenticationInfo info = null;
AuthenticationResult result =
authenticator.authenticate(requestProvider.get(), responseProvider.get(),
token.getUsername(), new String(token.getPassword()));
if ((result != null) && (AuthenticationState.SUCCESS == result.getState()))
{
info = createAuthenticationInfo(token, result);
}
else if ((result != null)
&& (AuthenticationState.NOT_FOUND == result.getState()))
{
throw new UnknownAccountException(
"unknown account ".concat(token.getUsername()));
}
else
{
throw new AccountException("authentication failed");
}
return info;
}
/**
* Method description
*
*
* @param principals
*
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(
PrincipalCollection principals)
{
User user = principals.oneByType(User.class);
AuthorizationInfo info = cache.get(user.getId());
if (info == null)
{
if (logger.isTraceEnabled())
{
logger.trace("collect AuthorizationInfo for user {}", user.getName());
}
GroupNames groups = principals.oneByType(GroupNames.class);
info = createAuthorizationInfo(user, groups);
cache.put(user.getId(), info);
}
else if (logger.isTraceEnabled())
{
logger.trace("retrieve AuthorizationInfo for user {} from cache",
user.getName());
}
return info;
}
/**
* Method description
*
*
* @param request
* @param password
* @param ar
*
* @return
*/
private Set<String> authenticate(HttpServletRequest request, String password,
AuthenticationResult ar)
{
Set<String> groupSet = null;
User user = ar.getUser();
try
{
groupSet = createGroupSet(ar);
// check for admin user
checkForAuthenticatedAdmin(user, groupSet);
// store user
User dbUser = userDAO.get(user.getName());
if (dbUser != null)
{
checkDBForAdmin(user, dbUser);
checkDBForActive(user, dbUser);
}
// create new user
else if (user.isValid())
{
user.setCreationDate(System.currentTimeMillis());
// TODO find a better way
UserEventHack.fireEvent(userManager, user, HandlerEvent.BEFORE_CREATE);
userDAO.add(user);
UserEventHack.fireEvent(userManager, user, HandlerEvent.CREATE);
}
else if (logger.isErrorEnabled())
{
logger.error("could not create user {}, beacause it is not valid",
user.getName());
}
if (user.isActive())
{
if (logger.isDebugEnabled())
{
logGroups(user, groupSet);
}
// store encrypted credentials in session
String credentials = user.getName();
if (Util.isNotEmpty(password))
{
credentials = credentials.concat(":").concat(password);
}
credentials = CipherUtil.getInstance().encode(credentials);
request.getSession(true).setAttribute(SCM_CREDENTIALS, credentials);
}
else
{
String msg = "user ".concat(user.getName()).concat(" is deactivated");
if (logger.isWarnEnabled())
{
logger.warn(msg);
}
throw new DisabledAccountException(msg);
}
}
catch (Exception ex)
{
logger.error("authentication failed", ex);
throw new AuthenticationException("authentication failed", ex);
}
return groupSet;
}
/**
* Method description
*
*
* @param user
* @param dbUser
*/
private void checkDBForActive(User user, User dbUser)
{
// user is deactivated by database
if (!dbUser.isActive())
{
if (logger.isDebugEnabled())
{
logger.debug("user {} is marked as deactivated by local database",
user.getName());
}
user.setActive(false);
}
}
/**
* Method description
*
*
* @param user
* @param dbUser
*
* @throws IOException
* @throws UserException
*/
private void checkDBForAdmin(User user, User dbUser)
throws UserException, IOException
{
// if database user is an admin, set admin for the current user
if (dbUser.isAdmin())
{
if (logger.isDebugEnabled())
{
logger.debug("user {} of type {} is marked as admin by local database",
user.getName(), user.getType());
}
user.setAdmin(true);
}
// modify existing user, copy properties except password and admin
if (user.copyProperties(dbUser, false))
{
userManager.modify(dbUser);
}
}
/**
* Method description
*
*
* @param user
* @param groupSet
*/
private void checkForAuthenticatedAdmin(User user, Set<String> groupSet)
{
if (!user.isAdmin())
{
user.setAdmin(isAdmin(user, groupSet));
if (logger.isDebugEnabled() && user.isAdmin())
{
logger.debug("user {} is marked as admin by configuration",
user.getName());
}
}
else if (logger.isDebugEnabled())
{
logger.debug("authenticator {} marked user {} as admin", user.getType(),
user.getName());
}
}
/**
* Method description
*
*
* @param user
* @param groups
*
* @return
*/
private List<org.apache.shiro.authz.Permission> collectRepositoryPermissions(
User user, GroupNames groups)
{
List<org.apache.shiro.authz.Permission> permissions = Lists.newArrayList();
for (Repository repository : repositoryDAO.getAll())
{
if (logger.isTraceEnabled())
{
logger.trace("collect permissions for repository {} and user {}",
repository.getName(), user.getName());
}
collectRepositoryPermissions(permissions, repository, user, groups);
}
return permissions;
}
/**
* Method description
*
*
* @param permissions
* @param repository
* @param user
* @param groups
*/
private void collectRepositoryPermissions(
List<org.apache.shiro.authz.Permission> permissions, Repository repository,
User user, GroupNames groups)
{
List<Permission> repositoryPermissions = repository.getPermissions();
if (Util.isNotEmpty(repositoryPermissions))
{
for (Permission permission : repositoryPermissions)
{
if (isUserPermission(user, groups, permission))
{
RepositoryPermission rp = new RepositoryPermission(repository,
permission.getType());
if (logger.isTraceEnabled())
{
logger.trace("add repository permission {} for user {}", rp,
user.getName());
}
permissions.add(rp);
}
}
}
else if (logger.isTraceEnabled())
{
logger.trace("repository {} has not permission entries",
repository.getName());
}
}
/**
* Method description
*
*
* @param token
* @param result
*
* @return
*/
private AuthenticationInfo createAuthenticationInfo(
UsernamePasswordToken token, AuthenticationResult result)
{
User user = result.getUser();
Collection<String> groups = authenticate(requestProvider.get(),
new String(token.getPassword()), result);
SimplePrincipalCollection collection = new SimplePrincipalCollection();
/*
* the first (primary) principal should be a unique identifier
*/
collection.add(user.getId(), NAME);
collection.add(user, NAME);
collection.add(new GroupNames(groups), NAME);
return new SimpleAuthenticationInfo(collection, token.getPassword());
}
/**
* Method description
*
*
* @param user
* @param groups
*
* @return
*/
private AuthorizationInfo createAuthorizationInfo(User user,
GroupNames groups)
{
Set<String> roles = Sets.newHashSet();
List<org.apache.shiro.authz.Permission> permissions = null;
roles.add(Role.USER);
if (user.isAdmin())
{
if (logger.isDebugEnabled())
{
logger.debug("grant admin role for user {}", user.getName());
}
roles.add(Role.ADMIN);
permissions = Lists.newArrayList();
permissions.add(new RepositoryPermission(RepositoryPermission.WILDCARD,
PermissionType.OWNER));
}
else
{
permissions = collectRepositoryPermissions(user, groups);
}
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
info.addObjectPermissions(permissions);
return info;
}
/**
* Method description
*
*
* @param ar
*
* @return
*/
private Set<String> createGroupSet(AuthenticationResult ar)
{
Set<String> groupSet = Sets.newHashSet();
// load external groups
Collection<String> extGroups = ar.getGroups();
if (extGroups != null)
{
groupSet.addAll(extGroups);
}
// load internal groups
loadGroups(ar.getUser(), groupSet);
return groupSet;
}
/**
* Method description
*
*
*
* @param user
* @param groupSet
*/
private void loadGroups(User user, Set<String> groupSet)
{
Collection<Group> groupCollection =
groupManager.getGroupsForMember(user.getName());
if (groupCollection != null)
{
for (Group group : groupCollection)
{
groupSet.add(group.getName());
}
}
}
/**
* Method description
*
*
* @param user
* @param groups
*/
private void logGroups(User user, Set<String> groups)
{
StringBuilder msg = new StringBuilder("user ");
msg.append(user.getName());
if (Util.isNotEmpty(groups))
{
msg.append(" is member of ");
Joiner.on(", ").appendTo(msg, groups);
}
else
{
msg.append(" is not a member of a group");
}
logger.debug(msg.toString());
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
*
*
* @param user
* @param groups
* @return
*/
private boolean isAdmin(User user, Collection<String> groups)
{
boolean result = false;
Set<String> adminUsers = configuration.getAdminUsers();
if (adminUsers != null)
{
result = adminUsers.contains(user.getName());
}
if (!result)
{
Set<String> adminGroups = configuration.getAdminGroups();
if (adminGroups != null)
{
result = Util.containsOne(adminGroups, groups);
}
}
return result;
}
/**
* Method description
*
*
* @param user
* @param groups
* @param perm
*
* @return
*/
private boolean isUserPermission(User user, GroupNames groups,
Permission perm)
{
//J-
return (perm.isGroupPermission() && groups.contains(perm.getName()))
|| ((!perm.isGroupPermission()) && user.getName().equals(perm.getName()));
//J+
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private AuthenticationManager authenticator;
/** Field description */
private Cache<String, AuthorizationInfo> cache;
/** Field description */
private ScmConfiguration configuration;
/** Field description */
private GroupManager groupManager;
/** Field description */
private RepositoryDAO repositoryDAO;
/** Field description */
private Provider<HttpServletRequest> requestProvider;
/** Field description */
private Provider<HttpServletResponse> responseProvider;
/** Field description */
private UserDAO userDAO;
/** Field description */
private UserManager userManager;
}

View File

@@ -1,34 +1,35 @@
/**
* Copyright (c) 2010, Sebastian Sdorra All rights reserved.
* Copyright (c) 2010, 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 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.
* 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.template;
/**

View File

@@ -39,6 +39,9 @@ import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -47,13 +50,13 @@ import sonia.scm.SCMContextProvider;
import sonia.scm.TransformFilter;
import sonia.scm.search.SearchRequest;
import sonia.scm.search.SearchUtil;
import sonia.scm.security.Role;
import sonia.scm.security.ScmSecurityException;
import sonia.scm.util.AssertUtil;
import sonia.scm.util.CollectionAppender;
import sonia.scm.util.IOUtil;
import sonia.scm.util.SecurityUtil;
import sonia.scm.util.Util;
import sonia.scm.web.security.WebSecurityContext;
//~--- JDK imports ------------------------------------------------------------
@@ -104,11 +107,9 @@ public class DefaultUserManager extends AbstractUserManager
* @param userListenerProvider
*/
@Inject
public DefaultUserManager(
Provider<WebSecurityContext> scurityContextProvider, UserDAO userDAO,
Provider<Set<UserListener>> userListenerProvider)
public DefaultUserManager(UserDAO userDAO,
Provider<Set<UserListener>> userListenerProvider)
{
this.scurityContextProvider = scurityContextProvider;
this.userDAO = userDAO;
this.userListenerProvider = userListenerProvider;
}
@@ -166,9 +167,14 @@ public class DefaultUserManager extends AbstractUserManager
logger.info("create user {} of type {}", user.getName(), user.getType());
}
User currentUser = SecurityUtil.getCurrentUser(scurityContextProvider);
Subject subject = SecurityUtils.getSubject();
if (!user.equals(currentUser) &&!currentUser.isAdmin())
if (!subject.isAuthenticated())
{
throw new ScmSecurityException("user is not authenticated");
}
if (!subject.hasRole(Role.ADMIN))
{
throw new ScmSecurityException("admin account is required");
}
@@ -202,7 +208,7 @@ public class DefaultUserManager extends AbstractUserManager
logger.info("delete user {} of type {}", user.getName(), user.getType());
}
SecurityUtil.assertIsAdmin(scurityContextProvider);
SecurityUtil.assertIsAdmin();
String name = user.getName();
@@ -259,9 +265,17 @@ public class DefaultUserManager extends AbstractUserManager
logger.info("modify user {} of type {}", user.getName(), user.getType());
}
User currentUser = SecurityUtil.getCurrentUser(scurityContextProvider);
Subject subject = SecurityUtils.getSubject();
if (!user.getName().equals(currentUser.getName()) &&!currentUser.isAdmin())
if (!subject.isAuthenticated())
{
throw new ScmSecurityException("user is not authenticated");
}
User currentUser = subject.getPrincipals().oneByType(User.class);
if (!user.getName().equals(currentUser.getName())
&&!subject.hasRole(Role.ADMIN))
{
throw new ScmSecurityException("admin account is required");
}
@@ -299,7 +313,7 @@ public class DefaultUserManager extends AbstractUserManager
logger.info("refresh user {} of type {}", user.getName(), user.getType());
}
SecurityUtil.assertIsAdmin(scurityContextProvider);
SecurityUtil.assertIsAdmin();
User fresh = userDAO.get(user.getName());
@@ -328,7 +342,7 @@ public class DefaultUserManager extends AbstractUserManager
}
return SearchUtil.search(searchRequest, userDAO.getAll(),
new TransformFilter<User>()
new TransformFilter<User>()
{
@Override
public User accept(User user)
@@ -336,7 +350,7 @@ public class DefaultUserManager extends AbstractUserManager
User result = null;
if (SearchUtil.matchesOne(searchRequest, user.getName(),
user.getDisplayName(), user.getMail()))
user.getDisplayName(), user.getMail()))
{
result = user.clone();
}
@@ -394,7 +408,7 @@ public class DefaultUserManager extends AbstractUserManager
@Override
public Collection<User> getAll(Comparator<User> comparator)
{
SecurityUtil.assertIsAdmin(scurityContextProvider);
SecurityUtil.assertIsAdmin();
List<User> users = new ArrayList<User>();
@@ -424,12 +438,12 @@ public class DefaultUserManager extends AbstractUserManager
*/
@Override
public Collection<User> getAll(Comparator<User> comaparator, int start,
int limit)
int limit)
{
SecurityUtil.assertIsAdmin(scurityContextProvider);
SecurityUtil.assertIsAdmin();
return Util.createSubCollection(userDAO.getAll(), comaparator,
new CollectionAppender<User>()
new CollectionAppender<User>()
{
@Override
public void append(Collection<User> collection, User item)
@@ -525,15 +539,12 @@ public class DefaultUserManager extends AbstractUserManager
}
catch (JAXBException ex)
{
logger.error(ex.getMessage(), ex);
logger.error("could not create default accounts", ex);
}
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private Provider<WebSecurityContext> scurityContextProvider;
/** Field description */
private UserDAO userDAO;

View File

@@ -0,0 +1,76 @@
/**
* Copyright (c) 2010, 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.user;
//~--- non-JDK imports --------------------------------------------------------
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.HandlerEvent;
/**
*
* @author Sebastian Sdorra
*/
public class UserEventHack
{
/**
* the logger for UserEventHack
*/
private static final Logger logger =
LoggerFactory.getLogger(UserEventHack.class);
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param userManager
* @param user
* @param event
*/
public static void fireEvent(UserManager userManager, User user,
HandlerEvent event)
{
if (userManager instanceof AbstractUserManager)
{
((AbstractUserManager) userManager).fireEvent(user, event);
}
else if (logger.isWarnEnabled())
{
logger.warn("user manager is not an instance of AbstractUserManager");
}
}
}

View File

@@ -36,9 +36,9 @@ package sonia.scm.web.security;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.web.filter.BasicAuthenticationFilter;
//~--- JDK imports ------------------------------------------------------------
@@ -73,13 +73,12 @@ public class ApiBasicAuthenticationFilter extends BasicAuthenticationFilter
* Constructs ...
*
*
* @param securityContextProvider
* @param configuration
*/
@Inject
public ApiBasicAuthenticationFilter(
Provider<WebSecurityContext> securityContextProvider)
public ApiBasicAuthenticationFilter(ScmConfiguration configuration)
{
super(securityContextProvider);
super(configuration);
}
//~--- methods --------------------------------------------------------------
@@ -97,14 +96,14 @@ public class ApiBasicAuthenticationFilter extends BasicAuthenticationFilter
*/
@Override
protected void doFilter(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)
throws IOException, ServletException
HttpServletResponse response, FilterChain chain)
throws IOException, ServletException
{
// skip filter on authentication resource
if (request.getRequestURI().contains(URI_LOGIN)
|| request.getRequestURI().contains(URI_STATE)
|| request.getRequestURI().contains(URI_LOGOUT))
|| request.getRequestURI().contains(URI_STATE)
|| request.getRequestURI().contains(URI_LOGOUT))
{
chain.doFilter(request, response);
}
@@ -127,9 +126,8 @@ public class ApiBasicAuthenticationFilter extends BasicAuthenticationFilter
*/
@Override
protected void handleUnauthorized(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain)
throws IOException, ServletException
HttpServletResponse response, FilterChain chain)
throws IOException, ServletException
{
chain.doFilter(request, response);
}

View File

@@ -35,30 +35,26 @@ package sonia.scm.web.security;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.collect.Sets;
import com.google.inject.Inject;
import com.google.inject.servlet.SessionScoped;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.config.ScmConfiguration;
import sonia.scm.group.Group;
import sonia.scm.group.GroupManager;
import sonia.scm.security.CipherUtil;
import sonia.scm.group.GroupNames;
import sonia.scm.security.Tokens;
import sonia.scm.user.User;
import sonia.scm.user.UserException;
import sonia.scm.user.UserManager;
import sonia.scm.util.Util;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.Collections;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -68,7 +64,6 @@ import javax.servlet.http.HttpSession;
*
* @author Sebastian Sdorra
*/
@SessionScoped
public class BasicSecurityContext implements WebSecurityContext
{
@@ -88,21 +83,14 @@ public class BasicSecurityContext implements WebSecurityContext
* Constructs ...
*
*
*
* @param configuration
* @param authenticator
* @param groupManager
* @param userManager
*/
@Inject
public BasicSecurityContext(ScmConfiguration configuration,
AuthenticationManager authenticator,
GroupManager groupManager,
UserManager userManager)
UserManager userManager)
{
this.configuration = configuration;
this.authenticator = authenticator;
this.groupManager = groupManager;
this.userManager = userManager;
}
@@ -121,22 +109,27 @@ public class BasicSecurityContext implements WebSecurityContext
*/
@Override
public User authenticate(HttpServletRequest request,
HttpServletResponse response, String username,
String password)
HttpServletResponse response, String username, String password)
{
if ( logger.isTraceEnabled() ){
logger.trace("start authentication for user {}", username);
}
AuthenticationResult ar = authenticator.authenticate(request, response,
username, password);
User user = null;
if ( logger.isTraceEnabled() ){
logger.trace("authentication ends with {}", ar);
}
if ((ar != null) && (ar.getState() == AuthenticationState.SUCCESS))
try
{
authenticate(request, password, ar);
Subject subject = SecurityUtils.getSubject();
subject.login(Tokens.createAuthenticationToken(request, username,
password));
user = subject.getPrincipals().oneByType(User.class);
}
catch (AuthenticationException ex)
{
if (logger.isWarnEnabled())
{
logger.warn("authentication failed", ex);
}
}
return user;
@@ -152,8 +145,7 @@ public class BasicSecurityContext implements WebSecurityContext
@Override
public void logout(HttpServletRequest request, HttpServletResponse response)
{
user = null;
groups = new HashSet<String>();
SecurityUtils.getSubject().logout();
HttpSession session = request.getSession(false);
@@ -174,12 +166,20 @@ public class BasicSecurityContext implements WebSecurityContext
@Override
public Collection<String> getGroups()
{
if (groups == null)
GroupNames groups = getPrincipal(GroupNames.class);
Collection<String> groupCollection = null;
if (groups != null)
{
groups = new HashSet<String>();
groupCollection = groups.getCollection();
}
else
{
groupCollection = Collections.EMPTY_LIST;
}
return groups;
return groupCollection;
}
/**
@@ -191,6 +191,8 @@ public class BasicSecurityContext implements WebSecurityContext
@Override
public User getUser()
{
User user = getPrincipal(User.class);
if ((user == null) && configuration.isAnonymousAccessEnabled())
{
user = userManager.get(USER_ANONYMOUS);
@@ -211,276 +213,27 @@ public class BasicSecurityContext implements WebSecurityContext
return getUser() != null;
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param request
* @param password
* @param ar
*/
private void authenticate(HttpServletRequest request, String password,
AuthenticationResult ar)
{
user = ar.getUser();
try
{
Set<String> groupSet = createGroupSet(ar);
// check for admin user
checkForAuthenticatedAdmin(user, groupSet);
// store user
User dbUser = userManager.get(user.getName());
if (dbUser != null)
{
checkDBForAdmin(user, dbUser);
checkDBForActive(user, dbUser);
}
// create new user
else
{
userManager.create(user);
}
if (user.isActive())
{
groups = groupSet;
if (logger.isDebugEnabled())
{
logGroups();
}
// store encrypted credentials in session
String credentials = user.getName();
if (Util.isNotEmpty(password))
{
credentials = credentials.concat(":").concat(password);
}
credentials = CipherUtil.getInstance().encode(credentials);
request.getSession(true).setAttribute(SCM_CREDENTIALS, credentials);
}
else
{
if (logger.isWarnEnabled())
{
logger.warn("user {} is deactivated", user.getName());
}
user = null;
groups = null;
}
}
catch (Exception ex)
{
user = null;
if (groups != null)
{
groups.clear();
}
logger.error("authentication failed", ex);
}
}
/**
* Method description
*
*
* @param user
* @param dbUser
*/
private void checkDBForActive(User user, User dbUser)
{
// user is deactivated by database
if (!dbUser.isActive())
{
if (logger.isDebugEnabled())
{
logger.debug("user {} is marked as deactivated by local database",
user.getName());
}
user.setActive(false);
}
}
/**
* Method description
*
*
* @param user
* @param dbUser
*
* @throws IOException
* @throws UserException
*/
private void checkDBForAdmin(User user, User dbUser)
throws UserException, IOException
{
// if database user is an admin, set admin for the current user
if (dbUser.isAdmin())
{
if (logger.isDebugEnabled())
{
logger.debug("user {} of type {} is marked as admin by local database",
user.getName(), user.getType());
}
user.setAdmin(true);
}
// modify existing user, copy properties except password and admin
if (user.copyProperties(dbUser, false))
{
userManager.modify(dbUser);
}
}
/**
* Method description
*
*
* @param user
* @param groupSet
*/
private void checkForAuthenticatedAdmin(User user, Set<String> groupSet)
{
if (!user.isAdmin())
{
user.setAdmin(isAdmin(groupSet));
if (logger.isDebugEnabled() && user.isAdmin())
{
logger.debug("user {} is marked as admin by configuration",
user.getName());
}
}
else if (logger.isDebugEnabled())
{
logger.debug("authenticator {} marked user {} as admin", user.getType(),
user.getName());
}
}
/**
* Method description
*
*
* @param ar
* @param clazz
* @param <T>
*
* @return
*/
private Set<String> createGroupSet(AuthenticationResult ar)
private <T> T getPrincipal(Class<T> clazz)
{
Set<String> groupSet = Sets.newHashSet();
T result = null;
Subject subject = SecurityUtils.getSubject();
// load external groups
Collection<String> extGroups = ar.getGroups();
if (extGroups != null)
if (subject.isAuthenticated())
{
groupSet.addAll(extGroups);
}
PrincipalCollection pc = subject.getPrincipals();
// load internal groups
loadGroups(groupSet);
return groupSet;
}
/**
* Method description
*
*
* @param groupSet
*/
private void loadGroups(Set<String> groupSet)
{
Collection<Group> groupCollection =
groupManager.getGroupsForMember(user.getName());
if (groupCollection != null)
{
for (Group group : groupCollection)
if (pc != null)
{
groupSet.add(group.getName());
}
}
}
/**
* Method description
*
*/
private void logGroups()
{
StringBuilder msg = new StringBuilder("user ");
msg.append(user.getName());
if (Util.isNotEmpty(groups))
{
msg.append(" is member of ");
Iterator<String> groupIt = groups.iterator();
while (groupIt.hasNext())
{
msg.append(groupIt.next());
if (groupIt.hasNext())
{
msg.append(", ");
}
}
}
else
{
msg.append(" is not a member of a group");
}
logger.debug(msg.toString());
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
*
* @param groups
* @return
*/
private boolean isAdmin(Collection<String> groups)
{
boolean result = false;
Set<String> adminUsers = configuration.getAdminUsers();
if (adminUsers != null)
{
result = adminUsers.contains(user.getName());
}
if (!result)
{
Set<String> adminGroups = configuration.getAdminGroups();
if (adminGroups != null)
{
result = Util.containsOne(adminGroups, groups);
result = pc.oneByType(clazz);
}
}
@@ -489,21 +242,9 @@ public class BasicSecurityContext implements WebSecurityContext
//~--- fields ---------------------------------------------------------------
/** Field description */
private AuthenticationManager authenticator;
/** Field description */
private ScmConfiguration configuration;
/** Field description */
private GroupManager groupManager;
/** Field description */
private Set<String> groups = new HashSet<String>();
/** Field description */
private User user;
/** Field description */
private UserManager userManager;
}

View File

@@ -89,9 +89,9 @@ public class ChainAuthenticatonManager extends AbstractAuthenticationManager
*/
@Inject
public ChainAuthenticatonManager(
Set<AuthenticationHandler> authenticationHandlerSet,
EncryptionHandler encryptionHandler, CacheManager cacheManager,
Provider<Set<AuthenticationListener>> authenticationListenerProvider)
Set<AuthenticationHandler> authenticationHandlerSet,
EncryptionHandler encryptionHandler, CacheManager cacheManager,
Provider<Set<AuthenticationListener>> authenticationListenerProvider)
{
AssertUtil.assertIsNotEmpty(authenticationHandlerSet);
AssertUtil.assertIsNotNull(cacheManager);
@@ -99,8 +99,7 @@ public class ChainAuthenticatonManager extends AbstractAuthenticationManager
this.encryptionHandler = encryptionHandler;
this.authenticationListenerProvider = authenticationListenerProvider;
this.cache = cacheManager.getCache(String.class,
AuthenticationCacheValue.class,
CACHE_NAME);
AuthenticationCacheValue.class, CACHE_NAME);
// addListeners(authenticationListeners);
}
@@ -120,7 +119,7 @@ public class ChainAuthenticatonManager extends AbstractAuthenticationManager
*/
@Override
public AuthenticationResult authenticate(HttpServletRequest request,
HttpServletResponse response, String username, String password)
HttpServletResponse response, String username, String password)
{
AssertUtil.assertIsNotEmpty(username);
AssertUtil.assertIsNotEmpty(password);
@@ -133,7 +132,7 @@ public class ChainAuthenticatonManager extends AbstractAuthenticationManager
if (logger.isTraceEnabled())
{
logger.trace("no authentication result for user {} found in cache",
username);
username);
}
ar = doAuthentication(request, response, username, password);
@@ -141,7 +140,7 @@ public class ChainAuthenticatonManager extends AbstractAuthenticationManager
if ((ar != null) && ar.isCacheable())
{
cache.put(username,
new AuthenticationCacheValue(ar, encryptedPassword));
new AuthenticationCacheValue(ar, encryptedPassword));
}
}
else if (logger.isDebugEnabled())
@@ -212,7 +211,7 @@ public class ChainAuthenticatonManager extends AbstractAuthenticationManager
* @return
*/
private AuthenticationResult doAuthentication(HttpServletRequest request,
HttpServletResponse response, String username, String password)
HttpServletResponse response, String username, String password)
{
AuthenticationResult ar = null;
@@ -226,7 +225,7 @@ public class ChainAuthenticatonManager extends AbstractAuthenticationManager
if (logger.isTraceEnabled())
{
logger.trace("check authenticator {} for user {}",
authenticator.getClass(), username);
authenticator.getClass(), username);
}
try
@@ -237,12 +236,12 @@ public class ChainAuthenticatonManager extends AbstractAuthenticationManager
if (logger.isDebugEnabled())
{
logger.debug("authenticator {} ends with result, {}",
authenticator.getClass().getName(), result);
authenticator.getClass().getName(), result);
}
if ((result != null) && (result.getState() != null)
&& (result.getState().isSuccessfully()
|| (result.getState() == AuthenticationState.FAILED)))
&& (result.getState().isSuccessfully()
|| (result.getState() == AuthenticationState.FAILED)))
{
if (result.getState().isSuccessfully() && (result.getUser() != null))
{
@@ -260,7 +259,9 @@ public class ChainAuthenticatonManager extends AbstractAuthenticationManager
}
catch (Exception ex)
{
logger.error(ex.getMessage(), ex);
logger.error(
"error durring authentication process of ".concat(
authenticator.getClass().getName()), ex);
}
}
@@ -279,7 +280,7 @@ public class ChainAuthenticatonManager extends AbstractAuthenticationManager
* @return
*/
private AuthenticationResult getCached(String username,
String encryptedPassword)
String encryptedPassword)
{
AuthenticationResult result = null;
AuthenticationCacheValue value = cache.get(username);
@@ -326,7 +327,7 @@ public class ChainAuthenticatonManager extends AbstractAuthenticationManager
{
this.authenticationResult =
new AuthenticationResult(ar.getUser().clone(), ar.getGroups(),
ar.getState());
ar.getState());
this.password = password;
}

View File

@@ -30,19 +30,29 @@
*/
package sonia.scm.web.security;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.support.SubjectThreadState;
import org.apache.shiro.util.ThreadContext;
import org.apache.shiro.util.ThreadState;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.SCMContext;
import sonia.scm.group.GroupNames;
import sonia.scm.security.ScmRealm;
import sonia.scm.user.User;
import sonia.scm.util.AssertUtil;
@@ -77,15 +87,14 @@ public class DefaultAdministrationContext implements AdministrationContext
* @param injector
* @param userSessionProvider
* @param contextHolder
* @param securityManager
*/
@Inject
public DefaultAdministrationContext(Injector injector,
@Named("userSession") Provider<WebSecurityContext> userSessionProvider,
LocalSecurityContextHolder contextHolder)
org.apache.shiro.mgt.SecurityManager securityManager)
{
this.injector = injector;
this.userSessionProvider = userSessionProvider;
this.contextHolder = contextHolder;
this.securityManager = securityManager;
URL url = DefaultAdministrationContext.class.getResource(SYSTEM_ACCOUNT);
@@ -94,9 +103,9 @@ public class DefaultAdministrationContext implements AdministrationContext
throw new RuntimeException("could not find resource for system account");
}
User user = JAXB.unmarshal(url, User.class);
User adminUser = JAXB.unmarshal(url, User.class);
adminContext = new AdministrationSecurityContext(user);
principalCollection = createAdminCollection(adminUser);
}
//~--- methods --------------------------------------------------------------
@@ -112,24 +121,15 @@ public class DefaultAdministrationContext implements AdministrationContext
{
AssertUtil.assertIsNotNull(action);
if (logger.isWarnEnabled())
if (ThreadContext.getSecurityManager() != null)
{
String user = SecurityUtil.getUsername(userSessionProvider);
logger.warn("user {} executes {} as admin", user,
action.getClass().getName());
doRunAsInWebSessionContext(action);
}
else
{
doRunAsInNonWebSessionContext(action);
}
contextHolder.set(adminContext);
try
{
action.run();
}
finally
{
contextHolder.remove();
}
}
/**
@@ -146,17 +146,136 @@ public class DefaultAdministrationContext implements AdministrationContext
runAsAdmin(action);
}
/**
* Method description
*
*
* @param adminUser
*
* @return
*/
private PrincipalCollection createAdminCollection(User adminUser)
{
SimplePrincipalCollection collection = new SimplePrincipalCollection();
collection.add(adminUser.getId(), ScmRealm.NAME);
collection.add(adminUser, ScmRealm.NAME);
collection.add(new GroupNames(), ScmRealm.NAME);
return collection;
}
/**
* Method description
*
*
* @param action
*/
private void doRunAsInNonWebSessionContext(PrivilegedAction action)
{
if (logger.isTraceEnabled())
{
logger.trace("bind shiro security manager to current thread");
}
try
{
SecurityUtils.setSecurityManager(securityManager);
//J-
Subject subject = new Subject.Builder(securityManager)
.authenticated(true)
.principals(principalCollection)
.buildSubject();
//J+
ThreadState state = new SubjectThreadState(subject);
state.bind();
try
{
if (logger.isInfoEnabled())
{
logger.info("execute action {} in administration context",
action.getClass().getName());
}
action.run();
}
finally
{
state.clear();
}
}
finally
{
SecurityUtils.setSecurityManager(null);
}
}
/**
* Method description
*
*
* @param action
*/
private void doRunAsInWebSessionContext(PrivilegedAction action)
{
Subject subject = SecurityUtils.getSubject();
String principal = (String) subject.getPrincipal();
if (logger.isInfoEnabled())
{
String username = null;
if (subject.isAuthenticated())
{
username = principal;
}
else
{
username = SCMContext.USER_ANONYMOUS;
}
logger.info("user {} executes {} as admin", username,
action.getClass().getName());
}
subject.runAs(principalCollection);
try
{
action.run();
}
finally
{
PrincipalCollection collection = subject.releaseRunAs();
if (logger.isDebugEnabled())
{
logger.debug("release runas for user {}",
collection.getPrimaryPrincipal());
}
if (!subject.getPrincipal().equals(principal))
{
logger.error("release runas failed, {} is not equal with {}, logout.",
subject.getPrincipal(), principal);
subject.logout();
}
}
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private AdministrationSecurityContext adminContext;
/** Field description */
private LocalSecurityContextHolder contextHolder;
/** Field description */
private Injector injector;
/** Field description */
private Provider<WebSecurityContext> userSessionProvider;
private PrincipalCollection principalCollection;
/** Field description */
private org.apache.shiro.mgt.SecurityManager securityManager;
}

View File

@@ -1,108 +0,0 @@
/**
* Copyright (c) 2010, 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.web.security;
//~--- non-JDK imports --------------------------------------------------------
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.name.Named;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author Sebastian Sdorra
*/
public class SecurityContextProvider implements Provider<WebSecurityContext>
{
/** the logger for SecurityContextProvider */
private static final Logger logger =
LoggerFactory.getLogger(SecurityContextProvider.class);
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
*
* @param sessionContext
* @param localContext
*/
@Inject
public SecurityContextProvider(
@Named("userSession") Provider<WebSecurityContext> sessionContext,
LocalSecurityContextHolder localContext)
{
this.sessionContext = sessionContext;
this.localContext = localContext;
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
@Override
public WebSecurityContext get()
{
WebSecurityContext context = localContext.get();
if (context == null)
{
context = sessionContext.get();
}
else if (logger.isDebugEnabled())
{
String user = SecurityUtil.getUsername(sessionContext);
logger.debug("return system session for user {}", user);
}
return context;
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private LocalSecurityContextHolder localContext;
/** Field description */
private Provider<WebSecurityContext> sessionContext;
}

View File

@@ -42,7 +42,9 @@ import sonia.scm.SCMContext;
/**
*
* @author Sebastian Sdorra
* @deprecated
*/
@Deprecated
public class SecurityUtil
{