diff --git a/core/src/main/java/google/registry/config/RegistryConfig.java b/core/src/main/java/google/registry/config/RegistryConfig.java index 355d9373e..c1566592b 100644 --- a/core/src/main/java/google/registry/config/RegistryConfig.java +++ b/core/src/main/java/google/registry/config/RegistryConfig.java @@ -1174,44 +1174,6 @@ public final class RegistryConfig { return CONFIG_SETTINGS.get(); } - /** - * Provides the OAuth scopes that authentication logic should detect on access tokens. - * - *

This list should be a superset of the required OAuth scope set provided below. Note that - * ideally, this setting would not be required and all scopes on an access token would be - * detected automatically, but that is not the case due to the way {@code OAuthService} works. - * - *

This is an independent setting from the required OAuth scopes (below) to support use cases - * where certain actions require some additional scope (e.g. access to a user's Google Drive) - * but that scope shouldn't be required for authentication alone; in that case the Drive scope - * would be specified only for this setting, allowing that action to check for its presence. - */ - @Provides - @Config("availableOauthScopes") - public static ImmutableSet provideAvailableOauthScopes(RegistryConfigSettings config) { - return ImmutableSet.copyOf(config.auth.availableOauthScopes); - } - - /** - * Provides the OAuth scopes that are required for authenticating successfully. - * - *

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. - * - *

If we feel the need, we could define additional fixed scopes, similar to the Java remote - * API, which requires at least one of: - * - *

- */ - @Provides - @Config("requiredOauthScopes") - public static ImmutableSet provideRequiredOauthScopes(RegistryConfigSettings config) { - return ImmutableSet.copyOf(config.auth.requiredOauthScopes); - } - /** * Provides service account email addresses allowed to authenticate with the app at {@link * google.registry.request.auth.AuthSettings.AuthLevel#APP} level. @@ -1223,13 +1185,6 @@ public final class RegistryConfig { return ImmutableSet.copyOf(config.auth.allowedServiceAccountEmails); } - /** Provides the allowed OAuth client IDs (could be multibinding). */ - @Provides - @Config("allowedOauthClientIds") - public static ImmutableSet provideAllowedOauthClientIds(RegistryConfigSettings config) { - return ImmutableSet.copyOf(config.auth.allowedOauthClientIds); - } - @Provides @Config("oauthClientId") public static String provideOauthClientId(RegistryConfigSettings config) { diff --git a/core/src/main/java/google/registry/config/RegistryConfigSettings.java b/core/src/main/java/google/registry/config/RegistryConfigSettings.java index b9b424c6c..8d62a0a53 100644 --- a/core/src/main/java/google/registry/config/RegistryConfigSettings.java +++ b/core/src/main/java/google/registry/config/RegistryConfigSettings.java @@ -58,9 +58,6 @@ public class RegistryConfigSettings { /** Configuration options for authenticating users. */ public static class Auth { - public List availableOauthScopes; - public List requiredOauthScopes; - public List allowedOauthClientIds; public List allowedServiceAccountEmails; public String oauthClientId; } diff --git a/core/src/main/java/google/registry/config/files/default-config.yaml b/core/src/main/java/google/registry/config/files/default-config.yaml index 1d772f5de..97c403008 100644 --- a/core/src/main/java/google/registry/config/files/default-config.yaml +++ b/core/src/main/java/google/registry/config/files/default-config.yaml @@ -304,24 +304,6 @@ caching: # Note: Only allowedServiceAccountEmails and oauthClientId should be configured. # Other fields are related to OAuth-based authentication and will be removed. auth: - # Deprecated: Use OIDC-based auth instead. This field is for OAuth-based auth. - # OAuth scopes to detect on access tokens. Superset of requiredOauthScopes. - availableOauthScopes: - - https://www.googleapis.com/auth/userinfo.email - - # Deprecated: Use OIDC-based auth instead. This field is for OAuth-based auth. - # OAuth scopes required for authenticating. Subset of availableOauthScopes. - requiredOauthScopes: - - https://www.googleapis.com/auth/userinfo.email - - # Deprecated: Use OIDC-based auth instead. This field is for OAuth-based auth. - # OAuth client IDs that are allowed to authenticate and communicate with - # backend services, e.g. nomulus tool, EPP proxy, etc. The value in - # registryTool.clientId field should be included in this list. Client IDs are - # typically of the format - # numbers-alphanumerics.apps.googleusercontent.com - allowedOauthClientIds: [] - # Service accounts (e.g. default service account, account used by Cloud # Scheduler) allowed to send authenticated requests. allowedServiceAccountEmails: diff --git a/core/src/main/java/google/registry/config/files/nomulus-config-production-sample.yaml b/core/src/main/java/google/registry/config/files/nomulus-config-production-sample.yaml deleted file mode 100644 index 73ca90a1e..000000000 --- a/core/src/main/java/google/registry/config/files/nomulus-config-production-sample.yaml +++ /dev/null @@ -1,76 +0,0 @@ -# This is a sample production config (to be deployed in the WEB-INF directory). -# This is the same as what Google Registry runs in production, except with -# placeholders for Google-specific settings. - -gcpProject: - projectId: placeholder - # Set to true if running against local servers (localhost) - isLocal: false - # The "-dot-" prefix is used on the project ID in this URL in order - # to get around an issue with double-wildcard SSL certs. - defaultServiceUrl: https://domain-registry-placeholder.appspot.com - backendServiceUrl: https://backend-dot-domain-registry-placeholder.appspot.com - toolsServiceUrl: https://tools-dot-domain-registry-placeholder.appspot.com - pubapiServiceUrl: https://pubapi-dot-domain-registry-placeholder.appspot.com - -gSuite: - domainName: placeholder - outgoingEmailDisplayName: placeholder - outgoingEmailAddress: placeholder - adminAccountEmailAddress: placeholder - supportGroupEmailAddress: placeholder - -registryPolicy: - contactAndHostRoidSuffix: placeholder - productName: placeholder - greetingServerId: placeholder - registrarChangesNotificationEmailAddresses: - - placeholder - - placeholder - defaultRegistrarWhoisServer: placeholder - tmchCaMode: PRODUCTION - tmchCrlUrl: http://crl.icann.org/tmch.crl - tmchMarksDbUrl: https://ry.marksdb.org - checkApiServletClientId: placeholder - registryAdminClientId: placeholder - whoisDisclaimer: | - multi-line - placeholder - -icannReporting: - icannTransactionsReportingUploadUrl: https://ry-api.icann.org/report/registrar-transactions - icannActivityReportingUploadUrl: https://ry-api.icann.org/report/registry-functions-activity - -oAuth: - allowedOauthClientIds: - - placeholder.apps.googleusercontent.com - - placeholder-for-proxy - -rde: - reportUrlPrefix: https://ry-api.icann.org/report/registry-escrow-report - uploadUrl: sftp://placeholder@sftpipm2.ironmountain.com/Outbox - sshIdentityEmailAddress: placeholder - -registrarConsole: - logoFilename: placeholder - supportPhoneNumber: placeholder - supportEmailAddress: placeholder - announcementsEmailAddress: placeholder - integrationEmailAddress: placeholder - technicalDocsUrl: https://drive.google.com/drive/folders/placeholder - -misc: - sheetExportId: placeholder - -cloudDns: - rootUrl: null - servicePath: null - -keyring: - activeKeyring: KMS - kms: - projectId: placeholder - -registryTool: - clientId: placeholder.apps.googleusercontent.com - clientSecret: placeholder diff --git a/core/src/main/java/google/registry/flows/EppTlsAction.java b/core/src/main/java/google/registry/flows/EppTlsAction.java index a1f63cae6..6b34988fa 100644 --- a/core/src/main/java/google/registry/flows/EppTlsAction.java +++ b/core/src/main/java/google/registry/flows/EppTlsAction.java @@ -29,7 +29,7 @@ import javax.servlet.http.HttpSession; service = Action.Service.DEFAULT, path = "/_dr/epp", method = Method.POST, - auth = Auth.AUTH_API_PUBLIC) + auth = Auth.AUTH_API_ADMIN) public class EppTlsAction implements Runnable { @Inject @Payload byte[] inputXmlBytes; diff --git a/core/src/main/java/google/registry/request/auth/Auth.java b/core/src/main/java/google/registry/request/auth/Auth.java index 3acc4d32c..5bd7f25b9 100644 --- a/core/src/main/java/google/registry/request/auth/Auth.java +++ b/core/src/main/java/google/registry/request/auth/Auth.java @@ -15,8 +15,6 @@ package google.registry.request.auth; import com.google.common.collect.ImmutableList; -import google.registry.flows.EppTlsAction; -import google.registry.flows.TlsCredentials; import google.registry.request.auth.AuthSettings.AuthLevel; import google.registry.request.auth.AuthSettings.AuthMethod; import google.registry.request.auth.AuthSettings.UserPolicy; @@ -48,30 +46,18 @@ public enum Auth { * Allows anyone to access, as long as they are logged in. * *

This is used by legacy registrar console programmatic endpoints (those that extend {@link - * JsonGetAction}, which are accessed via XHR requests sent from a logged-in user when performing + * JsonGetAction}), which are accessed via XHR requests sent from a logged-in user when performing * actions on the console. */ AUTH_PUBLIC_LOGGED_IN( ImmutableList.of(AuthMethod.API, AuthMethod.LEGACY), AuthLevel.USER, UserPolicy.PUBLIC), /** - * Allows any client to access, as long as they are logged in via API-based authentication - * mechanisms. + * Allows only the app itself (via service accounts) or admins to access. * - *

This is used by the proxy to access Nomulus endpoints. The proxy service account does NOT - * have admin privileges. For EPP, we handle client authentication within {@link EppTlsAction}, - * using {@link TlsCredentials}. For WHOIS, anyone connecting to the proxy can access. - * - *

Note that the proxy service account DOES need to be allow-listed in the {@code - * auth.allowedServiceAccountEmails} field in the config YAML file in order for OIDC-based - * authentication to pass. - */ - AUTH_API_PUBLIC(ImmutableList.of(AuthMethod.API), AuthLevel.APP, UserPolicy.PUBLIC), - - /** - * Allows only admins to access. - * - *

This applies to the majority of the endpoints. + *

This applies to the majority of the endpoints. For APP level authentication to work, the + * associated service account needs to be allowlisted in the {@code + * auth.allowedServiceAccountEmails} field in the config YAML file. */ AUTH_API_ADMIN(ImmutableList.of(AuthMethod.API), AuthLevel.APP, UserPolicy.ADMIN); diff --git a/core/src/main/java/google/registry/request/auth/AuthModule.java b/core/src/main/java/google/registry/request/auth/AuthModule.java index b416e5b00..1ac8d8cb7 100644 --- a/core/src/main/java/google/registry/request/auth/AuthModule.java +++ b/core/src/main/java/google/registry/request/auth/AuthModule.java @@ -16,8 +16,6 @@ package google.registry.request.auth; import static com.google.common.net.HttpHeaders.AUTHORIZATION; -import com.google.appengine.api.oauth.OAuthService; -import com.google.appengine.api.oauth.OAuthServiceFactory; import com.google.auth.oauth2.TokenVerifier; import com.google.common.collect.ImmutableList; import dagger.Module; @@ -36,9 +34,6 @@ public class AuthModule { // IAP-signed JWT will be in this header. // See https://cloud.google.com/iap/docs/signed-headers-howto#securing_iap_headers. public static final String IAP_HEADER_NAME = "X-Goog-IAP-JWT-Assertion"; - // GAE will put the content in header "proxy-authorization" in this header when it routes the - // request to the app. - public static final String PROXY_HEADER_NAME = "X-Google-Proxy-Authorization"; public static final String BEARER_PREFIX = "Bearer "; // TODO: Change the IAP audience format once we are on GKE. // See: https://cloud.google.com/iap/docs/signed-headers-howto#verifying_the_jwt_payload @@ -46,16 +41,12 @@ public class AuthModule { private static final String IAP_ISSUER_URL = "https://cloud.google.com/iap"; private static final String REGULAR_ISSUER_URL = "https://accounts.google.com"; - /** Provides the custom authentication mechanisms (including OAuth and OIDC). */ + /** Provides the custom authentication mechanisms. */ @Provides ImmutableList provideApiAuthenticationMechanisms( - OAuthAuthenticationMechanism oauthAuthenticationMechanism, IapOidcAuthenticationMechanism iapOidcAuthenticationMechanism, RegularOidcAuthenticationMechanism regularOidcAuthenticationMechanism) { - return ImmutableList.of( - oauthAuthenticationMechanism, - iapOidcAuthenticationMechanism, - regularOidcAuthenticationMechanism); + return ImmutableList.of(iapOidcAuthenticationMechanism, regularOidcAuthenticationMechanism); } @Qualifier @@ -64,12 +55,6 @@ public class AuthModule { @Qualifier @interface RegularOidc {} - /** Provides the OAuthService instance. */ - @Provides - OAuthService provideOauthService() { - return OAuthServiceFactory.getOAuthService(); - } - @Provides @IapOidc @Singleton @@ -98,11 +83,7 @@ public class AuthModule { @Singleton TokenExtractor provideRegularTokenExtractor() { return request -> { - // TODO: only check the Authorizaiton header after the migration to OIDC is complete. - String rawToken = request.getHeader(PROXY_HEADER_NAME); - if (rawToken == null) { - rawToken = request.getHeader(AUTHORIZATION); - } + String rawToken = request.getHeader(AUTHORIZATION); if (rawToken != null && rawToken.startsWith(BEARER_PREFIX)) { return rawToken.substring(BEARER_PREFIX.length()); } diff --git a/core/src/main/java/google/registry/request/auth/AuthResult.java b/core/src/main/java/google/registry/request/auth/AuthResult.java index 1e4432a63..82e64b007 100644 --- a/core/src/main/java/google/registry/request/auth/AuthResult.java +++ b/core/src/main/java/google/registry/request/auth/AuthResult.java @@ -14,7 +14,9 @@ package google.registry.request.auth; -import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkArgument; +import static google.registry.request.auth.AuthSettings.AuthLevel.APP; +import static google.registry.request.auth.AuthSettings.AuthLevel.USER; import com.google.auto.value.AutoValue; import google.registry.request.auth.AuthSettings.AuthLevel; @@ -22,8 +24,8 @@ import java.util.Optional; import javax.annotation.Nullable; /** - * Results of authentication for a given HTTP request, as emitted by an - * {@link AuthenticationMechanism}. + * Results of authentication for a given HTTP request, as emitted by an {@link + * AuthenticationMechanism}. */ @AutoValue public abstract class AuthResult { @@ -33,6 +35,10 @@ public abstract class AuthResult { /** Information about the authenticated user, if there is one. */ public abstract Optional userAuthInfo(); + /** Service account email of the authenticated app, if there is one. */ + @SuppressWarnings("unused") // The service account will be logged upon successful login. + public abstract Optional appServiceAccount(); + public boolean isAuthenticated() { return authLevel() != AuthLevel.NONE; } @@ -47,15 +53,27 @@ public abstract class AuthResult { .orElse(""); } - public static AuthResult create(AuthLevel authLevel) { - return new AutoValue_AuthResult(authLevel, Optional.empty()); + public static AuthResult createApp(String email) { + return create(APP, null, email); } - public static AuthResult create(AuthLevel authLevel, @Nullable UserAuthInfo userAuthInfo) { - if (authLevel == AuthLevel.USER) { - checkNotNull(userAuthInfo); - } - return new AutoValue_AuthResult(authLevel, Optional.ofNullable(userAuthInfo)); + public static AuthResult createUser(UserAuthInfo userAuthInfo) { + return create(USER, userAuthInfo, null); + } + + private static AuthResult create( + AuthLevel authLevel, @Nullable UserAuthInfo userAuthInfo, @Nullable String email) { + checkArgument( + userAuthInfo == null || email == null, + "User auth info and service account email cannot be specificed at the same time"); + checkArgument( + authLevel != USER || userAuthInfo != null, + "User auth info must be specified for auth level USER"); + checkArgument( + authLevel != APP || email != null, + "Service account email must be specified for auth level APP"); + return new AutoValue_AuthResult( + authLevel, Optional.ofNullable(userAuthInfo), Optional.ofNullable(email)); } /** @@ -67,5 +85,5 @@ public abstract class AuthResult { * returns NOT_AUTHENTICATED in this case, as opposed to absent() if authentication failed and was * required. So as a return from an authorization check, this can be treated as a success. */ - public static final AuthResult NOT_AUTHENTICATED = create(AuthLevel.NONE); + public static final AuthResult NOT_AUTHENTICATED = create(AuthLevel.NONE, null, null); } diff --git a/core/src/main/java/google/registry/request/auth/AuthSettings.java b/core/src/main/java/google/registry/request/auth/AuthSettings.java index 5448ae8c0..b685f818b 100644 --- a/core/src/main/java/google/registry/request/auth/AuthSettings.java +++ b/core/src/main/java/google/registry/request/auth/AuthSettings.java @@ -17,6 +17,7 @@ package google.registry.request.auth; import com.google.auto.value.AutoValue; import com.google.common.collect.ImmutableList; import com.google.errorprone.annotations.Immutable; +import google.registry.model.console.UserRoles; /** * Parameters used to configure the authenticator. @@ -42,7 +43,10 @@ public abstract class AuthSettings { /** Available methods for authentication. */ public enum AuthMethod { - /** Authentication methods suitable for API-style access, such as OAuth 2. */ + /** + * Authentication methods suitable for API-style access, such as {@link + * OidcTokenAuthenticationMechanism}. + */ API, /** Legacy authentication using cookie-based App Engine Users API. Must come last if present. */ @@ -68,10 +72,11 @@ public abstract class AuthSettings { /** * Authentication required, but user not required. * - *

In Auth: Authentication is required, but app-internal authentication (which isn't - * associated with a specific user) is permitted. + *

In Auth: authentication is required, but App-internal authentication (which isn't + * associated with a specific user, but a service account) is permitted. Examples include + * requests from Cloud Tasks, Cloud Scheduler, and the proxy. * - *

In AuthResult: App-internal authentication was successful. + *

In AuthResult: App-internal authentication (via service accounts) was successful. */ APP, @@ -93,10 +98,14 @@ public abstract class AuthSettings { PUBLIC, /** - * If there is a user, it must be an admin, as determined by isUserAdmin(). + * If there is a user, it must be an admin, as determined by {@link UserAuthInfo#isUserAdmin()}. * - *

Note that, according to App Engine, anybody with access to the app in the GCP Console, + *

Note that, if the user returned is an App Engine {@link + * com.google.appengine.api.users.User} , anybody with access to the app in the GCP Console, * including editors and viewers, is an admin. + * + *

On the other hand, if the user is a {@link google.registry.model.console.User}, the admin + * role is explicitly defined in that object via the {@link UserRoles#isAdmin()} method. */ ADMIN } diff --git a/core/src/main/java/google/registry/request/auth/AuthenticationMechanism.java b/core/src/main/java/google/registry/request/auth/AuthenticationMechanism.java index 8427c102e..db30f77b0 100644 --- a/core/src/main/java/google/registry/request/auth/AuthenticationMechanism.java +++ b/core/src/main/java/google/registry/request/auth/AuthenticationMechanism.java @@ -19,7 +19,7 @@ import javax.servlet.http.HttpServletRequest; /** * A particular way to authenticate an HTTP request, returning an {@link AuthResult}. * - *

For instance, a request could be authenticated using OAuth, via special request headers, etc. + *

For instance, a request could be authenticated using OIDC, via special request headers, etc. */ public interface AuthenticationMechanism { diff --git a/core/src/main/java/google/registry/request/auth/LegacyAuthenticationMechanism.java b/core/src/main/java/google/registry/request/auth/LegacyAuthenticationMechanism.java index b7fc660b7..3e2e9ee32 100644 --- a/core/src/main/java/google/registry/request/auth/LegacyAuthenticationMechanism.java +++ b/core/src/main/java/google/registry/request/auth/LegacyAuthenticationMechanism.java @@ -16,8 +16,7 @@ package google.registry.request.auth; import static com.google.common.base.Strings.emptyToNull; import static com.google.common.base.Strings.nullToEmpty; -import static google.registry.request.auth.AuthSettings.AuthLevel.NONE; -import static google.registry.request.auth.AuthSettings.AuthLevel.USER; +import static google.registry.request.auth.AuthResult.NOT_AUTHENTICATED; import static google.registry.security.XsrfTokenManager.P_CSRF_TOKEN; import static google.registry.security.XsrfTokenManager.X_CSRF_TOKEN; @@ -49,15 +48,14 @@ public class LegacyAuthenticationMechanism implements AuthenticationMechanism { @Override public AuthResult authenticate(HttpServletRequest request) { if (!userService.isUserLoggedIn()) { - return AuthResult.create(NONE); + return NOT_AUTHENTICATED; } if (!SAFE_METHODS.contains(request.getMethod()) && !validateXsrf(request)) { - return AuthResult.create(NONE); + return NOT_AUTHENTICATED; } - return AuthResult.create( - USER, + return AuthResult.createUser( UserAuthInfo.create(userService.getCurrentUser(), userService.isUserAdmin())); } diff --git a/core/src/main/java/google/registry/request/auth/OAuthAuthenticationMechanism.java b/core/src/main/java/google/registry/request/auth/OAuthAuthenticationMechanism.java deleted file mode 100644 index 085466f16..000000000 --- a/core/src/main/java/google/registry/request/auth/OAuthAuthenticationMechanism.java +++ /dev/null @@ -1,136 +0,0 @@ -// 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.request.auth; - -import static com.google.common.net.HttpHeaders.AUTHORIZATION; -import static google.registry.request.auth.AuthModule.BEARER_PREFIX; -import static google.registry.request.auth.AuthSettings.AuthLevel.NONE; -import static google.registry.request.auth.AuthSettings.AuthLevel.USER; - -import com.google.appengine.api.oauth.OAuthRequestException; -import com.google.appengine.api.oauth.OAuthService; -import com.google.appengine.api.oauth.OAuthServiceFailureException; -import com.google.appengine.api.users.User; -import com.google.common.collect.ImmutableSet; -import com.google.common.flogger.FluentLogger; -import google.registry.config.RegistryConfig.Config; -import google.registry.config.RegistryEnvironment; -import javax.inject.Inject; -import javax.servlet.http.HttpServletRequest; - -/** - * OAuth authentication mechanism, using the OAuthService interface. - * - *

Only OAuth version 2 is supported. - */ -public class OAuthAuthenticationMechanism implements AuthenticationMechanism { - - private static final FluentLogger logger = FluentLogger.forEnclosingClass(); - - private final OAuthService oauthService; - - /** The available OAuth scopes for which {@link OAuthService} should check. */ - private final ImmutableSet availableOauthScopes; - - /** The OAuth scopes which must all be present for authentication to succeed. */ - private final ImmutableSet requiredOauthScopes; - - private final ImmutableSet allowedOauthClientIds; - - @Inject - public OAuthAuthenticationMechanism( - OAuthService oauthService, - @Config("availableOauthScopes") ImmutableSet availableOauthScopes, - @Config("requiredOauthScopes") ImmutableSet requiredOauthScopes, - @Config("allowedOauthClientIds") ImmutableSet allowedOauthClientIds) { - this.oauthService = oauthService; - this.availableOauthScopes = availableOauthScopes; - this.requiredOauthScopes = requiredOauthScopes; - this.allowedOauthClientIds = allowedOauthClientIds; - } - - @Override - public AuthResult authenticate(HttpServletRequest request) { - - // Make sure that there is an Authorization header in Bearer form. OAuthService also accepts - // tokens in the request body and URL string, but we should not use those, since they are more - // likely to be logged than the Authorization header. Checking to make sure there's a token also - // avoids unnecessary RPCs, since OAuthService itself does not check whether the header is - // present. In theory, there could be more than one Authorization header, but we only check the - // first one, because there's not a legitimate use case for having more than one, and - // OAuthService itself only looks at the first one anyway. - String header = request.getHeader(AUTHORIZATION); - if ((header == null) || !header.startsWith(BEARER_PREFIX)) { - if (header != null) { - logger.atInfo().log("Invalid authorization header."); - } - return AuthResult.create(NONE); - } - // Assume that, if a bearer token is found, it's what OAuthService will use to attempt - // authentication. This is not technically guaranteed by the contract of OAuthService; see - // OAuthTokenInfo for more information. - String rawAccessToken = - RegistryEnvironment.get() == RegistryEnvironment.PRODUCTION - ? "Raw token redacted in prod" - : header.substring(BEARER_PREFIX.length()); - - // Get the OAuth information. The various oauthService method calls use a single cached - // authentication result, so we can call them one by one. - User currentUser; - boolean isUserAdmin; - String oauthClientId; - ImmutableSet authorizedScopes; - try { - String[] availableOauthScopeArray = availableOauthScopes.toArray(new String[0]); - currentUser = oauthService.getCurrentUser(availableOauthScopeArray); - isUserAdmin = oauthService.isUserAdmin(availableOauthScopeArray); - logger.atInfo().log( - "Current user: %s (%s).", currentUser, isUserAdmin ? "admin" : "not admin"); - oauthClientId = oauthService.getClientId(availableOauthScopeArray); - logger.atInfo().log("OAuth client ID: %s", oauthClientId); - authorizedScopes = - ImmutableSet.copyOf(oauthService.getAuthorizedScopes(availableOauthScopeArray)); - logger.atInfo().log("Authorized scope(s): %s", authorizedScopes); - } catch (OAuthRequestException | OAuthServiceFailureException e) { - logger.atInfo().withCause(e).log("Unable to get OAuth information."); - return AuthResult.create(NONE); - } - if ((currentUser == null) || (oauthClientId == null) || (authorizedScopes == null)) { - return AuthResult.create(NONE); - } - - // Make sure that the client ID matches, to avoid a confused deputy attack; see: - // http://stackoverflow.com/a/17439317/1179226 - if (!allowedOauthClientIds.contains(oauthClientId)) { - logger.atInfo().log("OAuth client ID is not allowed."); - return AuthResult.create(NONE); - } - - // Make sure that all required scopes are present. - if (!authorizedScopes.containsAll(requiredOauthScopes)) { - logger.atInfo().log("Missing required scope(s)."); - return AuthResult.create(NONE); - } - - // Create the {@link AuthResult}, including the OAuth token info. - return AuthResult.create( - USER, - UserAuthInfo.create( - currentUser, - isUserAdmin, - OAuthTokenInfo.create( - ImmutableSet.copyOf(authorizedScopes), oauthClientId, rawAccessToken))); - } -} diff --git a/core/src/main/java/google/registry/request/auth/OAuthTokenInfo.java b/core/src/main/java/google/registry/request/auth/OAuthTokenInfo.java deleted file mode 100644 index 6cb3d3950..000000000 --- a/core/src/main/java/google/registry/request/auth/OAuthTokenInfo.java +++ /dev/null @@ -1,48 +0,0 @@ -// 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.request.auth; - -import com.google.auto.value.AutoValue; -import com.google.common.collect.ImmutableSet; - -/** Information provided by the OAuth authentication mechanism (only) about the session. */ -@AutoValue -public abstract class OAuthTokenInfo { - - /** Authorized OAuth scopes granted by the access token provided with the request. */ - abstract ImmutableSet authorizedScopes(); - - /** OAuth client ID from the access token provided with the request. */ - abstract String oauthClientId(); - - /** - * Raw OAuth access token value provided with the request, for passing along to downstream APIs as - * appropriate. - * - *

Note that the request parsing code makes certain assumptions about whether the Authorization - * header was used as the source of the token. Because OAuthService could theoretically fall back - * to some other source of authentication, it might be possible for rawAccessToken not to have - * been the source of OAuth authentication. Looking at the code of OAuthService, that could not - * currently happen, but if OAuthService were modified in the future so that it tried the bearer - * token, and then when that failed, fell back to another, successful authentication path, then - * rawAccessToken might not be valid. - */ - abstract String rawAccessToken(); - - static OAuthTokenInfo create( - ImmutableSet authorizedScopes, String oauthClientId, String rawAccessToken) { - return new AutoValue_OAuthTokenInfo(authorizedScopes, oauthClientId, rawAccessToken); - } -} diff --git a/core/src/main/java/google/registry/request/auth/OidcTokenAuthenticationMechanism.java b/core/src/main/java/google/registry/request/auth/OidcTokenAuthenticationMechanism.java index 9efaa96cc..7a5f31f9b 100644 --- a/core/src/main/java/google/registry/request/auth/OidcTokenAuthenticationMechanism.java +++ b/core/src/main/java/google/registry/request/auth/OidcTokenAuthenticationMechanism.java @@ -14,8 +14,6 @@ package google.registry.request.auth; -import static google.registry.request.auth.AuthSettings.AuthLevel.APP; - import com.google.api.client.json.webtoken.JsonWebSignature; import com.google.auth.oauth2.TokenVerifier; import com.google.common.annotations.VisibleForTesting; @@ -97,12 +95,11 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication } Optional maybeUser = UserDao.loadUser(email); if (maybeUser.isPresent()) { - return AuthResult.create(AuthLevel.USER, UserAuthInfo.create(maybeUser.get())); + return AuthResult.createUser(UserAuthInfo.create(maybeUser.get())); } - // TODO: implement caching so we don't have to look up the database for every request. logger.atInfo().log("No end user found for email address %s", email); if (serviceAccountEmails.stream().anyMatch(e -> e.equals(email))) { - return AuthResult.create(APP); + return AuthResult.createApp(email); } logger.atInfo().log("No service account found for email address %s", email); logger.atWarning().log( @@ -153,15 +150,8 @@ public abstract class OidcTokenAuthenticationMechanism implements Authentication * *

If the endpoint is not behind IAP, we can try to authenticate the OIDC token supplied in the * request header directly. Ideally we would like all endpoints to be behind IAP, but being able - * to authenticate the token directly provides us with the flexibility to do away with OAuth-based - * {@link OAuthAuthenticationMechanism} that is tied to App Engine runtime without having to turn - * on IAP, which is an all-or-nothing switch for each GAE service (i.e. no way to turn it on only - * for certain GAE endpoints). - * - *

Note that this mechanism will try to first extract the token under the "proxy-authorization" - * header, before trying "authorization". This is because currently the GAE OAuth service always - * uses "authorization", and we would like to provide a way for both auth mechanisms to be working - * at the same time for the same request. + * to authenticate the token directly provides us with some extra flexibility that comes in handy, + * at least during the migration to GKE. * * @see Bearer Token Usage */ diff --git a/core/src/main/java/google/registry/request/auth/RequestAuthenticator.java b/core/src/main/java/google/registry/request/auth/RequestAuthenticator.java index 10747dd6f..bec01c865 100644 --- a/core/src/main/java/google/registry/request/auth/RequestAuthenticator.java +++ b/core/src/main/java/google/registry/request/auth/RequestAuthenticator.java @@ -60,7 +60,8 @@ public class RequestAuthenticator { if (auth.minimumLevel() == APP && !authResult.isAuthenticated()) { logger.atWarning().log("Not authorized; no authentication found."); return Optional.empty(); - } else if (auth.minimumLevel() == USER && authResult.authLevel() != USER) { + } + if (auth.minimumLevel() == USER && authResult.authLevel() != USER) { logger.atWarning().log("Not authorized; no authenticated user."); return Optional.empty(); } @@ -81,12 +82,12 @@ public class RequestAuthenticator { * @param req the {@link HttpServletRequest}; some authentication mechanisms use HTTP headers * @return an authentication result; if no authentication was made, returns NOT_AUTHENTICATED */ - private AuthResult authenticate(AuthSettings auth, HttpServletRequest req) { + AuthResult authenticate(AuthSettings auth, HttpServletRequest req) { checkAuthConfig(auth); for (AuthMethod authMethod : auth.methods()) { AuthResult authResult; switch (authMethod) { - // API-based user authentication mechanisms, such as OAuth and OIDC. + // API-based user authentication mechanisms, such as OIDC. case API: for (AuthenticationMechanism authMechanism : apiAuthenticationMechanisms) { authResult = authMechanism.authenticate(req); @@ -113,10 +114,9 @@ public class RequestAuthenticator { /** Validates an AuthSettings object, checking for invalid setting combinations. */ static void checkAuthConfig(AuthSettings auth) { - ImmutableList authMethods = ImmutableList.copyOf(auth.methods()); - checkArgument(!authMethods.isEmpty(), "Must specify at least one auth method"); + checkArgument(!auth.methods().isEmpty(), "Must specify at least one auth method"); checkArgument( - Ordering.explicit(AuthMethod.API, AuthMethod.LEGACY).isStrictlyOrdered(authMethods), + Ordering.explicit(AuthMethod.API, AuthMethod.LEGACY).isStrictlyOrdered(auth.methods()), "Auth methods must be unique and strictly in order - API, LEGACY"); checkArgument( (auth.minimumLevel() != NONE) || (auth.userPolicy() != ADMIN), diff --git a/core/src/main/java/google/registry/request/auth/UserAuthInfo.java b/core/src/main/java/google/registry/request/auth/UserAuthInfo.java index 54d1fb98c..d92430dde 100644 --- a/core/src/main/java/google/registry/request/auth/UserAuthInfo.java +++ b/core/src/main/java/google/registry/request/auth/UserAuthInfo.java @@ -22,6 +22,8 @@ import java.util.Optional; @AutoValue public abstract class UserAuthInfo { + public abstract Optional consoleUser(); + /** User object from the AppEngine Users API. */ public abstract Optional appEngineUser(); @@ -34,11 +36,6 @@ public abstract class UserAuthInfo { */ public abstract boolean isUserAdmin(); - public abstract Optional consoleUser(); - - /** Used by the OAuth authentication mechanism (only) to return information about the session. */ - public abstract Optional oauthTokenInfo(); - public String getEmailAddress() { return appEngineUser() .map(User::getEmail) @@ -51,20 +48,12 @@ public abstract class UserAuthInfo { .orElseGet(() -> consoleUser().get().getEmailAddress()); } - public static UserAuthInfo create( - User user, boolean isUserAdmin) { - return new AutoValue_UserAuthInfo( - Optional.of(user), isUserAdmin, Optional.empty(), Optional.empty()); - } - - public static UserAuthInfo create( - User user, boolean isUserAdmin, OAuthTokenInfo oauthTokenInfo) { - return new AutoValue_UserAuthInfo( - Optional.of(user), isUserAdmin, Optional.empty(), Optional.of(oauthTokenInfo)); + public static UserAuthInfo create(User user, boolean isUserAdmin) { + return new AutoValue_UserAuthInfo(Optional.empty(), Optional.of(user), isUserAdmin); } public static UserAuthInfo create(google.registry.model.console.User user) { return new AutoValue_UserAuthInfo( - Optional.empty(), user.getUserRoles().isAdmin(), Optional.of(user), Optional.empty()); + Optional.of(user), Optional.empty(), user.getUserRoles().isAdmin()); } } diff --git a/core/src/main/java/google/registry/tools/RegistryCli.java b/core/src/main/java/google/registry/tools/RegistryCli.java index 72a2b3819..a5f06d41b 100644 --- a/core/src/main/java/google/registry/tools/RegistryCli.java +++ b/core/src/main/java/google/registry/tools/RegistryCli.java @@ -50,13 +50,6 @@ final class RegistryCli implements CommandRunner { description = "Sets the default environment to run the command.") private RegistryToolEnvironment environment = RegistryToolEnvironment.PRODUCTION; - @Parameter( - names = "--oauth", - description = - "Turn on OAuth-based authentication, the usage of which is to be deprecated. Use" - + " `create_user` to create an Admin user that allows for OIDC-based authentication.") - private boolean oAuth = false; - @Parameter( names = {"-c", "--commands"}, description = "Returns all command names.") @@ -168,7 +161,6 @@ final class RegistryCli implements CommandRunner { DaggerRegistryToolComponent.builder() .credentialFilePath(credentialJson) .sqlAccessInfoFile(sqlAccessInfoFile) - .addOAuthHeader(oAuth) .build(); // JCommander stores sub-commands as nested JCommander objects containing a list of user objects diff --git a/core/src/main/java/google/registry/tools/RegistryToolComponent.java b/core/src/main/java/google/registry/tools/RegistryToolComponent.java index 099c94377..783899b32 100644 --- a/core/src/main/java/google/registry/tools/RegistryToolComponent.java +++ b/core/src/main/java/google/registry/tools/RegistryToolComponent.java @@ -191,9 +191,6 @@ interface RegistryToolComponent { @BindsInstance Builder sqlAccessInfoFile(@Nullable @Config("sqlAccessInfoFile") String sqlAccessInfoFile); - @BindsInstance - Builder addOAuthHeader(@Config("addOauthHeader") boolean addOauthHeader); - RegistryToolComponent build(); } } diff --git a/core/src/main/java/google/registry/tools/RequestFactoryModule.java b/core/src/main/java/google/registry/tools/RequestFactoryModule.java index 5f7118553..8b8a69883 100644 --- a/core/src/main/java/google/registry/tools/RequestFactoryModule.java +++ b/core/src/main/java/google/registry/tools/RequestFactoryModule.java @@ -14,7 +14,7 @@ package google.registry.tools; -import static com.google.common.net.HttpHeaders.PROXY_AUTHORIZATION; +import static com.google.common.net.HttpHeaders.AUTHORIZATION; import com.google.api.client.http.HttpRequestFactory; import com.google.api.client.http.javanet.NetHttpTransport; @@ -42,8 +42,7 @@ final class RequestFactoryModule { @Provides static HttpRequestFactory provideHttpRequestFactory( @ApplicationDefaultCredential GoogleCredentialsBundle credentialsBundle, - @Config("oauthClientId") String oauthClientId, - @Config("addOauthHeader") boolean addOauthHeader) { + @Config("oauthClientId") String oauthClientId) { if (RegistryConfig.areServersLocal()) { return new NetHttpTransport() .createRequestFactory( @@ -55,15 +54,11 @@ final class RequestFactoryModule { return new NetHttpTransport() .createRequestFactory( request -> { - if (addOauthHeader) { - // Use the standard credential initializer to set the Authorization header - credentialsBundle.getHttpRequestInitializer().initialize(request); - } - // Set OIDC token as the alternative bearer token. + // Set OIDC token as the bearer token. request .getHeaders() .set( - PROXY_AUTHORIZATION, + AUTHORIZATION, "Bearer " + OidcTokenUtils.createOidcToken(credentialsBundle, oauthClientId)); // GAE request times out after 10 min, so here we set the timeout to 10 min. This is diff --git a/core/src/main/java/google/registry/whois/WhoisAction.java b/core/src/main/java/google/registry/whois/WhoisAction.java index 81f6d28df..c07421b53 100644 --- a/core/src/main/java/google/registry/whois/WhoisAction.java +++ b/core/src/main/java/google/registry/whois/WhoisAction.java @@ -51,7 +51,7 @@ import org.joda.time.DateTime; service = Action.Service.PUBAPI, path = "/_dr/whois", method = POST, - auth = Auth.AUTH_API_PUBLIC) + auth = Auth.AUTH_API_ADMIN) public class WhoisAction implements Runnable { private static final FluentLogger logger = FluentLogger.forEnclosingClass(); diff --git a/core/src/test/java/google/registry/rdap/RdapActionBaseTestCase.java b/core/src/test/java/google/registry/rdap/RdapActionBaseTestCase.java index f8fb34037..e78ae58f7 100644 --- a/core/src/test/java/google/registry/rdap/RdapActionBaseTestCase.java +++ b/core/src/test/java/google/registry/rdap/RdapActionBaseTestCase.java @@ -28,7 +28,6 @@ import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension; import google.registry.request.Actions; import google.registry.request.auth.AuthResult; -import google.registry.request.auth.AuthSettings.AuthLevel; import google.registry.request.auth.UserAuthInfo; import google.registry.testing.FakeClock; import google.registry.testing.FakeResponse; @@ -48,13 +47,11 @@ abstract class RdapActionBaseTestCase { new JpaTestExtensions.Builder().buildIntegrationTestExtension(); protected static final AuthResult AUTH_RESULT = - AuthResult.create( - AuthLevel.USER, + AuthResult.createUser( UserAuthInfo.create(new User("rdap.user@user.com", "gmail.com", "12345"), false)); protected static final AuthResult AUTH_RESULT_ADMIN = - AuthResult.create( - AuthLevel.USER, + AuthResult.createUser( UserAuthInfo.create(new User("rdap.admin@google.com", "gmail.com", "12345"), true)); protected FakeResponse response = new FakeResponse(); diff --git a/core/src/test/java/google/registry/request/RequestHandlerTest.java b/core/src/test/java/google/registry/request/RequestHandlerTest.java index 8551e7986..e04ea9e74 100644 --- a/core/src/test/java/google/registry/request/RequestHandlerTest.java +++ b/core/src/test/java/google/registry/request/RequestHandlerTest.java @@ -20,6 +20,7 @@ import static google.registry.request.Action.Method.GET; import static google.registry.request.Action.Method.POST; import static google.registry.request.auth.Auth.AUTH_API_ADMIN; import static google.registry.request.auth.Auth.AUTH_PUBLIC; +import static google.registry.request.auth.AuthResult.NOT_AUTHENTICATED; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; @@ -228,7 +229,7 @@ public final class RequestHandlerTest { when(req.getMethod()).thenReturn("GET"); when(req.getRequestURI()).thenReturn("/bumblebee"); when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req)) - .thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE))); + .thenReturn(Optional.of(NOT_AUTHENTICATED)); handler.handleRequest(req, rsp); @@ -242,7 +243,7 @@ public final class RequestHandlerTest { when(req.getMethod()).thenReturn("POST"); when(req.getRequestURI()).thenReturn("/bumblebee"); when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req)) - .thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE))); + .thenReturn(Optional.of(NOT_AUTHENTICATED)); handler.handleRequest(req, rsp); @@ -255,7 +256,7 @@ public final class RequestHandlerTest { when(req.getMethod()).thenReturn("GET"); when(req.getRequestURI()).thenReturn("/bumblebee/hive"); when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req)) - .thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE))); + .thenReturn(Optional.of(NOT_AUTHENTICATED)); handler.handleRequest(req, rsp); @@ -268,7 +269,7 @@ public final class RequestHandlerTest { when(req.getMethod()).thenReturn("POST"); when(req.getRequestURI()).thenReturn("/sloth"); when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req)) - .thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE))); + .thenReturn(Optional.of(NOT_AUTHENTICATED)); handler.handleRequest(req, rsp); @@ -284,7 +285,7 @@ public final class RequestHandlerTest { when(req.getMethod()).thenReturn("POST"); when(req.getRequestURI()).thenReturn("/sloth/nest"); when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req)) - .thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE))); + .thenReturn(Optional.of(NOT_AUTHENTICATED)); handler.handleRequest(req, rsp); @@ -296,7 +297,7 @@ public final class RequestHandlerTest { when(req.getMethod()).thenReturn("GET"); when(req.getRequestURI()).thenReturn("/fail"); when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req)) - .thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE))); + .thenReturn(Optional.of(NOT_AUTHENTICATED)); handler.handleRequest(req, rsp); @@ -311,7 +312,7 @@ public final class RequestHandlerTest { when(req.getMethod()).thenReturn("GET"); when(req.getRequestURI()).thenReturn("/failAtConstruction"); when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req)) - .thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE))); + .thenReturn(Optional.of(NOT_AUTHENTICATED)); handler.handleRequest(req, rsp); @@ -324,7 +325,7 @@ public final class RequestHandlerTest { when(req.getMethod()).thenReturn("GET"); when(req.getRequestURI()).thenReturn("/bogus"); when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req)) - .thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE))); + .thenReturn(Optional.of(NOT_AUTHENTICATED)); handler.handleRequest(req, rsp); @@ -336,7 +337,7 @@ public final class RequestHandlerTest { when(req.getMethod()).thenReturn("POST"); when(req.getRequestURI()).thenReturn("/fail"); when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req)) - .thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE))); + .thenReturn(Optional.of(NOT_AUTHENTICATED)); handler.handleRequest(req, rsp); @@ -348,7 +349,7 @@ public final class RequestHandlerTest { when(req.getMethod()).thenReturn("FIREAWAY"); when(req.getRequestURI()).thenReturn("/fail"); when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req)) - .thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE))); + .thenReturn(Optional.of(NOT_AUTHENTICATED)); handler.handleRequest(req, rsp); @@ -364,7 +365,7 @@ public final class RequestHandlerTest { when(req.getMethod()).thenReturn("get"); when(req.getRequestURI()).thenReturn("/bumblebee"); when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req)) - .thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE))); + .thenReturn(Optional.of(NOT_AUTHENTICATED)); handler.handleRequest(req, rsp); @@ -386,7 +387,7 @@ public final class RequestHandlerTest { when(req.getMethod()).thenReturn("POST"); when(req.getRequestURI()).thenReturn("/safe-sloth"); when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req)) - .thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE))); + .thenReturn(Optional.of(NOT_AUTHENTICATED)); handler.handleRequest(req, rsp); @@ -399,7 +400,7 @@ public final class RequestHandlerTest { when(req.getMethod()).thenReturn("GET"); when(req.getRequestURI()).thenReturn("/safe-sloth"); when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req)) - .thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE))); + .thenReturn(Optional.of(NOT_AUTHENTICATED)); handler.handleRequest(req, rsp); @@ -412,7 +413,7 @@ public final class RequestHandlerTest { when(req.getMethod()).thenReturn("GET"); when(req.getRequestURI()).thenReturn("/auth/none"); when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req)) - .thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE))); + .thenReturn(Optional.of(NOT_AUTHENTICATED)); handler.handleRequest(req, rsp); @@ -440,8 +441,7 @@ public final class RequestHandlerTest { when(req.getMethod()).thenReturn("GET"); when(req.getRequestURI()).thenReturn("/auth/adminUser"); when(requestAuthenticator.authorize(AUTH_API_ADMIN.authSettings(), req)) - .thenReturn( - Optional.of(AuthResult.create(AuthLevel.USER, UserAuthInfo.create(testUser, true)))); + .thenReturn(Optional.of(AuthResult.createUser(UserAuthInfo.create(testUser, true)))); handler.handleRequest(req, rsp); @@ -449,7 +449,6 @@ public final class RequestHandlerTest { assertThat(providedAuthResult.authLevel()).isEqualTo(AuthLevel.USER); assertThat(providedAuthResult.userAuthInfo()).isPresent(); assertThat(providedAuthResult.userAuthInfo().get().appEngineUser()).hasValue(testUser); - assertThat(providedAuthResult.userAuthInfo().get().oauthTokenInfo()).isEmpty(); assertMetric("/auth/adminUser", GET, AuthLevel.USER, true); } } diff --git a/core/src/test/java/google/registry/request/auth/AuthenticatedRegistrarAccessorTest.java b/core/src/test/java/google/registry/request/auth/AuthenticatedRegistrarAccessorTest.java index bbb0af9dc..552fc95df 100644 --- a/core/src/test/java/google/registry/request/auth/AuthenticatedRegistrarAccessorTest.java +++ b/core/src/test/java/google/registry/request/auth/AuthenticatedRegistrarAccessorTest.java @@ -15,6 +15,7 @@ package google.registry.request.auth; import static com.google.common.truth.Truth.assertThat; +import static google.registry.request.auth.AuthResult.NOT_AUTHENTICATED; import static google.registry.request.auth.AuthenticatedRegistrarAccessor.Role.ADMIN; import static google.registry.request.auth.AuthenticatedRegistrarAccessor.Role.OWNER; import static google.registry.testing.DatabaseHelper.loadRegistrar; @@ -40,7 +41,6 @@ import google.registry.model.registrar.Registrar; import google.registry.model.registrar.Registrar.State; import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension; -import google.registry.request.auth.AuthSettings.AuthLevel; import google.registry.request.auth.AuthenticatedRegistrarAccessor.RegistrarAccessDeniedException; import google.registry.util.JdkLoggerConfig; import java.util.Optional; @@ -75,7 +75,7 @@ class AuthenticatedRegistrarAccessorTest { private static final AuthResult USER = createAuthResult(false); private static final AuthResult GAE_ADMIN = createAuthResult(true); - private static final AuthResult NO_USER = AuthResult.create(AuthLevel.NONE); + private static final AuthResult NO_USER = NOT_AUTHENTICATED; private static final Optional SUPPORT_GROUP = Optional.of("support@registry.example"); /** Registrar ID of a REAL registrar with a RegistrarContact for USER and GAE_ADMIN. */ private static final String REGISTRAR_ID_WITH_CONTACT = "TheRegistrar"; @@ -94,8 +94,7 @@ class AuthenticatedRegistrarAccessorTest { * @param isAdmin if true, the user is an administrator for the app-engine project. */ private static AuthResult createAuthResult(boolean isAdmin) { - return AuthResult.create( - AuthLevel.USER, + return AuthResult.createUser( UserAuthInfo.create(new User("johndoe@theregistrar.com", "theregistrar.com"), isAdmin)); } @@ -295,8 +294,7 @@ class AuthenticatedRegistrarAccessorTest { void testGetRegistrarForUser_inContacts_isNotAdmin_caseInsensitive() throws Exception { expectGetRegistrarSuccess( REGISTRAR_ID_WITH_CONTACT, - AuthResult.create( - AuthLevel.USER, + AuthResult.createUser( UserAuthInfo.create(new User("JohnDoe@theregistrar.com", "theregistrar.com"), false)), "user JohnDoe@theregistrar.com has [OWNER] access to registrar TheRegistrar"); verify(lazyGroupsConnection).get(); @@ -421,7 +419,7 @@ class AuthenticatedRegistrarAccessorTest { .setUserRoles( new UserRoles.Builder().setIsAdmin(true).setGlobalRole(GlobalRole.FTE).build()) .build(); - AuthResult authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(consoleUser)); + AuthResult authResult = AuthResult.createUser(UserAuthInfo.create(consoleUser)); AuthenticatedRegistrarAccessor registrarAccessor = new AuthenticatedRegistrarAccessor( authResult, ADMIN_REGISTRAR_ID, SUPPORT_GROUP, lazyGroupsConnection); @@ -446,7 +444,7 @@ class AuthenticatedRegistrarAccessorTest { .setEmailAddress("email@email.com") .setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.SUPPORT_AGENT).build()) .build(); - AuthResult authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(consoleUser)); + AuthResult authResult = AuthResult.createUser(UserAuthInfo.create(consoleUser)); AuthenticatedRegistrarAccessor registrarAccessor = new AuthenticatedRegistrarAccessor( authResult, ADMIN_REGISTRAR_ID, SUPPORT_GROUP, lazyGroupsConnection); @@ -471,7 +469,7 @@ class AuthenticatedRegistrarAccessorTest { RegistrarRole.ACCOUNT_MANAGER)) .build()) .build(); - AuthResult authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(consoleUser)); + AuthResult authResult = AuthResult.createUser(UserAuthInfo.create(consoleUser)); AuthenticatedRegistrarAccessor registrarAccessor = new AuthenticatedRegistrarAccessor( authResult, ADMIN_REGISTRAR_ID, SUPPORT_GROUP, lazyGroupsConnection); diff --git a/core/src/test/java/google/registry/request/auth/OidcTokenAuthenticationMechanismTest.java b/core/src/test/java/google/registry/request/auth/OidcTokenAuthenticationMechanismTest.java index 2f57a9723..3a53cf595 100644 --- a/core/src/test/java/google/registry/request/auth/OidcTokenAuthenticationMechanismTest.java +++ b/core/src/test/java/google/registry/request/auth/OidcTokenAuthenticationMechanismTest.java @@ -18,7 +18,6 @@ import static com.google.common.net.HttpHeaders.AUTHORIZATION; import static com.google.common.truth.Truth.assertThat; import static google.registry.request.auth.AuthModule.BEARER_PREFIX; import static google.registry.request.auth.AuthModule.IAP_HEADER_NAME; -import static google.registry.request.auth.AuthModule.PROXY_HEADER_NAME; import static google.registry.testing.DatabaseHelper.insertInDb; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; @@ -92,9 +91,8 @@ public class OidcTokenAuthenticationMechanismTest { @Test void testAuthResultBypass() { - OidcTokenAuthenticationMechanism.setAuthResultForTesting(AuthResult.create(AuthLevel.APP)); - assertThat(authenticationMechanism.authenticate(null)) - .isEqualTo(AuthResult.create(AuthLevel.APP)); + OidcTokenAuthenticationMechanism.setAuthResultForTesting(AuthResult.NOT_AUTHENTICATED); + assertThat(authenticationMechanism.authenticate(null)).isEqualTo(AuthResult.NOT_AUTHENTICATED); } @Test @@ -169,16 +167,10 @@ public class OidcTokenAuthenticationMechanismTest { void testRegular_tokenExtractor() throws Exception { useRegularOidcMechanism(); // The token does not have the "Bearer " prefix. - when(request.getHeader(PROXY_HEADER_NAME)).thenReturn(rawToken); + when(request.getHeader(AUTHORIZATION)).thenReturn(rawToken); assertThat(authenticationMechanism.tokenExtractor.extract(request)).isNull(); // The token is in the correct format. - when(request.getHeader(PROXY_HEADER_NAME)) - .thenReturn(String.format("%s%s", BEARER_PREFIX, rawToken)); - assertThat(authenticationMechanism.tokenExtractor.extract(request)).isEqualTo(rawToken); - - // The token is in the correct format, and under the alternative header. - when(request.getHeader(PROXY_HEADER_NAME)).thenReturn(null); when(request.getHeader(AUTHORIZATION)) .thenReturn(String.format("%s%s", BEARER_PREFIX, rawToken)); assertThat(authenticationMechanism.tokenExtractor.extract(request)).isEqualTo(rawToken); diff --git a/core/src/test/java/google/registry/request/auth/RequestAuthenticatorTest.java b/core/src/test/java/google/registry/request/auth/RequestAuthenticatorTest.java index 19b3cbe6e..1351261d2 100644 --- a/core/src/test/java/google/registry/request/auth/RequestAuthenticatorTest.java +++ b/core/src/test/java/google/registry/request/auth/RequestAuthenticatorTest.java @@ -14,361 +14,276 @@ package google.registry.request.auth; -import static com.google.common.net.HttpHeaders.AUTHORIZATION; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth8.assertThat; +import static google.registry.request.auth.AuthResult.NOT_AUTHENTICATED; +import static google.registry.request.auth.AuthSettings.AuthLevel.APP; +import static google.registry.request.auth.AuthSettings.AuthLevel.NONE; +import static google.registry.request.auth.AuthSettings.AuthLevel.USER; +import static google.registry.request.auth.AuthSettings.AuthMethod.API; +import static google.registry.request.auth.AuthSettings.AuthMethod.LEGACY; +import static google.registry.request.auth.AuthSettings.UserPolicy.ADMIN; +import static google.registry.request.auth.AuthSettings.UserPolicy.PUBLIC; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -import com.google.appengine.api.users.User; -import com.google.appengine.api.users.UserService; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import google.registry.persistence.transaction.JpaTestExtensions; -import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension; +import google.registry.model.console.GlobalRole; +import google.registry.model.console.User; +import google.registry.model.console.UserRoles; import google.registry.request.auth.AuthSettings.AuthLevel; import google.registry.request.auth.AuthSettings.AuthMethod; import google.registry.request.auth.AuthSettings.UserPolicy; -import google.registry.security.XsrfTokenManager; -import google.registry.testing.FakeClock; -import google.registry.testing.FakeOAuthService; -import google.registry.testing.FakeUserService; import java.util.Optional; import javax.servlet.http.HttpServletRequest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; /** Unit tests for {@link RequestAuthenticator}. */ class RequestAuthenticatorTest { - @RegisterExtension - final JpaIntegrationTestExtension jpa = - new JpaTestExtensions.Builder().buildIntegrationTestExtension(); + private static final AuthResult APP_AUTH = AuthResult.createApp("app@registry.example"); - private static final AuthSettings AUTH_NONE = - AuthSettings.create(ImmutableList.of(AuthMethod.API), AuthLevel.NONE, UserPolicy.PUBLIC); + private static final AuthResult USER_PUBLIC_AUTH = + AuthResult.createUser( + UserAuthInfo.create( + new User.Builder() + .setEmailAddress("user@registry.example") + .setUserRoles( + new UserRoles.Builder() + .setIsAdmin(false) + .setGlobalRole(GlobalRole.NONE) + .build()) + .build())); - private static final AuthSettings AUTH_ANY_USER_ANY_METHOD = - AuthSettings.create( - ImmutableList.of(AuthMethod.API, AuthMethod.LEGACY), AuthLevel.USER, UserPolicy.PUBLIC); + private static final AuthResult USER_ADMIN_AUTH = + AuthResult.createUser( + UserAuthInfo.create( + new User.Builder() + .setEmailAddress("admin@registry.example") + .setUserRoles( + new UserRoles.Builder() + .setIsAdmin(true) + .setGlobalRole(GlobalRole.FTE) + .build()) + .build())); - private static final AuthSettings AUTH_ANY_USER_NO_LEGACY = - AuthSettings.create(ImmutableList.of(AuthMethod.API), AuthLevel.USER, UserPolicy.PUBLIC); - - private static final AuthSettings AUTH_ADMIN_USER_ANY_METHOD = - AuthSettings.create( - ImmutableList.of(AuthMethod.API, AuthMethod.LEGACY), AuthLevel.USER, UserPolicy.ADMIN); - - private static final AuthSettings AUTH_NO_METHODS = - AuthSettings.create(ImmutableList.of(), AuthLevel.APP, UserPolicy.PUBLIC); - - private static final AuthSettings AUTH_WRONG_METHOD_ORDERING = - AuthSettings.create( - ImmutableList.of(AuthMethod.LEGACY, AuthMethod.API), AuthLevel.APP, UserPolicy.PUBLIC); - - private static final AuthSettings AUTH_DUPLICATE_METHODS = - AuthSettings.create( - ImmutableList.of(AuthMethod.API, AuthMethod.API), AuthLevel.APP, UserPolicy.PUBLIC); - - private static final AuthSettings AUTH_NONE_REQUIRES_ADMIN = - AuthSettings.create(ImmutableList.of(AuthMethod.API), AuthLevel.NONE, UserPolicy.ADMIN); - - private final UserService mockUserService = mock(UserService.class); private final HttpServletRequest req = mock(HttpServletRequest.class); - private final User testUser = new User("test@google.com", "test@google.com"); - private final FakeUserService fakeUserService = new FakeUserService(); - private final XsrfTokenManager xsrfTokenManager = - new XsrfTokenManager(new FakeClock(), fakeUserService); - private final FakeOAuthService fakeOAuthService = - new FakeOAuthService( - false /* isOAuthEnabled */, - testUser, - false /* isUserAdmin */, - "test-client-id", - ImmutableList.of("test-scope1", "test-scope2", "nontest-scope")); + private final AuthenticationMechanism apiAuthenticationMechanism1 = + mock(AuthenticationMechanism.class); + private final AuthenticationMechanism apiAuthenticationMechanism2 = + mock(AuthenticationMechanism.class); + private final LegacyAuthenticationMechanism legacyAuthenticationMechanism = + mock(LegacyAuthenticationMechanism.class); + + private Optional authorize(AuthLevel authLevel, UserPolicy userPolicy) { + return new RequestAuthenticator( + ImmutableList.of(apiAuthenticationMechanism1, apiAuthenticationMechanism2), + legacyAuthenticationMechanism) + .authorize(AuthSettings.create(ImmutableList.of(API, LEGACY), authLevel, userPolicy), req); + } + + private AuthResult authenticate(AuthMethod... methods) { + return new RequestAuthenticator( + ImmutableList.of(apiAuthenticationMechanism1, apiAuthenticationMechanism2), + legacyAuthenticationMechanism) + .authenticate(AuthSettings.create(ImmutableList.copyOf(methods), NONE, PUBLIC), req); + } @BeforeEach void beforeEach() { - when(req.getMethod()).thenReturn("POST"); - } - - private RequestAuthenticator createRequestAuthenticator(UserService userService) { - return new RequestAuthenticator( - ImmutableList.of( - new OAuthAuthenticationMechanism( - fakeOAuthService, - ImmutableSet.of("test-scope1", "test-scope2", "test-scope3"), - ImmutableSet.of("test-scope1", "test-scope2"), - ImmutableSet.of("test-client-id", "other-test-client-id"))), - new LegacyAuthenticationMechanism(userService, xsrfTokenManager)); - } - - private Optional runTest(UserService userService, AuthSettings auth) { - return createRequestAuthenticator(userService).authorize(auth, req); + when(apiAuthenticationMechanism1.authenticate(req)).thenReturn(NOT_AUTHENTICATED); + when(apiAuthenticationMechanism2.authenticate(req)).thenReturn(NOT_AUTHENTICATED); + when(legacyAuthenticationMechanism.authenticate(req)).thenReturn(NOT_AUTHENTICATED); } @Test - void testNoAuthNeeded_noneFound() { - Optional authResult = runTest(mockUserService, AUTH_NONE); - - verifyNoInteractions(mockUserService); - assertThat(authResult).isPresent(); - assertThat(authResult.get().authLevel()).isEqualTo(AuthLevel.NONE); + void testAuthorize_noneRequired() { + for (AuthResult resultFound : + ImmutableList.of(NOT_AUTHENTICATED, APP_AUTH, USER_ADMIN_AUTH, USER_PUBLIC_AUTH)) { + when(apiAuthenticationMechanism1.authenticate(req)).thenReturn(resultFound); + assertThat(authorize(NONE, PUBLIC)).hasValue(resultFound); + } } @Test - void testAnyUserAnyMethod_notLoggedIn() { - Optional authResult = runTest(fakeUserService, AUTH_ANY_USER_ANY_METHOD); + void testAuthorize_appPublicRequired() { + authorize(APP, PUBLIC); + assertThat(authorize(APP, PUBLIC)).isEmpty(); - assertThat(authResult).isEmpty(); + for (AuthResult resultFound : ImmutableList.of(APP_AUTH, USER_ADMIN_AUTH, USER_PUBLIC_AUTH)) { + when(apiAuthenticationMechanism1.authenticate(req)).thenReturn(resultFound); + assertThat(authorize(APP, PUBLIC)).hasValue(resultFound); + } } @Test - void testAnyUserAnyMethod_xsrfFailure() { - fakeUserService.setUser(testUser, false); + void testAuthorize_appAdminRequired() { + for (AuthResult resultFound : ImmutableList.of(NOT_AUTHENTICATED, USER_PUBLIC_AUTH)) { + when(apiAuthenticationMechanism1.authenticate(req)).thenReturn(resultFound); + assertThat(authorize(APP, ADMIN)).isEmpty(); + } - Optional authResult = runTest(fakeUserService, AUTH_ANY_USER_ANY_METHOD); - - assertThat(authResult).isEmpty(); + for (AuthResult resultFound : ImmutableList.of(APP_AUTH, USER_ADMIN_AUTH)) { + when(apiAuthenticationMechanism1.authenticate(req)).thenReturn(resultFound); + assertThat(authorize(APP, ADMIN)).hasValue(resultFound); + } } @Test - void testAnyUserAnyMethod_success() { - fakeUserService.setUser(testUser, false /* isAdmin */); - when(req.getHeader(XsrfTokenManager.X_CSRF_TOKEN)) - .thenReturn(xsrfTokenManager.generateToken(testUser.getEmail())); + void testAuthorize_userPublicRequired() { + for (AuthResult resultFound : ImmutableList.of(NOT_AUTHENTICATED, APP_AUTH)) { + when(apiAuthenticationMechanism1.authenticate(req)).thenReturn(resultFound); + assertThat(authorize(USER, PUBLIC)).isEmpty(); + } - Optional authResult = runTest(fakeUserService, AUTH_ANY_USER_ANY_METHOD); - - assertThat(authResult).isPresent(); - assertThat(authResult.get().authLevel()).isEqualTo(AuthLevel.USER); - assertThat(authResult.get().userAuthInfo()).isPresent(); - assertThat(authResult.get().userAuthInfo().get().appEngineUser()).hasValue(testUser); - assertThat(authResult.get().userAuthInfo().get().isUserAdmin()).isFalse(); - assertThat(authResult.get().userAuthInfo().get().oauthTokenInfo()).isEmpty(); + for (AuthResult resultFound : ImmutableList.of(USER_PUBLIC_AUTH, USER_ADMIN_AUTH)) { + when(apiAuthenticationMechanism1.authenticate(req)).thenReturn(resultFound); + assertThat(authorize(USER, PUBLIC)).hasValue(resultFound); + } } @Test - void testAnyUserAnyMethod_xsrfNotRequiredForGet() { - fakeUserService.setUser(testUser, false); - when(req.getMethod()).thenReturn("GET"); + void testAuthorize_userAdminRequired() { + for (AuthResult resultFound : ImmutableList.of(NOT_AUTHENTICATED, APP_AUTH, USER_PUBLIC_AUTH)) { + when(apiAuthenticationMechanism1.authenticate(req)).thenReturn(resultFound); + assertThat(authorize(USER, ADMIN)).isEmpty(); + } - Optional authResult = runTest(fakeUserService, AUTH_ANY_USER_ANY_METHOD); - - assertThat(authResult).isPresent(); - assertThat(authResult.get().authLevel()).isEqualTo(AuthLevel.USER); - assertThat(authResult.get().userAuthInfo()).isPresent(); - assertThat(authResult.get().userAuthInfo().get().appEngineUser()).hasValue(testUser); - assertThat(authResult.get().userAuthInfo().get().oauthTokenInfo()).isEmpty(); + when(apiAuthenticationMechanism1.authenticate(req)).thenReturn(USER_ADMIN_AUTH); + assertThat(authorize(USER, ADMIN)).hasValue(USER_ADMIN_AUTH); } @Test - void testAdminUserAnyMethod_notLoggedIn() { - Optional authResult = runTest(fakeUserService, AUTH_ADMIN_USER_ANY_METHOD); - - assertThat(authResult).isEmpty(); + void testAuthenticate_apiFirst() { + when(apiAuthenticationMechanism1.authenticate(req)).thenReturn(APP_AUTH); + assertThat(authenticate(API, LEGACY)).isEqualTo(APP_AUTH); + verify(apiAuthenticationMechanism1).authenticate(req); + verifyNoMoreInteractions(apiAuthenticationMechanism1); + verifyNoMoreInteractions(apiAuthenticationMechanism2); + verifyNoMoreInteractions(legacyAuthenticationMechanism); } @Test - void testAdminUserAnyMethod_notAdminUser() { - fakeUserService.setUser(testUser, false /* isAdmin */); - - Optional authResult = runTest(fakeUserService, AUTH_ADMIN_USER_ANY_METHOD); - - assertThat(authResult).isEmpty(); + void testAuthenticate_apiSecond() { + when(apiAuthenticationMechanism2.authenticate(req)).thenReturn(APP_AUTH); + assertThat(authenticate(API, LEGACY)).isEqualTo(APP_AUTH); + verify(apiAuthenticationMechanism1).authenticate(req); + verify(apiAuthenticationMechanism2).authenticate(req); + verifyNoMoreInteractions(apiAuthenticationMechanism1); + verifyNoMoreInteractions(apiAuthenticationMechanism2); + verifyNoMoreInteractions(legacyAuthenticationMechanism); } @Test - void testAdminUserAnyMethod_xsrfFailure() { - fakeUserService.setUser(testUser, true); - - Optional authResult = runTest(fakeUserService, AUTH_ADMIN_USER_ANY_METHOD); - - assertThat(authResult).isEmpty(); + void testAuthenticate_legacy() { + when(legacyAuthenticationMechanism.authenticate(req)).thenReturn(APP_AUTH); + assertThat(authenticate(API, LEGACY)).isEqualTo(APP_AUTH); + verify(apiAuthenticationMechanism1).authenticate(req); + verify(apiAuthenticationMechanism2).authenticate(req); + verify(legacyAuthenticationMechanism).authenticate(req); + verifyNoMoreInteractions(apiAuthenticationMechanism1); + verifyNoMoreInteractions(apiAuthenticationMechanism2); + verifyNoMoreInteractions(legacyAuthenticationMechanism); } @Test - void testAdminUserAnyMethod_success() { - fakeUserService.setUser(testUser, true /* isAdmin */); - when(req.getHeader(XsrfTokenManager.X_CSRF_TOKEN)) - .thenReturn(xsrfTokenManager.generateToken(testUser.getEmail())); - - Optional authResult = runTest(fakeUserService, AUTH_ADMIN_USER_ANY_METHOD); - - assertThat(authResult).isPresent(); - assertThat(authResult.get().authLevel()).isEqualTo(AuthLevel.USER); - assertThat(authResult.get().userAuthInfo()).isPresent(); - assertThat(authResult.get().userAuthInfo().get().appEngineUser()).hasValue(testUser); - assertThat(authResult.get().userAuthInfo().get().isUserAdmin()).isTrue(); - assertThat(authResult.get().userAuthInfo().get().oauthTokenInfo()).isEmpty(); + void testAuthenticate_returnFirstResult() { + // API auth 2 returns an authenticted auth result, so we don't bother trying the next auth + // (legacy auth). + when(apiAuthenticationMechanism2.authenticate(req)).thenReturn(APP_AUTH); + when(legacyAuthenticationMechanism.authenticate(req)).thenReturn(USER_PUBLIC_AUTH); + assertThat(authenticate(API, LEGACY)).isEqualTo(APP_AUTH); + verify(apiAuthenticationMechanism1).authenticate(req); + verify(apiAuthenticationMechanism2).authenticate(req); + verifyNoMoreInteractions(apiAuthenticationMechanism1); + verifyNoMoreInteractions(apiAuthenticationMechanism2); + verifyNoMoreInteractions(legacyAuthenticationMechanism); } @Test - void testOAuth_success() { - fakeOAuthService.setUser(testUser); - fakeOAuthService.setOAuthEnabled(true); - when(req.getHeader(AUTHORIZATION)).thenReturn("Bearer TOKEN"); - - Optional authResult = runTest(fakeUserService, AUTH_ANY_USER_NO_LEGACY); - - assertThat(authResult).isPresent(); - assertThat(authResult.get().authLevel()).isEqualTo(AuthLevel.USER); - assertThat(authResult.get().userAuthInfo()).isPresent(); - assertThat(authResult.get().userAuthInfo().get().appEngineUser()).hasValue(testUser); - assertThat(authResult.get().userAuthInfo().get().isUserAdmin()).isFalse(); - assertThat(authResult.get().userAuthInfo().get().oauthTokenInfo()).isPresent(); - assertThat(authResult.get().userAuthInfo().get().oauthTokenInfo().get().authorizedScopes()) - .containsAtLeast("test-scope1", "test-scope2"); - assertThat(authResult.get().userAuthInfo().get().oauthTokenInfo().get().oauthClientId()) - .isEqualTo("test-client-id"); - assertThat(authResult.get().userAuthInfo().get().oauthTokenInfo().get().rawAccessToken()) - .isEqualTo("TOKEN"); + void testAuthenticate_notAuthenticated() { + assertThat(authenticate(API, LEGACY)).isEqualTo(NOT_AUTHENTICATED); + verify(apiAuthenticationMechanism1).authenticate(req); + verify(apiAuthenticationMechanism2).authenticate(req); + verify(legacyAuthenticationMechanism).authenticate(req); + verifyNoMoreInteractions(apiAuthenticationMechanism1); + verifyNoMoreInteractions(apiAuthenticationMechanism2); + verifyNoMoreInteractions(legacyAuthenticationMechanism); } @Test - void testOAuthAdmin_success() { - fakeOAuthService.setUser(testUser); - fakeOAuthService.setUserAdmin(true); - fakeOAuthService.setOAuthEnabled(true); - when(req.getHeader(AUTHORIZATION)).thenReturn("Bearer TOKEN"); - - Optional authResult = runTest(fakeUserService, AUTH_ANY_USER_NO_LEGACY); - - assertThat(authResult).isPresent(); - assertThat(authResult.get().authLevel()).isEqualTo(AuthLevel.USER); - assertThat(authResult.get().userAuthInfo()).isPresent(); - assertThat(authResult.get().userAuthInfo().get().appEngineUser()).hasValue(testUser); - assertThat(authResult.get().userAuthInfo().get().isUserAdmin()).isTrue(); - assertThat(authResult.get().userAuthInfo().get().oauthTokenInfo()).isPresent(); - assertThat(authResult.get().userAuthInfo().get().oauthTokenInfo().get().authorizedScopes()) - .containsAtLeast("test-scope1", "test-scope2"); - assertThat(authResult.get().userAuthInfo().get().oauthTokenInfo().get().oauthClientId()) - .isEqualTo("test-client-id"); - assertThat(authResult.get().userAuthInfo().get().oauthTokenInfo().get().rawAccessToken()) - .isEqualTo("TOKEN"); + void testAuthenticate_apiOnly() { + when(legacyAuthenticationMechanism.authenticate(req)).thenReturn(USER_PUBLIC_AUTH); + assertThat(authenticate(API)).isEqualTo(NOT_AUTHENTICATED); + verify(apiAuthenticationMechanism1).authenticate(req); + verify(apiAuthenticationMechanism2).authenticate(req); + verifyNoMoreInteractions(apiAuthenticationMechanism1); + verifyNoMoreInteractions(apiAuthenticationMechanism2); + verifyNoMoreInteractions(legacyAuthenticationMechanism); } @Test - void testOAuthMissingAuthenticationToken_failure() { - fakeOAuthService.setUser(testUser); - fakeOAuthService.setOAuthEnabled(true); - - Optional authResult = runTest(fakeUserService, AUTH_ANY_USER_NO_LEGACY); - - assertThat(authResult).isEmpty(); + void testAuthenticate_legacyOnly() { + when(apiAuthenticationMechanism1.authenticate(req)).thenReturn(USER_PUBLIC_AUTH); + assertThat(authenticate(LEGACY)).isEqualTo(NOT_AUTHENTICATED); + verify(legacyAuthenticationMechanism).authenticate(req); + verifyNoMoreInteractions(apiAuthenticationMechanism1); + verifyNoMoreInteractions(apiAuthenticationMechanism2); + verifyNoMoreInteractions(legacyAuthenticationMechanism); } @Test - void testOAuthClientIdMismatch_failure() { - fakeOAuthService.setUser(testUser); - fakeOAuthService.setOAuthEnabled(true); - fakeOAuthService.setClientId("wrong-client-id"); - when(req.getHeader(AUTHORIZATION)).thenReturn("Bearer TOKEN"); - - Optional authResult = runTest(fakeUserService, AUTH_ANY_USER_NO_LEGACY); - - assertThat(authResult).isEmpty(); - } - - @Test - void testOAuthNoScopes_failure() { - fakeOAuthService.setUser(testUser); - fakeOAuthService.setOAuthEnabled(true); - fakeOAuthService.setAuthorizedScopes(); - when(req.getHeader(AUTHORIZATION)).thenReturn("Bearer TOKEN"); - - Optional authResult = runTest(fakeUserService, AUTH_ANY_USER_NO_LEGACY); - - assertThat(authResult).isEmpty(); - } - - @Test - void testOAuthMissingScope_failure() { - fakeOAuthService.setUser(testUser); - fakeOAuthService.setOAuthEnabled(true); - fakeOAuthService.setAuthorizedScopes("test-scope1", "test-scope3"); - when(req.getHeader(AUTHORIZATION)).thenReturn("Bearer TOKEN"); - - Optional authResult = runTest(fakeUserService, AUTH_ANY_USER_NO_LEGACY); - - assertThat(authResult).isEmpty(); - } - - @Test - void testOAuthExtraScope_success() { - fakeOAuthService.setUser(testUser); - fakeOAuthService.setOAuthEnabled(true); - fakeOAuthService.setAuthorizedScopes("test-scope1", "test-scope2", "test-scope3"); - when(req.getHeader(AUTHORIZATION)).thenReturn("Bearer TOKEN"); - - Optional authResult = runTest(fakeUserService, AUTH_ANY_USER_NO_LEGACY); - - assertThat(authResult).isPresent(); - assertThat(authResult.get().authLevel()).isEqualTo(AuthLevel.USER); - assertThat(authResult.get().userAuthInfo()).isPresent(); - assertThat(authResult.get().userAuthInfo().get().appEngineUser()).hasValue(testUser); - assertThat(authResult.get().userAuthInfo().get().isUserAdmin()).isFalse(); - assertThat(authResult.get().userAuthInfo().get().oauthTokenInfo()).isPresent(); - assertThat(authResult.get().userAuthInfo().get().oauthTokenInfo().get().authorizedScopes()) - .containsAtLeast("test-scope1", "test-scope2", "test-scope3"); - assertThat(authResult.get().userAuthInfo().get().oauthTokenInfo().get().oauthClientId()) - .isEqualTo("test-client-id"); - assertThat(authResult.get().userAuthInfo().get().oauthTokenInfo().get().rawAccessToken()) - .isEqualTo("TOKEN"); - } - - @Test - void testAnyUserNoLegacy_failureWithLegacyUser() { - fakeUserService.setUser(testUser, false /* isAdmin */); - - Optional authResult = runTest(fakeUserService, AUTH_ANY_USER_NO_LEGACY); - - assertThat(authResult).isEmpty(); - } - - @Test - void testCheckAuthConfig_noMethods_failure() { + void testFailure_checkAuthConfig_noMethods() { IllegalArgumentException thrown = assertThrows( IllegalArgumentException.class, - () -> RequestAuthenticator.checkAuthConfig(AUTH_NO_METHODS)); + () -> + RequestAuthenticator.checkAuthConfig( + AuthSettings.create(ImmutableList.of(), NONE, PUBLIC))); assertThat(thrown).hasMessageThat().contains("Must specify at least one auth method"); } @Test - void testCheckAuthConfig_wrongMethodOrdering_failure() { + void testFailure_checkAuthConfig_wrongMethodOrder() { IllegalArgumentException thrown = assertThrows( IllegalArgumentException.class, - () -> RequestAuthenticator.checkAuthConfig(AUTH_WRONG_METHOD_ORDERING)); + () -> + RequestAuthenticator.checkAuthConfig( + AuthSettings.create(ImmutableList.of(LEGACY, API), NONE, PUBLIC))); assertThat(thrown) .hasMessageThat() .contains("Auth methods must be unique and strictly in order - API, LEGACY"); } @Test - void testCheckAuthConfig_noneAuthLevelRequiresAdmin_failure() { + void testFailure_CheckAuthConfig_duplicateMethods() { IllegalArgumentException thrown = assertThrows( IllegalArgumentException.class, - () -> RequestAuthenticator.checkAuthConfig(AUTH_NONE_REQUIRES_ADMIN)); + () -> + RequestAuthenticator.checkAuthConfig( + AuthSettings.create(ImmutableList.of(API, API), NONE, PUBLIC))); + assertThat(thrown) + .hasMessageThat() + .contains("Auth methods must be unique and strictly in order - API, LEGACY"); + } + + @Test + void testFailure_checkAuthConfig_noneAuthLevelRequiresAdmin() { + IllegalArgumentException thrown = + assertThrows( + IllegalArgumentException.class, + () -> + RequestAuthenticator.checkAuthConfig( + AuthSettings.create(ImmutableList.of(API, LEGACY), NONE, ADMIN))); assertThat(thrown) .hasMessageThat() .contains("Actions with minimal auth level at NONE should not specify ADMIN user policy"); } - - @Test - void testCheckAuthConfig_DuplicateMethods_failure() { - IllegalArgumentException thrown = - assertThrows( - IllegalArgumentException.class, - () -> RequestAuthenticator.checkAuthConfig(AUTH_DUPLICATE_METHODS)); - assertThat(thrown) - .hasMessageThat() - .contains("Auth methods must be unique and strictly in order - API, LEGACY"); - } } diff --git a/core/src/test/java/google/registry/security/XsrfTokenManagerTest.java b/core/src/test/java/google/registry/security/XsrfTokenManagerTest.java index c1cbdc4d4..363293db7 100644 --- a/core/src/test/java/google/registry/security/XsrfTokenManagerTest.java +++ b/core/src/test/java/google/registry/security/XsrfTokenManagerTest.java @@ -16,13 +16,15 @@ package google.registry.security; import static com.google.common.truth.Truth.assertThat; import static google.registry.util.DateTimeUtils.START_OF_TIME; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import com.google.appengine.api.users.User; +import com.google.appengine.api.users.UserService; import com.google.common.base.Splitter; import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension; import google.registry.testing.FakeClock; -import google.registry.testing.FakeUserService; import org.joda.time.Duration; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -37,14 +39,16 @@ class XsrfTokenManagerTest { private final User testUser = new User("test@example.com", "test@example.com"); private final FakeClock clock = new FakeClock(START_OF_TIME); - private final FakeUserService userService = new FakeUserService(); + private final UserService userService = mock(UserService.class); private final XsrfTokenManager xsrfTokenManager = new XsrfTokenManager(clock, userService); private String token; @BeforeEach void beforeEach() { - userService.setUser(testUser, false); + when(userService.isUserLoggedIn()).thenReturn(true); + when(userService.getCurrentUser()).thenReturn(testUser); + when(userService.isUserAdmin()).thenReturn(false); token = xsrfTokenManager.generateToken(testUser.getEmail()); } diff --git a/core/src/test/java/google/registry/server/RegistryTestServer.java b/core/src/test/java/google/registry/server/RegistryTestServer.java index 4d44d23e1..b8a846dbb 100644 --- a/core/src/test/java/google/registry/server/RegistryTestServer.java +++ b/core/src/test/java/google/registry/server/RegistryTestServer.java @@ -84,7 +84,7 @@ public final class RegistryTestServer { private final TestServer server; - /** @see TestServer#TestServer(HostAndPort, ImmutableMap, ImmutableList, ImmutableList) */ + /** @see TestServer#TestServer(HostAndPort, ImmutableMap, ImmutableList) */ public RegistryTestServer(HostAndPort address) { server = new TestServer(address, RUNFILES, ROUTES); } @@ -104,7 +104,7 @@ public final class RegistryTestServer { server.stop(); } - /** @see TestServer#getUrl(java.lang.String) */ + /** @see TestServer#getUrl(String) */ public URL getUrl(String path) { return server.getUrl(path); } diff --git a/core/src/test/java/google/registry/server/RegistryTestServerMain.java b/core/src/test/java/google/registry/server/RegistryTestServerMain.java index 762fe3132..13e71586a 100644 --- a/core/src/test/java/google/registry/server/RegistryTestServerMain.java +++ b/core/src/test/java/google/registry/server/RegistryTestServerMain.java @@ -25,7 +25,6 @@ import google.registry.model.console.UserRoles; import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.persistence.transaction.JpaTransactionManagerExtension; import google.registry.request.auth.AuthResult; -import google.registry.request.auth.AuthSettings.AuthLevel; import google.registry.request.auth.OidcTokenAuthenticationMechanism; import google.registry.request.auth.UserAuthInfo; import google.registry.testing.UserInfo; @@ -148,7 +147,7 @@ public final class RegistryTestServerMain { .setRegistryLockPassword("registryLockPassword") .build(); OidcTokenAuthenticationMechanism.setAuthResultForTesting( - AuthResult.create(AuthLevel.USER, UserAuthInfo.create(user))); + AuthResult.createUser(UserAuthInfo.create(user))); new JpaTestExtensions.Builder().buildIntegrationTestExtension().beforeEach(null); JpaTransactionManagerExtension.loadInitialData(); System.out.printf("%sLoading fixtures...%s\n", BLUE, RESET); diff --git a/core/src/test/java/google/registry/testing/FakeOAuthService.java b/core/src/test/java/google/registry/testing/FakeOAuthService.java deleted file mode 100644 index c74beb93c..000000000 --- a/core/src/test/java/google/registry/testing/FakeOAuthService.java +++ /dev/null @@ -1,130 +0,0 @@ -// 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.oauth.OAuthRequestException; -import com.google.appengine.api.oauth.OAuthService; -import com.google.appengine.api.users.User; -import com.google.common.collect.ImmutableList; -import java.util.List; - -/** A fake {@link OAuthService} implementation for testing. */ -public class FakeOAuthService implements OAuthService { - - private boolean isOAuthEnabled; - private User currentUser; - private boolean isUserAdmin; - private String clientId; - private ImmutableList authorizedScopes; - - public FakeOAuthService( - boolean isOAuthEnabled, - User currentUser, - boolean isUserAdmin, - String clientId, - List authorizedScopes) { - this.isOAuthEnabled = isOAuthEnabled; - this.currentUser = currentUser; - this.isUserAdmin = isUserAdmin; - this.clientId = clientId; - this.authorizedScopes = ImmutableList.copyOf(authorizedScopes); - } - - public void setOAuthEnabled(boolean isOAuthEnabled) { - this.isOAuthEnabled = isOAuthEnabled; - } - - public void setUser(User currentUser) { - this.currentUser = currentUser; - } - - public void setUserAdmin(boolean isUserAdmin) { - this.isUserAdmin = isUserAdmin; - } - - public void setClientId(String clientId) { - this.clientId = clientId; - } - - public void setAuthorizedScopes(String... scopes) { - this.authorizedScopes = ImmutableList.copyOf(scopes); - } - - @Override - public User getCurrentUser() throws OAuthRequestException { - if (!isOAuthEnabled) { - throw new OAuthRequestException("invalid OAuth request"); - } - return currentUser; - } - - @Override - public User getCurrentUser(String scope) throws OAuthRequestException { - return getCurrentUser(); - } - - @Override - public User getCurrentUser(String... scopes) throws OAuthRequestException { - return getCurrentUser(); - } - - @Override - public boolean isUserAdmin() throws OAuthRequestException { - if (!isOAuthEnabled) { - throw new OAuthRequestException("invalid OAuth request"); - } - return isUserAdmin; - } - - @Override - public boolean isUserAdmin(String scope) throws OAuthRequestException { - return isUserAdmin(); - } - - @Override - public boolean isUserAdmin(String... scopes) throws OAuthRequestException { - return isUserAdmin(); - } - - @Override - public String getClientId(String scope) throws OAuthRequestException { - if (!isOAuthEnabled) { - throw new OAuthRequestException("invalid OAuth request"); - } - return clientId; - } - - @Override - public String getClientId(String... scopes) throws OAuthRequestException { - if (!isOAuthEnabled) { - throw new OAuthRequestException("invalid OAuth request"); - } - return clientId; - } - - @Override - public String[] getAuthorizedScopes(String... scopes) throws OAuthRequestException { - if (!isOAuthEnabled) { - throw new OAuthRequestException("invalid OAuth request"); - } - return authorizedScopes.toArray(new String[0]); - } - - @Deprecated - @Override - public String getOAuthConsumerKey() { - throw new UnsupportedOperationException(); - } -} diff --git a/core/src/test/java/google/registry/testing/FakeUserService.java b/core/src/test/java/google/registry/testing/FakeUserService.java deleted file mode 100644 index a78a5fcb5..000000000 --- a/core/src/test/java/google/registry/testing/FakeUserService.java +++ /dev/null @@ -1,76 +0,0 @@ -// 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 google.registry.model.annotations.DeleteAfterMigration; -import java.util.Set; -import javax.annotation.Nullable; - -/** Fake implementation of {@link UserService} for testing. */ -@DeleteAfterMigration -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) { - return String.format("/login?dest=%s", destinationURL); - } - - @Override - public String createLoginURL(String destinationURL, String authDomain) { - return createLoginURL(destinationURL); - } - - @Deprecated - @Override - public String createLoginURL(String destinationURL, String authDomain, String federatedIdentity, - Set attributesRequest) { - throw new UnsupportedOperationException(); - } - - @Override - public String createLogoutURL(String destinationURL) { - return String.format("/logout?dest=%s", destinationURL); - } - - @Override - public String createLogoutURL(String destinationURL, String authDomain) { - return createLogoutURL(destinationURL); - } - - @Override - public boolean isUserLoggedIn() { - return user != null; - } - - @Override - public boolean isUserAdmin() { - return isAdmin; - } - - @Override - public User getCurrentUser() { - return user; - } -} diff --git a/core/src/test/java/google/registry/tools/AuthModuleTest.java b/core/src/test/java/google/registry/tools/AuthModuleTest.java index 3e32e1688..e3676d6bf 100644 --- a/core/src/test/java/google/registry/tools/AuthModuleTest.java +++ b/core/src/test/java/google/registry/tools/AuthModuleTest.java @@ -72,7 +72,7 @@ class AuthModuleTest { } }) // We need to set the following fields because they are checked when - // Credential#setRefreshToken is called. However they are not actually persisted in the + // Credential#setRefreshToken is called. However, they are not actually persisted in the // DataStore and not actually used in tests. .setJsonFactory(new GsonFactory()) .setTransport(new NetHttpTransport()) @@ -104,7 +104,7 @@ class AuthModuleTest { AuthModule.provideClientScopeQualifier("client-id", ImmutableList.of("foo", "bar")); // If we change the way we encode client id and scopes, this assertion will break. That's - // probably ok and you can just change the text. The things you have to be aware of are: + // probably ok, and you can just change the text. The things you have to be aware of are: // - Names in the new encoding should have a low risk of collision with the old encoding. // - Changing the encoding will force all OAuth users of the nomulus tool to do a new login // (existing credentials will not be used). @@ -155,7 +155,7 @@ class AuthModuleTest { AuthModule.provideClientScopeQualifier(AuthModule.provideClientId(clientSecrets), scopes)); } - private GoogleClientSecrets getSecrets() { + private static GoogleClientSecrets getSecrets() { return new GoogleClientSecrets() .setInstalled( AuthModule.provideDefaultInstalledDetails() @@ -166,7 +166,8 @@ class AuthModuleTest { @Test void test_provideLocalCredentialJson() { String credentialJson = - AuthModule.provideLocalCredentialJson(this::getSecrets, this::getCredential, null); + AuthModule.provideLocalCredentialJson( + AuthModuleTest::getSecrets, this::getCredential, null); Map jsonMap = new Gson().fromJson(credentialJson, new TypeToken>() {}.getType()); assertThat(jsonMap.get("type")).isEqualTo("authorized_user"); @@ -182,7 +183,7 @@ class AuthModuleTest { Files.write(credentialFile.toPath(), "{some_field: some_value}".getBytes(UTF_8)); String credentialJson = AuthModule.provideLocalCredentialJson( - this::getSecrets, this::getCredential, credentialFile.getCanonicalPath()); + AuthModuleTest::getSecrets, this::getCredential, credentialFile.getCanonicalPath()); assertThat(credentialJson).isEqualTo("{some_field: some_value}"); } diff --git a/core/src/test/java/google/registry/tools/RequestFactoryModuleTest.java b/core/src/test/java/google/registry/tools/RequestFactoryModuleTest.java index 21866b95d..54f3a32cd 100644 --- a/core/src/test/java/google/registry/tools/RequestFactoryModuleTest.java +++ b/core/src/test/java/google/registry/tools/RequestFactoryModuleTest.java @@ -18,9 +18,6 @@ import static com.google.common.truth.Truth.assertThat; import static google.registry.tools.RequestFactoryModule.REQUEST_TIMEOUT_MS; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import com.google.api.client.http.GenericUrl; @@ -35,6 +32,7 @@ import com.google.auth.oauth2.UserCredentials; import google.registry.config.RegistryConfig; import google.registry.testing.SystemPropertyExtension; import google.registry.util.GoogleCredentialsBundle; +import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -50,7 +48,6 @@ public class RequestFactoryModuleTest { final SystemPropertyExtension systemPropertyExtension = new SystemPropertyExtension(); @Mock public GoogleCredentialsBundle credentialsBundle; - @Mock public HttpRequestInitializer httpRequestInitializer; @BeforeEach void beforeEach() { @@ -64,12 +61,11 @@ public class RequestFactoryModuleTest { RegistryConfig.CONFIG_SETTINGS.get().gcpProject.isLocal = true; try { HttpRequestFactory factory = - RequestFactoryModule.provideHttpRequestFactory(credentialsBundle, "client-id", false); + RequestFactoryModule.provideHttpRequestFactory(credentialsBundle, "client-id"); HttpRequestInitializer initializer = factory.getInitializer(); assertThat(initializer).isNotNull(); HttpRequest request = factory.buildGetRequest(new GenericUrl("http://localhost")); initializer.initialize(request); - verifyNoInteractions(httpRequestInitializer); } finally { RegistryConfig.CONFIG_SETTINGS.get().gcpProject.isLocal = origIsLocal; } @@ -77,7 +73,6 @@ public class RequestFactoryModuleTest { @Test void test_provideHttpRequestFactory_remote() throws Exception { - when(credentialsBundle.getHttpRequestInitializer()).thenReturn(httpRequestInitializer); // Mock the request/response to/from the OIDC server requesting an ID token UserCredentials mockUserCredentials = mock(UserCredentials.class); when(credentialsBundle.getGoogleCredentials()).thenReturn(mockUserCredentials); @@ -97,23 +92,15 @@ public class RequestFactoryModuleTest { boolean origIsLocal = RegistryConfig.CONFIG_SETTINGS.get().gcpProject.isLocal; RegistryConfig.CONFIG_SETTINGS.get().gcpProject.isLocal = false; try { - // With OAuth header. HttpRequestFactory factory = - RequestFactoryModule.provideHttpRequestFactory(credentialsBundle, "clientId", true); + RequestFactoryModule.provideHttpRequestFactory(credentialsBundle, "clientId"); HttpRequest request = factory.buildGetRequest(new GenericUrl("http://localhost")); - assertThat(request.getHeaders().get("Proxy-Authorization")).isEqualTo("Bearer oidc.token"); + @SuppressWarnings("unchecked") + List authHeaders = (List) request.getHeaders().get("Authorization"); + assertThat(authHeaders.size()).isEqualTo(1); + assertThat(authHeaders.get(0)).isEqualTo("Bearer oidc.token"); assertThat(request.getConnectTimeout()).isEqualTo(REQUEST_TIMEOUT_MS); assertThat(request.getReadTimeout()).isEqualTo(REQUEST_TIMEOUT_MS); - verify(httpRequestInitializer).initialize(request); - verifyNoMoreInteractions(httpRequestInitializer); - // No OAuth header. - factory = - RequestFactoryModule.provideHttpRequestFactory(credentialsBundle, "clientId", false); - request = factory.buildGetRequest(new GenericUrl("http://localhost")); - assertThat(request.getHeaders().get("Proxy-Authorization")).isEqualTo("Bearer oidc.token"); - assertThat(request.getConnectTimeout()).isEqualTo(REQUEST_TIMEOUT_MS); - assertThat(request.getReadTimeout()).isEqualTo(REQUEST_TIMEOUT_MS); - verifyNoMoreInteractions(httpRequestInitializer); } finally { RegistryConfig.CONFIG_SETTINGS.get().gcpProject.isLocal = origIsLocal; } diff --git a/core/src/test/java/google/registry/ui/server/console/ConsoleDomainGetActionTest.java b/core/src/test/java/google/registry/ui/server/console/ConsoleDomainGetActionTest.java index e75f5990d..8ccdcc2b5 100644 --- a/core/src/test/java/google/registry/ui/server/console/ConsoleDomainGetActionTest.java +++ b/core/src/test/java/google/registry/ui/server/console/ConsoleDomainGetActionTest.java @@ -27,7 +27,6 @@ import google.registry.model.console.UserRoles; import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.request.RequestModule; import google.registry.request.auth.AuthResult; -import google.registry.request.auth.AuthSettings.AuthLevel; import google.registry.request.auth.UserAuthInfo; import google.registry.testing.DatabaseHelper; import google.registry.testing.FakeResponse; @@ -55,8 +54,7 @@ public class ConsoleDomainGetActionTest { void testSuccess_fullJsonRepresentation() { ConsoleDomainGetAction action = createAction( - AuthResult.create( - AuthLevel.USER, + AuthResult.createUser( UserAuthInfo.create( createUser( new UserRoles.Builder() @@ -85,7 +83,8 @@ public class ConsoleDomainGetActionTest { @Test void testFailure_appAuth() { - ConsoleDomainGetAction action = createAction(AuthResult.create(AuthLevel.APP), "exists.tld"); + ConsoleDomainGetAction action = + createAction(AuthResult.createApp("service@registry.example"), "exists.tld"); action.run(); assertThat(RESPONSE.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_UNAUTHORIZED); } @@ -94,8 +93,7 @@ public class ConsoleDomainGetActionTest { void testFailure_wrongTypeOfUser() { ConsoleDomainGetAction action = createAction( - AuthResult.create( - AuthLevel.USER, + AuthResult.createUser( UserAuthInfo.create(mock(com.google.appengine.api.users.User.class), false)), "exists.tld"); action.run(); @@ -106,8 +104,7 @@ public class ConsoleDomainGetActionTest { void testFailure_noAccessToRegistrar() { ConsoleDomainGetAction action = createAction( - AuthResult.create( - AuthLevel.USER, UserAuthInfo.create(createUser(new UserRoles.Builder().build()))), + AuthResult.createUser(UserAuthInfo.create(createUser(new UserRoles.Builder().build()))), "exists.tld"); action.run(); assertThat(RESPONSE.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_NOT_FOUND); @@ -117,8 +114,7 @@ public class ConsoleDomainGetActionTest { void testFailure_nonexistentDomain() { ConsoleDomainGetAction action = createAction( - AuthResult.create( - AuthLevel.USER, + AuthResult.createUser( UserAuthInfo.create(createUser(new UserRoles.Builder().setIsAdmin(true).build()))), "nonexistent.tld"); action.run(); diff --git a/core/src/test/java/google/registry/ui/server/console/ConsoleUserDataActionTest.java b/core/src/test/java/google/registry/ui/server/console/ConsoleUserDataActionTest.java index 424560ace..30b61b81b 100644 --- a/core/src/test/java/google/registry/ui/server/console/ConsoleUserDataActionTest.java +++ b/core/src/test/java/google/registry/ui/server/console/ConsoleUserDataActionTest.java @@ -24,7 +24,6 @@ import google.registry.model.console.UserRoles; import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.request.RequestModule; import google.registry.request.auth.AuthResult; -import google.registry.request.auth.AuthSettings.AuthLevel; import google.registry.request.auth.UserAuthInfo; import google.registry.testing.FakeResponse; import java.io.IOException; @@ -50,8 +49,7 @@ class ConsoleUserDataActionTest { .setUserRoles(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build()) .build(); - ConsoleUserDataAction action = - createAction(AuthResult.create(AuthLevel.USER, UserAuthInfo.create(user))); + ConsoleUserDataAction action = createAction(AuthResult.createUser(UserAuthInfo.create(user))); action.run(); assertThat(response.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_OK); Map jsonObject = GSON.fromJson(response.getPayload(), Map.class); @@ -63,8 +61,7 @@ class ConsoleUserDataActionTest { void testFailure_notAConsoleUser() throws IOException { ConsoleUserDataAction action = createAction( - AuthResult.create( - AuthLevel.USER, + AuthResult.createUser( UserAuthInfo.create( new com.google.appengine.api.users.User( "JohnDoe@theregistrar.com", "theregistrar.com"), diff --git a/core/src/test/java/google/registry/ui/server/console/RegistrarsActionTest.java b/core/src/test/java/google/registry/ui/server/console/RegistrarsActionTest.java index 5cd6f8226..ff6db88f5 100644 --- a/core/src/test/java/google/registry/ui/server/console/RegistrarsActionTest.java +++ b/core/src/test/java/google/registry/ui/server/console/RegistrarsActionTest.java @@ -38,7 +38,6 @@ import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.request.Action; import google.registry.request.RequestModule; import google.registry.request.auth.AuthResult; -import google.registry.request.auth.AuthSettings.AuthLevel; import google.registry.request.auth.UserAuthInfo; import google.registry.testing.DeterministicStringGenerator; import google.registry.testing.FakeResponse; @@ -108,8 +107,7 @@ class RegistrarsActionTest { RegistrarsAction action = createAction( Action.Method.GET, - AuthResult.create( - AuthLevel.USER, + AuthResult.createUser( UserAuthInfo.create( createUser( new UserRoles.Builder().setGlobalRole(GlobalRole.SUPPORT_LEAD).build())))); @@ -129,8 +127,7 @@ class RegistrarsActionTest { RegistrarsAction action = createAction( Action.Method.GET, - AuthResult.create( - AuthLevel.USER, + AuthResult.createUser( UserAuthInfo.create( createUser(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build())))); action.run(); @@ -151,8 +148,7 @@ class RegistrarsActionTest { RegistrarsAction action = createAction( Action.Method.POST, - AuthResult.create( - AuthLevel.USER, + AuthResult.createUser( UserAuthInfo.create(createUser(new UserRoles.Builder().setIsAdmin(true).build())))); action.run(); assertThat(response.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_OK); @@ -180,8 +176,7 @@ class RegistrarsActionTest { RegistrarsAction action = createAction( Action.Method.POST, - AuthResult.create( - AuthLevel.USER, + AuthResult.createUser( UserAuthInfo.create( createUser(new UserRoles.Builder().setIsAdmin(true).build())))); action.run(); @@ -200,8 +195,7 @@ class RegistrarsActionTest { RegistrarsAction action = createAction( Action.Method.POST, - AuthResult.create( - AuthLevel.USER, + AuthResult.createUser( UserAuthInfo.create(createUser(new UserRoles.Builder().setIsAdmin(true).build())))); action.run(); assertThat(response.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_BAD_REQUEST); @@ -215,8 +209,7 @@ class RegistrarsActionTest { RegistrarsAction action = createAction( Action.Method.GET, - AuthResult.create( - AuthLevel.USER, + AuthResult.createUser( UserAuthInfo.create( createUser( new UserRoles.Builder() diff --git a/core/src/test/java/google/registry/ui/server/console/settings/ContactActionTest.java b/core/src/test/java/google/registry/ui/server/console/settings/ContactActionTest.java index 33441f208..48aedf0af 100644 --- a/core/src/test/java/google/registry/ui/server/console/settings/ContactActionTest.java +++ b/core/src/test/java/google/registry/ui/server/console/settings/ContactActionTest.java @@ -37,7 +37,6 @@ import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.request.Action; import google.registry.request.RequestModule; import google.registry.request.auth.AuthResult; -import google.registry.request.auth.AuthSettings.AuthLevel; import google.registry.request.auth.UserAuthInfo; import google.registry.testing.FakeResponse; import google.registry.ui.server.registrar.RegistrarConsoleModule; @@ -103,8 +102,7 @@ class ContactActionTest { ContactAction action = createAction( Action.Method.GET, - AuthResult.create( - AuthLevel.USER, + AuthResult.createUser( UserAuthInfo.create( createUser(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build()))), testRegistrar.getRegistrarId(), @@ -121,8 +119,7 @@ class ContactActionTest { ContactAction action = createAction( Action.Method.GET, - AuthResult.create( - AuthLevel.USER, + AuthResult.createUser( UserAuthInfo.create( createUser(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build()))), testRegistrar.getRegistrarId(), @@ -137,8 +134,7 @@ class ContactActionTest { ContactAction action = createAction( Action.Method.POST, - AuthResult.create( - AuthLevel.USER, + AuthResult.createUser( UserAuthInfo.create( createUser(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build()))), testRegistrar.getRegistrarId(), @@ -160,8 +156,7 @@ class ContactActionTest { ContactAction action = createAction( Action.Method.POST, - AuthResult.create( - AuthLevel.USER, + AuthResult.createUser( UserAuthInfo.create( createUser(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build()))), testRegistrar.getRegistrarId(), @@ -186,8 +181,7 @@ class ContactActionTest { ContactAction action = createAction( Action.Method.POST, - AuthResult.create( - AuthLevel.USER, + AuthResult.createUser( UserAuthInfo.create( createUser(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build()))), testRegistrar.getRegistrarId(), @@ -208,8 +202,7 @@ class ContactActionTest { ContactAction action = createAction( Action.Method.POST, - AuthResult.create( - AuthLevel.USER, + AuthResult.createUser( UserAuthInfo.create( createUser( new UserRoles.Builder() diff --git a/core/src/test/java/google/registry/ui/server/console/settings/SecurityActionTest.java b/core/src/test/java/google/registry/ui/server/console/settings/SecurityActionTest.java index ee47a729f..f5acd5c13 100644 --- a/core/src/test/java/google/registry/ui/server/console/settings/SecurityActionTest.java +++ b/core/src/test/java/google/registry/ui/server/console/settings/SecurityActionTest.java @@ -35,7 +35,6 @@ import google.registry.model.registrar.Registrar; import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.request.RequestModule; import google.registry.request.auth.AuthResult; -import google.registry.request.auth.AuthSettings.AuthLevel; import google.registry.request.auth.AuthenticatedRegistrarAccessor; import google.registry.request.auth.UserAuthInfo; import google.registry.testing.FakeClock; @@ -92,8 +91,7 @@ class SecurityActionTest { clock.setTo(DateTime.parse("2020-11-01T00:00:00Z")); SecurityAction action = createAction( - AuthResult.create( - AuthLevel.USER, + AuthResult.createUser( UserAuthInfo.create( createUser(new UserRoles.Builder().setGlobalRole(GlobalRole.FTE).build()))), testRegistrar.getRegistrarId()); diff --git a/core/src/test/java/google/registry/ui/server/console/settings/WhoisRegistrarFieldsActionTest.java b/core/src/test/java/google/registry/ui/server/console/settings/WhoisRegistrarFieldsActionTest.java index 535b5d8f7..1e2aef926 100644 --- a/core/src/test/java/google/registry/ui/server/console/settings/WhoisRegistrarFieldsActionTest.java +++ b/core/src/test/java/google/registry/ui/server/console/settings/WhoisRegistrarFieldsActionTest.java @@ -33,7 +33,6 @@ import google.registry.model.registrar.Registrar; import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.request.RequestModule; import google.registry.request.auth.AuthResult; -import google.registry.request.auth.AuthSettings.AuthLevel; import google.registry.request.auth.AuthenticatedRegistrarAccessor; import google.registry.request.auth.AuthenticatedRegistrarAccessor.Role; import google.registry.request.auth.UserAuthInfo; @@ -127,8 +126,7 @@ public class WhoisRegistrarFieldsActionTest { void testFailure_noAccessToRegistrar() throws Exception { Registrar newRegistrar = Registrar.loadByRegistrarIdCached("NewRegistrar").get(); AuthResult onlyTheRegistrar = - AuthResult.create( - AuthLevel.USER, + AuthResult.createUser( UserAuthInfo.create( new User.Builder() .setEmailAddress("email@email.example") @@ -147,8 +145,7 @@ public class WhoisRegistrarFieldsActionTest { } private AuthResult defaultUserAuth() { - return AuthResult.create( - AuthLevel.USER, + return AuthResult.createUser( UserAuthInfo.create( new User.Builder() .setEmailAddress("email@email.example") diff --git a/core/src/test/java/google/registry/ui/server/registrar/ConsoleOteSetupActionTest.java b/core/src/test/java/google/registry/ui/server/registrar/ConsoleOteSetupActionTest.java index b3d78c130..b71f91fb2 100644 --- a/core/src/test/java/google/registry/ui/server/registrar/ConsoleOteSetupActionTest.java +++ b/core/src/test/java/google/registry/ui/server/registrar/ConsoleOteSetupActionTest.java @@ -37,7 +37,6 @@ import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension; import google.registry.request.Action.Method; import google.registry.request.auth.AuthResult; -import google.registry.request.auth.AuthSettings.AuthLevel; import google.registry.request.auth.AuthenticatedRegistrarAccessor; import google.registry.request.auth.UserAuthInfo; import google.registry.security.XsrfTokenManager; @@ -93,7 +92,7 @@ public final class ConsoleOteSetupActionTest { ImmutableSetMultimap.of("unused", AuthenticatedRegistrarAccessor.Role.ADMIN)); action.userService = UserServiceFactory.getUserService(); action.xsrfTokenManager = new XsrfTokenManager(new FakeClock(), action.userService); - action.authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(user, false)); + action.authResult = AuthResult.createUser(UserAuthInfo.create(user, false)); action.sendEmailUtils = new SendEmailUtils( new InternetAddress("outgoing@registry.example"), diff --git a/core/src/test/java/google/registry/ui/server/registrar/ConsoleRegistrarCreatorActionTest.java b/core/src/test/java/google/registry/ui/server/registrar/ConsoleRegistrarCreatorActionTest.java index 41439e5f3..16dba14d9 100644 --- a/core/src/test/java/google/registry/ui/server/registrar/ConsoleRegistrarCreatorActionTest.java +++ b/core/src/test/java/google/registry/ui/server/registrar/ConsoleRegistrarCreatorActionTest.java @@ -37,7 +37,6 @@ import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension; import google.registry.request.Action.Method; import google.registry.request.auth.AuthResult; -import google.registry.request.auth.AuthSettings.AuthLevel; import google.registry.request.auth.AuthenticatedRegistrarAccessor; import google.registry.request.auth.UserAuthInfo; import google.registry.security.XsrfTokenManager; @@ -93,7 +92,7 @@ final class ConsoleRegistrarCreatorActionTest { ImmutableSetMultimap.of("unused", AuthenticatedRegistrarAccessor.Role.ADMIN)); action.userService = UserServiceFactory.getUserService(); action.xsrfTokenManager = new XsrfTokenManager(new FakeClock(), action.userService); - action.authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(user, false)); + action.authResult = AuthResult.createUser(UserAuthInfo.create(user, false)); action.sendEmailUtils = new SendEmailUtils( new InternetAddress("outgoing@registry.example"), diff --git a/core/src/test/java/google/registry/ui/server/registrar/ConsoleUiActionTest.java b/core/src/test/java/google/registry/ui/server/registrar/ConsoleUiActionTest.java index a851c5a99..0cc27215c 100644 --- a/core/src/test/java/google/registry/ui/server/registrar/ConsoleUiActionTest.java +++ b/core/src/test/java/google/registry/ui/server/registrar/ConsoleUiActionTest.java @@ -32,7 +32,6 @@ import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension; import google.registry.request.Action.Method; import google.registry.request.auth.AuthResult; -import google.registry.request.auth.AuthSettings.AuthLevel; import google.registry.request.auth.AuthenticatedRegistrarAccessor; import google.registry.request.auth.UserAuthInfo; import google.registry.security.XsrfTokenManager; @@ -78,7 +77,7 @@ class ConsoleUiActionTest { action.xsrfTokenManager = new XsrfTokenManager(new FakeClock(), action.userService); action.method = Method.GET; action.paramClientId = Optional.empty(); - action.authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(user, false)); + action.authResult = AuthResult.createUser(UserAuthInfo.create(user, false)); action.analyticsConfig = ImmutableMap.of("googleAnalyticsId", "sampleId"); action.registrarAccessor = diff --git a/core/src/test/java/google/registry/ui/server/registrar/RegistrarSettingsActionTestCase.java b/core/src/test/java/google/registry/ui/server/registrar/RegistrarSettingsActionTestCase.java index be8ac9693..bc1f111b5 100644 --- a/core/src/test/java/google/registry/ui/server/registrar/RegistrarSettingsActionTestCase.java +++ b/core/src/test/java/google/registry/ui/server/registrar/RegistrarSettingsActionTestCase.java @@ -44,7 +44,6 @@ import google.registry.request.JsonActionRunner; import google.registry.request.JsonResponse; import google.registry.request.ResponseImpl; import google.registry.request.auth.AuthResult; -import google.registry.request.auth.AuthSettings.AuthLevel; import google.registry.request.auth.AuthenticatedRegistrarAccessor; import google.registry.request.auth.UserAuthInfo; import google.registry.testing.CloudTasksHelper; @@ -113,8 +112,7 @@ public abstract class RegistrarSettingsActionTestCase { gmailClient); action.registrarConsoleMetrics = new RegistrarConsoleMetrics(); action.authResult = - AuthResult.create( - AuthLevel.USER, + AuthResult.createUser( UserAuthInfo.create(new User("user@email.com", "email.com", "12345"), false)); action.certificateChecker = new CertificateChecker( diff --git a/core/src/test/java/google/registry/ui/server/registrar/RegistryLockGetActionTest.java b/core/src/test/java/google/registry/ui/server/registrar/RegistryLockGetActionTest.java index a9e1f5c2f..e348ded0b 100644 --- a/core/src/test/java/google/registry/ui/server/registrar/RegistryLockGetActionTest.java +++ b/core/src/test/java/google/registry/ui/server/registrar/RegistryLockGetActionTest.java @@ -40,7 +40,6 @@ import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension; import google.registry.request.Action.Method; import google.registry.request.auth.AuthResult; -import google.registry.request.auth.AuthSettings.AuthLevel; import google.registry.request.auth.AuthenticatedRegistrarAccessor; import google.registry.request.auth.UserAuthInfo; import google.registry.testing.FakeClock; @@ -75,7 +74,7 @@ final class RegistryLockGetActionTest { void beforeEach() { user = userFromRegistrarPoc(makeRegistrarContact3()); fakeClock.setTo(DateTime.parse("2000-06-08T22:00:00.0Z")); - authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(user, false)); + authResult = AuthResult.createUser(UserAuthInfo.create(user, false)); accessor = AuthenticatedRegistrarAccessor.createForTesting( ImmutableSetMultimap.of( @@ -109,7 +108,7 @@ final class RegistryLockGetActionTest { .build()) .build(); - action.authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(consoleUser)); + action.authResult = AuthResult.createUser(UserAuthInfo.create(consoleUser)); action.run(); assertThat(response.getStatus()).isEqualTo(HttpStatusCodes.STATUS_CODE_OK); assertThat(GSON.fromJson(response.getPayload(), Map.class)) @@ -336,7 +335,7 @@ final class RegistryLockGetActionTest { persistResource(makeRegistrar2().asBuilder().setRegistryLockAllowed(false).build()); // disallow the other user persistResource(makeRegistrarContact2().asBuilder().setLoginEmailAddress(null).build()); - authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(user, true)); + authResult = AuthResult.createUser(UserAuthInfo.create(user, true)); accessor = AuthenticatedRegistrarAccessor.createForTesting( ImmutableSetMultimap.of( @@ -364,7 +363,7 @@ final class RegistryLockGetActionTest { void testSuccess_linkedToLoginContactEmail() { // Note that the email address is case-insensitive. user = new User("marla.singer@crr.com", "crr.com", user.getUserId()); - authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(user, false)); + authResult = AuthResult.createUser(UserAuthInfo.create(user, false)); action = new RegistryLockGetAction( Method.GET, response, accessor, authResult, Optional.of("TheRegistrar")); diff --git a/core/src/test/java/google/registry/ui/server/registrar/RegistryLockPostActionTest.java b/core/src/test/java/google/registry/ui/server/registrar/RegistryLockPostActionTest.java index b2a7c96c3..65411647c 100644 --- a/core/src/test/java/google/registry/ui/server/registrar/RegistryLockPostActionTest.java +++ b/core/src/test/java/google/registry/ui/server/registrar/RegistryLockPostActionTest.java @@ -45,7 +45,6 @@ import google.registry.request.JsonActionRunner; import google.registry.request.JsonResponse; import google.registry.request.ResponseImpl; import google.registry.request.auth.AuthResult; -import google.registry.request.auth.AuthSettings.AuthLevel; import google.registry.request.auth.AuthenticatedRegistrarAccessor; import google.registry.request.auth.AuthenticatedRegistrarAccessor.Role; import google.registry.request.auth.UserAuthInfo; @@ -115,8 +114,7 @@ final class RegistryLockPostActionTest { when(mockRequest.getServerName()).thenReturn("registrarconsole.tld"); action = - createAction( - AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userWithLockPermission, false))); + createAction(AuthResult.createUser(UserAuthInfo.create(userWithLockPermission, false))); } @Test @@ -154,9 +152,7 @@ final class RegistryLockPostActionTest { saveRegistryLock( createLock().asBuilder().isSuperuser(true).setLockCompletionTime(clock.nowUtc()).build()); persistResource(domain.asBuilder().setStatusValues(REGISTRY_LOCK_STATUSES).build()); - action = - createAction( - AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userWithoutPermission, true))); + action = createAction(AuthResult.createUser(UserAuthInfo.create(userWithoutPermission, true))); Map response = action.handleJsonRequest(unlockRequest()); // we should still email the admin user's email address assertSuccess(response, "unlock", "johndoe@theregistrar.com"); @@ -166,8 +162,7 @@ final class RegistryLockPostActionTest { void testSuccess_linkedToLoginEmail() throws Exception { userWithLockPermission = new User("Marla.Singer@crr.com", "crr.com"); action = - createAction( - AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userWithLockPermission, false))); + createAction(AuthResult.createUser(UserAuthInfo.create(userWithLockPermission, false))); Map response = action.handleJsonRequest(lockRequest()); assertSuccess(response, "lock", "Marla.Singer.RegistryLock@crr.com"); } @@ -205,18 +200,14 @@ final class RegistryLockPostActionTest { @Test void testSuccess_adminUser() throws Exception { // Admin user should be able to lock/unlock regardless -- and we use the admin user's email - action = - createAction( - AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userWithoutPermission, true))); + action = createAction(AuthResult.createUser(UserAuthInfo.create(userWithoutPermission, true))); Map response = action.handleJsonRequest(lockRequest()); assertSuccess(response, "lock", "johndoe@theregistrar.com"); } @Test void testSuccess_adminUser_doesNotRequirePassword() throws Exception { - action = - createAction( - AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userWithoutPermission, true))); + action = createAction(AuthResult.createUser(UserAuthInfo.create(userWithoutPermission, true))); Map response = action.handleJsonRequest( ImmutableMap.of( @@ -239,8 +230,7 @@ final class RegistryLockPostActionTest { .build()) .setRegistryLockPassword("hi") .build(); - AuthResult consoleAuthResult = - AuthResult.create(AuthLevel.USER, UserAuthInfo.create(consoleUser)); + AuthResult consoleAuthResult = AuthResult.createUser(UserAuthInfo.create(consoleUser)); action = createAction(consoleAuthResult); Map response = action.handleJsonRequest(lockRequest()); assertSuccess(response, "lock", "johndoe@theregistrar.com"); @@ -253,8 +243,7 @@ final class RegistryLockPostActionTest { .setEmailAddress("johndoe@theregistrar.com") .setUserRoles(new UserRoles.Builder().setIsAdmin(true).build()) .build(); - AuthResult consoleAuthResult = - AuthResult.create(AuthLevel.USER, UserAuthInfo.create(consoleUser)); + AuthResult consoleAuthResult = AuthResult.createUser(UserAuthInfo.create(consoleUser)); action = createAction(consoleAuthResult); Map requestMapWithoutPassword = ImmutableMap.of( @@ -286,7 +275,7 @@ final class RegistryLockPostActionTest { @Test void testFailure_unauthorizedRegistrarId() { AuthResult authResult = - AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userWithLockPermission, false)); + AuthResult.createUser(UserAuthInfo.create(userWithLockPermission, false)); action = createAction(authResult, ImmutableSet.of("TheRegistrar")); Map response = action.handleJsonRequest( @@ -358,9 +347,7 @@ final class RegistryLockPostActionTest { @Test void testFailure_notEnabledForRegistrarPoc() { - action = - createAction( - AuthResult.create(AuthLevel.USER, UserAuthInfo.create(userWithoutPermission, false))); + action = createAction(AuthResult.createUser(UserAuthInfo.create(userWithoutPermission, false))); Map response = action.handleJsonRequest( ImmutableMap.of( @@ -453,8 +440,7 @@ final class RegistryLockPostActionTest { .build()) .setRegistryLockPassword("hi") .build(); - AuthResult consoleAuthResult = - AuthResult.create(AuthLevel.USER, UserAuthInfo.create(consoleUser)); + AuthResult consoleAuthResult = AuthResult.createUser(UserAuthInfo.create(consoleUser)); action = createAction(consoleAuthResult); Map response = action.handleJsonRequest( diff --git a/core/src/test/java/google/registry/ui/server/registrar/RegistryLockVerifyActionTest.java b/core/src/test/java/google/registry/ui/server/registrar/RegistryLockVerifyActionTest.java index 1ff6a1868..adb7bebe8 100644 --- a/core/src/test/java/google/registry/ui/server/registrar/RegistryLockVerifyActionTest.java +++ b/core/src/test/java/google/registry/ui/server/registrar/RegistryLockVerifyActionTest.java @@ -43,7 +43,6 @@ import google.registry.model.tld.Tld; import google.registry.persistence.transaction.JpaTestExtensions; import google.registry.persistence.transaction.JpaTestExtensions.JpaIntegrationTestExtension; import google.registry.request.auth.AuthResult; -import google.registry.request.auth.AuthSettings.AuthLevel; import google.registry.request.auth.UserAuthInfo; import google.registry.security.XsrfTokenManager; import google.registry.testing.CloudTasksHelper; @@ -132,7 +131,7 @@ final class RegistryLockVerifyActionTest { @Test void testSuccess_adminLock_createsOnlyHistoryEntry() { - action.authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(user, true)); + action.authResult = AuthResult.createUser(UserAuthInfo.create(user, true)); saveRegistryLock(createLock().asBuilder().isSuperuser(true).build()); action.run(); @@ -332,7 +331,7 @@ final class RegistryLockVerifyActionTest { stringGenerator, "adminreg", cloudTasksHelper.getTestCloudTasksUtils()), lockVerificationCode, isLock); - authResult = AuthResult.create(AuthLevel.USER, UserAuthInfo.create(user, false)); + authResult = AuthResult.createUser(UserAuthInfo.create(user, false)); action.req = request; action.response = response; action.authResult = authResult; diff --git a/core/src/test/resources/google/registry/module/frontend/frontend_routing.txt b/core/src/test/resources/google/registry/module/frontend/frontend_routing.txt index e0f7fccf9..23d1886c6 100644 --- a/core/src/test/resources/google/registry/module/frontend/frontend_routing.txt +++ b/core/src/test/resources/google/registry/module/frontend/frontend_routing.txt @@ -1,5 +1,5 @@ PATH CLASS METHODS OK AUTH_METHODS MIN USER_POLICY -/_dr/epp EppTlsAction POST n API APP PUBLIC +/_dr/epp EppTlsAction POST n API APP ADMIN /console-api/domain ConsoleDomainGetAction GET n API,LEGACY USER PUBLIC /console-api/registrars RegistrarsAction GET,POST n API,LEGACY USER PUBLIC /console-api/settings/contacts ContactAction GET,POST n API,LEGACY USER PUBLIC @@ -13,4 +13,4 @@ PATH CLASS METHODS OK AUT /registrar-settings RegistrarSettingsAction POST n API,LEGACY USER PUBLIC /registry-lock-get RegistryLockGetAction GET n API,LEGACY USER PUBLIC /registry-lock-post RegistryLockPostAction POST n API,LEGACY USER PUBLIC -/registry-lock-verify RegistryLockVerifyAction GET n API,LEGACY NONE PUBLIC \ No newline at end of file +/registry-lock-verify RegistryLockVerifyAction GET n API,LEGACY NONE PUBLIC diff --git a/core/src/test/resources/google/registry/module/pubapi/pubapi_routing.txt b/core/src/test/resources/google/registry/module/pubapi/pubapi_routing.txt index 10d924c9e..2d832632a 100644 --- a/core/src/test/resources/google/registry/module/pubapi/pubapi_routing.txt +++ b/core/src/test/resources/google/registry/module/pubapi/pubapi_routing.txt @@ -1,5 +1,5 @@ PATH CLASS METHODS OK AUTH_METHODS MIN USER_POLICY -/_dr/whois WhoisAction POST n API APP PUBLIC +/_dr/whois WhoisAction POST n API APP ADMIN /check CheckApiAction GET n API,LEGACY NONE PUBLIC /rdap/autnum/(*) RdapAutnumAction GET,HEAD n API,LEGACY NONE PUBLIC /rdap/domain/(*) RdapDomainAction GET,HEAD n API,LEGACY NONE PUBLIC diff --git a/docs/configuration.md b/docs/configuration.md index 1db9024a4..84b0caa6c 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -88,8 +88,7 @@ gSuite: For fully-featured production environments that need the full range of features (e.g. RDE, correct contact information on the registrar console, etc.) you will -need to specify more settings. The `nomulus-config-production-sample.yaml` file -contains an exhaustive list of all settings to override. +need to specify more settings. From a code perspective, all configuration settings ultimately come through the [`RegistryConfig`][registry-config] class. This includes a Dagger module called diff --git a/docs/proxy-setup.md b/docs/proxy-setup.md index b5f784276..7d6edad8f 100644 --- a/docs/proxy-setup.md +++ b/docs/proxy-setup.md @@ -134,16 +134,16 @@ takes a couple of minutes. ### Setup Nomulus After terraform completes, it outputs some information, among which is the -client id of the service account created for the proxy. This needs to be added -to the Nomulus configuration file so that Nomulus accepts traffic from the +email address of the service account created for the proxy. This needs to be +added to the Nomulus configuration file so that Nomulus accepts traffic from the proxy. Edit the following section in `java/google/registry/config/files/nomulus-config-.yaml` and redeploy Nomulus: ```yaml -oAuth: - allowedOauthClientIds: - - +auth: + allowedServiceAccountEmails: + - ``` ### Setup nameservers @@ -304,15 +304,15 @@ $ gcloud iam service-accounts keys create proxy-key.json --iam-account \ A `proxy-key.json` file will be created inside the current working directory. -The `client_id` inside the key file needs to be added to the Nomulus +The service account email address needs to be added to the Nomulus configuration file so that Nomulus accepts the OAuth tokens generated for this service account. Add its value to `java/google/registry/config/files/nomulus-config-.yaml`: ```yaml -oAuth: - allowedOauthClientIds: - - +auth: + allowedServiceAccountEmails: + - ``` Redeploy Nomulus for the change to take effect.