mirror of
https://github.com/google/nomulus.git
synced 2025-05-06 06:57:50 +02:00
Make first pass at new OAuth-aware server authentication framework
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=147081745
This commit is contained in:
parent
cb215adac3
commit
c41f5bb31c
17 changed files with 327 additions and 22 deletions
|
@ -23,6 +23,7 @@ import com.google.common.base.Optional;
|
||||||
import com.google.common.base.Supplier;
|
import com.google.common.base.Supplier;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.net.HostAndPort;
|
import com.google.common.net.HostAndPort;
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
|
@ -904,6 +905,44 @@ public final class RegistryConfig {
|
||||||
return CONFIG_SETTINGS.get();
|
return CONFIG_SETTINGS.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the OAuth scopes to check for on access tokens.
|
||||||
|
*
|
||||||
|
* <p>This list should be a superset of the required OAuth scope set provided below.
|
||||||
|
*
|
||||||
|
* <p>If we feel the need, we could define additional fixed scopes, similar to the Java remote
|
||||||
|
* API, which requires at least one of:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>https://www.googleapis.com/auth/appengine.apis</li>
|
||||||
|
* <li>https://www.googleapis.com/auth/cloud-platform</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
@Provides
|
||||||
|
@Config("availableOauthScopes")
|
||||||
|
public static ImmutableSet<String> provideAvailableOauthScopes() {
|
||||||
|
return ImmutableSet.of("https://www.googleapis.com/auth/userinfo.email");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the required OAuth scopes for simply authenticating.
|
||||||
|
*
|
||||||
|
* <p>This set contains the scopes which must be present to authenticate a user. It should be a
|
||||||
|
* subset of the scopes we request from the OAuth interface, provided above.
|
||||||
|
*/
|
||||||
|
@Provides
|
||||||
|
@Config("requiredOauthScopes")
|
||||||
|
public static ImmutableSet<String> provideRequiredOauthScopes() {
|
||||||
|
return ImmutableSet.of("https://www.googleapis.com/auth/userinfo.email");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Provides the allowed OAuth client IDs (could be multibinding). */
|
||||||
|
@Provides
|
||||||
|
@Config("allowedOauthClientIds")
|
||||||
|
public static ImmutableSet<String> provideAllowedOauthClientIds() {
|
||||||
|
return ImmutableSet.of("PUT.YOUR.PROXY.CLIENT.ID.HERE", "PUT.YOUR.REGTOOL.CLIENT.ID.HERE");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the help path for the RDAP terms of service.
|
* Returns the help path for the RDAP terms of service.
|
||||||
*
|
*
|
||||||
|
|
|
@ -31,6 +31,7 @@ java_library(
|
||||||
"//java/google/registry/rde/imports",
|
"//java/google/registry/rde/imports",
|
||||||
"//java/google/registry/request",
|
"//java/google/registry/request",
|
||||||
"//java/google/registry/request:modules",
|
"//java/google/registry/request:modules",
|
||||||
|
"//java/google/registry/request/auth",
|
||||||
"//java/google/registry/tmch",
|
"//java/google/registry/tmch",
|
||||||
"//java/google/registry/util",
|
"//java/google/registry/util",
|
||||||
"@com_google_appengine_api_1_0_sdk",
|
"@com_google_appengine_api_1_0_sdk",
|
||||||
|
|
|
@ -39,6 +39,7 @@ import google.registry.request.Modules.URLFetchServiceModule;
|
||||||
import google.registry.request.Modules.UrlFetchTransportModule;
|
import google.registry.request.Modules.UrlFetchTransportModule;
|
||||||
import google.registry.request.Modules.UseAppIdentityCredentialForGoogleApisModule;
|
import google.registry.request.Modules.UseAppIdentityCredentialForGoogleApisModule;
|
||||||
import google.registry.request.Modules.UserServiceModule;
|
import google.registry.request.Modules.UserServiceModule;
|
||||||
|
import google.registry.request.auth.AuthModule;
|
||||||
import google.registry.util.SystemClock.SystemClockModule;
|
import google.registry.util.SystemClock.SystemClockModule;
|
||||||
import google.registry.util.SystemSleeper.SystemSleeperModule;
|
import google.registry.util.SystemSleeper.SystemSleeperModule;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
@ -48,6 +49,7 @@ import javax.inject.Singleton;
|
||||||
@Component(
|
@Component(
|
||||||
modules = {
|
modules = {
|
||||||
AppIdentityCredentialModule.class,
|
AppIdentityCredentialModule.class,
|
||||||
|
AuthModule.class,
|
||||||
BackendRequestComponentModule.class,
|
BackendRequestComponentModule.class,
|
||||||
BigqueryModule.class,
|
BigqueryModule.class,
|
||||||
ConfigModule.class,
|
ConfigModule.class,
|
||||||
|
|
|
@ -16,6 +16,7 @@ package google.registry.module.backend;
|
||||||
|
|
||||||
import com.google.appengine.api.users.UserService;
|
import com.google.appengine.api.users.UserService;
|
||||||
import google.registry.request.RequestHandler;
|
import google.registry.request.RequestHandler;
|
||||||
|
import google.registry.request.auth.RequestAuthenticator;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
|
|
||||||
|
@ -25,7 +26,8 @@ public class BackendRequestHandler
|
||||||
|
|
||||||
@Inject BackendRequestHandler(
|
@Inject BackendRequestHandler(
|
||||||
Provider<BackendRequestComponent.Builder> componentBuilderProvider,
|
Provider<BackendRequestComponent.Builder> componentBuilderProvider,
|
||||||
UserService userService) {
|
UserService userService,
|
||||||
super(componentBuilderProvider, userService);
|
RequestAuthenticator requestAuthenticator) {
|
||||||
|
super(componentBuilderProvider, userService, requestAuthenticator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ java_library(
|
||||||
"//java/google/registry/rdap",
|
"//java/google/registry/rdap",
|
||||||
"//java/google/registry/request",
|
"//java/google/registry/request",
|
||||||
"//java/google/registry/request:modules",
|
"//java/google/registry/request:modules",
|
||||||
|
"//java/google/registry/request/auth",
|
||||||
"//java/google/registry/ui",
|
"//java/google/registry/ui",
|
||||||
"//java/google/registry/ui/server/registrar",
|
"//java/google/registry/ui/server/registrar",
|
||||||
"//java/google/registry/util",
|
"//java/google/registry/util",
|
||||||
|
|
|
@ -29,6 +29,7 @@ import google.registry.request.Modules.ModulesServiceModule;
|
||||||
import google.registry.request.Modules.UrlFetchTransportModule;
|
import google.registry.request.Modules.UrlFetchTransportModule;
|
||||||
import google.registry.request.Modules.UseAppIdentityCredentialForGoogleApisModule;
|
import google.registry.request.Modules.UseAppIdentityCredentialForGoogleApisModule;
|
||||||
import google.registry.request.Modules.UserServiceModule;
|
import google.registry.request.Modules.UserServiceModule;
|
||||||
|
import google.registry.request.auth.AuthModule;
|
||||||
import google.registry.ui.ConsoleConfigModule;
|
import google.registry.ui.ConsoleConfigModule;
|
||||||
import google.registry.util.SystemClock.SystemClockModule;
|
import google.registry.util.SystemClock.SystemClockModule;
|
||||||
import google.registry.util.SystemSleeper.SystemSleeperModule;
|
import google.registry.util.SystemSleeper.SystemSleeperModule;
|
||||||
|
@ -39,6 +40,7 @@ import javax.inject.Singleton;
|
||||||
@Component(
|
@Component(
|
||||||
modules = {
|
modules = {
|
||||||
AppIdentityCredentialModule.class,
|
AppIdentityCredentialModule.class,
|
||||||
|
AuthModule.class,
|
||||||
BraintreeModule.class,
|
BraintreeModule.class,
|
||||||
ConfigModule.class,
|
ConfigModule.class,
|
||||||
ConsoleConfigModule.class,
|
ConsoleConfigModule.class,
|
||||||
|
|
|
@ -16,6 +16,7 @@ package google.registry.module.frontend;
|
||||||
|
|
||||||
import com.google.appengine.api.users.UserService;
|
import com.google.appengine.api.users.UserService;
|
||||||
import google.registry.request.RequestHandler;
|
import google.registry.request.RequestHandler;
|
||||||
|
import google.registry.request.auth.RequestAuthenticator;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
|
|
||||||
|
@ -25,7 +26,8 @@ public class FrontendRequestHandler
|
||||||
|
|
||||||
@Inject FrontendRequestHandler(
|
@Inject FrontendRequestHandler(
|
||||||
Provider<FrontendRequestComponent.Builder> componentBuilderProvider,
|
Provider<FrontendRequestComponent.Builder> componentBuilderProvider,
|
||||||
UserService userService) {
|
UserService userService,
|
||||||
super(componentBuilderProvider, userService);
|
RequestAuthenticator requestAuthenticator) {
|
||||||
|
super(componentBuilderProvider, userService, requestAuthenticator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ java_library(
|
||||||
"//java/google/registry/monitoring/whitebox",
|
"//java/google/registry/monitoring/whitebox",
|
||||||
"//java/google/registry/request",
|
"//java/google/registry/request",
|
||||||
"//java/google/registry/request:modules",
|
"//java/google/registry/request:modules",
|
||||||
|
"//java/google/registry/request/auth",
|
||||||
"//java/google/registry/tools/server",
|
"//java/google/registry/tools/server",
|
||||||
"//java/google/registry/tools/server/javascrap",
|
"//java/google/registry/tools/server/javascrap",
|
||||||
"//java/google/registry/util",
|
"//java/google/registry/util",
|
||||||
|
|
|
@ -33,6 +33,7 @@ import google.registry.request.Modules.ModulesServiceModule;
|
||||||
import google.registry.request.Modules.UrlFetchTransportModule;
|
import google.registry.request.Modules.UrlFetchTransportModule;
|
||||||
import google.registry.request.Modules.UseAppIdentityCredentialForGoogleApisModule;
|
import google.registry.request.Modules.UseAppIdentityCredentialForGoogleApisModule;
|
||||||
import google.registry.request.Modules.UserServiceModule;
|
import google.registry.request.Modules.UserServiceModule;
|
||||||
|
import google.registry.request.auth.AuthModule;
|
||||||
import google.registry.util.SystemClock.SystemClockModule;
|
import google.registry.util.SystemClock.SystemClockModule;
|
||||||
import google.registry.util.SystemSleeper.SystemSleeperModule;
|
import google.registry.util.SystemSleeper.SystemSleeperModule;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
@ -42,6 +43,7 @@ import javax.inject.Singleton;
|
||||||
@Component(
|
@Component(
|
||||||
modules = {
|
modules = {
|
||||||
AppIdentityCredentialModule.class,
|
AppIdentityCredentialModule.class,
|
||||||
|
AuthModule.class,
|
||||||
ConfigModule.class,
|
ConfigModule.class,
|
||||||
CustomLogicFactoryModule.class,
|
CustomLogicFactoryModule.class,
|
||||||
DatastoreServiceModule.class,
|
DatastoreServiceModule.class,
|
||||||
|
|
|
@ -16,6 +16,7 @@ package google.registry.module.tools;
|
||||||
|
|
||||||
import com.google.appengine.api.users.UserService;
|
import com.google.appengine.api.users.UserService;
|
||||||
import google.registry.request.RequestHandler;
|
import google.registry.request.RequestHandler;
|
||||||
|
import google.registry.request.auth.RequestAuthenticator;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
|
|
||||||
|
@ -25,7 +26,8 @@ public class ToolsRequestHandler
|
||||||
|
|
||||||
@Inject ToolsRequestHandler(
|
@Inject ToolsRequestHandler(
|
||||||
Provider<ToolsRequestComponent.Builder> componentBuilderProvider,
|
Provider<ToolsRequestComponent.Builder> componentBuilderProvider,
|
||||||
UserService userService) {
|
UserService userService,
|
||||||
super(componentBuilderProvider, userService);
|
RequestAuthenticator requestAuthenticator) {
|
||||||
|
super(componentBuilderProvider, userService, requestAuthenticator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
package google.registry.request;
|
package google.registry.request;
|
||||||
|
|
||||||
|
import google.registry.request.auth.Auth;
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
@ -61,4 +62,7 @@ public @interface Action {
|
||||||
* {@code web.xml} with {@code <role-name>*</role-name>}.
|
* {@code web.xml} with {@code <role-name>*</role-name>}.
|
||||||
*/
|
*/
|
||||||
boolean requireLogin() default false;
|
boolean requireLogin() default false;
|
||||||
|
|
||||||
|
/** Authentication settings. */
|
||||||
|
Auth auth() default @Auth;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ java_library(
|
||||||
exclude = ["Modules.java"],
|
exclude = ["Modules.java"],
|
||||||
),
|
),
|
||||||
deps = [
|
deps = [
|
||||||
|
"//java/google/registry/request/auth",
|
||||||
"//java/google/registry/security",
|
"//java/google/registry/security",
|
||||||
"//java/google/registry/util",
|
"//java/google/registry/util",
|
||||||
"@com_google_appengine_api_1_0_sdk",
|
"@com_google_appengine_api_1_0_sdk",
|
||||||
|
|
|
@ -27,6 +27,8 @@ import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
|
||||||
|
|
||||||
import com.google.appengine.api.users.UserService;
|
import com.google.appengine.api.users.UserService;
|
||||||
import com.google.common.base.Optional;
|
import com.google.common.base.Optional;
|
||||||
|
import google.registry.request.auth.AuthResult;
|
||||||
|
import google.registry.request.auth.RequestAuthenticator;
|
||||||
import google.registry.util.FormattingLogger;
|
import google.registry.util.FormattingLogger;
|
||||||
import google.registry.util.TypeUtils.TypeInstantiator;
|
import google.registry.util.TypeUtils.TypeInstantiator;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -77,6 +79,7 @@ public class RequestHandler<C, B extends RequestComponentBuilder<C, B>> {
|
||||||
private final Router router;
|
private final Router router;
|
||||||
private final Provider<B> requestComponentBuilderProvider;
|
private final Provider<B> requestComponentBuilderProvider;
|
||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
|
private final RequestAuthenticator requestAuthenticator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for subclasses to create a new request handler for a specific request component.
|
* Constructor for subclasses to create a new request handler for a specific request component.
|
||||||
|
@ -89,22 +92,33 @@ public class RequestHandler<C, B extends RequestComponentBuilder<C, B>> {
|
||||||
* be used to construct new instances of the request component (with the required
|
* be used to construct new instances of the request component (with the required
|
||||||
* request-derived modules provided by this class)
|
* request-derived modules provided by this class)
|
||||||
* @param userService an instance of the App Engine UserService API
|
* @param userService an instance of the App Engine UserService API
|
||||||
|
* @param requestAuthenticator an instance of the RequestAuthenticator class
|
||||||
*/
|
*/
|
||||||
protected RequestHandler(Provider<B> requestComponentBuilderProvider, UserService userService) {
|
protected RequestHandler(
|
||||||
this(null, requestComponentBuilderProvider, userService);
|
Provider<B> requestComponentBuilderProvider,
|
||||||
|
UserService userService,
|
||||||
|
RequestAuthenticator requestAuthenticator) {
|
||||||
|
this(null, requestComponentBuilderProvider, userService, requestAuthenticator);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a new RequestHandler with an explicit component class for test purposes. */
|
/** Creates a new RequestHandler with an explicit component class for test purposes. */
|
||||||
public static <C, B extends RequestComponentBuilder<C, B>> RequestHandler<C, B> createForTest(
|
public static <C, B extends RequestComponentBuilder<C, B>> RequestHandler<C, B> createForTest(
|
||||||
Class<C> component, Provider<B> requestComponentBuilderProvider, UserService userService) {
|
Class<C> component,
|
||||||
|
Provider<B> requestComponentBuilderProvider,
|
||||||
|
UserService userService,
|
||||||
|
RequestAuthenticator requestAuthenticator) {
|
||||||
return new RequestHandler<>(
|
return new RequestHandler<>(
|
||||||
checkNotNull(component), requestComponentBuilderProvider, userService);
|
checkNotNull(component),
|
||||||
|
requestComponentBuilderProvider,
|
||||||
|
userService,
|
||||||
|
requestAuthenticator);
|
||||||
}
|
}
|
||||||
|
|
||||||
private RequestHandler(
|
private RequestHandler(
|
||||||
@Nullable Class<C> component,
|
@Nullable Class<C> component,
|
||||||
Provider<B> requestComponentBuilderProvider,
|
Provider<B> requestComponentBuilderProvider,
|
||||||
UserService userService) {
|
UserService userService,
|
||||||
|
RequestAuthenticator requestAuthenticator) {
|
||||||
// If the component class isn't explicitly provided, infer it from the class's own typing.
|
// If the component class isn't explicitly provided, infer it from the class's own typing.
|
||||||
// This is safe only for use by subclasses of RequestHandler where the generic parameter is
|
// This is safe only for use by subclasses of RequestHandler where the generic parameter is
|
||||||
// preserved at runtime, so only expose that option via the protected constructor.
|
// preserved at runtime, so only expose that option via the protected constructor.
|
||||||
|
@ -112,6 +126,7 @@ public class RequestHandler<C, B extends RequestComponentBuilder<C, B>> {
|
||||||
component != null ? component : new TypeInstantiator<C>(getClass()){}.getExactType());
|
component != null ? component : new TypeInstantiator<C>(getClass()){}.getExactType());
|
||||||
this.requestComponentBuilderProvider = checkNotNull(requestComponentBuilderProvider);
|
this.requestComponentBuilderProvider = checkNotNull(requestComponentBuilderProvider);
|
||||||
this.userService = checkNotNull(userService);
|
this.userService = checkNotNull(userService);
|
||||||
|
this.requestAuthenticator = checkNotNull(requestAuthenticator);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Runs the appropriate action for a servlet request. */
|
/** Runs the appropriate action for a servlet request. */
|
||||||
|
@ -152,9 +167,16 @@ public class RequestHandler<C, B extends RequestComponentBuilder<C, B>> {
|
||||||
rsp.sendError(SC_FORBIDDEN, "Invalid " + X_CSRF_TOKEN);
|
rsp.sendError(SC_FORBIDDEN, "Invalid " + X_CSRF_TOKEN);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Optional<AuthResult> authResult =
|
||||||
|
requestAuthenticator.authorize(route.get().action().auth(), req);
|
||||||
|
if (!authResult.isPresent()) {
|
||||||
|
rsp.sendError(SC_FORBIDDEN);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Build a new request component using any modules we've constructed by this point.
|
// Build a new request component using any modules we've constructed by this point.
|
||||||
C component = requestComponentBuilderProvider.get()
|
C component = requestComponentBuilderProvider.get()
|
||||||
.requestModule(new RequestModule(req, rsp))
|
.requestModule(new RequestModule(req, rsp, authResult.get()))
|
||||||
.build();
|
.build();
|
||||||
// Apply the selected Route to the component to produce an Action instance, and run it.
|
// Apply the selected Route to the component to produce an Action instance, and run it.
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -26,6 +26,7 @@ import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
import google.registry.request.HttpException.BadRequestException;
|
import google.registry.request.HttpException.BadRequestException;
|
||||||
import google.registry.request.HttpException.UnsupportedMediaTypeException;
|
import google.registry.request.HttpException.UnsupportedMediaTypeException;
|
||||||
|
import google.registry.request.auth.AuthResult;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
@ -40,10 +41,18 @@ public final class RequestModule {
|
||||||
|
|
||||||
private final HttpServletRequest req;
|
private final HttpServletRequest req;
|
||||||
private final HttpServletResponse rsp;
|
private final HttpServletResponse rsp;
|
||||||
|
private final AuthResult authResult;
|
||||||
|
|
||||||
public RequestModule(HttpServletRequest req, HttpServletResponse rsp) {
|
public RequestModule(
|
||||||
|
HttpServletRequest req, HttpServletResponse rsp) {
|
||||||
|
this(req, rsp, AuthResult.NOT_AUTHENTICATED);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RequestModule(
|
||||||
|
HttpServletRequest req, HttpServletResponse rsp, AuthResult authResult) {
|
||||||
this.req = req;
|
this.req = req;
|
||||||
this.rsp = rsp;
|
this.rsp = rsp;
|
||||||
|
this.authResult = authResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
@ -66,6 +75,11 @@ public final class RequestModule {
|
||||||
return rsp;
|
return rsp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
AuthResult provideAuthResult() {
|
||||||
|
return authResult;
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@RequestPath
|
@RequestPath
|
||||||
static String provideRequestPath(HttpServletRequest req) {
|
static String provideRequestPath(HttpServletRequest req) {
|
||||||
|
|
|
@ -12,14 +12,15 @@ java_library(
|
||||||
srcs = glob(["*.java"]),
|
srcs = glob(["*.java"]),
|
||||||
deps = [
|
deps = [
|
||||||
"//java/google/registry/request",
|
"//java/google/registry/request",
|
||||||
|
"//java/google/registry/request/auth",
|
||||||
"//java/google/registry/security",
|
"//java/google/registry/security",
|
||||||
"//javatests/google/registry/security",
|
|
||||||
"//javatests/google/registry/testing",
|
"//javatests/google/registry/testing",
|
||||||
"@com_google_appengine_api_1_0_sdk//:testonly",
|
"@com_google_appengine_api_1_0_sdk//:testonly",
|
||||||
"@com_google_guava",
|
"@com_google_guava",
|
||||||
"@com_google_guava_testlib",
|
"@com_google_guava_testlib",
|
||||||
"@com_google_truth",
|
"@com_google_truth",
|
||||||
"@com_googlecode_json_simple",
|
"@com_googlecode_json_simple",
|
||||||
|
"@javax_inject",
|
||||||
"@javax_servlet_api",
|
"@javax_servlet_api",
|
||||||
"@joda_time",
|
"@joda_time",
|
||||||
"@junit",
|
"@junit",
|
||||||
|
|
|
@ -23,15 +23,27 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import com.google.appengine.api.oauth.OAuthServiceFactory;
|
||||||
|
import com.google.appengine.api.users.User;
|
||||||
import com.google.appengine.api.users.UserService;
|
import com.google.appengine.api.users.UserService;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.testing.NullPointerTester;
|
import com.google.common.testing.NullPointerTester;
|
||||||
import google.registry.request.HttpException.ServiceUnavailableException;
|
import google.registry.request.HttpException.ServiceUnavailableException;
|
||||||
|
import google.registry.request.auth.AppEngineInternalAuthenticationMechanism;
|
||||||
|
import google.registry.request.auth.Auth;
|
||||||
|
import google.registry.request.auth.AuthLevel;
|
||||||
|
import google.registry.request.auth.AuthResult;
|
||||||
|
import google.registry.request.auth.LegacyAuthenticationMechanism;
|
||||||
|
import google.registry.request.auth.OAuthAuthenticationMechanism;
|
||||||
|
import google.registry.request.auth.RequestAuthenticator;
|
||||||
import google.registry.testing.AppEngineRule;
|
import google.registry.testing.AppEngineRule;
|
||||||
import google.registry.testing.InjectRule;
|
import google.registry.testing.InjectRule;
|
||||||
import google.registry.testing.Providers;
|
import google.registry.testing.Providers;
|
||||||
import google.registry.testing.UserInfo;
|
import google.registry.testing.UserInfo;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
|
import javax.inject.Inject;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
|
@ -57,31 +69,31 @@ public final class RequestHandlerTest {
|
||||||
public final InjectRule inject = new InjectRule();
|
public final InjectRule inject = new InjectRule();
|
||||||
|
|
||||||
@Action(path = "/bumblebee", method = {GET, POST}, isPrefix = true)
|
@Action(path = "/bumblebee", method = {GET, POST}, isPrefix = true)
|
||||||
public class BumblebeeTask implements Runnable {
|
public static class BumblebeeTask implements Runnable {
|
||||||
@Override
|
@Override
|
||||||
public void run() {}
|
public void run() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Action(path = "/sloth", method = POST, automaticallyPrintOk = true)
|
@Action(path = "/sloth", method = POST, automaticallyPrintOk = true)
|
||||||
public class SlothTask implements Runnable {
|
public static class SlothTask implements Runnable {
|
||||||
@Override
|
@Override
|
||||||
public void run() {}
|
public void run() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Action(path = "/safe-sloth", method = {GET, POST}, xsrfProtection = true, xsrfScope = "vampire")
|
@Action(path = "/safe-sloth", method = {GET, POST}, xsrfProtection = true, xsrfScope = "vampire")
|
||||||
public class SafeSlothTask implements Runnable {
|
public static class SafeSlothTask implements Runnable {
|
||||||
@Override
|
@Override
|
||||||
public void run() {}
|
public void run() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Action(path = "/users-only", method = GET, requireLogin = true)
|
@Action(path = "/users-only", method = GET, requireLogin = true)
|
||||||
public class UsersOnlyAction implements Runnable {
|
public static class UsersOnlyAction implements Runnable {
|
||||||
@Override
|
@Override
|
||||||
public void run() {}
|
public void run() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Action(path = "/fail")
|
@Action(path = "/fail")
|
||||||
public final class FailTask implements Runnable {
|
public static final class FailTask implements Runnable {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
throw new ServiceUnavailableException("Set sail for fail");
|
throw new ServiceUnavailableException("Set sail for fail");
|
||||||
|
@ -89,7 +101,7 @@ public final class RequestHandlerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Action(path = "/failAtConstruction")
|
@Action(path = "/failAtConstruction")
|
||||||
public final class FailAtConstructionTask implements Runnable {
|
public static final class FailAtConstructionTask implements Runnable {
|
||||||
public FailAtConstructionTask() {
|
public FailAtConstructionTask() {
|
||||||
throw new ServiceUnavailableException("Fail at construction");
|
throw new ServiceUnavailableException("Fail at construction");
|
||||||
}
|
}
|
||||||
|
@ -100,7 +112,57 @@ public final class RequestHandlerTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class AuthBase implements Runnable {
|
||||||
|
private final AuthResult authResult;
|
||||||
|
|
||||||
|
AuthBase(AuthResult authResult) {
|
||||||
|
this.authResult = authResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
injectedAuthResult = authResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Action(
|
||||||
|
path = "/auth/none",
|
||||||
|
auth = @Auth(
|
||||||
|
methods = {Auth.AuthMethod.INTERNAL},
|
||||||
|
minimumLevel = AuthLevel.NONE,
|
||||||
|
userPolicy = Auth.UserPolicy.IGNORED),
|
||||||
|
method = Action.Method.GET)
|
||||||
|
public class AuthNoneAction extends AuthBase {
|
||||||
|
@Inject AuthNoneAction(AuthResult authResult) {
|
||||||
|
super(authResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Action(
|
||||||
|
path = "/auth/adminUserAnyMethod",
|
||||||
|
auth = @Auth(
|
||||||
|
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API, Auth.AuthMethod.LEGACY},
|
||||||
|
minimumLevel = AuthLevel.USER,
|
||||||
|
userPolicy = Auth.UserPolicy.ADMIN),
|
||||||
|
method = Action.Method.GET)
|
||||||
|
public class AuthAdminUserAnyMethodAction extends AuthBase {
|
||||||
|
@Inject AuthAdminUserAnyMethodAction(AuthResult authResult) {
|
||||||
|
super(authResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class Component {
|
public class Component {
|
||||||
|
|
||||||
|
private RequestModule requestModule = null;
|
||||||
|
|
||||||
|
public RequestModule getRequestModule() {
|
||||||
|
return requestModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRequestModule(RequestModule requestModule) {
|
||||||
|
this.requestModule = requestModule;
|
||||||
|
}
|
||||||
|
|
||||||
public BumblebeeTask bumblebeeTask() {
|
public BumblebeeTask bumblebeeTask() {
|
||||||
return bumblebeeTask;
|
return bumblebeeTask;
|
||||||
}
|
}
|
||||||
|
@ -124,12 +186,22 @@ public final class RequestHandlerTest {
|
||||||
public FailAtConstructionTask failAtConstructionTask() {
|
public FailAtConstructionTask failAtConstructionTask() {
|
||||||
return new FailAtConstructionTask();
|
return new FailAtConstructionTask();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AuthNoneAction authNoneAction() {
|
||||||
|
return new AuthNoneAction(component.getRequestModule().provideAuthResult());
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthAdminUserAnyMethodAction authAdminUserAnyMethodAction() {
|
||||||
|
return new AuthAdminUserAnyMethodAction(
|
||||||
|
component.getRequestModule().provideAuthResult());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Fake Builder for the fake component above to satisfy RequestHandler expectations. */
|
/** Fake Builder for the fake component above to satisfy RequestHandler expectations. */
|
||||||
public abstract static class Builder implements RequestComponentBuilder<Component, Builder> {
|
public abstract class Builder implements RequestComponentBuilder<Component, Builder> {
|
||||||
@Override
|
@Override
|
||||||
public Builder requestModule(RequestModule requestModule) {
|
public Builder requestModule(RequestModule requestModule) {
|
||||||
|
component.setRequestModule(requestModule);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -158,9 +230,22 @@ public final class RequestHandlerTest {
|
||||||
private final Component component = new Component();
|
private final Component component = new Component();
|
||||||
private final StringWriter httpOutput = new StringWriter();
|
private final StringWriter httpOutput = new StringWriter();
|
||||||
private RequestHandler<Component, Builder> handler;
|
private RequestHandler<Component, Builder> handler;
|
||||||
|
private AuthResult injectedAuthResult = null;
|
||||||
|
private final User testUser = new User("test@example.com", "test@example.com");
|
||||||
|
private RequestAuthenticator requestAuthenticator;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void before() throws Exception {
|
public void before() throws Exception {
|
||||||
|
requestAuthenticator = new RequestAuthenticator(
|
||||||
|
new AppEngineInternalAuthenticationMechanism(),
|
||||||
|
ImmutableList.of(
|
||||||
|
new OAuthAuthenticationMechanism(
|
||||||
|
OAuthServiceFactory.getOAuthService(),
|
||||||
|
ImmutableSet.of("https://www.googleapis.com/auth/userinfo.email"),
|
||||||
|
ImmutableSet.of("https://www.googleapis.com/auth/userinfo.email"),
|
||||||
|
ImmutableSet.of("proxy-client-id", "regtool-client-id"))),
|
||||||
|
new LegacyAuthenticationMechanism(userService));
|
||||||
|
|
||||||
// Initialize here, not inline, so that we pick up the mocked UserService.
|
// Initialize here, not inline, so that we pick up the mocked UserService.
|
||||||
handler = RequestHandler.<Component, Builder>createForTest(
|
handler = RequestHandler.<Component, Builder>createForTest(
|
||||||
Component.class,
|
Component.class,
|
||||||
|
@ -171,7 +256,8 @@ public final class RequestHandlerTest {
|
||||||
return component;
|
return component;
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
userService);
|
userService,
|
||||||
|
requestAuthenticator);
|
||||||
when(rsp.getWriter()).thenReturn(new PrintWriter(httpOutput));
|
when(rsp.getWriter()).thenReturn(new PrintWriter(httpOutput));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,6 +365,7 @@ public final class RequestHandlerTest {
|
||||||
@Test
|
@Test
|
||||||
public void testNullness() {
|
public void testNullness() {
|
||||||
NullPointerTester tester = new NullPointerTester();
|
NullPointerTester tester = new NullPointerTester();
|
||||||
|
tester.setDefault(RequestAuthenticator.class, requestAuthenticator);
|
||||||
tester.testAllPublicStaticMethods(RequestHandler.class);
|
tester.testAllPublicStaticMethods(RequestHandler.class);
|
||||||
tester.testAllPublicInstanceMethods(handler);
|
tester.testAllPublicInstanceMethods(handler);
|
||||||
}
|
}
|
||||||
|
@ -331,9 +418,56 @@ public final class RequestHandlerTest {
|
||||||
@Test
|
@Test
|
||||||
public void testMustBeLoggedIn_loggedIn_runsAction() throws Exception {
|
public void testMustBeLoggedIn_loggedIn_runsAction() throws Exception {
|
||||||
when(userService.isUserLoggedIn()).thenReturn(true);
|
when(userService.isUserLoggedIn()).thenReturn(true);
|
||||||
|
when(userService.getCurrentUser()).thenReturn(testUser);
|
||||||
when(req.getMethod()).thenReturn("GET");
|
when(req.getMethod()).thenReturn("GET");
|
||||||
when(req.getRequestURI()).thenReturn("/users-only");
|
when(req.getRequestURI()).thenReturn("/users-only");
|
||||||
handler.handleRequest(req, rsp);
|
handler.handleRequest(req, rsp);
|
||||||
verify(usersOnlyAction).run();
|
verify(usersOnlyAction).run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoAuthNeeded_success() throws Exception {
|
||||||
|
when(req.getMethod()).thenReturn("GET");
|
||||||
|
when(req.getRequestURI()).thenReturn("/auth/none");
|
||||||
|
handler.handleRequest(req, rsp);
|
||||||
|
assertThat(injectedAuthResult).isNotNull();
|
||||||
|
assertThat(injectedAuthResult.authLevel()).isEqualTo(AuthLevel.NONE);
|
||||||
|
assertThat(injectedAuthResult.userAuthInfo()).isAbsent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAuthNeeded_notLoggedIn() throws Exception {
|
||||||
|
when(req.getMethod()).thenReturn("GET");
|
||||||
|
when(req.getRequestURI()).thenReturn("/auth/adminUserAnyMethod");
|
||||||
|
handler.handleRequest(req, rsp);
|
||||||
|
verify(rsp).sendError(403);
|
||||||
|
assertThat(injectedAuthResult).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAuthNeeded_notAuthorized() throws Exception {
|
||||||
|
when(userService.isUserLoggedIn()).thenReturn(true);
|
||||||
|
when(userService.getCurrentUser()).thenReturn(testUser);
|
||||||
|
when(userService.isUserAdmin()).thenReturn(false);
|
||||||
|
when(req.getMethod()).thenReturn("GET");
|
||||||
|
when(req.getRequestURI()).thenReturn("/auth/adminUserAnyMethod");
|
||||||
|
handler.handleRequest(req, rsp);
|
||||||
|
verify(rsp).sendError(403);
|
||||||
|
assertThat(injectedAuthResult).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAuthNeeded_success() throws Exception {
|
||||||
|
when(userService.isUserLoggedIn()).thenReturn(true);
|
||||||
|
when(userService.getCurrentUser()).thenReturn(testUser);
|
||||||
|
when(userService.isUserAdmin()).thenReturn(true);
|
||||||
|
when(req.getMethod()).thenReturn("GET");
|
||||||
|
when(req.getRequestURI()).thenReturn("/auth/adminUserAnyMethod");
|
||||||
|
handler.handleRequest(req, rsp);
|
||||||
|
assertThat(injectedAuthResult).isNotNull();
|
||||||
|
assertThat(injectedAuthResult.authLevel()).isEqualTo(AuthLevel.USER);
|
||||||
|
assertThat(injectedAuthResult.userAuthInfo()).isPresent();
|
||||||
|
assertThat(injectedAuthResult.userAuthInfo().get().user()).isEqualTo(testUser);
|
||||||
|
assertThat(injectedAuthResult.userAuthInfo().get().oauthTokenInfo()).isAbsent();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
75
javatests/google/registry/testing/FakeUserService.java
Normal file
75
javatests/google/registry/testing/FakeUserService.java
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// 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 google.registry.testing;
|
||||||
|
|
||||||
|
import com.google.appengine.api.users.User;
|
||||||
|
import com.google.appengine.api.users.UserService;
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
// TODO: Consider reconciling this with AppEngineRule.withUserService()
|
||||||
|
|
||||||
|
/** Fake implementation of {@link UserService} for testing. */
|
||||||
|
public class FakeUserService implements UserService {
|
||||||
|
|
||||||
|
@Nullable private User user = null;
|
||||||
|
private boolean isAdmin = false;
|
||||||
|
|
||||||
|
public void setUser(@Nullable User user, boolean isAdmin) {
|
||||||
|
this.user = user;
|
||||||
|
this.isAdmin = isAdmin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String createLoginURL(String destinationURL) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String createLoginURL(String destinationURL, String authDomain) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String createLoginURL(String destinationURL, String authDomain, String federatedIdentity,
|
||||||
|
Set<String> attributesRequest) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String createLogoutURL(String destinationURL) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String createLogoutURL(String destinationURL, String authDomain) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isUserLoggedIn() {
|
||||||
|
return user != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isUserAdmin() {
|
||||||
|
return isAdmin;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public User getCurrentUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue