aurora-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jco...@apache.org
Subject aurora git commit: Allow for plugging in cli-configurable filters that are invoked post shiro filters.
Date Thu, 14 Jan 2016 22:28:14 GMT
Repository: aurora
Updated Branches:
  refs/heads/master e0fef5abf -> 4dff5da84


Allow for plugging in cli-configurable filters that are invoked post shiro filters.

Bugs closed: AURORA-1576

Reviewed at https://reviews.apache.org/r/42046/


Project: http://git-wip-us.apache.org/repos/asf/aurora/repo
Commit: http://git-wip-us.apache.org/repos/asf/aurora/commit/4dff5da8
Tree: http://git-wip-us.apache.org/repos/asf/aurora/tree/4dff5da8
Diff: http://git-wip-us.apache.org/repos/asf/aurora/diff/4dff5da8

Branch: refs/heads/master
Commit: 4dff5da84e078aa8ab85be01d7c71495e2f1a940
Parents: e0fef5a
Author: Amol Deshmukh <amol@apache.org>
Authored: Thu Jan 14 16:26:46 2016 -0600
Committer: Joshua Cohen <jcohen@apache.org>
Committed: Thu Jan 14 16:26:46 2016 -0600

----------------------------------------------------------------------
 NEWS                                            |  3 +
 docs/security.md                                |  8 +++
 .../http/api/security/HttpSecurityModule.java   | 58 +++++++++++++++---
 .../http/api/security/HttpSecurityIT.java       | 63 +++++++++++++++-----
 4 files changed, 111 insertions(+), 21 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/aurora/blob/4dff5da8/NEWS
----------------------------------------------------------------------
diff --git a/NEWS b/NEWS
index 809077f..3937108 100644
--- a/NEWS
+++ b/NEWS
@@ -9,6 +9,9 @@
     `-enable_cors_for`.
   - `-deduplicate_snapshots` and `-deflate_snapshots`.  These features are good to always
enable.
   - `-enable_job_updates` and `-enable_job_creation`
+- Added scheduler command line argument:
+  - `-shiro_after_auth_filter`. Optionally specify a class implementing javax.servlet.Filter
+    that will be included in the Filter chain following the Shiro auth filters.
 - Upgraded the scheduler ZooKeeper client from 3.3.4 to 3.4.6.
 - Added support for jobs to specify arbitrary ZooKeeper paths for service registration. 
See
   https://github.com/apache/aurora/blob/master/docs/configuration-reference.md#announcer-objects

http://git-wip-us.apache.org/repos/asf/aurora/blob/4dff5da8/docs/security.md
----------------------------------------------------------------------
diff --git a/docs/security.md b/docs/security.md
index f9b60e9..32bea42 100644
--- a/docs/security.md
+++ b/docs/security.md
@@ -217,6 +217,14 @@ You might find documentation on the Internet suggesting there are additional
sec
 like `[main]` and `[urls]`. These are not supported by Aurora as it uses a different mechanism
to configure
 those parts of Shiro. Think of Aurora's `security.ini` as a subset with only `[users]` and
`[roles]` sections.
 
+## Implementing Delegated Authorization
+
+It is possible to leverage Shiro's `runAs` feature by implementing a custom Servlet Filter
that provides
+the capability and passing it's fully qualified class name to the command line argument
+`-shiro_after_auth_filter`. The filter is registered in the same filter chain as the Shiro
auth filters
+and is placed after the Shiro auth filters in the filter chain. This ensures that the Filter
is invoked
+after the Shiro filters have had a chance to authenticate the request.
+
 # Implementing a Custom Realm
 
 Since Aurora’s security is backed by [Apache Shiro](https://shiro.apache.org), you can
implement a

http://git-wip-us.apache.org/repos/asf/aurora/blob/4dff5da8/src/main/java/org/apache/aurora/scheduler/http/api/security/HttpSecurityModule.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/http/api/security/HttpSecurityModule.java
b/src/main/java/org/apache/aurora/scheduler/http/api/security/HttpSecurityModule.java
index 4b6a872..e328620 100644
--- a/src/main/java/org/apache/aurora/scheduler/http/api/security/HttpSecurityModule.java
+++ b/src/main/java/org/apache/aurora/scheduler/http/api/security/HttpSecurityModule.java
@@ -16,7 +16,6 @@ package org.apache.aurora.scheduler.http.api.security;
 import java.lang.reflect.Method;
 import java.util.Optional;
 import java.util.Set;
-
 import javax.servlet.Filter;
 
 import com.google.common.annotations.VisibleForTesting;
@@ -26,6 +25,7 @@ import com.google.inject.Key;
 import com.google.inject.Module;
 import com.google.inject.Provides;
 import com.google.inject.TypeLiteral;
+import com.google.inject.binder.AnnotatedBindingBuilder;
 import com.google.inject.matcher.Matcher;
 import com.google.inject.matcher.Matchers;
 import com.google.inject.name.Names;
@@ -43,6 +43,8 @@ import org.apache.aurora.scheduler.thrift.aop.AnnotatedAuroraAdmin;
 import org.apache.shiro.SecurityUtils;
 import org.apache.shiro.guice.aop.ShiroAopModule;
 import org.apache.shiro.guice.web.ShiroWebModule;
+import org.apache.shiro.session.mgt.DefaultSessionManager;
+import org.apache.shiro.session.mgt.SessionManager;
 import org.apache.shiro.subject.Subject;
 
 import static java.util.Objects.requireNonNull;
@@ -76,6 +78,11 @@ public class HttpSecurityModule extends ServletModule {
   private static final Arg<Set<Module>> SHIRO_REALM_MODULE = Arg.create(
       ImmutableSet.of(MoreModules.lazilyInstantiated(IniShiroRealmModule.class)));
 
+  @CmdLine(name = "shiro_after_auth_filter",
+      help = "Fully qualified class name of the servlet filter to be applied after the"
+          + " shiro auth filters are applied.")
+  private static final Arg<Class<? extends Filter>> SHIRO_AFTER_AUTH_FILTER =
Arg.create();
+
   @VisibleForTesting
   static final Matcher<Method> AURORA_SCHEDULER_MANAGER_SERVICE =
       GuiceUtils.interfaceMatcher(AuroraSchedulerManager.Iface.class, true);
@@ -107,22 +114,33 @@ public class HttpSecurityModule extends ServletModule {
 
   private final HttpAuthenticationMechanism mechanism;
   private final Set<Module> shiroConfigurationModules;
+  private final Optional<Key<? extends Filter>> shiroAfterAuthFilterKey;
 
   public HttpSecurityModule() {
-    this(HTTP_AUTHENTICATION_MECHANISM.get(), SHIRO_REALM_MODULE.get());
+    this(
+        HTTP_AUTHENTICATION_MECHANISM.get(),
+        SHIRO_REALM_MODULE.get(),
+        SHIRO_AFTER_AUTH_FILTER.hasAppliedValue() ? Key.get(SHIRO_AFTER_AUTH_FILTER.get())
: null);
   }
 
   @VisibleForTesting
-  HttpSecurityModule(Module shiroConfigurationModule) {
-    this(HttpAuthenticationMechanism.BASIC, ImmutableSet.of(shiroConfigurationModule));
+  HttpSecurityModule(
+      Module shiroConfigurationModule,
+      Key<? extends Filter> shiroAfterAuthFilterKey) {
+
+    this(HttpAuthenticationMechanism.BASIC,
+        ImmutableSet.of(shiroConfigurationModule),
+        shiroAfterAuthFilterKey);
   }
 
   private HttpSecurityModule(
       HttpAuthenticationMechanism mechanism,
-      Set<Module> shiroConfigurationModules) {
+      Set<Module> shiroConfigurationModules,
+      Key<? extends Filter> shiroAfterAuthFilterKey) {
 
     this.mechanism = requireNonNull(mechanism);
     this.shiroConfigurationModules = requireNonNull(shiroConfigurationModules);
+    this.shiroAfterAuthFilterKey = Optional.ofNullable(shiroAfterAuthFilterKey);
   }
 
   @Override
@@ -153,6 +171,14 @@ public class HttpSecurityModule extends ServletModule {
     install(guiceFilterModule(H2_PATH));
     install(guiceFilterModule(H2_PATH + "/*"));
     install(new ShiroWebModule(getServletContext()) {
+
+      // Replace the ServletContainerSessionManager which causes subject.runAs(...) in a
+      // downstream user-defined filter to fail. See also: SHIRO-554
+      @Override
+      protected void bindSessionManager(AnnotatedBindingBuilder<SessionManager> bind)
{
+        bind.to(DefaultSessionManager.class).asEagerSingleton();
+      }
+
       @Override
       @SuppressWarnings("unchecked")
       protected void configureShiroWeb() {
@@ -167,12 +193,12 @@ public class HttpSecurityModule extends ServletModule {
         switch (mechanism) {
           case BASIC:
             addFilterChain(H2_PATTERN, NO_SESSION_CREATION, AUTHC_BASIC, config(PERMS, H2_PERM));
-            addFilterChain(ALL_PATTERN, NO_SESSION_CREATION, config(AUTHC_BASIC, PERMISSIVE));
+            addFilterChainWithAfterAuthFilter(config(AUTHC_BASIC, PERMISSIVE));
             break;
 
           case NEGOTIATE:
             addFilterChain(H2_PATTERN, NO_SESSION_CREATION, K_STRICT, config(PERMS, H2_PERM));
-            addFilterChain(ALL_PATTERN, NO_SESSION_CREATION, K_PERMISSIVE);
+            addFilterChainWithAfterAuthFilter(K_PERMISSIVE);
             break;
 
           default:
@@ -180,6 +206,24 @@ public class HttpSecurityModule extends ServletModule {
             break;
         }
       }
+
+      private void addFilterChainWithAfterAuthFilter(Key<? extends Filter> filter)
{
+        if (shiroAfterAuthFilterKey.isPresent()) {
+          addFilterChain(filter, shiroAfterAuthFilterKey.get());
+        } else {
+          addFilterChain(filter);
+        }
+      }
+
+      @SuppressWarnings("unchecked")
+      private void addFilterChain(Key<? extends Filter> filter) {
+        addFilterChain(ALL_PATTERN, NO_SESSION_CREATION, filter);
+      }
+
+      @SuppressWarnings("unchecked")
+      private void addFilterChain(Key<? extends Filter> filter1, Key<? extends Filter>
filter2) {
+        addFilterChain(ALL_PATTERN, NO_SESSION_CREATION, filter1, filter2);
+      }
     });
 
     bindConstant().annotatedWith(Names.named("shiro.applicationName")).to(HTTP_REALM_NAME);

http://git-wip-us.apache.org/repos/asf/aurora/blob/4dff5da8/src/test/java/org/apache/aurora/scheduler/http/api/security/HttpSecurityIT.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/http/api/security/HttpSecurityIT.java
b/src/test/java/org/apache/aurora/scheduler/http/api/security/HttpSecurityIT.java
index ac92117..3e811a4 100644
--- a/src/test/java/org/apache/aurora/scheduler/http/api/security/HttpSecurityIT.java
+++ b/src/test/java/org/apache/aurora/scheduler/http/api/security/HttpSecurityIT.java
@@ -15,12 +15,20 @@ package org.apache.aurora.scheduler.http.api.security;
 
 import java.io.IOException;
 import java.util.Set;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
 
 import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 import com.google.inject.AbstractModule;
+import com.google.inject.Key;
 import com.google.inject.Module;
+import com.google.inject.name.Named;
+import com.google.inject.name.Names;
 import com.google.inject.util.Modules;
 import com.sun.jersey.api.client.ClientResponse;
 
@@ -53,6 +61,7 @@ import org.apache.thrift.protocol.TJSONProtocol;
 import org.apache.thrift.transport.THttpClient;
 import org.apache.thrift.transport.TTransport;
 import org.apache.thrift.transport.TTransportException;
+import org.easymock.IExpectationSetters;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -60,6 +69,9 @@ import static org.apache.aurora.scheduler.http.H2ConsoleModule.H2_PATH;
 import static org.apache.aurora.scheduler.http.H2ConsoleModule.H2_PERM;
 import static org.apache.aurora.scheduler.http.api.ApiModule.API_PATH;
 import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.getCurrentArguments;
+import static org.easymock.EasyMock.isA;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
@@ -92,15 +104,17 @@ public class HttpSecurityIT extends AbstractJettyTest {
 
   private static final IJobKey ADS_STAGING_JOB = JobKeys.from("ads", "staging", "job");
 
-  private Ini ini;
-  private AnnotatedAuroraAdmin auroraAdmin;
-
   private static final Joiner COMMA_JOINER = Joiner.on(", ");
   private static final String ADMIN_ROLE = "admin";
   private static final String ENG_ROLE = "eng";
   private static final String BACKUP_ROLE = "backup";
   private static final String DEPLOY_ROLE = "deploy";
   private static final String H2_ROLE = "h2access";
+  private static final Named SHIRO_AFTER_AUTH_FILTER_ANNOTATION = Names.named("shiro_post_filter");
+
+  private Ini ini;
+  private AnnotatedAuroraAdmin auroraAdmin;
+  private Filter shiroAfterAuthFilter;
 
   @Before
   public void setUp() {
@@ -133,6 +147,7 @@ public class HttpSecurityIT extends AbstractJettyTest {
     roles.put(H2_ROLE, H2_PERM);
 
     auroraAdmin = createMock(AnnotatedAuroraAdmin.class);
+    shiroAfterAuthFilter = createMock(Filter.class);
   }
 
   @Override
@@ -140,10 +155,15 @@ public class HttpSecurityIT extends AbstractJettyTest {
     return Modules.combine(
         new ApiModule(),
         new H2ConsoleModule(true),
-        new HttpSecurityModule(new IniShiroRealmModule(ini)),
+        new HttpSecurityModule(
+            new IniShiroRealmModule(ini),
+            Key.get(Filter.class, SHIRO_AFTER_AUTH_FILTER_ANNOTATION)),
         new AbstractModule() {
           @Override
           protected void configure() {
+            bind(Filter.class)
+                .annotatedWith(SHIRO_AFTER_AUTH_FILTER_ANNOTATION)
+                .toInstance(shiroAfterAuthFilter);
             MockDecoratedThrift.bindForwardedMock(binder(), auroraAdmin);
           }
         });
@@ -175,9 +195,25 @@ public class HttpSecurityIT extends AbstractJettyTest {
     return getClient(defaultHttpClient);
   }
 
+  private IExpectationSetters<Object> expectShiroAfterAuthFilter()
+      throws ServletException, IOException {
+
+    shiroAfterAuthFilter.doFilter(
+        isA(HttpServletRequest.class),
+        isA(HttpServletResponse.class),
+        isA(FilterChain.class));
+
+    return expectLastCall().andAnswer(() -> {
+      Object[] args = getCurrentArguments();
+      ((FilterChain) args[2]).doFilter((HttpServletRequest) args[0], (HttpServletResponse)
args[1]);
+      return null;
+    });
+  }
+
   @Test
-  public void testReadOnlyScheduler() throws TException {
+  public void testReadOnlyScheduler() throws TException, ServletException, IOException {
     expect(auroraAdmin.getRoleSummary()).andReturn(OK).times(3);
+    expectShiroAfterAuthFilter().times(3);
 
     replayAndStart();
 
@@ -198,7 +234,7 @@ public class HttpSecurityIT extends AbstractJettyTest {
   }
 
   @Test
-  public void testAuroraSchedulerManager() throws TException, IOException {
+  public void testAuroraSchedulerManager() throws TException, ServletException, IOException
{
     expect(auroraAdmin.killTasks(null, new Lock().setMessage("1"))).andReturn(OK);
     expect(auroraAdmin.killTasks(null, new Lock().setMessage("2"))).andReturn(OK);
 
@@ -206,12 +242,12 @@ public class HttpSecurityIT extends AbstractJettyTest {
     TaskQuery adsScopedQuery = Query.jobScoped(ADS_STAGING_JOB).get();
     expect(auroraAdmin.killTasks(adsScopedQuery, null)).andReturn(OK);
 
+    expectShiroAfterAuthFilter().times(19);
+
     replayAndStart();
 
-    assertEquals(OK,
-        getAuthenticatedClient(WFARNER).killTasks(null, new Lock().setMessage("1")));
-    assertEquals(OK,
-        getAuthenticatedClient(ROOT).killTasks(null, new Lock().setMessage("2")));
+    assertEquals(OK, getAuthenticatedClient(WFARNER).killTasks(null, new Lock().setMessage("1")));
+    assertEquals(OK, getAuthenticatedClient(ROOT).killTasks(null, new Lock().setMessage("2")));
     assertEquals(
         ResponseCode.INVALID_REQUEST,
         getAuthenticatedClient(UNPRIVILEGED).killTasks(null, null).getResponseCode());
@@ -233,9 +269,7 @@ public class HttpSecurityIT extends AbstractJettyTest {
         getAuthenticatedClient(DEPLOY_SERVICE)
             .killTasks(jobScopedQuery, null)
             .getResponseCode());
-    assertEquals(
-        OK,
-        getAuthenticatedClient(DEPLOY_SERVICE).killTasks(adsScopedQuery, null));
+    assertEquals(OK, getAuthenticatedClient(DEPLOY_SERVICE).killTasks(adsScopedQuery, null));
 
     assertKillTasksFails(getUnauthenticatedClient());
     assertKillTasksFails(getAuthenticatedClient(INCORRECT));
@@ -252,9 +286,10 @@ public class HttpSecurityIT extends AbstractJettyTest {
   }
 
   @Test
-  public void testAuroraAdmin() throws TException {
+  public void testAuroraAdmin() throws TException, ServletException, IOException {
     expect(auroraAdmin.snapshot()).andReturn(OK);
     expect(auroraAdmin.listBackups()).andReturn(OK);
+    expectShiroAfterAuthFilter().times(12);
 
     replayAndStart();
 


Mime
View raw message