mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-11 16:05:44 +01:00
Replace method interceptor with request filter
This commit is contained in:
@@ -45,7 +45,6 @@ import sonia.scm.api.rest.ObjectMapperProvider;
|
|||||||
import sonia.scm.cache.CacheManager;
|
import sonia.scm.cache.CacheManager;
|
||||||
import sonia.scm.cache.GuavaCacheManager;
|
import sonia.scm.cache.GuavaCacheManager;
|
||||||
import sonia.scm.config.ScmConfiguration;
|
import sonia.scm.config.ScmConfiguration;
|
||||||
import sonia.scm.debug.DebugResource;
|
|
||||||
import sonia.scm.event.ScmEventBus;
|
import sonia.scm.event.ScmEventBus;
|
||||||
import sonia.scm.group.DefaultGroupManager;
|
import sonia.scm.group.DefaultGroupManager;
|
||||||
import sonia.scm.group.GroupDAO;
|
import sonia.scm.group.GroupDAO;
|
||||||
@@ -88,7 +87,6 @@ import sonia.scm.security.DefaultKeyGenerator;
|
|||||||
import sonia.scm.security.DefaultSecuritySystem;
|
import sonia.scm.security.DefaultSecuritySystem;
|
||||||
import sonia.scm.security.KeyGenerator;
|
import sonia.scm.security.KeyGenerator;
|
||||||
import sonia.scm.security.LoginAttemptHandler;
|
import sonia.scm.security.LoginAttemptHandler;
|
||||||
import sonia.scm.security.SecurityInterceptor;
|
|
||||||
import sonia.scm.security.SecuritySystem;
|
import sonia.scm.security.SecuritySystem;
|
||||||
import sonia.scm.store.BlobStoreFactory;
|
import sonia.scm.store.BlobStoreFactory;
|
||||||
import sonia.scm.store.ConfigurationEntryStoreFactory;
|
import sonia.scm.store.ConfigurationEntryStoreFactory;
|
||||||
@@ -120,16 +118,7 @@ import sonia.scm.web.security.DefaultAdministrationContext;
|
|||||||
|
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
import javax.servlet.ServletContext;
|
import javax.servlet.ServletContext;
|
||||||
import javax.ws.rs.DELETE;
|
|
||||||
import javax.ws.rs.GET;
|
|
||||||
import javax.ws.rs.HEAD;
|
|
||||||
import javax.ws.rs.OPTIONS;
|
|
||||||
import javax.ws.rs.POST;
|
|
||||||
import javax.ws.rs.PUT;
|
|
||||||
|
|
||||||
import static com.google.inject.matcher.Matchers.annotatedWith;
|
|
||||||
import static com.google.inject.matcher.Matchers.not;
|
|
||||||
import static com.google.inject.matcher.Matchers.subclassesOf;
|
|
||||||
import static sonia.scm.api.v2.resources.ScmPathInfo.REST_API_PATH;
|
import static sonia.scm.api.v2.resources.ScmPathInfo.REST_API_PATH;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -330,14 +319,6 @@ public class ScmServletModule extends ServletModule
|
|||||||
// bind(LastModifiedUpdateListener.class);
|
// bind(LastModifiedUpdateListener.class);
|
||||||
|
|
||||||
bind(PushStateDispatcher.class).toProvider(PushStateDispatcherProvider.class);
|
bind(PushStateDispatcher.class).toProvider(PushStateDispatcherProvider.class);
|
||||||
bindInterceptor(not(subclassesOf(DebugResource.class)),
|
|
||||||
annotatedWith(GET.class)
|
|
||||||
.or(annotatedWith(POST.class))
|
|
||||||
.or(annotatedWith(HEAD.class))
|
|
||||||
.or(annotatedWith(PUT.class))
|
|
||||||
.or(annotatedWith(DELETE.class))
|
|
||||||
.or(annotatedWith(OPTIONS.class)),
|
|
||||||
new SecurityInterceptor());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
package sonia.scm.security;
|
|
||||||
|
|
||||||
import org.aopalliance.intercept.MethodInterceptor;
|
|
||||||
import org.aopalliance.intercept.MethodInvocation;
|
|
||||||
import org.apache.shiro.SecurityUtils;
|
|
||||||
import org.apache.shiro.authc.AuthenticationException;
|
|
||||||
import org.apache.shiro.subject.Subject;
|
|
||||||
|
|
||||||
public class SecurityInterceptor implements MethodInterceptor {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
|
|
||||||
if (hasPermission() || anonymousAccessIsAllowed(methodInvocation)) {
|
|
||||||
return methodInvocation.proceed();
|
|
||||||
} else {
|
|
||||||
throw new AuthenticationException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean anonymousAccessIsAllowed(MethodInvocation methodInvocation) {
|
|
||||||
return methodInvocation.getMethod().isAnnotationPresent(AllowAnonymousAccess.class)
|
|
||||||
|| methodInvocation.getMethod().getDeclaringClass().isAnnotationPresent(AllowAnonymousAccess.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hasPermission() {
|
|
||||||
Subject subject = SecurityUtils.getSubject();
|
|
||||||
return subject.isAuthenticated() || subject.isRemembered();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package sonia.scm.security;
|
||||||
|
|
||||||
|
import org.apache.shiro.SecurityUtils;
|
||||||
|
import org.apache.shiro.authc.AuthenticationException;
|
||||||
|
import org.apache.shiro.subject.Subject;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.ws.rs.container.ContainerRequestContext;
|
||||||
|
import javax.ws.rs.container.ContainerRequestFilter;
|
||||||
|
import javax.ws.rs.container.ResourceInfo;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
import javax.ws.rs.ext.Provider;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
@Provider
|
||||||
|
public class SecurityRequestFilter implements ContainerRequestFilter {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(SecurityRequestFilter.class);
|
||||||
|
|
||||||
|
@Context
|
||||||
|
private ResourceInfo resourceInfo;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void filter(ContainerRequestContext requestContext) {
|
||||||
|
Method resourceMethod = resourceInfo.getResourceMethod();
|
||||||
|
LOG.info("jax-rs method {}", resourceMethod.getName());
|
||||||
|
if (hasPermission() || anonymousAccessIsAllowed(resourceMethod)) {
|
||||||
|
LOG.debug("allowed unauthenticated request to method {}", resourceMethod);
|
||||||
|
// nothing further to do
|
||||||
|
} else {
|
||||||
|
LOG.debug("blocked unauthenticated request to method {}", resourceMethod);
|
||||||
|
throw new AuthenticationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean anonymousAccessIsAllowed(Method method) {
|
||||||
|
return method.isAnnotationPresent(AllowAnonymousAccess.class)
|
||||||
|
|| method.getDeclaringClass().isAnnotationPresent(AllowAnonymousAccess.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasPermission() {
|
||||||
|
Subject subject = SecurityUtils.getSubject();
|
||||||
|
return subject.isAuthenticated() || subject.isRemembered();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package sonia.scm.security;
|
||||||
|
|
||||||
|
import com.github.sdorra.shiro.ShiroRule;
|
||||||
|
import com.github.sdorra.shiro.SubjectAware;
|
||||||
|
import org.apache.shiro.authc.AuthenticationException;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
import javax.ws.rs.container.ContainerRequestContext;
|
||||||
|
import javax.ws.rs.container.ResourceInfo;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
@SubjectAware(configuration = "classpath:sonia/scm/shiro-001.ini")
|
||||||
|
public class SecurityRequestFilterTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public ShiroRule shiroRule = new ShiroRule();
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private ResourceInfo resourceInfo;
|
||||||
|
@Mock
|
||||||
|
private ContainerRequestContext context;
|
||||||
|
@InjectMocks
|
||||||
|
private SecurityRequestFilter securityRequestFilter;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldAllowUnauthenticatedAccessForAnnotatedMethod() throws NoSuchMethodException {
|
||||||
|
when(resourceInfo.getResourceMethod()).thenReturn(SecurityTestClass.class.getMethod("anonymousAccessAllowed"));
|
||||||
|
|
||||||
|
securityRequestFilter.filter(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = AuthenticationException.class)
|
||||||
|
public void shouldRejectUnauthenticatedAccessForAnnotatedMethod() throws NoSuchMethodException {
|
||||||
|
when(resourceInfo.getResourceMethod()).thenReturn(SecurityTestClass.class.getMethod("loginRequired"));
|
||||||
|
|
||||||
|
securityRequestFilter.filter(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SubjectAware(username = "trillian", password = "secret")
|
||||||
|
public void shouldAllowAuthenticatedAccessForMethodWithoutAnnotation() throws NoSuchMethodException {
|
||||||
|
when(resourceInfo.getResourceMethod()).thenReturn(SecurityTestClass.class.getMethod("loginRequired"));
|
||||||
|
|
||||||
|
securityRequestFilter.filter(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SecurityTestClass {
|
||||||
|
@AllowAnonymousAccess
|
||||||
|
public void anonymousAccessAllowed() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loginRequired() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user