You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aurora.apache.org by ma...@apache.org on 2015/06/04 23:41:08 UTC
aurora git commit: Adding H2 management console.
Repository: aurora
Updated Branches:
refs/heads/master ed4415c12 -> a1f7b3da6
Adding H2 management console.
Bugs closed: AURORA-1287
Reviewed at https://reviews.apache.org/r/34566/
Project: http://git-wip-us.apache.org/repos/asf/aurora/repo
Commit: http://git-wip-us.apache.org/repos/asf/aurora/commit/a1f7b3da
Tree: http://git-wip-us.apache.org/repos/asf/aurora/tree/a1f7b3da
Diff: http://git-wip-us.apache.org/repos/asf/aurora/diff/a1f7b3da
Branch: refs/heads/master
Commit: a1f7b3da64c84fd84f145e0d4cce4ca69341f1ca
Parents: ed4415c
Author: Maxim Khutornenko <ma...@apache.org>
Authored: Thu Jun 4 14:40:47 2015 -0700
Committer: Maxim Khutornenko <ma...@apache.org>
Committed: Thu Jun 4 14:40:47 2015 -0700
----------------------------------------------------------------------
.../aurora/scheduler/http/H2ConsoleModule.java | 45 +++
.../scheduler/http/JettyServerModule.java | 5 +-
.../http/api/security/ApiSecurityModule.java | 189 -----------
.../http/api/security/HttpSecurityModule.java | 203 ++++++++++++
.../http/api/security/ShiroIniParser.java | 2 +-
.../ShiroKerberosAuthenticationFilter.java | 16 +-
...oKerberosPermissiveAuthenticationFilter.java | 51 +++
.../scheduler/http/H2ConsoleModuleIT.java | 40 +++
.../http/api/security/ApiSecurityIT.java | 274 ----------------
.../http/api/security/HttpSecurityIT.java | 324 +++++++++++++++++++
.../ShiroAuthorizingParamInterceptorTest.java | 4 +-
.../ShiroKerberosAuthenticationFilterTest.java | 25 +-
...berosPermissiveAuthenticationFilterTest.java | 98 ++++++
.../aurora/e2e/test_kerberos_end_to_end.sh | 20 ++
14 files changed, 801 insertions(+), 495 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/aurora/blob/a1f7b3da/src/main/java/org/apache/aurora/scheduler/http/H2ConsoleModule.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/http/H2ConsoleModule.java b/src/main/java/org/apache/aurora/scheduler/http/H2ConsoleModule.java
new file mode 100644
index 0000000..a44ea08
--- /dev/null
+++ b/src/main/java/org/apache/aurora/scheduler/http/H2ConsoleModule.java
@@ -0,0 +1,45 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.aurora.scheduler.http;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.inject.servlet.ServletModule;
+import com.twitter.common.args.Arg;
+import com.twitter.common.args.CmdLine;
+
+import org.h2.server.web.WebServlet;
+
+/**
+ * Binding module for the H2 management console.
+ * <p>
+ * See: http://www.h2database.com/html/tutorial.html#tutorial_starting_h2_console
+ */
+public class H2ConsoleModule extends ServletModule {
+ public static final String H2_PATH = "/h2console";
+ public static final String H2_PERM = "h2_management_console";
+
+ @CmdLine(name = "enable_h2_console", help = "Enable H2 DB management console.")
+ private static final Arg<Boolean> ENABLE_H2_CONSOLE = Arg.create(true);
+
+ @Override
+ protected void configureServlets() {
+ if (ENABLE_H2_CONSOLE.get()) {
+ filter(H2_PATH, H2_PATH + "/*").through(LeaderRedirectFilter.class);
+ serve(H2_PATH, H2_PATH + "/*").with(new WebServlet(), ImmutableMap.of(
+ "webAllowOthers", "true",
+ "ifExists", "true"
+ ));
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/a1f7b3da/src/main/java/org/apache/aurora/scheduler/http/JettyServerModule.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/http/JettyServerModule.java b/src/main/java/org/apache/aurora/scheduler/http/JettyServerModule.java
index 16515f6..b470129 100644
--- a/src/main/java/org/apache/aurora/scheduler/http/JettyServerModule.java
+++ b/src/main/java/org/apache/aurora/scheduler/http/JettyServerModule.java
@@ -64,7 +64,7 @@ import com.twitter.common.net.pool.DynamicHostSet.MonitorException;
import org.apache.aurora.scheduler.SchedulerServicesModule;
import org.apache.aurora.scheduler.http.api.ApiModule;
-import org.apache.aurora.scheduler.http.api.security.ApiSecurityModule;
+import org.apache.aurora.scheduler.http.api.security.HttpSecurityModule;
import org.apache.aurora.scheduler.thrift.ThriftModule;
import org.apache.aurora.scheduler.thrift.auth.ThriftAuthModule;
import org.eclipse.jetty.rewrite.handler.RewriteHandler;
@@ -188,7 +188,8 @@ public class JettyServerModule extends AbstractModule {
parentInjector,
Modules.combine(
new ApiModule(),
- new ApiSecurityModule(),
+ new H2ConsoleModule(),
+ new HttpSecurityModule(),
new ThriftModule(),
new ThriftAuthModule()));
}
http://git-wip-us.apache.org/repos/asf/aurora/blob/a1f7b3da/src/main/java/org/apache/aurora/scheduler/http/api/security/ApiSecurityModule.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/http/api/security/ApiSecurityModule.java b/src/main/java/org/apache/aurora/scheduler/http/api/security/ApiSecurityModule.java
deleted file mode 100644
index 079ff5d..0000000
--- a/src/main/java/org/apache/aurora/scheduler/http/api/security/ApiSecurityModule.java
+++ /dev/null
@@ -1,189 +0,0 @@
-/**
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.aurora.scheduler.http.api.security;
-
-import java.lang.reflect.Method;
-import java.util.Set;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableSet;
-import com.google.inject.Key;
-import com.google.inject.Module;
-import com.google.inject.Provides;
-import com.google.inject.matcher.Matcher;
-import com.google.inject.matcher.Matchers;
-import com.google.inject.name.Names;
-import com.google.inject.servlet.RequestScoped;
-import com.google.inject.servlet.ServletModule;
-import com.twitter.common.args.Arg;
-import com.twitter.common.args.CmdLine;
-
-import org.aopalliance.intercept.MethodInterceptor;
-import org.apache.aurora.GuiceUtils;
-import org.apache.aurora.gen.AuroraAdmin;
-import org.apache.aurora.gen.AuroraSchedulerManager;
-import org.apache.aurora.scheduler.app.Modules;
-import org.apache.aurora.scheduler.http.api.ApiModule;
-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.subject.Subject;
-import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
-
-import static java.util.Objects.requireNonNull;
-
-import static org.apache.aurora.scheduler.spi.Permissions.Domain.THRIFT_AURORA_ADMIN;
-import static org.apache.aurora.scheduler.spi.Permissions.Domain.THRIFT_AURORA_SCHEDULER_MANAGER;
-
-/**
- * Provides HTTP Basic Authentication for the API using Apache Shiro. When enabled, prevents
- * unauthenticated access to write APIs. Write API access must also be authorized, with permissions
- * configured in a shiro.ini file. For an example of this file, see the test resources included with
- * this package.
- */
-public class ApiSecurityModule extends ServletModule {
- public static final String HTTP_REALM_NAME = "Apache Aurora Scheduler";
-
- @CmdLine(name = "shiro_realm_modules",
- help = "Guice modules for configuring Shiro Realms.")
- private static final Arg<Set<Module>> SHIRO_REALM_MODULE = Arg.<Set<Module>>create(
- ImmutableSet.of(Modules.lazilyInstantiated(IniShiroRealmModule.class)));
-
- @VisibleForTesting
- static final Matcher<Method> AURORA_SCHEDULER_MANAGER_SERVICE =
- GuiceUtils.interfaceMatcher(AuroraSchedulerManager.Iface.class, true);
-
- @VisibleForTesting
- static final Matcher<Method> AURORA_ADMIN_SERVICE =
- GuiceUtils.interfaceMatcher(AuroraAdmin.Iface.class, true);
-
- public enum HttpAuthenticationMechanism {
- /**
- * No security.
- */
- NONE,
-
- /**
- * HTTP Basic Authentication, produces {@link org.apache.shiro.authc.UsernamePasswordToken}s.
- */
- BASIC,
-
- /**
- * Use GSS-Negotiate. Only Kerberos and SPNEGO-with-Kerberos GSS mechanisms are supported.
- */
- NEGOTIATE,
- }
-
- @CmdLine(name = "http_authentication_mechanism", help = "HTTP Authentication mechanism to use.")
- private static final Arg<HttpAuthenticationMechanism> HTTP_AUTHENTICATION_MECHANISM =
- Arg.create(HttpAuthenticationMechanism.NONE);
-
- private final HttpAuthenticationMechanism mechanism;
- private final Set<Module> shiroConfigurationModules;
-
- public ApiSecurityModule() {
- this(HTTP_AUTHENTICATION_MECHANISM.get(), SHIRO_REALM_MODULE.get());
- }
-
- @VisibleForTesting
- ApiSecurityModule(Module shiroConfigurationModule) {
- this(HttpAuthenticationMechanism.BASIC, ImmutableSet.of(shiroConfigurationModule));
- }
-
- private ApiSecurityModule(
- HttpAuthenticationMechanism mechanism,
- Set<Module> shiroConfigurationModules) {
-
- this.mechanism = requireNonNull(mechanism);
- this.shiroConfigurationModules = requireNonNull(shiroConfigurationModules);
- }
-
- @Override
- protected void configureServlets() {
- if (mechanism != HttpAuthenticationMechanism.NONE) {
- doConfigureServlets();
- }
- }
-
- private void doConfigureServlets() {
- install(ShiroWebModule.guiceFilterModule(ApiModule.API_PATH));
- install(new ShiroWebModule(getServletContext()) {
- @Override
- @SuppressWarnings("unchecked")
- protected void configureShiroWeb() {
- for (Module module : shiroConfigurationModules) {
- // We can't wrap this in a PrivateModule because Guice Multibindings don't work with them
- // and we need a Set<Realm>.
- install(module);
- }
-
- switch (mechanism) {
- case BASIC:
- addFilterChain("/**",
- ShiroWebModule.NO_SESSION_CREATION,
- config(ShiroWebModule.AUTHC_BASIC, BasicHttpAuthenticationFilter.PERMISSIVE));
- break;
-
- case NEGOTIATE:
- addFilterChain("/**",
- ShiroWebModule.NO_SESSION_CREATION,
- Key.get(ShiroKerberosAuthenticationFilter.class));
- break;
-
- default:
- addError("Unrecognized HTTP authentication mechanism: " + mechanism);
- break;
- }
- }
- });
-
- bindConstant().annotatedWith(Names.named("shiro.applicationName")).to(HTTP_REALM_NAME);
-
- // TODO(ksweeney): Disable session cookie.
- // TODO(ksweeney): Disable RememberMe cookie.
-
- install(new ShiroAopModule());
-
- // It is important that authentication happen before authorization is attempted, otherwise
- // the authorizing interceptor will always fail.
- MethodInterceptor authenticatingInterceptor = new ShiroAuthenticatingThriftInterceptor();
- requestInjection(authenticatingInterceptor);
- bindInterceptor(
- Matchers.subclassesOf(AuroraSchedulerManager.Iface.class),
- AURORA_SCHEDULER_MANAGER_SERVICE.or(AURORA_ADMIN_SERVICE),
- authenticatingInterceptor);
-
- MethodInterceptor apiInterceptor = new ShiroAuthorizingParamInterceptor(
- THRIFT_AURORA_SCHEDULER_MANAGER);
- requestInjection(apiInterceptor);
- bindInterceptor(
- Matchers.subclassesOf(AuroraSchedulerManager.Iface.class),
- AURORA_SCHEDULER_MANAGER_SERVICE,
- apiInterceptor);
-
- MethodInterceptor adminInterceptor = new ShiroAuthorizingInterceptor(THRIFT_AURORA_ADMIN);
- requestInjection(adminInterceptor);
- bindInterceptor(
- Matchers.subclassesOf(AnnotatedAuroraAdmin.class),
- AURORA_ADMIN_SERVICE,
- adminInterceptor);
- }
-
- @Provides
- @RequestScoped
- Subject provideSubject() {
- return SecurityUtils.getSubject();
- }
-}
http://git-wip-us.apache.org/repos/asf/aurora/blob/a1f7b3da/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
new file mode 100644
index 0000000..1e9b1c3
--- /dev/null
+++ b/src/main/java/org/apache/aurora/scheduler/http/api/security/HttpSecurityModule.java
@@ -0,0 +1,203 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.aurora.scheduler.http.api.security;
+
+import java.lang.reflect.Method;
+import java.util.Set;
+
+import javax.servlet.Filter;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Key;
+import com.google.inject.Module;
+import com.google.inject.Provides;
+import com.google.inject.matcher.Matcher;
+import com.google.inject.matcher.Matchers;
+import com.google.inject.name.Names;
+import com.google.inject.servlet.RequestScoped;
+import com.google.inject.servlet.ServletModule;
+import com.twitter.common.args.Arg;
+import com.twitter.common.args.CmdLine;
+
+import org.aopalliance.intercept.MethodInterceptor;
+import org.apache.aurora.GuiceUtils;
+import org.apache.aurora.gen.AuroraAdmin;
+import org.apache.aurora.gen.AuroraSchedulerManager;
+import org.apache.aurora.scheduler.app.Modules;
+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.subject.Subject;
+
+import static java.util.Objects.requireNonNull;
+
+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.apache.aurora.scheduler.spi.Permissions.Domain.THRIFT_AURORA_ADMIN;
+import static org.apache.aurora.scheduler.spi.Permissions.Domain.THRIFT_AURORA_SCHEDULER_MANAGER;
+import static org.apache.shiro.guice.web.ShiroWebModule.guiceFilterModule;
+import static org.apache.shiro.web.filter.authc.AuthenticatingFilter.PERMISSIVE;
+
+/**
+ * Provides HTTP Basic Authentication using Apache Shiro. When enabled, prevents unauthenticated
+ * access to write APIs and configured servlets. Write API access must also be authorized, with
+ * permissions configured in a shiro.ini file. For an example of this file, see the test resources
+ * included with this package.
+ */
+public class HttpSecurityModule extends ServletModule {
+ public static final String HTTP_REALM_NAME = "Apache Aurora Scheduler";
+
+ private static final String H2_PATTERN = H2_PATH + "/**";
+ private static final String ALL_PATTERN = "/**";
+ private static final Key<? extends Filter> K_STRICT =
+ Key.get(ShiroKerberosAuthenticationFilter.class);
+ private static final Key<? extends Filter> K_PERMISSIVE =
+ Key.get(ShiroKerberosPermissiveAuthenticationFilter.class);
+
+ @CmdLine(name = "shiro_realm_modules",
+ help = "Guice modules for configuring Shiro Realms.")
+ private static final Arg<Set<Module>> SHIRO_REALM_MODULE = Arg.<Set<Module>>create(
+ ImmutableSet.of(Modules.lazilyInstantiated(IniShiroRealmModule.class)));
+
+ @VisibleForTesting
+ static final Matcher<Method> AURORA_SCHEDULER_MANAGER_SERVICE =
+ GuiceUtils.interfaceMatcher(AuroraSchedulerManager.Iface.class, true);
+
+ @VisibleForTesting
+ static final Matcher<Method> AURORA_ADMIN_SERVICE =
+ GuiceUtils.interfaceMatcher(AuroraAdmin.Iface.class, true);
+
+ public enum HttpAuthenticationMechanism {
+ /**
+ * No security.
+ */
+ NONE,
+
+ /**
+ * HTTP Basic Authentication, produces {@link org.apache.shiro.authc.UsernamePasswordToken}s.
+ */
+ BASIC,
+
+ /**
+ * Use GSS-Negotiate. Only Kerberos and SPNEGO-with-Kerberos GSS mechanisms are supported.
+ */
+ NEGOTIATE,
+ }
+
+ @CmdLine(name = "http_authentication_mechanism", help = "HTTP Authentication mechanism to use.")
+ private static final Arg<HttpAuthenticationMechanism> HTTP_AUTHENTICATION_MECHANISM =
+ Arg.create(HttpAuthenticationMechanism.NONE);
+
+ private final HttpAuthenticationMechanism mechanism;
+ private final Set<Module> shiroConfigurationModules;
+
+ public HttpSecurityModule() {
+ this(HTTP_AUTHENTICATION_MECHANISM.get(), SHIRO_REALM_MODULE.get());
+ }
+
+ @VisibleForTesting
+ HttpSecurityModule(Module shiroConfigurationModule) {
+ this(HttpAuthenticationMechanism.BASIC, ImmutableSet.of(shiroConfigurationModule));
+ }
+
+ private HttpSecurityModule(
+ HttpAuthenticationMechanism mechanism,
+ Set<Module> shiroConfigurationModules) {
+
+ this.mechanism = requireNonNull(mechanism);
+ this.shiroConfigurationModules = requireNonNull(shiroConfigurationModules);
+ }
+
+ @Override
+ protected void configureServlets() {
+ if (mechanism != HttpAuthenticationMechanism.NONE) {
+ doConfigureServlets();
+ }
+ }
+
+ private void doConfigureServlets() {
+ install(guiceFilterModule(API_PATH));
+ install(guiceFilterModule(H2_PATH));
+ install(guiceFilterModule(H2_PATH + "/*"));
+ install(new ShiroWebModule(getServletContext()) {
+ @Override
+ @SuppressWarnings("unchecked")
+ protected void configureShiroWeb() {
+ for (Module module : shiroConfigurationModules) {
+ // We can't wrap this in a PrivateModule because Guice Multibindings don't work with them
+ // and we need a Set<Realm>.
+ install(module);
+ }
+
+ // Filter registration order is important here and is defined by the matching pattern:
+ // more specific pattern first.
+ 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));
+ break;
+
+ case NEGOTIATE:
+ addFilterChain(H2_PATTERN, NO_SESSION_CREATION, K_STRICT, config(PERMS, H2_PERM));
+ addFilterChain(ALL_PATTERN, NO_SESSION_CREATION, K_PERMISSIVE);
+ break;
+
+ default:
+ addError("Unrecognized HTTP authentication mechanism: " + mechanism);
+ break;
+ }
+ }
+ });
+
+ bindConstant().annotatedWith(Names.named("shiro.applicationName")).to(HTTP_REALM_NAME);
+
+ // TODO(ksweeney): Disable session cookie.
+ // TODO(ksweeney): Disable RememberMe cookie.
+
+ install(new ShiroAopModule());
+
+ // It is important that authentication happen before authorization is attempted, otherwise
+ // the authorizing interceptor will always fail.
+ MethodInterceptor authenticatingInterceptor = new ShiroAuthenticatingThriftInterceptor();
+ requestInjection(authenticatingInterceptor);
+ bindInterceptor(
+ Matchers.subclassesOf(AuroraSchedulerManager.Iface.class),
+ AURORA_SCHEDULER_MANAGER_SERVICE.or(AURORA_ADMIN_SERVICE),
+ authenticatingInterceptor);
+
+ MethodInterceptor apiInterceptor = new ShiroAuthorizingParamInterceptor(
+ THRIFT_AURORA_SCHEDULER_MANAGER);
+ requestInjection(apiInterceptor);
+ bindInterceptor(
+ Matchers.subclassesOf(AuroraSchedulerManager.Iface.class),
+ AURORA_SCHEDULER_MANAGER_SERVICE,
+ apiInterceptor);
+
+ MethodInterceptor adminInterceptor = new ShiroAuthorizingInterceptor(THRIFT_AURORA_ADMIN);
+ requestInjection(adminInterceptor);
+ bindInterceptor(
+ Matchers.subclassesOf(AnnotatedAuroraAdmin.class),
+ AURORA_ADMIN_SERVICE,
+ adminInterceptor);
+ }
+
+ @Provides
+ @RequestScoped
+ Subject provideSubject() {
+ return SecurityUtils.getSubject();
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/a1f7b3da/src/main/java/org/apache/aurora/scheduler/http/api/security/ShiroIniParser.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/http/api/security/ShiroIniParser.java b/src/main/java/org/apache/aurora/scheduler/http/api/security/ShiroIniParser.java
index 0163ba1..671b14f 100644
--- a/src/main/java/org/apache/aurora/scheduler/http/api/security/ShiroIniParser.java
+++ b/src/main/java/org/apache/aurora/scheduler/http/api/security/ShiroIniParser.java
@@ -30,7 +30,7 @@ import org.apache.shiro.realm.text.IniRealm;
* The provided ini file must have only the sections required for configuration
* ({@link IniRealm.ROLES_SECTION_NAME} and {@link IniRealm.USERS_SECTION_NAME}) and no extras -
* Aurora uses Guice in to configure those sections in
- * {@link org.apache.aurora.scheduler.http.api.security.ApiSecurityModule}}.
+ * {@link HttpSecurityModule}}.
*/
@ArgParser
public class ShiroIniParser extends NonParameterizedTypeParser<Ini> {
http://git-wip-us.apache.org/repos/asf/aurora/blob/a1f7b3da/src/main/java/org/apache/aurora/scheduler/http/api/security/ShiroKerberosAuthenticationFilter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/http/api/security/ShiroKerberosAuthenticationFilter.java b/src/main/java/org/apache/aurora/scheduler/http/api/security/ShiroKerberosAuthenticationFilter.java
index 28e6b98..a18903e 100644
--- a/src/main/java/org/apache/aurora/scheduler/http/api/security/ShiroKerberosAuthenticationFilter.java
+++ b/src/main/java/org/apache/aurora/scheduler/http/api/security/ShiroKerberosAuthenticationFilter.java
@@ -28,7 +28,6 @@ import com.google.common.base.Optional;
import org.apache.aurora.scheduler.http.AbstractFilter;
import org.apache.shiro.authc.AuthenticationException;
-import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.subject.Subject;
import static java.util.Objects.requireNonNull;
@@ -75,15 +74,18 @@ public class ShiroKerberosAuthenticationFilter extends AbstractFilter {
sendChallenge(response);
}
} else {
- // Incoming request is unauthenticated, but some RPCs might be okay with that.
- try {
- chain.doFilter(request, response);
- } catch (UnauthenticatedException e) {
- sendChallenge(response);
- }
+ handleUnauthenticated(request, response, chain);
}
}
+ protected void handleUnauthenticated(
+ HttpServletRequest request,
+ HttpServletResponse response,
+ FilterChain chain) throws IOException, ServletException {
+
+ sendChallenge(response);
+ }
+
private void sendChallenge(HttpServletResponse response) throws IOException {
response.setHeader(HttpHeaders.WWW_AUTHENTICATE, NEGOTIATE);
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
http://git-wip-us.apache.org/repos/asf/aurora/blob/a1f7b3da/src/main/java/org/apache/aurora/scheduler/http/api/security/ShiroKerberosPermissiveAuthenticationFilter.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/http/api/security/ShiroKerberosPermissiveAuthenticationFilter.java b/src/main/java/org/apache/aurora/scheduler/http/api/security/ShiroKerberosPermissiveAuthenticationFilter.java
new file mode 100644
index 0000000..054d416
--- /dev/null
+++ b/src/main/java/org/apache/aurora/scheduler/http/api/security/ShiroKerberosPermissiveAuthenticationFilter.java
@@ -0,0 +1,51 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.aurora.scheduler.http.api.security;
+
+import java.io.IOException;
+
+import javax.inject.Inject;
+import javax.inject.Provider;
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.shiro.authz.UnauthenticatedException;
+import org.apache.shiro.subject.Subject;
+
+/**
+ * Allows unauthenticated requests to proceed. It's up to the decorated RPCs to enforce auth
+ * requirement by throwing {@link UnauthenticatedException}.
+ */
+public class ShiroKerberosPermissiveAuthenticationFilter extends ShiroKerberosAuthenticationFilter {
+ @Inject
+ ShiroKerberosPermissiveAuthenticationFilter(Provider<Subject> subjectProvider) {
+ super(subjectProvider);
+ }
+
+ @Override
+ protected void handleUnauthenticated(
+ HttpServletRequest request,
+ HttpServletResponse response,
+ FilterChain chain) throws IOException, ServletException {
+
+ // Incoming request is unauthenticated, but some RPCs might be okay with that.
+ try {
+ chain.doFilter(request, response);
+ } catch (UnauthenticatedException e) {
+ super.handleUnauthenticated(request, response, chain);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/a1f7b3da/src/test/java/org/apache/aurora/scheduler/http/H2ConsoleModuleIT.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/http/H2ConsoleModuleIT.java b/src/test/java/org/apache/aurora/scheduler/http/H2ConsoleModuleIT.java
new file mode 100644
index 0000000..9536fe3
--- /dev/null
+++ b/src/test/java/org/apache/aurora/scheduler/http/H2ConsoleModuleIT.java
@@ -0,0 +1,40 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.aurora.scheduler.http;
+
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+
+import com.google.inject.Module;
+import com.sun.jersey.api.client.ClientResponse;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class H2ConsoleModuleIT extends JettyServerModuleTest {
+ @Override
+ protected Module getChildServletModule() {
+ return new H2ConsoleModule();
+ }
+
+ @Test
+ public void testConsoleBinding() {
+ replayAndStart();
+ ClientResponse response = getRequestBuilder(H2ConsoleModule.H2_PATH + "/")
+ .post(ClientResponse.class);
+ assertEquals(ClientResponse.Status.OK.getStatusCode(), response.getStatus());
+ assertEquals(MediaType.TEXT_HTML, response.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE));
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/a1f7b3da/src/test/java/org/apache/aurora/scheduler/http/api/security/ApiSecurityIT.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/http/api/security/ApiSecurityIT.java b/src/test/java/org/apache/aurora/scheduler/http/api/security/ApiSecurityIT.java
deleted file mode 100644
index 6743d06..0000000
--- a/src/test/java/org/apache/aurora/scheduler/http/api/security/ApiSecurityIT.java
+++ /dev/null
@@ -1,274 +0,0 @@
-/**
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.aurora.scheduler.http.api.security;
-
-import java.io.IOException;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicLong;
-
-import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-import com.google.common.testing.TearDown;
-import com.google.inject.AbstractModule;
-import com.google.inject.Module;
-import com.google.inject.util.Modules;
-import com.twitter.common.stats.StatsProvider;
-
-import org.apache.aurora.gen.AuroraAdmin;
-import org.apache.aurora.gen.Lock;
-import org.apache.aurora.gen.Response;
-import org.apache.aurora.gen.ResponseCode;
-import org.apache.aurora.gen.TaskQuery;
-import org.apache.aurora.scheduler.base.JobKeys;
-import org.apache.aurora.scheduler.base.Query;
-import org.apache.aurora.scheduler.http.JettyServerModuleTest;
-import org.apache.aurora.scheduler.http.api.ApiModule;
-import org.apache.aurora.scheduler.storage.entities.IJobKey;
-import org.apache.aurora.scheduler.thrift.aop.AnnotatedAuroraAdmin;
-import org.apache.aurora.scheduler.thrift.aop.MockDecoratedThrift;
-import org.apache.http.auth.AuthScope;
-import org.apache.http.auth.Credentials;
-import org.apache.http.auth.UsernamePasswordCredentials;
-import org.apache.http.client.CredentialsProvider;
-import org.apache.http.client.HttpClient;
-import org.apache.http.impl.client.BasicCredentialsProvider;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.shiro.config.Ini;
-import org.apache.shiro.realm.text.IniRealm;
-import org.apache.thrift.TException;
-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.junit.Before;
-import org.junit.Test;
-
-import static org.apache.aurora.scheduler.http.api.ApiModule.API_PATH;
-import static org.easymock.EasyMock.anyString;
-import static org.easymock.EasyMock.expect;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-public class ApiSecurityIT extends JettyServerModuleTest {
- private static final Response OK = new Response().setResponseCode(ResponseCode.OK);
-
- private static final UsernamePasswordCredentials ROOT =
- new UsernamePasswordCredentials("root", "secret");
- private static final UsernamePasswordCredentials WFARNER =
- new UsernamePasswordCredentials("wfarner", "password");
- private static final UsernamePasswordCredentials UNPRIVILEGED =
- new UsernamePasswordCredentials("ksweeney", "12345");
- private static final UsernamePasswordCredentials BACKUP_SERVICE =
- new UsernamePasswordCredentials("backupsvc", "s3cret!!1");
- private static final UsernamePasswordCredentials DEPLOY_SERVICE =
- new UsernamePasswordCredentials("deploysvc", "0_0-x_0");
-
- private static final UsernamePasswordCredentials INCORRECT =
- new UsernamePasswordCredentials("root", "wrong");
- private static final UsernamePasswordCredentials NONEXISTENT =
- new UsernamePasswordCredentials("nobody", "12345");
-
- private static final Set<Credentials> INVALID_CREDENTIALS =
- ImmutableSet.<Credentials>of(INCORRECT, NONEXISTENT);
-
- private static final Set<Credentials> VALID_CREDENTIALS =
- ImmutableSet.<Credentials>of(ROOT, WFARNER, UNPRIVILEGED, BACKUP_SERVICE);
-
- private static final IJobKey ADS_STAGING_JOB = JobKeys.from("ads", "staging", "job");
-
- private Ini ini;
- private AnnotatedAuroraAdmin auroraAdmin;
- private StatsProvider statsProvider;
-
- 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";
-
- @Before
- public void setUp() {
- ini = new Ini();
-
- Ini.Section users = ini.addSection(IniRealm.USERS_SECTION_NAME);
- users.put(ROOT.getUserName(), COMMA_JOINER.join(ROOT.getPassword(), ADMIN_ROLE));
- users.put(WFARNER.getUserName(), COMMA_JOINER.join(WFARNER.getPassword(), ENG_ROLE));
- users.put(UNPRIVILEGED.getUserName(), UNPRIVILEGED.getPassword());
- users.put(
- BACKUP_SERVICE.getUserName(),
- COMMA_JOINER.join(BACKUP_SERVICE.getPassword(), BACKUP_ROLE));
- users.put(
- DEPLOY_SERVICE.getUserName(),
- COMMA_JOINER.join(DEPLOY_SERVICE.getPassword(), DEPLOY_ROLE));
-
- Ini.Section roles = ini.addSection(IniRealm.ROLES_SECTION_NAME);
- roles.put(ADMIN_ROLE, "*");
- roles.put(ENG_ROLE, "thrift.AuroraSchedulerManager:*");
- roles.put(BACKUP_ROLE, "thrift.AuroraAdmin:listBackups");
- roles.put(
- DEPLOY_ROLE,
- "thrift.AuroraSchedulerManager:killTasks:"
- + ADS_STAGING_JOB.getRole()
- + ":"
- + ADS_STAGING_JOB.getEnvironment()
- + ":"
- + ADS_STAGING_JOB.getName());
-
- auroraAdmin = createMock(AnnotatedAuroraAdmin.class);
- statsProvider = createMock(StatsProvider.class);
- expect(statsProvider.makeCounter(anyString())).andStubReturn(new AtomicLong());
- }
-
- @Override
- protected Module getChildServletModule() {
- return Modules.combine(
- new ApiModule(),
- new ApiSecurityModule(new IniShiroRealmModule(ini)),
- new AbstractModule() {
- @Override
- protected void configure() {
- MockDecoratedThrift.bindForwardedMock(binder(), auroraAdmin);
- bind(StatsProvider.class).toInstance(statsProvider);
- }
- });
- }
-
- private AuroraAdmin.Client getUnauthenticatedClient() throws TTransportException {
- return getClient(null);
- }
-
- private AuroraAdmin.Client getClient(HttpClient httpClient) throws TTransportException {
- final TTransport httpClientTransport = new THttpClient(
- "http://" + httpServer.getHostText() + ":" + httpServer.getPort() + API_PATH,
- httpClient);
- addTearDown(new TearDown() {
- @Override
- public void tearDown() throws Exception {
- httpClientTransport.close();
- }
- });
- return new AuroraAdmin.Client(new TJSONProtocol(httpClientTransport));
- }
-
- private AuroraAdmin.Client getAuthenticatedClient(Credentials credentials)
- throws TTransportException {
-
- DefaultHttpClient defaultHttpClient = new DefaultHttpClient();
-
- CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
- credentialsProvider.setCredentials(AuthScope.ANY, credentials);
- defaultHttpClient.setCredentialsProvider(credentialsProvider);
-
- return getClient(defaultHttpClient);
- }
-
- @Test
- public void testReadOnlyScheduler() throws TException {
- expect(auroraAdmin.getRoleSummary()).andReturn(OK).times(3);
-
- replayAndStart();
-
- assertEquals(OK, getUnauthenticatedClient().getRoleSummary());
- assertEquals(OK, getAuthenticatedClient(ROOT).getRoleSummary());
- // Incorrect works because the server doesn't challenge for credentials to execute read-only
- // methods.
- assertEquals(OK, getAuthenticatedClient(INCORRECT).getRoleSummary());
- }
-
- private void assertKillTasksFails(AuroraAdmin.Client client) throws TException {
- try {
- client.killTasks(null, null, null);
- fail("killTasks should fail.");
- } catch (TTransportException e) {
- // Expected.
- }
- }
-
- @Test
- public void testAuroraSchedulerManager() throws TException, IOException {
- expect(auroraAdmin.killTasks(null, new Lock().setMessage("1"), null)).andReturn(OK);
- expect(auroraAdmin.killTasks(null, new Lock().setMessage("2"), null)).andReturn(OK);
-
- TaskQuery jobScopedQuery = Query.jobScoped(JobKeys.from("role", "env", "name")).get();
- TaskQuery adsScopedQuery = Query.jobScoped(ADS_STAGING_JOB).get();
- expect(auroraAdmin.killTasks(adsScopedQuery, null, null)).andReturn(OK);
-
- replayAndStart();
-
- assertEquals(OK,
- getAuthenticatedClient(WFARNER).killTasks(null, new Lock().setMessage("1"), null));
- assertEquals(OK,
- getAuthenticatedClient(ROOT).killTasks(null, new Lock().setMessage("2"), null));
- assertEquals(
- ResponseCode.INVALID_REQUEST,
- getAuthenticatedClient(UNPRIVILEGED).killTasks(null, null, null).getResponseCode());
- assertEquals(
- ResponseCode.AUTH_FAILED,
- getAuthenticatedClient(UNPRIVILEGED)
- .killTasks(jobScopedQuery, null, null)
- .getResponseCode());
- assertEquals(
- ResponseCode.INVALID_REQUEST,
- getAuthenticatedClient(BACKUP_SERVICE).killTasks(null, null, null).getResponseCode());
- assertEquals(
- ResponseCode.AUTH_FAILED,
- getAuthenticatedClient(BACKUP_SERVICE)
- .killTasks(jobScopedQuery, null, null)
- .getResponseCode());
- assertEquals(
- ResponseCode.AUTH_FAILED,
- getAuthenticatedClient(DEPLOY_SERVICE)
- .killTasks(jobScopedQuery, null, null)
- .getResponseCode());
- assertEquals(
- OK,
- getAuthenticatedClient(DEPLOY_SERVICE).killTasks(adsScopedQuery, null, null));
-
- assertKillTasksFails(getUnauthenticatedClient());
- assertKillTasksFails(getAuthenticatedClient(INCORRECT));
- assertKillTasksFails(getAuthenticatedClient(NONEXISTENT));
- }
-
- private void assertSnapshotFails(AuroraAdmin.Client client) throws TException {
- try {
- client.snapshot(null);
- fail("snapshot should fail");
- } catch (TTransportException e) {
- // Expected.
- }
- }
-
- @Test
- public void testAuroraAdmin() throws TException {
- expect(auroraAdmin.snapshot(null)).andReturn(OK);
- expect(auroraAdmin.listBackups(null)).andReturn(OK);
-
- replayAndStart();
-
- assertEquals(OK, getAuthenticatedClient(ROOT).snapshot(null));
-
- for (Credentials credentials : INVALID_CREDENTIALS) {
- assertSnapshotFails(getAuthenticatedClient(credentials));
- }
-
- for (Credentials credentials : Sets.difference(VALID_CREDENTIALS, ImmutableSet.of(ROOT))) {
- assertEquals(
- ResponseCode.AUTH_FAILED,
- getAuthenticatedClient(credentials).snapshot(null).getResponseCode());
- }
-
- assertEquals(OK, getAuthenticatedClient(BACKUP_SERVICE).listBackups(null));
- }
-}
http://git-wip-us.apache.org/repos/asf/aurora/blob/a1f7b3da/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
new file mode 100644
index 0000000..53ba949
--- /dev/null
+++ b/src/test/java/org/apache/aurora/scheduler/http/api/security/HttpSecurityIT.java
@@ -0,0 +1,324 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.aurora.scheduler.http.api.security;
+
+import java.io.IOException;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import com.google.common.testing.TearDown;
+import com.google.inject.AbstractModule;
+import com.google.inject.Module;
+import com.google.inject.util.Modules;
+import com.sun.jersey.api.client.ClientResponse;
+import com.twitter.common.stats.StatsProvider;
+
+import org.apache.aurora.gen.AuroraAdmin;
+import org.apache.aurora.gen.Lock;
+import org.apache.aurora.gen.Response;
+import org.apache.aurora.gen.ResponseCode;
+import org.apache.aurora.gen.TaskQuery;
+import org.apache.aurora.scheduler.base.JobKeys;
+import org.apache.aurora.scheduler.base.Query;
+import org.apache.aurora.scheduler.http.H2ConsoleModule;
+import org.apache.aurora.scheduler.http.JettyServerModuleTest;
+import org.apache.aurora.scheduler.http.api.ApiModule;
+import org.apache.aurora.scheduler.storage.entities.IJobKey;
+import org.apache.aurora.scheduler.thrift.aop.AnnotatedAuroraAdmin;
+import org.apache.aurora.scheduler.thrift.aop.MockDecoratedThrift;
+import org.apache.http.HttpResponse;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.CredentialsProvider;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.shiro.config.Ini;
+import org.apache.shiro.realm.text.IniRealm;
+import org.apache.thrift.TException;
+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.junit.Before;
+import org.junit.Test;
+
+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.anyString;
+import static org.easymock.EasyMock.expect;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class HttpSecurityIT extends JettyServerModuleTest {
+ private static final Response OK = new Response().setResponseCode(ResponseCode.OK);
+
+ private static final UsernamePasswordCredentials ROOT =
+ new UsernamePasswordCredentials("root", "secret");
+ private static final UsernamePasswordCredentials WFARNER =
+ new UsernamePasswordCredentials("wfarner", "password");
+ private static final UsernamePasswordCredentials UNPRIVILEGED =
+ new UsernamePasswordCredentials("ksweeney", "12345");
+ private static final UsernamePasswordCredentials BACKUP_SERVICE =
+ new UsernamePasswordCredentials("backupsvc", "s3cret!!1");
+ private static final UsernamePasswordCredentials DEPLOY_SERVICE =
+ new UsernamePasswordCredentials("deploysvc", "0_0-x_0");
+ private static final UsernamePasswordCredentials H2_USER =
+ new UsernamePasswordCredentials("dbuser", "pwd");
+
+ private static final UsernamePasswordCredentials INCORRECT =
+ new UsernamePasswordCredentials("root", "wrong");
+ private static final UsernamePasswordCredentials NONEXISTENT =
+ new UsernamePasswordCredentials("nobody", "12345");
+
+ private static final Set<Credentials> INVALID_CREDENTIALS =
+ ImmutableSet.<Credentials>of(INCORRECT, NONEXISTENT);
+
+ private static final Set<Credentials> VALID_CREDENTIALS =
+ ImmutableSet.<Credentials>of(ROOT, WFARNER, UNPRIVILEGED, BACKUP_SERVICE);
+
+ private static final IJobKey ADS_STAGING_JOB = JobKeys.from("ads", "staging", "job");
+
+ private Ini ini;
+ private AnnotatedAuroraAdmin auroraAdmin;
+ private StatsProvider statsProvider;
+
+ 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";
+
+ @Before
+ public void setUp() {
+ ini = new Ini();
+
+ Ini.Section users = ini.addSection(IniRealm.USERS_SECTION_NAME);
+ users.put(ROOT.getUserName(), COMMA_JOINER.join(ROOT.getPassword(), ADMIN_ROLE));
+ users.put(WFARNER.getUserName(), COMMA_JOINER.join(WFARNER.getPassword(), ENG_ROLE));
+ users.put(UNPRIVILEGED.getUserName(), UNPRIVILEGED.getPassword());
+ users.put(
+ BACKUP_SERVICE.getUserName(),
+ COMMA_JOINER.join(BACKUP_SERVICE.getPassword(), BACKUP_ROLE));
+ users.put(
+ DEPLOY_SERVICE.getUserName(),
+ COMMA_JOINER.join(DEPLOY_SERVICE.getPassword(), DEPLOY_ROLE));
+ users.put(H2_USER.getUserName(), COMMA_JOINER.join(H2_USER.getPassword(), H2_ROLE));
+
+ Ini.Section roles = ini.addSection(IniRealm.ROLES_SECTION_NAME);
+ roles.put(ADMIN_ROLE, "*");
+ roles.put(ENG_ROLE, "thrift.AuroraSchedulerManager:*");
+ roles.put(BACKUP_ROLE, "thrift.AuroraAdmin:listBackups");
+ roles.put(
+ DEPLOY_ROLE,
+ "thrift.AuroraSchedulerManager:killTasks:"
+ + ADS_STAGING_JOB.getRole()
+ + ":"
+ + ADS_STAGING_JOB.getEnvironment()
+ + ":"
+ + ADS_STAGING_JOB.getName());
+ roles.put(H2_ROLE, H2_PERM);
+
+ auroraAdmin = createMock(AnnotatedAuroraAdmin.class);
+ statsProvider = createMock(StatsProvider.class);
+ expect(statsProvider.makeCounter(anyString())).andStubReturn(new AtomicLong());
+ }
+
+ @Override
+ protected Module getChildServletModule() {
+ return Modules.combine(
+ new ApiModule(),
+ new H2ConsoleModule(),
+ new HttpSecurityModule(new IniShiroRealmModule(ini)),
+ new AbstractModule() {
+ @Override
+ protected void configure() {
+ MockDecoratedThrift.bindForwardedMock(binder(), auroraAdmin);
+ bind(StatsProvider.class).toInstance(statsProvider);
+ }
+ });
+ }
+
+ private AuroraAdmin.Client getUnauthenticatedClient() throws TTransportException {
+ return getClient(null);
+ }
+
+ private String formatUrl(String endpoint) {
+ return "http://" + httpServer.getHostText() + ":" + httpServer.getPort() + endpoint;
+ }
+
+ private AuroraAdmin.Client getClient(HttpClient httpClient) throws TTransportException {
+ final TTransport httpClientTransport = new THttpClient(formatUrl(API_PATH), httpClient);
+ addTearDown(new TearDown() {
+ @Override
+ public void tearDown() throws Exception {
+ httpClientTransport.close();
+ }
+ });
+ return new AuroraAdmin.Client(new TJSONProtocol(httpClientTransport));
+ }
+
+ private AuroraAdmin.Client getAuthenticatedClient(Credentials credentials)
+ throws TTransportException {
+
+ DefaultHttpClient defaultHttpClient = new DefaultHttpClient();
+
+ CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
+ credentialsProvider.setCredentials(AuthScope.ANY, credentials);
+ defaultHttpClient.setCredentialsProvider(credentialsProvider);
+
+ return getClient(defaultHttpClient);
+ }
+
+ @Test
+ public void testReadOnlyScheduler() throws TException {
+ expect(auroraAdmin.getRoleSummary()).andReturn(OK).times(3);
+
+ replayAndStart();
+
+ assertEquals(OK, getUnauthenticatedClient().getRoleSummary());
+ assertEquals(OK, getAuthenticatedClient(ROOT).getRoleSummary());
+ // Incorrect works because the server doesn't challenge for credentials to execute read-only
+ // methods.
+ assertEquals(OK, getAuthenticatedClient(INCORRECT).getRoleSummary());
+ }
+
+ private void assertKillTasksFails(AuroraAdmin.Client client) throws TException {
+ try {
+ client.killTasks(null, null, null);
+ fail("killTasks should fail.");
+ } catch (TTransportException e) {
+ // Expected.
+ }
+ }
+
+ @Test
+ public void testAuroraSchedulerManager() throws TException, IOException {
+ expect(auroraAdmin.killTasks(null, new Lock().setMessage("1"), null)).andReturn(OK);
+ expect(auroraAdmin.killTasks(null, new Lock().setMessage("2"), null)).andReturn(OK);
+
+ TaskQuery jobScopedQuery = Query.jobScoped(JobKeys.from("role", "env", "name")).get();
+ TaskQuery adsScopedQuery = Query.jobScoped(ADS_STAGING_JOB).get();
+ expect(auroraAdmin.killTasks(adsScopedQuery, null, null)).andReturn(OK);
+
+ replayAndStart();
+
+ assertEquals(OK,
+ getAuthenticatedClient(WFARNER).killTasks(null, new Lock().setMessage("1"), null));
+ assertEquals(OK,
+ getAuthenticatedClient(ROOT).killTasks(null, new Lock().setMessage("2"), null));
+ assertEquals(
+ ResponseCode.INVALID_REQUEST,
+ getAuthenticatedClient(UNPRIVILEGED).killTasks(null, null, null).getResponseCode());
+ assertEquals(
+ ResponseCode.AUTH_FAILED,
+ getAuthenticatedClient(UNPRIVILEGED)
+ .killTasks(jobScopedQuery, null, null)
+ .getResponseCode());
+ assertEquals(
+ ResponseCode.INVALID_REQUEST,
+ getAuthenticatedClient(BACKUP_SERVICE).killTasks(null, null, null).getResponseCode());
+ assertEquals(
+ ResponseCode.AUTH_FAILED,
+ getAuthenticatedClient(BACKUP_SERVICE)
+ .killTasks(jobScopedQuery, null, null)
+ .getResponseCode());
+ assertEquals(
+ ResponseCode.AUTH_FAILED,
+ getAuthenticatedClient(DEPLOY_SERVICE)
+ .killTasks(jobScopedQuery, null, null)
+ .getResponseCode());
+ assertEquals(
+ OK,
+ getAuthenticatedClient(DEPLOY_SERVICE).killTasks(adsScopedQuery, null, null));
+
+ assertKillTasksFails(getUnauthenticatedClient());
+ assertKillTasksFails(getAuthenticatedClient(INCORRECT));
+ assertKillTasksFails(getAuthenticatedClient(NONEXISTENT));
+ }
+
+ private void assertSnapshotFails(AuroraAdmin.Client client) throws TException {
+ try {
+ client.snapshot(null);
+ fail("snapshot should fail");
+ } catch (TTransportException e) {
+ // Expected.
+ }
+ }
+
+ @Test
+ public void testAuroraAdmin() throws TException {
+ expect(auroraAdmin.snapshot(null)).andReturn(OK);
+ expect(auroraAdmin.listBackups(null)).andReturn(OK);
+
+ replayAndStart();
+
+ assertEquals(OK, getAuthenticatedClient(ROOT).snapshot(null));
+
+ for (Credentials credentials : INVALID_CREDENTIALS) {
+ assertSnapshotFails(getAuthenticatedClient(credentials));
+ }
+
+ for (Credentials credentials : Sets.difference(VALID_CREDENTIALS, ImmutableSet.of(ROOT))) {
+ assertEquals(
+ ResponseCode.AUTH_FAILED,
+ getAuthenticatedClient(credentials).snapshot(null).getResponseCode());
+ }
+
+ assertEquals(OK, getAuthenticatedClient(BACKUP_SERVICE).listBackups(null));
+ }
+
+ private HttpResponse callH2Console(Credentials credentials) throws Exception {
+ DefaultHttpClient defaultHttpClient = new DefaultHttpClient();
+
+ CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
+ credentialsProvider.setCredentials(AuthScope.ANY, credentials);
+ defaultHttpClient.setCredentialsProvider(credentialsProvider);
+ return defaultHttpClient.execute(new HttpPost(formatUrl(H2_PATH + "/")));
+ }
+
+ @Test
+ public void testH2ConsoleUser() throws Exception {
+ replayAndStart();
+
+ assertEquals(
+ ClientResponse.Status.OK.getStatusCode(),
+ callH2Console(H2_USER).getStatusLine().getStatusCode());
+ }
+
+ @Test
+ public void testH2ConsoleAdmin() throws Exception {
+ replayAndStart();
+
+ assertEquals(
+ ClientResponse.Status.OK.getStatusCode(),
+ callH2Console(ROOT).getStatusLine().getStatusCode());
+ }
+
+ @Test
+ public void testH2ConsoleUnauthorized() throws Exception {
+ replayAndStart();
+
+ assertEquals(
+ ClientResponse.Status.UNAUTHORIZED.getStatusCode(),
+ callH2Console(UNPRIVILEGED).getStatusLine().getStatusCode());
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/a1f7b3da/src/test/java/org/apache/aurora/scheduler/http/api/security/ShiroAuthorizingParamInterceptorTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/http/api/security/ShiroAuthorizingParamInterceptorTest.java b/src/test/java/org/apache/aurora/scheduler/http/api/security/ShiroAuthorizingParamInterceptorTest.java
index 33783c8..b2b74c6 100644
--- a/src/test/java/org/apache/aurora/scheduler/http/api/security/ShiroAuthorizingParamInterceptorTest.java
+++ b/src/test/java/org/apache/aurora/scheduler/http/api/security/ShiroAuthorizingParamInterceptorTest.java
@@ -82,7 +82,7 @@ public class ShiroAuthorizingParamInterceptorTest extends EasyMockTest {
MockDecoratedThrift.bindForwardedMock(binder(), thrift);
bindInterceptor(
Matchers.subclassesOf(AnnotatedAuroraAdmin.class),
- ApiSecurityModule.AURORA_SCHEDULER_MANAGER_SERVICE,
+ HttpSecurityModule.AURORA_SCHEDULER_MANAGER_SERVICE,
interceptor);
bind(StatsProvider.class).toInstance(statsProvider);
requestInjection(interceptor);
@@ -95,7 +95,7 @@ public class ShiroAuthorizingParamInterceptorTest extends EasyMockTest {
control.replay();
for (Method method : AnnotatedAuroraAdmin.class.getMethods()) {
- if (ApiSecurityModule.AURORA_SCHEDULER_MANAGER_SERVICE.matches(method)) {
+ if (HttpSecurityModule.AURORA_SCHEDULER_MANAGER_SERVICE.matches(method)) {
interceptor.getAuthorizingParamGetters().getUnchecked(method);
}
}
http://git-wip-us.apache.org/repos/asf/aurora/blob/a1f7b3da/src/test/java/org/apache/aurora/scheduler/http/api/security/ShiroKerberosAuthenticationFilterTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/http/api/security/ShiroKerberosAuthenticationFilterTest.java b/src/test/java/org/apache/aurora/scheduler/http/api/security/ShiroKerberosAuthenticationFilterTest.java
index e335a43..f35dcb8 100644
--- a/src/test/java/org/apache/aurora/scheduler/http/api/security/ShiroKerberosAuthenticationFilterTest.java
+++ b/src/test/java/org/apache/aurora/scheduler/http/api/security/ShiroKerberosAuthenticationFilterTest.java
@@ -29,7 +29,6 @@ import com.sun.jersey.api.client.ClientResponse;
import org.apache.aurora.scheduler.http.JettyServerModuleTest;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
-import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.subject.Subject;
import org.junit.Before;
import org.junit.Test;
@@ -79,13 +78,14 @@ public class ShiroKerberosAuthenticationFilterTest extends JettyServerModuleTest
}
@Test
- public void testPermitsUnauthenticated() throws ServletException, IOException {
- mockServlet.service(anyObject(HttpServletRequest.class), anyObject(HttpServletResponse.class));
-
+ public void testDoesNotPermitUnauthenticated() throws ServletException, IOException {
replayAndStart();
ClientResponse clientResponse = getRequestBuilder(PATH).get(ClientResponse.class);
- assertEquals(HttpServletResponse.SC_OK, clientResponse.getStatus());
+ assertEquals(HttpServletResponse.SC_UNAUTHORIZED, clientResponse.getStatus());
+ assertEquals(
+ ShiroKerberosAuthenticationFilter.NEGOTIATE,
+ clientResponse.getHeaders().getFirst(HttpHeaders.WWW_AUTHENTICATE));
}
@Test
@@ -130,19 +130,4 @@ public class ShiroKerberosAuthenticationFilterTest extends JettyServerModuleTest
assertEquals(HttpServletResponse.SC_OK, clientResponse.getStatus());
}
-
- @Test
- public void testInterceptsUnauthenticatedException() throws ServletException, IOException {
- mockServlet.service(anyObject(HttpServletRequest.class), anyObject(HttpServletResponse.class));
- expectLastCall().andThrow(new UnauthenticatedException());
-
- replayAndStart();
-
- ClientResponse clientResponse = getRequestBuilder(PATH).get(ClientResponse.class);
-
- assertEquals(HttpServletResponse.SC_UNAUTHORIZED, clientResponse.getStatus());
- assertEquals(
- ShiroKerberosAuthenticationFilter.NEGOTIATE,
- clientResponse.getHeaders().getFirst(HttpHeaders.WWW_AUTHENTICATE));
- }
}
http://git-wip-us.apache.org/repos/asf/aurora/blob/a1f7b3da/src/test/java/org/apache/aurora/scheduler/http/api/security/ShiroKerberosPermissiveAuthenticationFilterTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/http/api/security/ShiroKerberosPermissiveAuthenticationFilterTest.java b/src/test/java/org/apache/aurora/scheduler/http/api/security/ShiroKerberosPermissiveAuthenticationFilterTest.java
new file mode 100644
index 0000000..6eb82b5
--- /dev/null
+++ b/src/test/java/org/apache/aurora/scheduler/http/api/security/ShiroKerberosPermissiveAuthenticationFilterTest.java
@@ -0,0 +1,98 @@
+/**
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.aurora.scheduler.http.api.security;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.HttpHeaders;
+
+import com.google.inject.Module;
+import com.google.inject.servlet.ServletModule;
+import com.google.inject.util.Providers;
+import com.sun.jersey.api.client.ClientResponse;
+
+import org.apache.aurora.scheduler.http.JettyServerModuleTest;
+import org.apache.shiro.authz.UnauthenticatedException;
+import org.apache.shiro.subject.Subject;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.easymock.EasyMock.anyObject;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.junit.Assert.assertEquals;
+
+public class ShiroKerberosPermissiveAuthenticationFilterTest extends JettyServerModuleTest {
+ private static final String PATH = "/test";
+
+ private Subject subject;
+ private HttpServlet mockServlet;
+
+ private ShiroKerberosPermissiveAuthenticationFilter filter;
+
+ @Before
+ public void setUp() {
+ subject = createMock(Subject.class);
+ mockServlet = createMock(HttpServlet.class);
+
+ filter = new ShiroKerberosPermissiveAuthenticationFilter(Providers.of(subject));
+ }
+
+ @Override
+ public Module getChildServletModule() {
+ return new ServletModule() {
+ @Override
+ protected void configureServlets() {
+ filter(PATH).through(filter);
+ serve(PATH).with(new HttpServlet() {
+ @Override
+ protected void service(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+
+ mockServlet.service(req, resp);
+ resp.setStatus(HttpServletResponse.SC_OK);
+ }
+ });
+ }
+ };
+ }
+
+ @Test
+ public void testPermitsUnauthenticated() throws ServletException, IOException {
+ mockServlet.service(anyObject(HttpServletRequest.class), anyObject(HttpServletResponse.class));
+
+ replayAndStart();
+
+ ClientResponse clientResponse = getRequestBuilder(PATH).get(ClientResponse.class);
+ assertEquals(HttpServletResponse.SC_OK, clientResponse.getStatus());
+ }
+
+ @Test
+ public void testInterceptsUnauthenticatedException() throws ServletException, IOException {
+ mockServlet.service(anyObject(HttpServletRequest.class), anyObject(HttpServletResponse.class));
+ expectLastCall().andThrow(new UnauthenticatedException());
+
+ replayAndStart();
+
+ ClientResponse clientResponse = getRequestBuilder(PATH).get(ClientResponse.class);
+
+ assertEquals(HttpServletResponse.SC_UNAUTHORIZED, clientResponse.getStatus());
+ assertEquals(
+ ShiroKerberosAuthenticationFilter.NEGOTIATE,
+ clientResponse.getHeaders().getFirst(HttpHeaders.WWW_AUTHENTICATE));
+ }
+}
http://git-wip-us.apache.org/repos/asf/aurora/blob/a1f7b3da/src/test/sh/org/apache/aurora/e2e/test_kerberos_end_to_end.sh
----------------------------------------------------------------------
diff --git a/src/test/sh/org/apache/aurora/e2e/test_kerberos_end_to_end.sh b/src/test/sh/org/apache/aurora/e2e/test_kerberos_end_to_end.sh
index 6e9e3b2..4d6043a 100755
--- a/src/test/sh/org/apache/aurora/e2e/test_kerberos_end_to_end.sh
+++ b/src/test/sh/org/apache/aurora/e2e/test_kerberos_end_to_end.sh
@@ -61,6 +61,16 @@ function snapshot_as {
kdestroy
}
+readonly H2_RESPONSE_OUTFILE="h2console-response.%s.json"
+function h2console_as {
+ local principal=$1
+ kinit -k -t "testdir/${principal}.keytab" $principal
+ curl -u : --negotiate -w '%{http_code}\n' \
+ -o $(printf $H2_RESPONSE_OUTFILE $principal) \
+ -s 'http://192.168.33.7:8081/h2console/'
+ kdestroy
+}
+
function setup {
cat >> $KRB5_CONFIG <<EOF
[domain_realm]
@@ -103,6 +113,15 @@ function test_snapshot {
grep -qv 'lacks permission' snapshot-response.root.json
}
+function test_h2console {
+ h2console_as vagrant
+ grep -q 'Error 401 Unauthorized' h2console-response.vagrant.json
+ h2console_as unpriv
+ grep -q 'Error 401 Unauthorized' h2console-response.unpriv.json
+ h2console_as root
+ grep -q 'Welcome to H2' h2console-response.root.json
+}
+
function test_clients {
sudo cp /vagrant/examples/vagrant/clusters_kerberos.json /etc/aurora/clusters.json
@@ -129,6 +148,7 @@ function main {
setup
test_snapshot
test_clients
+ test_h2console
set +x
echo
echo '*** OK (All tests passed) ***'