From 1f000b94e62c05dd60cb7ddcfa5ae094a17ff8a9 Mon Sep 17 00:00:00 2001 From: mountford Date: Fri, 10 Mar 2017 08:47:52 -0800 Subject: [PATCH] Set correct auth settings for all actions A test has been added to RequestHandlerTest, making sure that, while we merely log errors for the time being, the correct dummy AuthResult is being created. Most actions use the default settings, which have been changed to INTERNAL / APP / IGNORED. Actions with non-default settings are: INTERNAL/NONE/PUBLIC (non-auth public endpoints) CheckApiAction WhoisHttpServer Rdap*Action INTERNAL,API/APP/ADMIN (things currently protected by web.xml) EppTlsAction EppToolAction CreateGroupsAction CreatePremiumListAction DeleteEntityAction List*sAction UpdatePremiumListAction VerifyOteAction WhoisServer INTERNAL,API,LEGACY/USER/PUBLIC (registrar console) RegistrarPaymentAction RegistrarPaymentSetupAction RegistrarSettingsAction EppConsoleAction INTERNAL,API,LEGACY/NONE/PUBLIC (registrar console main page) ConsoleUiAction ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=149761652 --- java/google/registry/flows/BUILD | 1 + .../google/registry/flows/CheckApiAction.java | 7 +- .../registry/flows/EppConsoleAction.java | 17 +++- java/google/registry/flows/EppTlsAction.java | 13 ++- java/google/registry/flows/EppToolAction.java | 17 +++- java/google/registry/rdap/BUILD | 1 + .../registry/rdap/RdapAutnumAction.java | 9 +- .../registry/rdap/RdapDomainAction.java | 13 ++- .../registry/rdap/RdapDomainSearchAction.java | 17 ++-- .../registry/rdap/RdapEntityAction.java | 9 +- .../registry/rdap/RdapEntitySearchAction.java | 17 ++-- java/google/registry/rdap/RdapHelpAction.java | 13 ++- java/google/registry/rdap/RdapIpAction.java | 10 ++- .../registry/rdap/RdapNameserverAction.java | 13 ++- .../rdap/RdapNameserverSearchAction.java | 17 ++-- java/google/registry/request/auth/Auth.java | 3 +- .../request/auth/RequestAuthenticator.java | 1 + java/google/registry/tools/server/BUILD | 1 + .../tools/server/CreateGroupsAction.java | 13 ++- .../tools/server/CreatePremiumListAction.java | 13 ++- .../tools/server/DeleteEntityAction.java | 16 +++- .../tools/server/ListDomainsAction.java | 13 ++- .../tools/server/ListHostsAction.java | 13 ++- .../tools/server/ListPremiumListsAction.java | 10 ++- .../tools/server/ListRegistrarsAction.java | 13 ++- .../tools/server/ListReservedListsAction.java | 13 ++- .../registry/tools/server/ListTldsAction.java | 13 ++- .../tools/server/UpdatePremiumListAction.java | 13 ++- .../tools/server/VerifyOteAction.java | 17 +++- .../google/registry/ui/server/registrar/BUILD | 1 + .../ui/server/registrar/ConsoleUiAction.java | 14 +++- .../registrar/RegistrarPaymentAction.java | 54 +++++++----- .../RegistrarPaymentSetupAction.java | 45 ++++++---- .../registrar/RegistrarSettingsAction.java | 19 +++-- java/google/registry/whois/BUILD | 1 + .../registry/whois/WhoisHttpServer.java | 17 ++-- java/google/registry/whois/WhoisServer.java | 23 +++-- .../registry/request/RequestHandlerTest.java | 83 +++++++++++++++---- 38 files changed, 450 insertions(+), 133 deletions(-) diff --git a/java/google/registry/flows/BUILD b/java/google/registry/flows/BUILD index 90812c543..3f28e0f2e 100644 --- a/java/google/registry/flows/BUILD +++ b/java/google/registry/flows/BUILD @@ -30,6 +30,7 @@ java_library( "//java/google/registry/monitoring/whitebox", "//java/google/registry/pricing", "//java/google/registry/request", + "//java/google/registry/request/auth", "//java/google/registry/tldconfig/idn", "//java/google/registry/tmch", "//java/google/registry/util", diff --git a/java/google/registry/flows/CheckApiAction.java b/java/google/registry/flows/CheckApiAction.java index 16794367e..7677fea26 100644 --- a/java/google/registry/flows/CheckApiAction.java +++ b/java/google/registry/flows/CheckApiAction.java @@ -45,6 +45,8 @@ import google.registry.request.Action; import google.registry.request.Parameter; import google.registry.request.RequestParameters; import google.registry.request.Response; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthLevel; import google.registry.util.FormattingLogger; import java.util.Map; import javax.inject.Inject; @@ -57,7 +59,10 @@ import javax.servlet.http.HttpServletRequest; * user controlled, lest it open an XSS vector. Do not modify this to return the domain name in the * response. */ -@Action(path = "/check") +@Action( + path = "/check", + auth = @Auth(minimumLevel = AuthLevel.NONE, userPolicy = Auth.UserPolicy.PUBLIC) +) public class CheckApiAction implements Runnable { private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass(); diff --git a/java/google/registry/flows/EppConsoleAction.java b/java/google/registry/flows/EppConsoleAction.java index 059629629..7e3b82dd3 100644 --- a/java/google/registry/flows/EppConsoleAction.java +++ b/java/google/registry/flows/EppConsoleAction.java @@ -18,15 +18,24 @@ import com.google.appengine.api.users.UserService; import google.registry.request.Action; import google.registry.request.Action.Method; import google.registry.request.Payload; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthLevel; import javax.inject.Inject; import javax.servlet.http.HttpSession; /** Runs EPP from the console and requires GAE user authentication. */ @Action( - path = "/registrar-xhr", - xsrfProtection = true, - xsrfScope = EppConsoleAction.XSRF_SCOPE, - method = Method.POST) + path = "/registrar-xhr", + xsrfProtection = true, + xsrfScope = EppConsoleAction.XSRF_SCOPE, + method = Method.POST, + auth = + @Auth( + methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API, Auth.AuthMethod.LEGACY}, + minimumLevel = AuthLevel.USER, + userPolicy = Auth.UserPolicy.PUBLIC + ) +) public class EppConsoleAction implements Runnable { public static final String XSRF_SCOPE = "console"; diff --git a/java/google/registry/flows/EppTlsAction.java b/java/google/registry/flows/EppTlsAction.java index 540e34a58..87874100d 100644 --- a/java/google/registry/flows/EppTlsAction.java +++ b/java/google/registry/flows/EppTlsAction.java @@ -17,6 +17,8 @@ package google.registry.flows; import google.registry.request.Action; import google.registry.request.Action.Method; import google.registry.request.Payload; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthLevel; import google.registry.util.FormattingLogger; import javax.inject.Inject; import javax.servlet.http.HttpSession; @@ -26,8 +28,15 @@ import javax.servlet.http.HttpSession; * to RFC 5730. Commands must be requested via POST. */ @Action( - path = "/_dr/epp", - method = Method.POST) + path = "/_dr/epp", + method = Method.POST, + auth = + @Auth( + methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API}, + minimumLevel = AuthLevel.APP, + userPolicy = Auth.UserPolicy.ADMIN + ) +) public class EppTlsAction implements Runnable { private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass(); diff --git a/java/google/registry/flows/EppToolAction.java b/java/google/registry/flows/EppToolAction.java index 1aa930ce3..365520613 100644 --- a/java/google/registry/flows/EppToolAction.java +++ b/java/google/registry/flows/EppToolAction.java @@ -24,15 +24,24 @@ import google.registry.model.eppcommon.ProtocolDefinition; import google.registry.request.Action; import google.registry.request.Action.Method; import google.registry.request.Parameter; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthLevel; import javax.inject.Inject; import javax.servlet.http.HttpServletRequest; /** Runs EPP commands directly without logging in, verifying an XSRF token from the tool. */ @Action( - path = "/_dr/epptool", - xsrfProtection = true, - xsrfScope = "admin", - method = Method.POST) + path = "/_dr/epptool", + xsrfProtection = true, + xsrfScope = "admin", + method = Method.POST, + auth = + @Auth( + methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API}, + minimumLevel = AuthLevel.APP, + userPolicy = Auth.UserPolicy.ADMIN + ) +) public class EppToolAction implements Runnable { @Inject @Parameter("clientId") String clientId; diff --git a/java/google/registry/rdap/BUILD b/java/google/registry/rdap/BUILD index 8fecb3978..0499120a2 100644 --- a/java/google/registry/rdap/BUILD +++ b/java/google/registry/rdap/BUILD @@ -11,6 +11,7 @@ java_library( "//java/google/registry/config", "//java/google/registry/model", "//java/google/registry/request", + "//java/google/registry/request/auth", "//java/google/registry/util", "//third_party/java/objectify:objectify-v4_1", "@com_google_auto_value", diff --git a/java/google/registry/rdap/RdapAutnumAction.java b/java/google/registry/rdap/RdapAutnumAction.java index 3df098a79..f89709105 100644 --- a/java/google/registry/rdap/RdapAutnumAction.java +++ b/java/google/registry/rdap/RdapAutnumAction.java @@ -20,6 +20,8 @@ import static google.registry.request.Action.Method.HEAD; import com.google.common.collect.ImmutableMap; import google.registry.request.Action; import google.registry.request.HttpException.NotImplementedException; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthLevel; import javax.inject.Inject; /** @@ -28,7 +30,12 @@ import javax.inject.Inject; *

This feature is not implemented because it's only necessary for address registries like * ARIN, not domain registries. */ -@Action(path = RdapAutnumAction.PATH, method = {GET, HEAD}, isPrefix = true) +@Action( + path = RdapAutnumAction.PATH, + method = {GET, HEAD}, + isPrefix = true, + auth = @Auth(minimumLevel = AuthLevel.NONE, userPolicy = Auth.UserPolicy.PUBLIC) +) public class RdapAutnumAction extends RdapActionBase { public static final String PATH = "/rdap/autnum"; diff --git a/java/google/registry/rdap/RdapDomainAction.java b/java/google/registry/rdap/RdapDomainAction.java index 6753900a3..796474005 100644 --- a/java/google/registry/rdap/RdapDomainAction.java +++ b/java/google/registry/rdap/RdapDomainAction.java @@ -23,14 +23,19 @@ import google.registry.model.domain.DomainResource; import google.registry.rdap.RdapJsonFormatter.OutputDataType; import google.registry.request.Action; import google.registry.request.HttpException.NotFoundException; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthLevel; import google.registry.util.Clock; import javax.inject.Inject; import org.joda.time.DateTime; -/** - * RDAP (new WHOIS) action for domain requests. - */ -@Action(path = RdapDomainAction.PATH, method = {GET, HEAD}, isPrefix = true) +/** RDAP (new WHOIS) action for domain requests. */ +@Action( + path = RdapDomainAction.PATH, + method = {GET, HEAD}, + isPrefix = true, + auth = @Auth(minimumLevel = AuthLevel.NONE, userPolicy = Auth.UserPolicy.PUBLIC) +) public class RdapDomainAction extends RdapActionBase { public static final String PATH = "/rdap/domain/"; diff --git a/java/google/registry/rdap/RdapDomainSearchAction.java b/java/google/registry/rdap/RdapDomainSearchAction.java index 757bd10a5..28224e6cb 100644 --- a/java/google/registry/rdap/RdapDomainSearchAction.java +++ b/java/google/registry/rdap/RdapDomainSearchAction.java @@ -40,6 +40,8 @@ import google.registry.request.Action; import google.registry.request.HttpException.BadRequestException; import google.registry.request.HttpException.NotFoundException; import google.registry.request.Parameter; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthLevel; import google.registry.util.Clock; import google.registry.util.Idn; import java.net.InetAddress; @@ -54,12 +56,17 @@ import org.joda.time.DateTime; * *

All commands and responses conform to the RDAP spec as defined in RFCs 7480 through 7485. * - * @see - * RFC 7482: Registration Data Access Protocol (RDAP) Query Format - * @see - * RFC 7483: JSON Responses for the Registration Data Access Protocol (RDAP) + * @see RFC 7482: Registration Data Access Protocol + * (RDAP) Query Format + * @see RFC 7483: JSON Responses for the Registration + * Data Access Protocol (RDAP) */ -@Action(path = RdapDomainSearchAction.PATH, method = {GET, HEAD}, isPrefix = true) +@Action( + path = RdapDomainSearchAction.PATH, + method = {GET, HEAD}, + isPrefix = true, + auth = @Auth(minimumLevel = AuthLevel.NONE, userPolicy = Auth.UserPolicy.PUBLIC) +) public class RdapDomainSearchAction extends RdapActionBase { public static final String PATH = "/rdap/domains"; diff --git a/java/google/registry/rdap/RdapEntityAction.java b/java/google/registry/rdap/RdapEntityAction.java index cf7766a6e..0967fc56b 100644 --- a/java/google/registry/rdap/RdapEntityAction.java +++ b/java/google/registry/rdap/RdapEntityAction.java @@ -30,6 +30,8 @@ import google.registry.rdap.RdapJsonFormatter.OutputDataType; import google.registry.request.Action; import google.registry.request.HttpException.BadRequestException; import google.registry.request.HttpException.NotFoundException; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthLevel; import google.registry.util.Clock; import javax.inject.Inject; import org.joda.time.DateTime; @@ -44,7 +46,12 @@ import org.joda.time.DateTime; * response MUST contain a publicIDs member to identify the IANA Registrar ID from the IANA’s * Registrar ID registry. The type value of the publicID object MUST be equal to IANA Registrar ID. */ -@Action(path = RdapEntityAction.PATH, method = {GET, HEAD}, isPrefix = true) +@Action( + path = RdapEntityAction.PATH, + method = {GET, HEAD}, + isPrefix = true, + auth = @Auth(minimumLevel = AuthLevel.NONE, userPolicy = Auth.UserPolicy.PUBLIC) +) public class RdapEntityAction extends RdapActionBase { public static final String PATH = "/rdap/entity/"; diff --git a/java/google/registry/rdap/RdapEntitySearchAction.java b/java/google/registry/rdap/RdapEntitySearchAction.java index 113669c64..237575dff 100644 --- a/java/google/registry/rdap/RdapEntitySearchAction.java +++ b/java/google/registry/rdap/RdapEntitySearchAction.java @@ -36,6 +36,8 @@ import google.registry.request.HttpException.BadRequestException; import google.registry.request.HttpException.NotFoundException; import google.registry.request.HttpException.UnprocessableEntityException; import google.registry.request.Parameter; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthLevel; import google.registry.util.Clock; import java.util.ArrayList; import java.util.List; @@ -47,12 +49,17 @@ import org.joda.time.DateTime; * *

All commands and responses conform to the RDAP spec as defined in RFCs 7480 through 7485. * - * @see - * RFC 7482: Registration Data Access Protocol (RDAP) Query Format - * @see - * RFC 7483: JSON Responses for the Registration Data Access Protocol (RDAP) + * @see RFC 7482: Registration Data Access Protocol + * (RDAP) Query Format + * @see RFC 7483: JSON Responses for the Registration + * Data Access Protocol (RDAP) */ -@Action(path = RdapEntitySearchAction.PATH, method = {GET, HEAD}, isPrefix = true) +@Action( + path = RdapEntitySearchAction.PATH, + method = {GET, HEAD}, + isPrefix = true, + auth = @Auth(minimumLevel = AuthLevel.NONE, userPolicy = Auth.UserPolicy.PUBLIC) +) public class RdapEntitySearchAction extends RdapActionBase { public static final String PATH = "/rdap/entities"; diff --git a/java/google/registry/rdap/RdapHelpAction.java b/java/google/registry/rdap/RdapHelpAction.java index 2870275aa..e1f4cfffe 100644 --- a/java/google/registry/rdap/RdapHelpAction.java +++ b/java/google/registry/rdap/RdapHelpAction.java @@ -21,13 +21,18 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import google.registry.rdap.RdapJsonFormatter.BoilerplateType; import google.registry.request.Action; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthLevel; import google.registry.util.Clock; import javax.inject.Inject; -/** - * RDAP (new WHOIS) action for help requests. - */ -@Action(path = RdapHelpAction.PATH, method = {GET, HEAD}, isPrefix = true) +/** RDAP (new WHOIS) action for help requests. */ +@Action( + path = RdapHelpAction.PATH, + method = {GET, HEAD}, + isPrefix = true, + auth = @Auth(minimumLevel = AuthLevel.NONE, userPolicy = Auth.UserPolicy.PUBLIC) +) public class RdapHelpAction extends RdapActionBase { public static final String PATH = "/rdap/help"; diff --git a/java/google/registry/rdap/RdapIpAction.java b/java/google/registry/rdap/RdapIpAction.java index fbacb19f8..1bcc4a1a9 100644 --- a/java/google/registry/rdap/RdapIpAction.java +++ b/java/google/registry/rdap/RdapIpAction.java @@ -20,6 +20,8 @@ import static google.registry.request.Action.Method.HEAD; import com.google.common.collect.ImmutableMap; import google.registry.request.Action; import google.registry.request.HttpException.NotImplementedException; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthLevel; import javax.inject.Inject; /** @@ -27,9 +29,13 @@ import javax.inject.Inject; * *

This feature is not implemented because it's only necessary for address registries like * ARIN, not domain registries. - */ -@Action(path = RdapIpAction.PATH, method = {GET, HEAD}, isPrefix = true) +@Action( + path = RdapIpAction.PATH, + method = {GET, HEAD}, + isPrefix = true, + auth = @Auth(minimumLevel = AuthLevel.NONE, userPolicy = Auth.UserPolicy.PUBLIC) +) public class RdapIpAction extends RdapActionBase { public static final String PATH = "/rdap/ip"; diff --git a/java/google/registry/rdap/RdapNameserverAction.java b/java/google/registry/rdap/RdapNameserverAction.java index 101e86cf8..362f76d5e 100644 --- a/java/google/registry/rdap/RdapNameserverAction.java +++ b/java/google/registry/rdap/RdapNameserverAction.java @@ -23,14 +23,19 @@ import google.registry.model.host.HostResource; import google.registry.rdap.RdapJsonFormatter.OutputDataType; import google.registry.request.Action; import google.registry.request.HttpException.NotFoundException; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthLevel; import google.registry.util.Clock; import javax.inject.Inject; import org.joda.time.DateTime; -/** - * RDAP (new WHOIS) action for nameserver requests. - */ -@Action(path = RdapNameserverAction.PATH, method = {GET, HEAD}, isPrefix = true) +/** RDAP (new WHOIS) action for nameserver requests. */ +@Action( + path = RdapNameserverAction.PATH, + method = {GET, HEAD}, + isPrefix = true, + auth = @Auth(minimumLevel = AuthLevel.NONE, userPolicy = Auth.UserPolicy.PUBLIC) +) public class RdapNameserverAction extends RdapActionBase { public static final String PATH = "/rdap/nameserver/"; diff --git a/java/google/registry/rdap/RdapNameserverSearchAction.java b/java/google/registry/rdap/RdapNameserverSearchAction.java index 1459ae6e8..d10582208 100644 --- a/java/google/registry/rdap/RdapNameserverSearchAction.java +++ b/java/google/registry/rdap/RdapNameserverSearchAction.java @@ -36,6 +36,8 @@ import google.registry.request.Action; import google.registry.request.HttpException.BadRequestException; import google.registry.request.HttpException.NotFoundException; import google.registry.request.Parameter; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthLevel; import google.registry.util.Clock; import google.registry.util.Idn; import java.net.InetAddress; @@ -48,12 +50,17 @@ import org.joda.time.DateTime; * *

All commands and responses conform to the RDAP spec as defined in RFCs 7480 through 7485. * - * @see - * RFC 7482: Registration Data Access Protocol (RDAP) Query Format - * @see - * RFC 7483: JSON Responses for the Registration Data Access Protocol (RDAP) + * @see RFC 7482: Registration Data Access Protocol + * (RDAP) Query Format + * @see RFC 7483: JSON Responses for the Registration + * Data Access Protocol (RDAP) */ -@Action(path = RdapNameserverSearchAction.PATH, method = {GET, HEAD}, isPrefix = true) +@Action( + path = RdapNameserverSearchAction.PATH, + method = {GET, HEAD}, + isPrefix = true, + auth = @Auth(minimumLevel = AuthLevel.NONE, userPolicy = Auth.UserPolicy.PUBLIC) +) public class RdapNameserverSearchAction extends RdapActionBase { public static final String PATH = "/rdap/nameservers"; diff --git a/java/google/registry/request/auth/Auth.java b/java/google/registry/request/auth/Auth.java index f1ed4b941..fdd698e75 100644 --- a/java/google/registry/request/auth/Auth.java +++ b/java/google/registry/request/auth/Auth.java @@ -59,8 +59,7 @@ public @interface Auth { AuthMethod[] methods() default { AuthMethod.INTERNAL }; /** Required minimum level of authentication for this action. */ - // TODO(mountford) This should probably default to APP eventually. - AuthLevel minimumLevel() default AuthLevel.NONE; + AuthLevel minimumLevel() default AuthLevel.APP; /** Required user authorization policy for this action. */ UserPolicy userPolicy() default UserPolicy.IGNORED; diff --git a/java/google/registry/request/auth/RequestAuthenticator.java b/java/google/registry/request/auth/RequestAuthenticator.java index e90fcb0c8..6b4101e96 100644 --- a/java/google/registry/request/auth/RequestAuthenticator.java +++ b/java/google/registry/request/auth/RequestAuthenticator.java @@ -70,6 +70,7 @@ public class RequestAuthenticator { case USER: if (authResult.authLevel() != AuthLevel.USER) { logger.info("Not authorized; no authenticated user"); + // TODO(mountford): change this so that the caller knows to return a more helpful error return Optional.absent(); } break; diff --git a/java/google/registry/tools/server/BUILD b/java/google/registry/tools/server/BUILD index 421c58dcb..2bcab545f 100644 --- a/java/google/registry/tools/server/BUILD +++ b/java/google/registry/tools/server/BUILD @@ -17,6 +17,7 @@ java_library( "//java/google/registry/mapreduce/inputs", "//java/google/registry/model", "//java/google/registry/request", + "//java/google/registry/request/auth", "//java/google/registry/util", "//third_party/java/objectify:objectify-v4_1", "@com_beust_jcommander", diff --git a/java/google/registry/tools/server/CreateGroupsAction.java b/java/google/registry/tools/server/CreateGroupsAction.java index c4f316ecc..5eb01dcda 100644 --- a/java/google/registry/tools/server/CreateGroupsAction.java +++ b/java/google/registry/tools/server/CreateGroupsAction.java @@ -32,6 +32,8 @@ import google.registry.request.HttpException.BadRequestException; import google.registry.request.HttpException.InternalServerErrorException; import google.registry.request.Parameter; import google.registry.request.Response; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthLevel; import google.registry.util.Concurrent; import google.registry.util.FormattingLogger; import java.io.PrintWriter; @@ -41,7 +43,16 @@ import javax.annotation.Nullable; import javax.inject.Inject; /** Action that creates Google Groups for a registrar's mailing lists. */ -@Action(path = CreateGroupsAction.PATH, method = POST) +@Action( + path = CreateGroupsAction.PATH, + method = POST, + auth = + @Auth( + methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API}, + minimumLevel = AuthLevel.APP, + userPolicy = Auth.UserPolicy.ADMIN + ) +) public class CreateGroupsAction implements Runnable { public static final String PATH = "/_dr/admin/createGroups"; diff --git a/java/google/registry/tools/server/CreatePremiumListAction.java b/java/google/registry/tools/server/CreatePremiumListAction.java index dccee5355..a819ac0e0 100644 --- a/java/google/registry/tools/server/CreatePremiumListAction.java +++ b/java/google/registry/tools/server/CreatePremiumListAction.java @@ -25,6 +25,8 @@ import com.google.common.collect.ImmutableMap; import google.registry.model.registry.label.PremiumList; import google.registry.request.Action; import google.registry.request.Parameter; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthLevel; import java.util.List; import javax.inject.Inject; @@ -32,7 +34,16 @@ import javax.inject.Inject; * An action that creates a premium list, for use by the {@code nomulus create_premium_list} * command. */ -@Action(path = CreatePremiumListAction.PATH, method = POST) +@Action( + path = CreatePremiumListAction.PATH, + method = POST, + auth = + @Auth( + methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API}, + minimumLevel = AuthLevel.APP, + userPolicy = Auth.UserPolicy.ADMIN + ) +) public class CreatePremiumListAction extends CreateOrUpdatePremiumListAction { public static final String OVERRIDE_PARAM = "override"; diff --git a/java/google/registry/tools/server/DeleteEntityAction.java b/java/google/registry/tools/server/DeleteEntityAction.java index ee11412c5..b61790f1e 100644 --- a/java/google/registry/tools/server/DeleteEntityAction.java +++ b/java/google/registry/tools/server/DeleteEntityAction.java @@ -31,13 +31,15 @@ import google.registry.request.Action; import google.registry.request.HttpException.BadRequestException; import google.registry.request.Parameter; import google.registry.request.Response; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthLevel; import google.registry.util.FormattingLogger; import javax.inject.Inject; /** * An action to delete entities in Datastore specified by raw key ids, which can be found in - * Datastore Viewer in the AppEngine console - it's the really long alphanumeric key that is - * labeled "Entity key" on the page for an individual entity. + * Datastore Viewer in the AppEngine console - it's the really long alphanumeric key that is labeled + * "Entity key" on the page for an individual entity. * *

rawKeys is the only required parameter. It is a comma-delimited list of Strings. * @@ -47,7 +49,15 @@ import javax.inject.Inject; * malformed data that cannot be properly deleted using existing tools. Generally, if there already * exists an entity-specific deletion command, then use that one instead. */ -@Action(path = DeleteEntityAction.PATH) +@Action( + path = DeleteEntityAction.PATH, + auth = + @Auth( + methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API}, + minimumLevel = AuthLevel.APP, + userPolicy = Auth.UserPolicy.ADMIN + ) +) public class DeleteEntityAction implements Runnable { private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass(); diff --git a/java/google/registry/tools/server/ListDomainsAction.java b/java/google/registry/tools/server/ListDomainsAction.java index 76fcd350c..8393ab717 100644 --- a/java/google/registry/tools/server/ListDomainsAction.java +++ b/java/google/registry/tools/server/ListDomainsAction.java @@ -26,13 +26,24 @@ import com.google.common.collect.Lists; import google.registry.model.domain.DomainResource; import google.registry.request.Action; import google.registry.request.Parameter; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthLevel; import google.registry.util.Clock; import java.util.Comparator; import java.util.List; import javax.inject.Inject; /** An action that lists domains, for use by the {@code nomulus list_domains} command. */ -@Action(path = ListDomainsAction.PATH, method = {GET, POST}) +@Action( + path = ListDomainsAction.PATH, + method = {GET, POST}, + auth = + @Auth( + methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API}, + minimumLevel = AuthLevel.APP, + userPolicy = Auth.UserPolicy.ADMIN + ) +) public final class ListDomainsAction extends ListObjectsAction { /** An App Engine limitation on how many subqueries can be used in a single query. */ diff --git a/java/google/registry/tools/server/ListHostsAction.java b/java/google/registry/tools/server/ListHostsAction.java index ae5ed76b9..ef8102d0c 100644 --- a/java/google/registry/tools/server/ListHostsAction.java +++ b/java/google/registry/tools/server/ListHostsAction.java @@ -24,13 +24,24 @@ import com.google.common.collect.ImmutableSet; import google.registry.model.EppResourceUtils; import google.registry.model.host.HostResource; import google.registry.request.Action; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthLevel; import google.registry.util.Clock; import java.util.Comparator; import javax.inject.Inject; import org.joda.time.DateTime; /** An action that lists hosts, for use by the {@code nomulus list_hosts} command. */ -@Action(path = ListHostsAction.PATH, method = {GET, POST}) +@Action( + path = ListHostsAction.PATH, + method = {GET, POST}, + auth = + @Auth( + methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API}, + minimumLevel = AuthLevel.APP, + userPolicy = Auth.UserPolicy.ADMIN + ) +) public final class ListHostsAction extends ListObjectsAction { public static final String PATH = "/_dr/admin/list/hosts"; diff --git a/java/google/registry/tools/server/ListPremiumListsAction.java b/java/google/registry/tools/server/ListPremiumListsAction.java index 778196175..caeff6b61 100644 --- a/java/google/registry/tools/server/ListPremiumListsAction.java +++ b/java/google/registry/tools/server/ListPremiumListsAction.java @@ -22,6 +22,8 @@ import static google.registry.request.Action.Method.POST; import com.google.common.collect.ImmutableSet; import google.registry.model.registry.label.PremiumList; import google.registry.request.Action; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthLevel; import javax.inject.Inject; /** @@ -29,7 +31,13 @@ import javax.inject.Inject; */ @Action( path = ListPremiumListsAction.PATH, - method = {GET, POST} + method = {GET, POST}, + auth = + @Auth( + methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API}, + minimumLevel = AuthLevel.APP, + userPolicy = Auth.UserPolicy.ADMIN + ) ) public final class ListPremiumListsAction extends ListObjectsAction { diff --git a/java/google/registry/tools/server/ListRegistrarsAction.java b/java/google/registry/tools/server/ListRegistrarsAction.java index afc9ac302..156add7cb 100644 --- a/java/google/registry/tools/server/ListRegistrarsAction.java +++ b/java/google/registry/tools/server/ListRegistrarsAction.java @@ -22,10 +22,21 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import google.registry.model.registrar.Registrar; import google.registry.request.Action; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthLevel; import javax.inject.Inject; /** An action that lists registrars, for use by the {@code nomulus list_registrars} command. */ -@Action(path = ListRegistrarsAction.PATH, method = {GET, POST}) +@Action( + path = ListRegistrarsAction.PATH, + method = {GET, POST}, + auth = + @Auth( + methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API}, + minimumLevel = AuthLevel.APP, + userPolicy = Auth.UserPolicy.ADMIN + ) +) public final class ListRegistrarsAction extends ListObjectsAction { public static final String PATH = "/_dr/admin/list/registrars"; diff --git a/java/google/registry/tools/server/ListReservedListsAction.java b/java/google/registry/tools/server/ListReservedListsAction.java index 05ef81bc9..deb0e6eed 100644 --- a/java/google/registry/tools/server/ListReservedListsAction.java +++ b/java/google/registry/tools/server/ListReservedListsAction.java @@ -22,10 +22,21 @@ import static google.registry.request.Action.Method.POST; import com.google.common.collect.ImmutableSet; import google.registry.model.registry.label.ReservedList; import google.registry.request.Action; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthLevel; import javax.inject.Inject; /** A that lists reserved lists, for use by the {@code nomulus list_reserved_lists} command. */ -@Action(path = ListReservedListsAction.PATH, method = {GET, POST}) +@Action( + path = ListReservedListsAction.PATH, + method = {GET, POST}, + auth = + @Auth( + methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API}, + minimumLevel = AuthLevel.APP, + userPolicy = Auth.UserPolicy.ADMIN + ) +) public final class ListReservedListsAction extends ListObjectsAction { public static final String PATH = "/_dr/admin/list/reservedLists"; diff --git a/java/google/registry/tools/server/ListTldsAction.java b/java/google/registry/tools/server/ListTldsAction.java index 19f9c79be..e9fe81254 100644 --- a/java/google/registry/tools/server/ListTldsAction.java +++ b/java/google/registry/tools/server/ListTldsAction.java @@ -25,12 +25,23 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import google.registry.model.registry.Registry; import google.registry.request.Action; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthLevel; import google.registry.util.Clock; import javax.inject.Inject; import org.joda.time.DateTime; /** An action that lists top-level domains, for use by the {@code nomulus list_tlds} command. */ -@Action(path = ListTldsAction.PATH, method = {GET, POST}) +@Action( + path = ListTldsAction.PATH, + method = {GET, POST}, + auth = + @Auth( + methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API}, + minimumLevel = AuthLevel.APP, + userPolicy = Auth.UserPolicy.ADMIN + ) +) public final class ListTldsAction extends ListObjectsAction { public static final String PATH = "/_dr/admin/list/tlds"; diff --git a/java/google/registry/tools/server/UpdatePremiumListAction.java b/java/google/registry/tools/server/UpdatePremiumListAction.java index ba390231d..de732ee37 100644 --- a/java/google/registry/tools/server/UpdatePremiumListAction.java +++ b/java/google/registry/tools/server/UpdatePremiumListAction.java @@ -23,6 +23,8 @@ import com.google.common.base.Splitter; import com.google.common.collect.ImmutableMap; import google.registry.model.registry.label.PremiumList; import google.registry.request.Action; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthLevel; import java.util.List; import javax.inject.Inject; @@ -30,7 +32,16 @@ import javax.inject.Inject; * An action that creates a premium list, for use by the {@code nomulus create_premium_list} * command. */ -@Action(path = UpdatePremiumListAction.PATH, method = POST) +@Action( + path = UpdatePremiumListAction.PATH, + method = POST, + auth = + @Auth( + methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API}, + minimumLevel = AuthLevel.APP, + userPolicy = Auth.UserPolicy.ADMIN + ) +) public class UpdatePremiumListAction extends CreateOrUpdatePremiumListAction { public static final String PATH = "/_dr/admin/updatePremiumList"; diff --git a/java/google/registry/tools/server/VerifyOteAction.java b/java/google/registry/tools/server/VerifyOteAction.java index 8e982af22..204121506 100644 --- a/java/google/registry/tools/server/VerifyOteAction.java +++ b/java/google/registry/tools/server/VerifyOteAction.java @@ -67,6 +67,8 @@ import google.registry.model.reporting.HistoryEntry; import google.registry.request.Action; import google.registry.request.JsonActionRunner; import google.registry.request.JsonActionRunner.JsonAction; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthLevel; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; @@ -79,10 +81,17 @@ import javax.inject.Inject; * OT&E commands that have been run just previously to verification may not be picked up yet. */ @Action( - path = VerifyOteAction.PATH, - method = Action.Method.POST, - xsrfProtection = true, - xsrfScope = "admin") + path = VerifyOteAction.PATH, + method = Action.Method.POST, + xsrfProtection = true, + xsrfScope = "admin", + auth = + @Auth( + methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API}, + minimumLevel = AuthLevel.APP, + userPolicy = Auth.UserPolicy.ADMIN + ) +) public class VerifyOteAction implements Runnable, JsonAction { public static final String PATH = "/_dr/admin/verifyOte"; diff --git a/java/google/registry/ui/server/registrar/BUILD b/java/google/registry/ui/server/registrar/BUILD index fde767e30..324a0bae7 100644 --- a/java/google/registry/ui/server/registrar/BUILD +++ b/java/google/registry/ui/server/registrar/BUILD @@ -18,6 +18,7 @@ java_library( "//java/google/registry/flows", "//java/google/registry/model", "//java/google/registry/request", + "//java/google/registry/request/auth", "//java/google/registry/security", "//java/google/registry/ui/forms", "//java/google/registry/ui/server", diff --git a/java/google/registry/ui/server/registrar/ConsoleUiAction.java b/java/google/registry/ui/server/registrar/ConsoleUiAction.java index c9c92355b..dcbcab784 100644 --- a/java/google/registry/ui/server/registrar/ConsoleUiAction.java +++ b/java/google/registry/ui/server/registrar/ConsoleUiAction.java @@ -31,6 +31,8 @@ import google.registry.flows.EppConsoleAction; import google.registry.model.registrar.Registrar; import google.registry.request.Action; import google.registry.request.Response; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthLevel; import google.registry.security.XsrfTokenManager; import google.registry.ui.server.SoyTemplateUtils; import google.registry.ui.soy.registrar.ConsoleSoyInfo; @@ -38,7 +40,17 @@ import javax.inject.Inject; import javax.servlet.http.HttpServletRequest; /** Action that serves Registrar Console single HTML page (SPA). */ -@Action(path = ConsoleUiAction.PATH, requireLogin = true, xsrfProtection = false) +@Action( + path = ConsoleUiAction.PATH, + requireLogin = true, + xsrfProtection = false, + auth = + @Auth( + methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API, Auth.AuthMethod.LEGACY}, + minimumLevel = AuthLevel.NONE, + userPolicy = Auth.UserPolicy.PUBLIC + ) +) public final class ConsoleUiAction implements Runnable { public static final String PATH = "/registrar"; diff --git a/java/google/registry/ui/server/registrar/RegistrarPaymentAction.java b/java/google/registry/ui/server/registrar/RegistrarPaymentAction.java index 9411697c4..d7d750198 100644 --- a/java/google/registry/ui/server/registrar/RegistrarPaymentAction.java +++ b/java/google/registry/ui/server/registrar/RegistrarPaymentAction.java @@ -35,6 +35,8 @@ import google.registry.model.registrar.Registrar; import google.registry.request.Action; import google.registry.request.JsonActionRunner; import google.registry.request.JsonActionRunner.JsonAction; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthLevel; import google.registry.security.JsonResponseHelper; import google.registry.ui.forms.FormField; import google.registry.ui.forms.FormFieldException; @@ -56,15 +58,16 @@ import org.joda.money.Money; *

The request payload is a JSON object with the following fields: * *

- *
amount - *
String containing a fixed point value representing the amount of money the registrar - * customer wishes to send the registry. This amount is arbitrary and entered manually by the - * customer in the payment form, as there is currently no integration with the billing system. - *
currency - *
String containing a three letter ISO currency code, which is used to look up the Braintree - * merchant account ID to which payment should be posted. - *
paymentMethodNonce - *
UUID nonce string supplied by the Braintree JS SDK representing the selected payment method. + *
amount + *
String containing a fixed point value representing the amount of money the registrar + * customer wishes to send the registry. This amount is arbitrary and entered manually by the + * customer in the payment form, as there is currently no integration with the billing system. + *
currency + *
String containing a three letter ISO currency code, which is used to look up the Braintree + * merchant account ID to which payment should be posted. + *
paymentMethodNonce + *
UUID nonce string supplied by the Braintree JS SDK representing the selected payment + * method. *
* *

Response Object

@@ -73,28 +76,35 @@ import org.joda.money.Money; * which, if successful, will contain a single result object with the following fields: * *
- *
id - *
String containing transaction ID returned by Braintree gateway. - *
formattedAmount - *
String containing amount paid, which can be displayed to the customer on a success page. + *
id + *
String containing transaction ID returned by Braintree gateway. + *
formattedAmount + *
String containing amount paid, which can be displayed to the customer on a success page. *
* - *

Note: These definitions corresponds to Closure Compiler extern - * {@code registry.rpc.Payment} which must be updated should these definitions change. + *

Note: These definitions corresponds to Closure Compiler extern {@code + * registry.rpc.Payment} which must be updated should these definitions change. * *

PCI Compliance

* - *

The request object will not contain credit card information, but rather a - * {@code payment_method_nonce} field that's populated by the Braintree JS SDK iframe. + *

The request object will not contain credit card information, but rather a {@code + * payment_method_nonce} field that's populated by the Braintree JS SDK iframe. * * @see RegistrarPaymentSetupAction */ @Action( - path = "/registrar-payment", - method = Action.Method.POST, - xsrfProtection = true, - xsrfScope = "console", - requireLogin = true) + path = "/registrar-payment", + method = Action.Method.POST, + xsrfProtection = true, + xsrfScope = "console", + requireLogin = true, + auth = + @Auth( + methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API, Auth.AuthMethod.LEGACY}, + minimumLevel = AuthLevel.USER, + userPolicy = Auth.UserPolicy.PUBLIC + ) +) public final class RegistrarPaymentAction implements Runnable, JsonAction { private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass(); diff --git a/java/google/registry/ui/server/registrar/RegistrarPaymentSetupAction.java b/java/google/registry/ui/server/registrar/RegistrarPaymentSetupAction.java index 1f6ebb516..29fd12a43 100644 --- a/java/google/registry/ui/server/registrar/RegistrarPaymentSetupAction.java +++ b/java/google/registry/ui/server/registrar/RegistrarPaymentSetupAction.java @@ -28,6 +28,8 @@ import google.registry.model.registrar.Registrar; import google.registry.request.Action; import google.registry.request.JsonActionRunner; import google.registry.request.JsonActionRunner.JsonAction; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthLevel; import google.registry.security.JsonResponseHelper; import java.util.Map; import javax.inject.Inject; @@ -46,29 +48,38 @@ import org.joda.money.CurrencyUnit; * containing a single result object with the following fields: * *

- *
brainframe - *
URL for iframe that loads Braintree payment method selector. - *
token - *
Nonce string obtained from the Braintree API which is needed by the Braintree JS SDK. - *
currencies - *
Array of strings, each containing a three letter currency code, which should be displayed to - * the customer in a drop-down field. This will be all currencies for which a Braintree merchant - * account exists. A currency will even be displayed if no TLD is enabled on the customer - * account that bills in that currency. + *
brainframe + *
URL for iframe that loads Braintree payment method selector. + *
token + *
Nonce string obtained from the Braintree API which is needed by the Braintree JS SDK. + *
currencies + *
Array of strings, each containing a three letter currency code, which should be displayed + * to the customer in a drop-down field. This will be all currencies for which a Braintree + * merchant account exists. A currency will even be displayed if no TLD is enabled on the + * customer account that bills in that currency. *
* - *

Note: These definitions corresponds to Closure Compiler extern - * {@code registry.rpc.PaymentSetup} which must be updated should these definitions change. + *

Note: These definitions corresponds to Closure Compiler extern {@code + * registry.rpc.PaymentSetup} which must be updated should these definitions change. * * @see RegistrarPaymentAction - * @see Generate a client token + * @see Generate + * a client token */ @Action( - path = "/registrar-payment-setup", - method = Action.Method.POST, - xsrfProtection = true, - xsrfScope = "console", - requireLogin = true) + path = "/registrar-payment-setup", + method = Action.Method.POST, + xsrfProtection = true, + xsrfScope = "console", + requireLogin = true, + auth = + @Auth( + methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API, Auth.AuthMethod.LEGACY}, + minimumLevel = AuthLevel.USER, + userPolicy = Auth.UserPolicy.PUBLIC + ) +) public final class RegistrarPaymentSetupAction implements Runnable, JsonAction { @Inject BraintreeGateway braintreeGateway; diff --git a/java/google/registry/ui/server/registrar/RegistrarSettingsAction.java b/java/google/registry/ui/server/registrar/RegistrarSettingsAction.java index 5bff733a3..b5105945f 100644 --- a/java/google/registry/ui/server/registrar/RegistrarSettingsAction.java +++ b/java/google/registry/ui/server/registrar/RegistrarSettingsAction.java @@ -38,6 +38,8 @@ import google.registry.model.registrar.RegistrarContact; import google.registry.request.Action; import google.registry.request.HttpException.BadRequestException; import google.registry.request.JsonActionRunner; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthLevel; import google.registry.security.JsonResponseHelper; import google.registry.ui.forms.FormException; import google.registry.ui.forms.FormFieldException; @@ -57,11 +59,18 @@ import javax.servlet.http.HttpServletRequest; * preserve history. */ @Action( - path = RegistrarSettingsAction.PATH, - requireLogin = true, - xsrfProtection = true, - xsrfScope = "console", - method = Action.Method.POST) + path = RegistrarSettingsAction.PATH, + requireLogin = true, + xsrfProtection = true, + xsrfScope = "console", + method = Action.Method.POST, + auth = + @Auth( + methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API, Auth.AuthMethod.LEGACY}, + minimumLevel = AuthLevel.USER, + userPolicy = Auth.UserPolicy.PUBLIC + ) +) public class RegistrarSettingsAction implements Runnable, JsonActionRunner.JsonAction { public static final String PATH = "/registrar-settings"; diff --git a/java/google/registry/whois/BUILD b/java/google/registry/whois/BUILD index 10f1475a5..847a5a2bb 100644 --- a/java/google/registry/whois/BUILD +++ b/java/google/registry/whois/BUILD @@ -12,6 +12,7 @@ java_library( "//java/google/registry/model", "//java/google/registry/monitoring/metrics", "//java/google/registry/request", + "//java/google/registry/request/auth", "//java/google/registry/util", "//java/google/registry/xml", "//third_party/java/objectify:objectify-v4_1", diff --git a/java/google/registry/whois/WhoisHttpServer.java b/java/google/registry/whois/WhoisHttpServer.java index 99511b421..fce8fd2a8 100644 --- a/java/google/registry/whois/WhoisHttpServer.java +++ b/java/google/registry/whois/WhoisHttpServer.java @@ -32,6 +32,8 @@ import google.registry.config.RegistryConfig.Config; import google.registry.request.Action; import google.registry.request.RequestPath; import google.registry.request.Response; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthLevel; import google.registry.util.Clock; import google.registry.util.FormattingLogger; import google.registry.whois.WhoisMetrics.WhoisMetric; @@ -47,11 +49,10 @@ import org.joda.time.Duration; /** * Human-Friendly HTTP WHOIS API * - *

This API uses easy to understand paths rather than {@link WhoisServer} which - * requires a POST request containing a WHOIS command. Because the typical WHOIS command is - * along the lines of {@code "domain google.lol"} or the equivalent {@code "google.lol}, this - * servlet is just going to replace the slashes with spaces and let {@link WhoisReader} - * figure out what to do. + *

This API uses easy to understand paths rather than {@link WhoisServer} which requires a POST + * request containing a WHOIS command. Because the typical WHOIS command is along the lines of + * {@code "domain google.lol"} or the equivalent {@code "google.lol}, this servlet is just going to + * replace the slashes with spaces and let {@link WhoisReader} figure out what to do. * *

This servlet accepts requests from any origin. * @@ -95,7 +96,11 @@ import org.joda.time.Duration; * * @see WhoisServer */ -@Action(path = WhoisHttpServer.PATH, isPrefix = true) +@Action( + path = WhoisHttpServer.PATH, + isPrefix = true, + auth = @Auth(minimumLevel = AuthLevel.NONE, userPolicy = Auth.UserPolicy.PUBLIC) +) public final class WhoisHttpServer implements Runnable { public static final String PATH = "/whois/"; diff --git a/java/google/registry/whois/WhoisServer.java b/java/google/registry/whois/WhoisServer.java index 232539940..0e939d7fa 100644 --- a/java/google/registry/whois/WhoisServer.java +++ b/java/google/registry/whois/WhoisServer.java @@ -22,6 +22,8 @@ import com.google.common.net.MediaType; import google.registry.config.RegistryConfig.Config; import google.registry.request.Action; import google.registry.request.Response; +import google.registry.request.auth.Auth; +import google.registry.request.auth.AuthLevel; import google.registry.util.Clock; import google.registry.util.FormattingLogger; import google.registry.whois.WhoisMetrics.WhoisMetric; @@ -33,18 +35,27 @@ import org.joda.time.DateTime; /** * HTTP request handler for WHOIS protocol requests sent to us by a proxy. * - *

All commands and responses conform to the WHOIS spec as defined in RFC 3912. Commands must - * be sent via an HTTP POST in the request body. + *

All commands and responses conform to the WHOIS spec as defined in RFC 3912. Commands must be + * sent via an HTTP POST in the request body. * *

This servlet is meant to serve as a low level interface for the proxy app which forwards us - * requests received on port 43. However this interface is technically higher level because it - * sends back proper HTTP error codes such as 200, 400, 500, etc. These are discarded by the proxy - * because WHOIS specifies no manner for differentiating successful and erroneous requests. + * requests received on port 43. However this interface is technically higher level because it sends + * back proper HTTP error codes such as 200, 400, 500, etc. These are discarded by the proxy because + * WHOIS specifies no manner for differentiating successful and erroneous requests. * * @see WhoisHttpServer * @see RFC 3912: WHOIS Protocol Specification */ -@Action(path = "/_dr/whois", method = POST) +@Action( + path = "/_dr/whois", + method = POST, + auth = + @Auth( + methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API}, + minimumLevel = AuthLevel.APP, + userPolicy = Auth.UserPolicy.ADMIN + ) +) public class WhoisServer implements Runnable { private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass(); diff --git a/javatests/google/registry/request/RequestHandlerTest.java b/javatests/google/registry/request/RequestHandlerTest.java index 63fd8c8c9..62c3afa32 100644 --- a/javatests/google/registry/request/RequestHandlerTest.java +++ b/javatests/google/registry/request/RequestHandlerTest.java @@ -66,31 +66,52 @@ public final class RequestHandlerTest { .withUserService(UserInfo.create("test@example.com", "test@example.com")) .build(); - @Action(path = "/bumblebee", method = {GET, POST}, isPrefix = true) + @Action( + path = "/bumblebee", + method = {GET, POST}, + isPrefix = true, + auth = @Auth(minimumLevel = AuthLevel.NONE) + ) public static class BumblebeeTask implements Runnable { @Override public void run() {} } - @Action(path = "/sloth", method = POST, automaticallyPrintOk = true) + @Action( + path = "/sloth", + method = POST, + automaticallyPrintOk = true, + auth = @Auth(minimumLevel = AuthLevel.NONE) + ) public static class SlothTask implements Runnable { @Override public void run() {} } - @Action(path = "/safe-sloth", method = {GET, POST}, xsrfProtection = true, xsrfScope = "vampire") + @Action( + path = "/safe-sloth", + method = {GET, POST}, + xsrfProtection = true, + xsrfScope = "vampire", + auth = @Auth(minimumLevel = AuthLevel.NONE) + ) public static class SafeSlothTask implements Runnable { @Override public void run() {} } - @Action(path = "/users-only", method = GET, requireLogin = true) + @Action( + path = "/users-only", + method = GET, + requireLogin = true, + auth = @Auth(minimumLevel = AuthLevel.NONE) + ) public static class UsersOnlyAction implements Runnable { @Override public void run() {} } - @Action(path = "/fail") + @Action(path = "/fail", auth = @Auth(minimumLevel = AuthLevel.NONE)) public static final class FailTask implements Runnable { @Override public void run() { @@ -98,7 +119,7 @@ public final class RequestHandlerTest { } } - @Action(path = "/failAtConstruction") + @Action(path = "/failAtConstruction", auth = @Auth(minimumLevel = AuthLevel.NONE)) public static final class FailAtConstructionTask implements Runnable { public FailAtConstructionTask() { throw new ServiceUnavailableException("Fail at construction"); @@ -124,12 +145,10 @@ public final class RequestHandlerTest { } @Action( - path = "/auth/none", - auth = @Auth( - methods = {Auth.AuthMethod.INTERNAL}, - minimumLevel = AuthLevel.NONE, - userPolicy = Auth.UserPolicy.IGNORED), - method = Action.Method.GET) + path = "/auth/none", + auth = @Auth(minimumLevel = AuthLevel.NONE), + method = Action.Method.GET + ) public class AuthNoneAction extends AuthBase { AuthNoneAction(AuthResult authResult) { super(authResult); @@ -427,13 +446,13 @@ public final class RequestHandlerTest { verify(usersOnlyAction).run(); } - // TODO(b/28219927): turn this on once we actually do authorization - @Ignore @Test public void testNoAuthNeeded_success() throws Exception { when(req.getMethod()).thenReturn("GET"); when(req.getRequestURI()).thenReturn("/auth/none"); + handler.handleRequest(req, rsp); + assertThat(providedAuthResult).isNotNull(); assertThat(providedAuthResult.authLevel()).isEqualTo(AuthLevel.NONE); assertThat(providedAuthResult.userAuthInfo()).isAbsent(); @@ -445,9 +464,25 @@ public final class RequestHandlerTest { public void testAuthNeeded_notLoggedIn() throws Exception { when(req.getMethod()).thenReturn("GET"); when(req.getRequestURI()).thenReturn("/auth/adminUserAnyMethod"); + handler.handleRequest(req, rsp); + verify(rsp).sendError(403); assertThat(providedAuthResult).isNull(); + assertThat(providedAuthResult).isNull(); + } + + // TODO(b/28219927): remove this once we actually do authorization + @Test + public void testAuthNeeded_notLoggedIn_interim() throws Exception { + when(req.getMethod()).thenReturn("GET"); + when(req.getRequestURI()).thenReturn("/auth/adminUserAnyMethod"); + + handler.handleRequest(req, rsp); + + assertThat(providedAuthResult).isNotNull(); + assertThat(providedAuthResult.authLevel()).isEqualTo(AuthLevel.NONE); + assertThat(providedAuthResult.userAuthInfo()).isAbsent(); } // TODO(b/28219927): turn this on once we actually do authorization @@ -457,19 +492,35 @@ public final class RequestHandlerTest { userService.setUser(testUser, false); when(req.getMethod()).thenReturn("GET"); when(req.getRequestURI()).thenReturn("/auth/adminUserAnyMethod"); + handler.handleRequest(req, rsp); + verify(rsp).sendError(403); assertThat(providedAuthResult).isNull(); } - // TODO(b/28219927): turn this on once we actually do authorization - @Ignore + // TODO(b/28219927): remove this once we actually do authorization + @Test + public void testAuthNeeded_notAuthorized_interim() throws Exception { + userService.setUser(testUser, false); + when(req.getMethod()).thenReturn("GET"); + when(req.getRequestURI()).thenReturn("/auth/adminUserAnyMethod"); + + handler.handleRequest(req, rsp); + + assertThat(providedAuthResult).isNotNull(); + assertThat(providedAuthResult.authLevel()).isEqualTo(AuthLevel.NONE); + assertThat(providedAuthResult.userAuthInfo()).isAbsent(); + } + @Test public void testAuthNeeded_success() throws Exception { userService.setUser(testUser, true); when(req.getMethod()).thenReturn("GET"); when(req.getRequestURI()).thenReturn("/auth/adminUserAnyMethod"); + handler.handleRequest(req, rsp); + assertThat(providedAuthResult).isNotNull(); assertThat(providedAuthResult.authLevel()).isEqualTo(AuthLevel.USER); assertThat(providedAuthResult.userAuthInfo()).isPresent();