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
This commit is contained in:
mountford 2017-03-10 08:47:52 -08:00 committed by Ben McIlwain
parent f5e76868f0
commit 1f000b94e6
38 changed files with 450 additions and 133 deletions

View file

@ -30,6 +30,7 @@ java_library(
"//java/google/registry/monitoring/whitebox", "//java/google/registry/monitoring/whitebox",
"//java/google/registry/pricing", "//java/google/registry/pricing",
"//java/google/registry/request", "//java/google/registry/request",
"//java/google/registry/request/auth",
"//java/google/registry/tldconfig/idn", "//java/google/registry/tldconfig/idn",
"//java/google/registry/tmch", "//java/google/registry/tmch",
"//java/google/registry/util", "//java/google/registry/util",

View file

@ -45,6 +45,8 @@ import google.registry.request.Action;
import google.registry.request.Parameter; import google.registry.request.Parameter;
import google.registry.request.RequestParameters; import google.registry.request.RequestParameters;
import google.registry.request.Response; import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.request.auth.AuthLevel;
import google.registry.util.FormattingLogger; import google.registry.util.FormattingLogger;
import java.util.Map; import java.util.Map;
import javax.inject.Inject; 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 * user controlled, lest it open an XSS vector. Do not modify this to return the domain name in the
* response. * response.
*/ */
@Action(path = "/check") @Action(
path = "/check",
auth = @Auth(minimumLevel = AuthLevel.NONE, userPolicy = Auth.UserPolicy.PUBLIC)
)
public class CheckApiAction implements Runnable { public class CheckApiAction implements Runnable {
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass(); private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();

View file

@ -18,15 +18,24 @@ import com.google.appengine.api.users.UserService;
import google.registry.request.Action; import google.registry.request.Action;
import google.registry.request.Action.Method; import google.registry.request.Action.Method;
import google.registry.request.Payload; import google.registry.request.Payload;
import google.registry.request.auth.Auth;
import google.registry.request.auth.AuthLevel;
import javax.inject.Inject; import javax.inject.Inject;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
/** Runs EPP from the console and requires GAE user authentication. */ /** Runs EPP from the console and requires GAE user authentication. */
@Action( @Action(
path = "/registrar-xhr", path = "/registrar-xhr",
xsrfProtection = true, xsrfProtection = true,
xsrfScope = EppConsoleAction.XSRF_SCOPE, xsrfScope = EppConsoleAction.XSRF_SCOPE,
method = Method.POST) 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 class EppConsoleAction implements Runnable {
public static final String XSRF_SCOPE = "console"; public static final String XSRF_SCOPE = "console";

View file

@ -17,6 +17,8 @@ package google.registry.flows;
import google.registry.request.Action; import google.registry.request.Action;
import google.registry.request.Action.Method; import google.registry.request.Action.Method;
import google.registry.request.Payload; import google.registry.request.Payload;
import google.registry.request.auth.Auth;
import google.registry.request.auth.AuthLevel;
import google.registry.util.FormattingLogger; import google.registry.util.FormattingLogger;
import javax.inject.Inject; import javax.inject.Inject;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
@ -26,8 +28,15 @@ import javax.servlet.http.HttpSession;
* to RFC 5730. Commands must be requested via POST. * to RFC 5730. Commands must be requested via POST.
*/ */
@Action( @Action(
path = "/_dr/epp", path = "/_dr/epp",
method = Method.POST) method = Method.POST,
auth =
@Auth(
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API},
minimumLevel = AuthLevel.APP,
userPolicy = Auth.UserPolicy.ADMIN
)
)
public class EppTlsAction implements Runnable { public class EppTlsAction implements Runnable {
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass(); private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();

View file

@ -24,15 +24,24 @@ import google.registry.model.eppcommon.ProtocolDefinition;
import google.registry.request.Action; import google.registry.request.Action;
import google.registry.request.Action.Method; import google.registry.request.Action.Method;
import google.registry.request.Parameter; import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.request.auth.AuthLevel;
import javax.inject.Inject; import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
/** Runs EPP commands directly without logging in, verifying an XSRF token from the tool. */ /** Runs EPP commands directly without logging in, verifying an XSRF token from the tool. */
@Action( @Action(
path = "/_dr/epptool", path = "/_dr/epptool",
xsrfProtection = true, xsrfProtection = true,
xsrfScope = "admin", xsrfScope = "admin",
method = Method.POST) method = Method.POST,
auth =
@Auth(
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API},
minimumLevel = AuthLevel.APP,
userPolicy = Auth.UserPolicy.ADMIN
)
)
public class EppToolAction implements Runnable { public class EppToolAction implements Runnable {
@Inject @Parameter("clientId") String clientId; @Inject @Parameter("clientId") String clientId;

View file

@ -11,6 +11,7 @@ java_library(
"//java/google/registry/config", "//java/google/registry/config",
"//java/google/registry/model", "//java/google/registry/model",
"//java/google/registry/request", "//java/google/registry/request",
"//java/google/registry/request/auth",
"//java/google/registry/util", "//java/google/registry/util",
"//third_party/java/objectify:objectify-v4_1", "//third_party/java/objectify:objectify-v4_1",
"@com_google_auto_value", "@com_google_auto_value",

View file

@ -20,6 +20,8 @@ import static google.registry.request.Action.Method.HEAD;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import google.registry.request.Action; import google.registry.request.Action;
import google.registry.request.HttpException.NotImplementedException; import google.registry.request.HttpException.NotImplementedException;
import google.registry.request.auth.Auth;
import google.registry.request.auth.AuthLevel;
import javax.inject.Inject; import javax.inject.Inject;
/** /**
@ -28,7 +30,12 @@ import javax.inject.Inject;
* <p>This feature is not implemented because it's only necessary for <i>address</i> registries like * <p>This feature is not implemented because it's only necessary for <i>address</i> registries like
* ARIN, not domain registries. * 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 class RdapAutnumAction extends RdapActionBase {
public static final String PATH = "/rdap/autnum"; public static final String PATH = "/rdap/autnum";

View file

@ -23,14 +23,19 @@ import google.registry.model.domain.DomainResource;
import google.registry.rdap.RdapJsonFormatter.OutputDataType; import google.registry.rdap.RdapJsonFormatter.OutputDataType;
import google.registry.request.Action; import google.registry.request.Action;
import google.registry.request.HttpException.NotFoundException; import google.registry.request.HttpException.NotFoundException;
import google.registry.request.auth.Auth;
import google.registry.request.auth.AuthLevel;
import google.registry.util.Clock; import google.registry.util.Clock;
import javax.inject.Inject; import javax.inject.Inject;
import org.joda.time.DateTime; import org.joda.time.DateTime;
/** /** RDAP (new WHOIS) action for domain requests. */
* RDAP (new WHOIS) action for domain requests. @Action(
*/ path = RdapDomainAction.PATH,
@Action(path = RdapDomainAction.PATH, method = {GET, HEAD}, isPrefix = true) method = {GET, HEAD},
isPrefix = true,
auth = @Auth(minimumLevel = AuthLevel.NONE, userPolicy = Auth.UserPolicy.PUBLIC)
)
public class RdapDomainAction extends RdapActionBase { public class RdapDomainAction extends RdapActionBase {
public static final String PATH = "/rdap/domain/"; public static final String PATH = "/rdap/domain/";

View file

@ -40,6 +40,8 @@ import google.registry.request.Action;
import google.registry.request.HttpException.BadRequestException; import google.registry.request.HttpException.BadRequestException;
import google.registry.request.HttpException.NotFoundException; import google.registry.request.HttpException.NotFoundException;
import google.registry.request.Parameter; 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.Clock;
import google.registry.util.Idn; import google.registry.util.Idn;
import java.net.InetAddress; import java.net.InetAddress;
@ -54,12 +56,17 @@ import org.joda.time.DateTime;
* *
* <p>All commands and responses conform to the RDAP spec as defined in RFCs 7480 through 7485. * <p>All commands and responses conform to the RDAP spec as defined in RFCs 7480 through 7485.
* *
* @see <a href="http://tools.ietf.org/html/rfc7482"> * @see <a href="http://tools.ietf.org/html/rfc7482">RFC 7482: Registration Data Access Protocol
* RFC 7482: Registration Data Access Protocol (RDAP) Query Format</a> * (RDAP) Query Format</a>
* @see <a href="http://tools.ietf.org/html/rfc7483"> * @see <a href="http://tools.ietf.org/html/rfc7483">RFC 7483: JSON Responses for the Registration
* RFC 7483: JSON Responses for the Registration Data Access Protocol (RDAP)</a> * Data Access Protocol (RDAP)</a>
*/ */
@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 class RdapDomainSearchAction extends RdapActionBase {
public static final String PATH = "/rdap/domains"; public static final String PATH = "/rdap/domains";

View file

@ -30,6 +30,8 @@ import google.registry.rdap.RdapJsonFormatter.OutputDataType;
import google.registry.request.Action; import google.registry.request.Action;
import google.registry.request.HttpException.BadRequestException; import google.registry.request.HttpException.BadRequestException;
import google.registry.request.HttpException.NotFoundException; import google.registry.request.HttpException.NotFoundException;
import google.registry.request.auth.Auth;
import google.registry.request.auth.AuthLevel;
import google.registry.util.Clock; import google.registry.util.Clock;
import javax.inject.Inject; import javax.inject.Inject;
import org.joda.time.DateTime; 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 IANAs * response MUST contain a publicIDs member to identify the IANA Registrar ID from the IANAs
* Registrar ID registry. The type value of the publicID object MUST be equal to IANA Registrar ID. * 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 class RdapEntityAction extends RdapActionBase {
public static final String PATH = "/rdap/entity/"; public static final String PATH = "/rdap/entity/";

View file

@ -36,6 +36,8 @@ import google.registry.request.HttpException.BadRequestException;
import google.registry.request.HttpException.NotFoundException; import google.registry.request.HttpException.NotFoundException;
import google.registry.request.HttpException.UnprocessableEntityException; import google.registry.request.HttpException.UnprocessableEntityException;
import google.registry.request.Parameter; 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.Clock;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -47,12 +49,17 @@ import org.joda.time.DateTime;
* *
* <p>All commands and responses conform to the RDAP spec as defined in RFCs 7480 through 7485. * <p>All commands and responses conform to the RDAP spec as defined in RFCs 7480 through 7485.
* *
* @see <a href="http://tools.ietf.org/html/rfc7482"> * @see <a href="http://tools.ietf.org/html/rfc7482">RFC 7482: Registration Data Access Protocol
* RFC 7482: Registration Data Access Protocol (RDAP) Query Format</a> * (RDAP) Query Format</a>
* @see <a href="http://tools.ietf.org/html/rfc7483"> * @see <a href="http://tools.ietf.org/html/rfc7483">RFC 7483: JSON Responses for the Registration
* RFC 7483: JSON Responses for the Registration Data Access Protocol (RDAP)</a> * Data Access Protocol (RDAP)</a>
*/ */
@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 class RdapEntitySearchAction extends RdapActionBase {
public static final String PATH = "/rdap/entities"; public static final String PATH = "/rdap/entities";

View file

@ -21,13 +21,18 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import google.registry.rdap.RdapJsonFormatter.BoilerplateType; import google.registry.rdap.RdapJsonFormatter.BoilerplateType;
import google.registry.request.Action; import google.registry.request.Action;
import google.registry.request.auth.Auth;
import google.registry.request.auth.AuthLevel;
import google.registry.util.Clock; import google.registry.util.Clock;
import javax.inject.Inject; import javax.inject.Inject;
/** /** RDAP (new WHOIS) action for help requests. */
* RDAP (new WHOIS) action for help requests. @Action(
*/ path = RdapHelpAction.PATH,
@Action(path = RdapHelpAction.PATH, method = {GET, HEAD}, isPrefix = true) method = {GET, HEAD},
isPrefix = true,
auth = @Auth(minimumLevel = AuthLevel.NONE, userPolicy = Auth.UserPolicy.PUBLIC)
)
public class RdapHelpAction extends RdapActionBase { public class RdapHelpAction extends RdapActionBase {
public static final String PATH = "/rdap/help"; public static final String PATH = "/rdap/help";

View file

@ -20,6 +20,8 @@ import static google.registry.request.Action.Method.HEAD;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import google.registry.request.Action; import google.registry.request.Action;
import google.registry.request.HttpException.NotImplementedException; import google.registry.request.HttpException.NotImplementedException;
import google.registry.request.auth.Auth;
import google.registry.request.auth.AuthLevel;
import javax.inject.Inject; import javax.inject.Inject;
/** /**
@ -27,9 +29,13 @@ import javax.inject.Inject;
* *
* <p>This feature is not implemented because it's only necessary for <i>address</i> registries like * <p>This feature is not implemented because it's only necessary for <i>address</i> registries like
* ARIN, not domain registries. * 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 class RdapIpAction extends RdapActionBase {
public static final String PATH = "/rdap/ip"; public static final String PATH = "/rdap/ip";

View file

@ -23,14 +23,19 @@ import google.registry.model.host.HostResource;
import google.registry.rdap.RdapJsonFormatter.OutputDataType; import google.registry.rdap.RdapJsonFormatter.OutputDataType;
import google.registry.request.Action; import google.registry.request.Action;
import google.registry.request.HttpException.NotFoundException; import google.registry.request.HttpException.NotFoundException;
import google.registry.request.auth.Auth;
import google.registry.request.auth.AuthLevel;
import google.registry.util.Clock; import google.registry.util.Clock;
import javax.inject.Inject; import javax.inject.Inject;
import org.joda.time.DateTime; import org.joda.time.DateTime;
/** /** RDAP (new WHOIS) action for nameserver requests. */
* RDAP (new WHOIS) action for nameserver requests. @Action(
*/ path = RdapNameserverAction.PATH,
@Action(path = RdapNameserverAction.PATH, method = {GET, HEAD}, isPrefix = true) method = {GET, HEAD},
isPrefix = true,
auth = @Auth(minimumLevel = AuthLevel.NONE, userPolicy = Auth.UserPolicy.PUBLIC)
)
public class RdapNameserverAction extends RdapActionBase { public class RdapNameserverAction extends RdapActionBase {
public static final String PATH = "/rdap/nameserver/"; public static final String PATH = "/rdap/nameserver/";

View file

@ -36,6 +36,8 @@ import google.registry.request.Action;
import google.registry.request.HttpException.BadRequestException; import google.registry.request.HttpException.BadRequestException;
import google.registry.request.HttpException.NotFoundException; import google.registry.request.HttpException.NotFoundException;
import google.registry.request.Parameter; 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.Clock;
import google.registry.util.Idn; import google.registry.util.Idn;
import java.net.InetAddress; import java.net.InetAddress;
@ -48,12 +50,17 @@ import org.joda.time.DateTime;
* *
* <p>All commands and responses conform to the RDAP spec as defined in RFCs 7480 through 7485. * <p>All commands and responses conform to the RDAP spec as defined in RFCs 7480 through 7485.
* *
* @see <a href="http://tools.ietf.org/html/rfc7482"> * @see <a href="http://tools.ietf.org/html/rfc7482">RFC 7482: Registration Data Access Protocol
* RFC 7482: Registration Data Access Protocol (RDAP) Query Format</a> * (RDAP) Query Format</a>
* @see <a href="http://tools.ietf.org/html/rfc7483"> * @see <a href="http://tools.ietf.org/html/rfc7483">RFC 7483: JSON Responses for the Registration
* RFC 7483: JSON Responses for the Registration Data Access Protocol (RDAP)</a> * Data Access Protocol (RDAP)</a>
*/ */
@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 class RdapNameserverSearchAction extends RdapActionBase {
public static final String PATH = "/rdap/nameservers"; public static final String PATH = "/rdap/nameservers";

View file

@ -59,8 +59,7 @@ public @interface Auth {
AuthMethod[] methods() default { AuthMethod.INTERNAL }; AuthMethod[] methods() default { AuthMethod.INTERNAL };
/** Required minimum level of authentication for this action. */ /** Required minimum level of authentication for this action. */
// TODO(mountford) This should probably default to APP eventually. AuthLevel minimumLevel() default AuthLevel.APP;
AuthLevel minimumLevel() default AuthLevel.NONE;
/** Required user authorization policy for this action. */ /** Required user authorization policy for this action. */
UserPolicy userPolicy() default UserPolicy.IGNORED; UserPolicy userPolicy() default UserPolicy.IGNORED;

View file

@ -70,6 +70,7 @@ public class RequestAuthenticator {
case USER: case USER:
if (authResult.authLevel() != AuthLevel.USER) { if (authResult.authLevel() != AuthLevel.USER) {
logger.info("Not authorized; no authenticated 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(); return Optional.absent();
} }
break; break;

View file

@ -17,6 +17,7 @@ java_library(
"//java/google/registry/mapreduce/inputs", "//java/google/registry/mapreduce/inputs",
"//java/google/registry/model", "//java/google/registry/model",
"//java/google/registry/request", "//java/google/registry/request",
"//java/google/registry/request/auth",
"//java/google/registry/util", "//java/google/registry/util",
"//third_party/java/objectify:objectify-v4_1", "//third_party/java/objectify:objectify-v4_1",
"@com_beust_jcommander", "@com_beust_jcommander",

View file

@ -32,6 +32,8 @@ import google.registry.request.HttpException.BadRequestException;
import google.registry.request.HttpException.InternalServerErrorException; import google.registry.request.HttpException.InternalServerErrorException;
import google.registry.request.Parameter; import google.registry.request.Parameter;
import google.registry.request.Response; 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.Concurrent;
import google.registry.util.FormattingLogger; import google.registry.util.FormattingLogger;
import java.io.PrintWriter; import java.io.PrintWriter;
@ -41,7 +43,16 @@ import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
/** Action that creates Google Groups for a registrar's mailing lists. */ /** 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 class CreateGroupsAction implements Runnable {
public static final String PATH = "/_dr/admin/createGroups"; public static final String PATH = "/_dr/admin/createGroups";

View file

@ -25,6 +25,8 @@ import com.google.common.collect.ImmutableMap;
import google.registry.model.registry.label.PremiumList; import google.registry.model.registry.label.PremiumList;
import google.registry.request.Action; import google.registry.request.Action;
import google.registry.request.Parameter; import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.request.auth.AuthLevel;
import java.util.List; import java.util.List;
import javax.inject.Inject; 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} * An action that creates a premium list, for use by the {@code nomulus create_premium_list}
* command. * 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 class CreatePremiumListAction extends CreateOrUpdatePremiumListAction {
public static final String OVERRIDE_PARAM = "override"; public static final String OVERRIDE_PARAM = "override";

View file

@ -31,13 +31,15 @@ import google.registry.request.Action;
import google.registry.request.HttpException.BadRequestException; import google.registry.request.HttpException.BadRequestException;
import google.registry.request.Parameter; import google.registry.request.Parameter;
import google.registry.request.Response; import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.request.auth.AuthLevel;
import google.registry.util.FormattingLogger; import google.registry.util.FormattingLogger;
import javax.inject.Inject; import javax.inject.Inject;
/** /**
* An action to delete entities in Datastore specified by raw key ids, which can be found in * 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 * Datastore Viewer in the AppEngine console - it's the really long alphanumeric key that is labeled
* labeled "Entity key" on the page for an individual entity. * "Entity key" on the page for an individual entity.
* *
* <p>rawKeys is the only required parameter. It is a comma-delimited list of Strings. * <p>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 * 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. * 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 { public class DeleteEntityAction implements Runnable {
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass(); private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();

View file

@ -26,13 +26,24 @@ import com.google.common.collect.Lists;
import google.registry.model.domain.DomainResource; import google.registry.model.domain.DomainResource;
import google.registry.request.Action; import google.registry.request.Action;
import google.registry.request.Parameter; 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.Clock;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import javax.inject.Inject; import javax.inject.Inject;
/** An action that lists domains, for use by the {@code nomulus list_domains} command. */ /** 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<DomainResource> { public final class ListDomainsAction extends ListObjectsAction<DomainResource> {
/** An App Engine limitation on how many subqueries can be used in a single query. */ /** An App Engine limitation on how many subqueries can be used in a single query. */

View file

@ -24,13 +24,24 @@ import com.google.common.collect.ImmutableSet;
import google.registry.model.EppResourceUtils; import google.registry.model.EppResourceUtils;
import google.registry.model.host.HostResource; import google.registry.model.host.HostResource;
import google.registry.request.Action; import google.registry.request.Action;
import google.registry.request.auth.Auth;
import google.registry.request.auth.AuthLevel;
import google.registry.util.Clock; import google.registry.util.Clock;
import java.util.Comparator; import java.util.Comparator;
import javax.inject.Inject; import javax.inject.Inject;
import org.joda.time.DateTime; import org.joda.time.DateTime;
/** An action that lists hosts, for use by the {@code nomulus list_hosts} command. */ /** 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<HostResource> { public final class ListHostsAction extends ListObjectsAction<HostResource> {
public static final String PATH = "/_dr/admin/list/hosts"; public static final String PATH = "/_dr/admin/list/hosts";

View file

@ -22,6 +22,8 @@ import static google.registry.request.Action.Method.POST;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import google.registry.model.registry.label.PremiumList; import google.registry.model.registry.label.PremiumList;
import google.registry.request.Action; import google.registry.request.Action;
import google.registry.request.auth.Auth;
import google.registry.request.auth.AuthLevel;
import javax.inject.Inject; import javax.inject.Inject;
/** /**
@ -29,7 +31,13 @@ import javax.inject.Inject;
*/ */
@Action( @Action(
path = ListPremiumListsAction.PATH, 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<PremiumList> { public final class ListPremiumListsAction extends ListObjectsAction<PremiumList> {

View file

@ -22,10 +22,21 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import google.registry.model.registrar.Registrar; import google.registry.model.registrar.Registrar;
import google.registry.request.Action; import google.registry.request.Action;
import google.registry.request.auth.Auth;
import google.registry.request.auth.AuthLevel;
import javax.inject.Inject; import javax.inject.Inject;
/** An action that lists registrars, for use by the {@code nomulus list_registrars} command. */ /** 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<Registrar> { public final class ListRegistrarsAction extends ListObjectsAction<Registrar> {
public static final String PATH = "/_dr/admin/list/registrars"; public static final String PATH = "/_dr/admin/list/registrars";

View file

@ -22,10 +22,21 @@ import static google.registry.request.Action.Method.POST;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import google.registry.model.registry.label.ReservedList; import google.registry.model.registry.label.ReservedList;
import google.registry.request.Action; import google.registry.request.Action;
import google.registry.request.auth.Auth;
import google.registry.request.auth.AuthLevel;
import javax.inject.Inject; import javax.inject.Inject;
/** A that lists reserved lists, for use by the {@code nomulus list_reserved_lists} command. */ /** 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<ReservedList> { public final class ListReservedListsAction extends ListObjectsAction<ReservedList> {
public static final String PATH = "/_dr/admin/list/reservedLists"; public static final String PATH = "/_dr/admin/list/reservedLists";

View file

@ -25,12 +25,23 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import google.registry.model.registry.Registry; import google.registry.model.registry.Registry;
import google.registry.request.Action; import google.registry.request.Action;
import google.registry.request.auth.Auth;
import google.registry.request.auth.AuthLevel;
import google.registry.util.Clock; import google.registry.util.Clock;
import javax.inject.Inject; import javax.inject.Inject;
import org.joda.time.DateTime; import org.joda.time.DateTime;
/** An action that lists top-level domains, for use by the {@code nomulus list_tlds} command. */ /** 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<Registry> { public final class ListTldsAction extends ListObjectsAction<Registry> {
public static final String PATH = "/_dr/admin/list/tlds"; public static final String PATH = "/_dr/admin/list/tlds";

View file

@ -23,6 +23,8 @@ import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import google.registry.model.registry.label.PremiumList; import google.registry.model.registry.label.PremiumList;
import google.registry.request.Action; import google.registry.request.Action;
import google.registry.request.auth.Auth;
import google.registry.request.auth.AuthLevel;
import java.util.List; import java.util.List;
import javax.inject.Inject; 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} * An action that creates a premium list, for use by the {@code nomulus create_premium_list}
* command. * 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 class UpdatePremiumListAction extends CreateOrUpdatePremiumListAction {
public static final String PATH = "/_dr/admin/updatePremiumList"; public static final String PATH = "/_dr/admin/updatePremiumList";

View file

@ -67,6 +67,8 @@ import google.registry.model.reporting.HistoryEntry;
import google.registry.request.Action; import google.registry.request.Action;
import google.registry.request.JsonActionRunner; import google.registry.request.JsonActionRunner;
import google.registry.request.JsonActionRunner.JsonAction; 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.ArrayList;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List; import java.util.List;
@ -79,10 +81,17 @@ import javax.inject.Inject;
* OT&amp;E commands that have been run just previously to verification may not be picked up yet. * OT&amp;E commands that have been run just previously to verification may not be picked up yet.
*/ */
@Action( @Action(
path = VerifyOteAction.PATH, path = VerifyOteAction.PATH,
method = Action.Method.POST, method = Action.Method.POST,
xsrfProtection = true, xsrfProtection = true,
xsrfScope = "admin") 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 class VerifyOteAction implements Runnable, JsonAction {
public static final String PATH = "/_dr/admin/verifyOte"; public static final String PATH = "/_dr/admin/verifyOte";

View file

@ -18,6 +18,7 @@ java_library(
"//java/google/registry/flows", "//java/google/registry/flows",
"//java/google/registry/model", "//java/google/registry/model",
"//java/google/registry/request", "//java/google/registry/request",
"//java/google/registry/request/auth",
"//java/google/registry/security", "//java/google/registry/security",
"//java/google/registry/ui/forms", "//java/google/registry/ui/forms",
"//java/google/registry/ui/server", "//java/google/registry/ui/server",

View file

@ -31,6 +31,8 @@ import google.registry.flows.EppConsoleAction;
import google.registry.model.registrar.Registrar; import google.registry.model.registrar.Registrar;
import google.registry.request.Action; import google.registry.request.Action;
import google.registry.request.Response; 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.security.XsrfTokenManager;
import google.registry.ui.server.SoyTemplateUtils; import google.registry.ui.server.SoyTemplateUtils;
import google.registry.ui.soy.registrar.ConsoleSoyInfo; import google.registry.ui.soy.registrar.ConsoleSoyInfo;
@ -38,7 +40,17 @@ import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
/** Action that serves Registrar Console single HTML page (SPA). */ /** 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 final class ConsoleUiAction implements Runnable {
public static final String PATH = "/registrar"; public static final String PATH = "/registrar";

View file

@ -35,6 +35,8 @@ import google.registry.model.registrar.Registrar;
import google.registry.request.Action; import google.registry.request.Action;
import google.registry.request.JsonActionRunner; import google.registry.request.JsonActionRunner;
import google.registry.request.JsonActionRunner.JsonAction; 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.security.JsonResponseHelper;
import google.registry.ui.forms.FormField; import google.registry.ui.forms.FormField;
import google.registry.ui.forms.FormFieldException; import google.registry.ui.forms.FormFieldException;
@ -56,15 +58,16 @@ import org.joda.money.Money;
* <p>The request payload is a JSON object with the following fields: * <p>The request payload is a JSON object with the following fields:
* *
* <dl> * <dl>
* <dt>amount * <dt>amount
* <dd>String containing a fixed point value representing the amount of money the registrar * <dd>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 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. * customer in the payment form, as there is currently no integration with the billing system.
* <dt>currency * <dt>currency
* <dd>String containing a three letter ISO currency code, which is used to look up the Braintree * <dd>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. * merchant account ID to which payment should be posted.
* <dt>paymentMethodNonce * <dt>paymentMethodNonce
* <dd>UUID nonce string supplied by the Braintree JS SDK representing the selected payment method. * <dd>UUID nonce string supplied by the Braintree JS SDK representing the selected payment
* method.
* </dl> * </dl>
* *
* <h3>Response Object</h3> * <h3>Response Object</h3>
@ -73,28 +76,35 @@ import org.joda.money.Money;
* which, if successful, will contain a single result object with the following fields: * which, if successful, will contain a single result object with the following fields:
* *
* <dl> * <dl>
* <dt>id * <dt>id
* <dd>String containing transaction ID returned by Braintree gateway. * <dd>String containing transaction ID returned by Braintree gateway.
* <dt>formattedAmount * <dt>formattedAmount
* <dd>String containing amount paid, which can be displayed to the customer on a success page. * <dd>String containing amount paid, which can be displayed to the customer on a success page.
* </dl> * </dl>
* *
* <p><b>Note:</b> These definitions corresponds to Closure Compiler extern * <p><b>Note:</b> These definitions corresponds to Closure Compiler extern {@code
* {@code registry.rpc.Payment} which must be updated should these definitions change. * registry.rpc.Payment} which must be updated should these definitions change.
* *
* <h3>PCI Compliance</h3> * <h3>PCI Compliance</h3>
* *
* <p>The request object will not contain credit card information, but rather a * <p>The request object will not contain credit card information, but rather a {@code
* {@code payment_method_nonce} field that's populated by the Braintree JS SDK iframe. * payment_method_nonce} field that's populated by the Braintree JS SDK iframe.
* *
* @see RegistrarPaymentSetupAction * @see RegistrarPaymentSetupAction
*/ */
@Action( @Action(
path = "/registrar-payment", path = "/registrar-payment",
method = Action.Method.POST, method = Action.Method.POST,
xsrfProtection = true, xsrfProtection = true,
xsrfScope = "console", xsrfScope = "console",
requireLogin = true) 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 { public final class RegistrarPaymentAction implements Runnable, JsonAction {
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass(); private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();

View file

@ -28,6 +28,8 @@ import google.registry.model.registrar.Registrar;
import google.registry.request.Action; import google.registry.request.Action;
import google.registry.request.JsonActionRunner; import google.registry.request.JsonActionRunner;
import google.registry.request.JsonActionRunner.JsonAction; 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.security.JsonResponseHelper;
import java.util.Map; import java.util.Map;
import javax.inject.Inject; import javax.inject.Inject;
@ -46,29 +48,38 @@ import org.joda.money.CurrencyUnit;
* containing a single result object with the following fields: * containing a single result object with the following fields:
* *
* <dl> * <dl>
* <dt>brainframe * <dt>brainframe
* <dd>URL for iframe that loads Braintree payment method selector. * <dd>URL for iframe that loads Braintree payment method selector.
* <dt>token * <dt>token
* <dd>Nonce string obtained from the Braintree API which is needed by the Braintree JS SDK. * <dd>Nonce string obtained from the Braintree API which is needed by the Braintree JS SDK.
* <dt>currencies * <dt>currencies
* <dd>Array of strings, each containing a three letter currency code, which should be displayed to * <dd>Array of strings, each containing a three letter currency code, which should be displayed
* the customer in a drop-down field. This will be all currencies for which a Braintree merchant * to the customer in a drop-down field. This will be all currencies for which a Braintree
* account exists. A currency will even be displayed if no TLD is enabled on the customer * merchant account exists. A currency will even be displayed if no TLD is enabled on the
* account that bills in that currency. * customer account that bills in that currency.
* </dl> * </dl>
* *
* <p><b>Note:</b> These definitions corresponds to Closure Compiler extern * <p><b>Note:</b> These definitions corresponds to Closure Compiler extern {@code
* {@code registry.rpc.PaymentSetup} which must be updated should these definitions change. * registry.rpc.PaymentSetup} which must be updated should these definitions change.
* *
* @see RegistrarPaymentAction * @see RegistrarPaymentAction
* @see <a href="https://developers.braintreepayments.com/start/hello-server/java#generate-a-client-token">Generate a client token</a> * @see <a
* href="https://developers.braintreepayments.com/start/hello-server/java#generate-a-client-token">Generate
* a client token</a>
*/ */
@Action( @Action(
path = "/registrar-payment-setup", path = "/registrar-payment-setup",
method = Action.Method.POST, method = Action.Method.POST,
xsrfProtection = true, xsrfProtection = true,
xsrfScope = "console", xsrfScope = "console",
requireLogin = true) 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 { public final class RegistrarPaymentSetupAction implements Runnable, JsonAction {
@Inject BraintreeGateway braintreeGateway; @Inject BraintreeGateway braintreeGateway;

View file

@ -38,6 +38,8 @@ import google.registry.model.registrar.RegistrarContact;
import google.registry.request.Action; import google.registry.request.Action;
import google.registry.request.HttpException.BadRequestException; import google.registry.request.HttpException.BadRequestException;
import google.registry.request.JsonActionRunner; 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.security.JsonResponseHelper;
import google.registry.ui.forms.FormException; import google.registry.ui.forms.FormException;
import google.registry.ui.forms.FormFieldException; import google.registry.ui.forms.FormFieldException;
@ -57,11 +59,18 @@ import javax.servlet.http.HttpServletRequest;
* preserve history. * preserve history.
*/ */
@Action( @Action(
path = RegistrarSettingsAction.PATH, path = RegistrarSettingsAction.PATH,
requireLogin = true, requireLogin = true,
xsrfProtection = true, xsrfProtection = true,
xsrfScope = "console", xsrfScope = "console",
method = Action.Method.POST) 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 class RegistrarSettingsAction implements Runnable, JsonActionRunner.JsonAction {
public static final String PATH = "/registrar-settings"; public static final String PATH = "/registrar-settings";

View file

@ -12,6 +12,7 @@ java_library(
"//java/google/registry/model", "//java/google/registry/model",
"//java/google/registry/monitoring/metrics", "//java/google/registry/monitoring/metrics",
"//java/google/registry/request", "//java/google/registry/request",
"//java/google/registry/request/auth",
"//java/google/registry/util", "//java/google/registry/util",
"//java/google/registry/xml", "//java/google/registry/xml",
"//third_party/java/objectify:objectify-v4_1", "//third_party/java/objectify:objectify-v4_1",

View file

@ -32,6 +32,8 @@ import google.registry.config.RegistryConfig.Config;
import google.registry.request.Action; import google.registry.request.Action;
import google.registry.request.RequestPath; import google.registry.request.RequestPath;
import google.registry.request.Response; 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.Clock;
import google.registry.util.FormattingLogger; import google.registry.util.FormattingLogger;
import google.registry.whois.WhoisMetrics.WhoisMetric; import google.registry.whois.WhoisMetrics.WhoisMetric;
@ -47,11 +49,10 @@ import org.joda.time.Duration;
/** /**
* Human-Friendly HTTP WHOIS API * Human-Friendly HTTP WHOIS API
* *
* <p>This API uses easy to understand paths rather than {@link WhoisServer} which * <p>This API uses easy to understand paths rather than {@link WhoisServer} which requires a POST
* requires a POST request containing a WHOIS command. Because the typical WHOIS command is * request containing a WHOIS command. Because the typical WHOIS command is along the lines of
* along the lines of {@code "domain google.lol"} or the equivalent {@code "google.lol}, this * {@code "domain google.lol"} or the equivalent {@code "google.lol}, this servlet is just going to
* servlet is just going to replace the slashes with spaces and let {@link WhoisReader} * replace the slashes with spaces and let {@link WhoisReader} figure out what to do.
* figure out what to do.
* *
* <p>This servlet accepts requests from any origin. * <p>This servlet accepts requests from any origin.
* *
@ -95,7 +96,11 @@ import org.joda.time.Duration;
* *
* @see WhoisServer * @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 final class WhoisHttpServer implements Runnable {
public static final String PATH = "/whois/"; public static final String PATH = "/whois/";

View file

@ -22,6 +22,8 @@ import com.google.common.net.MediaType;
import google.registry.config.RegistryConfig.Config; import google.registry.config.RegistryConfig.Config;
import google.registry.request.Action; import google.registry.request.Action;
import google.registry.request.Response; 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.Clock;
import google.registry.util.FormattingLogger; import google.registry.util.FormattingLogger;
import google.registry.whois.WhoisMetrics.WhoisMetric; 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. * HTTP request handler for WHOIS protocol requests sent to us by a proxy.
* *
* <p>All commands and responses conform to the WHOIS spec as defined in RFC 3912. Commands must * <p>All commands and responses conform to the WHOIS spec as defined in RFC 3912. Commands must be
* be sent via an HTTP POST in the request body. * sent via an HTTP POST in the request body.
* *
* <p>This servlet is meant to serve as a low level interface for the proxy app which forwards us * <p>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 * requests received on port 43. However this interface is technically higher level because it sends
* sends back proper HTTP error codes such as 200, 400, 500, etc. These are discarded by the proxy * back proper HTTP error codes such as 200, 400, 500, etc. These are discarded by the proxy because
* because WHOIS specifies no manner for differentiating successful and erroneous requests. * WHOIS specifies no manner for differentiating successful and erroneous requests.
* *
* @see WhoisHttpServer * @see WhoisHttpServer
* @see <a href="http://www.ietf.org/rfc/rfc3912.txt">RFC 3912: WHOIS Protocol Specification</a> * @see <a href="http://www.ietf.org/rfc/rfc3912.txt">RFC 3912: WHOIS Protocol Specification</a>
*/ */
@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 { public class WhoisServer implements Runnable {
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass(); private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();

View file

@ -66,31 +66,52 @@ public final class RequestHandlerTest {
.withUserService(UserInfo.create("test@example.com", "test@example.com")) .withUserService(UserInfo.create("test@example.com", "test@example.com"))
.build(); .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 { public static class BumblebeeTask implements Runnable {
@Override @Override
public void run() {} public void run() {}
} }
@Action(path = "/sloth", method = POST, automaticallyPrintOk = true) @Action(
path = "/sloth",
method = POST,
automaticallyPrintOk = true,
auth = @Auth(minimumLevel = AuthLevel.NONE)
)
public static class SlothTask implements Runnable { public static class SlothTask implements Runnable {
@Override @Override
public void run() {} public void run() {}
} }
@Action(path = "/safe-sloth", method = {GET, POST}, xsrfProtection = true, xsrfScope = "vampire") @Action(
path = "/safe-sloth",
method = {GET, POST},
xsrfProtection = true,
xsrfScope = "vampire",
auth = @Auth(minimumLevel = AuthLevel.NONE)
)
public static class SafeSlothTask implements Runnable { public static class SafeSlothTask implements Runnable {
@Override @Override
public void run() {} public void run() {}
} }
@Action(path = "/users-only", method = GET, requireLogin = true) @Action(
path = "/users-only",
method = GET,
requireLogin = true,
auth = @Auth(minimumLevel = AuthLevel.NONE)
)
public static class UsersOnlyAction implements Runnable { public static class UsersOnlyAction implements Runnable {
@Override @Override
public void run() {} public void run() {}
} }
@Action(path = "/fail") @Action(path = "/fail", auth = @Auth(minimumLevel = AuthLevel.NONE))
public static final class FailTask implements Runnable { public static final class FailTask implements Runnable {
@Override @Override
public void run() { 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 static final class FailAtConstructionTask implements Runnable {
public FailAtConstructionTask() { public FailAtConstructionTask() {
throw new ServiceUnavailableException("Fail at construction"); throw new ServiceUnavailableException("Fail at construction");
@ -124,12 +145,10 @@ public final class RequestHandlerTest {
} }
@Action( @Action(
path = "/auth/none", path = "/auth/none",
auth = @Auth( auth = @Auth(minimumLevel = AuthLevel.NONE),
methods = {Auth.AuthMethod.INTERNAL}, method = Action.Method.GET
minimumLevel = AuthLevel.NONE, )
userPolicy = Auth.UserPolicy.IGNORED),
method = Action.Method.GET)
public class AuthNoneAction extends AuthBase { public class AuthNoneAction extends AuthBase {
AuthNoneAction(AuthResult authResult) { AuthNoneAction(AuthResult authResult) {
super(authResult); super(authResult);
@ -427,13 +446,13 @@ public final class RequestHandlerTest {
verify(usersOnlyAction).run(); verify(usersOnlyAction).run();
} }
// TODO(b/28219927): turn this on once we actually do authorization
@Ignore
@Test @Test
public void testNoAuthNeeded_success() throws Exception { public void testNoAuthNeeded_success() throws Exception {
when(req.getMethod()).thenReturn("GET"); when(req.getMethod()).thenReturn("GET");
when(req.getRequestURI()).thenReturn("/auth/none"); when(req.getRequestURI()).thenReturn("/auth/none");
handler.handleRequest(req, rsp); handler.handleRequest(req, rsp);
assertThat(providedAuthResult).isNotNull(); assertThat(providedAuthResult).isNotNull();
assertThat(providedAuthResult.authLevel()).isEqualTo(AuthLevel.NONE); assertThat(providedAuthResult.authLevel()).isEqualTo(AuthLevel.NONE);
assertThat(providedAuthResult.userAuthInfo()).isAbsent(); assertThat(providedAuthResult.userAuthInfo()).isAbsent();
@ -445,9 +464,25 @@ public final class RequestHandlerTest {
public void testAuthNeeded_notLoggedIn() throws Exception { public void testAuthNeeded_notLoggedIn() throws Exception {
when(req.getMethod()).thenReturn("GET"); when(req.getMethod()).thenReturn("GET");
when(req.getRequestURI()).thenReturn("/auth/adminUserAnyMethod"); when(req.getRequestURI()).thenReturn("/auth/adminUserAnyMethod");
handler.handleRequest(req, rsp); handler.handleRequest(req, rsp);
verify(rsp).sendError(403); verify(rsp).sendError(403);
assertThat(providedAuthResult).isNull(); 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 // TODO(b/28219927): turn this on once we actually do authorization
@ -457,19 +492,35 @@ public final class RequestHandlerTest {
userService.setUser(testUser, false); userService.setUser(testUser, false);
when(req.getMethod()).thenReturn("GET"); when(req.getMethod()).thenReturn("GET");
when(req.getRequestURI()).thenReturn("/auth/adminUserAnyMethod"); when(req.getRequestURI()).thenReturn("/auth/adminUserAnyMethod");
handler.handleRequest(req, rsp); handler.handleRequest(req, rsp);
verify(rsp).sendError(403); verify(rsp).sendError(403);
assertThat(providedAuthResult).isNull(); assertThat(providedAuthResult).isNull();
} }
// TODO(b/28219927): turn this on once we actually do authorization // TODO(b/28219927): remove this once we actually do authorization
@Ignore @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 @Test
public void testAuthNeeded_success() throws Exception { public void testAuthNeeded_success() throws Exception {
userService.setUser(testUser, true); userService.setUser(testUser, true);
when(req.getMethod()).thenReturn("GET"); when(req.getMethod()).thenReturn("GET");
when(req.getRequestURI()).thenReturn("/auth/adminUserAnyMethod"); when(req.getRequestURI()).thenReturn("/auth/adminUserAnyMethod");
handler.handleRequest(req, rsp); handler.handleRequest(req, rsp);
assertThat(providedAuthResult).isNotNull(); assertThat(providedAuthResult).isNotNull();
assertThat(providedAuthResult.authLevel()).isEqualTo(AuthLevel.USER); assertThat(providedAuthResult.authLevel()).isEqualTo(AuthLevel.USER);
assertThat(providedAuthResult.userAuthInfo()).isPresent(); assertThat(providedAuthResult.userAuthInfo()).isPresent();