Change @Auth to an AutoValue, and created a set of predefined Auths

We want to be safer and more explicit about the authentication needed by the many actions that exist.

As such, we make the 'auth' parameter required in @Action (so it's always clear who can run a specific action) and we replace the @Auth with an enum so that only pre-approved configurations that are aptly named and documented can be used.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=162210306
This commit is contained in:
guyben 2017-07-17 07:34:17 -07:00 committed by Ben McIlwain
parent 5966d8077b
commit e224a67eda
94 changed files with 614 additions and 511 deletions

View file

@ -26,6 +26,7 @@ import com.googlecode.objectify.VoidWork;
import google.registry.model.ofy.CommitLogCheckpoint;
import google.registry.model.ofy.CommitLogCheckpointRoot;
import google.registry.request.Action;
import google.registry.request.auth.Auth;
import google.registry.util.Clock;
import google.registry.util.FormattingLogger;
import google.registry.util.TaskEnqueuer;
@ -45,7 +46,9 @@ import org.joda.time.DateTime;
@Action(
path = "/_dr/cron/commitLogCheckpoint",
method = Action.Method.GET,
automaticallyPrintOk = true)
automaticallyPrintOk = true,
auth = Auth.AUTH_INTERNAL_ONLY
)
public final class CommitLogCheckpointAction implements Runnable {
private static final FormattingLogger logger = getLoggerForCallerClass();

View file

@ -37,6 +37,7 @@ import google.registry.model.ofy.CommitLogMutation;
import google.registry.model.translators.CommitLogRevisionsTranslatorFactory;
import google.registry.request.Action;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.Clock;
import google.registry.util.FormattingLogger;
import javax.inject.Inject;
@ -60,7 +61,10 @@ import org.joda.time.Duration;
* EppResource.
*
*/
@Action(path = "/_dr/task/deleteOldCommitLogs")
@Action(
path = "/_dr/task/deleteOldCommitLogs",
auth = Auth.AUTH_INTERNAL_ONLY
)
public final class DeleteOldCommitLogsAction implements Runnable {
private static final int NUM_REDUCE_SHARDS = 10;

View file

@ -47,6 +47,7 @@ import google.registry.model.ofy.CommitLogManifest;
import google.registry.model.ofy.CommitLogMutation;
import google.registry.request.Action;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.util.FormattingLogger;
import java.io.IOException;
import java.io.OutputStream;
@ -62,7 +63,9 @@ import org.joda.time.DateTime;
@Action(
path = ExportCommitLogDiffAction.PATH,
method = Action.Method.POST,
automaticallyPrintOk = true)
automaticallyPrintOk = true,
auth = Auth.AUTH_INTERNAL_ONLY
)
public final class ExportCommitLogDiffAction implements Runnable {
private static final FormattingLogger logger = getLoggerForCallerClass();

View file

@ -42,7 +42,6 @@ import google.registry.model.ofy.CommitLogMutation;
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.FormattingLogger;
import google.registry.util.Retrier;
import java.io.IOException;
@ -62,12 +61,7 @@ import org.joda.time.DateTime;
path = RestoreCommitLogsAction.PATH,
method = Action.Method.POST,
automaticallyPrintOk = true,
auth =
@Auth(
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API},
minimumLevel = AuthLevel.APP,
userPolicy = Auth.UserPolicy.ADMIN
)
auth = Auth.AUTH_INTERNAL_OR_ADMIN
)
public class RestoreCommitLogsAction implements Runnable {

View file

@ -19,6 +19,7 @@ java_library(
"//java/google/registry/pricing",
"//java/google/registry/request",
"//java/google/registry/request:modules",
"//java/google/registry/request/auth",
"//java/google/registry/util",
"//third_party/java/objectify:objectify-v4_1",
"@com_google_apis_google_api_services_bigquery",

View file

@ -85,6 +85,7 @@ import google.registry.model.reporting.HistoryEntry;
import google.registry.model.transfer.TransferStatus;
import google.registry.request.Action;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.Clock;
import google.registry.util.FormattingLogger;
import google.registry.util.NonFinalForTesting;
@ -103,7 +104,10 @@ import org.joda.time.DateTime;
* over all domains and domain applications and checking for any references to the contacts/hosts in
* pending deletion.
*/
@Action(path = "/_dr/task/deleteContactsAndHosts")
@Action(
path = "/_dr/task/deleteContactsAndHosts",
auth = Auth.AUTH_INTERNAL_ONLY
)
public class DeleteContactsAndHostsAction implements Runnable {
static final String KIND_CONTACT = getKind(ContactResource.class);

View file

@ -40,6 +40,7 @@ import google.registry.model.registry.Registry.TldType;
import google.registry.request.Action;
import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.FormattingLogger;
import google.registry.util.PipelineUtils;
import java.util.List;
@ -51,7 +52,11 @@ import javax.inject.Inject;
*
* <p>See: https://www.youtube.com/watch?v=xuuv0syoHnM
*/
@Action(path = "/_dr/task/deleteProberData", method = POST)
@Action(
path = "/_dr/task/deleteProberData",
method = POST,
auth = Auth.AUTH_INTERNAL_ONLY
)
public class DeleteProberDataAction implements Runnable {
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();

View file

@ -52,6 +52,7 @@ import google.registry.model.registry.Registry;
import google.registry.request.Action;
import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.Clock;
import google.registry.util.FormattingLogger;
import java.util.Set;
@ -67,7 +68,10 @@ import org.joda.time.DateTime;
* be expanded as a result of the job (the exclusive upper bound being the execution time of the
* job).
*/
@Action(path = "/_dr/task/expandRecurringBillingEvents")
@Action(
path = "/_dr/task/expandRecurringBillingEvents",
auth = Auth.AUTH_INTERNAL_ONLY
)
public class ExpandRecurringBillingEventsAction implements Runnable {
public static final String PARAM_CURSOR_TIME = "cursorTime";

View file

@ -25,6 +25,7 @@ import google.registry.mapreduce.MapreduceRunner;
import google.registry.request.Action;
import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.Clock;
import google.registry.util.FormattingLogger;
import java.util.Set;
@ -69,7 +70,10 @@ import org.joda.time.DateTime;
* are not in FINALIZED or STOPPED state.
*/
@Action(path = "/_dr/task/mapreduceEntityCleanup")
@Action(
path = "/_dr/task/mapreduceEntityCleanup",
auth = Auth.AUTH_INTERNAL_ONLY
)
public class MapreduceEntityCleanupAction implements Runnable {
private static final int DEFAULT_DAYS_OLD = 180;

View file

@ -52,6 +52,7 @@ import google.registry.model.domain.DomainResource;
import google.registry.model.host.HostResource;
import google.registry.request.Action;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.Clock;
import google.registry.util.FormattingLogger;
import google.registry.util.NonFinalForTesting;
@ -67,7 +68,10 @@ import javax.inject.Named;
import org.joda.time.DateTime;
/** Performs batched DNS refreshes for applicable domains following a host rename. */
@Action(path = "/_dr/task/refreshDnsOnHostRename")
@Action(
path = "/_dr/task/refreshDnsOnHostRename",
auth = Auth.AUTH_INTERNAL_ONLY
)
public class RefreshDnsOnHostRenameAction implements Runnable {
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();

View file

@ -59,6 +59,7 @@ import google.registry.model.index.ForeignKeyIndex.ForeignKeyHostIndex;
import google.registry.model.transfer.TransferData.TransferServerApproveEntity;
import google.registry.request.Action;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.FormattingLogger;
import google.registry.util.NonFinalForTesting;
import java.io.Serializable;
@ -88,7 +89,11 @@ import org.joda.time.DateTime;
* fullyQualifiedDomainName.
* </ul>
*/
@Action(path = "/_dr/task/verifyEntityIntegrity", method = POST)
@Action(
path = "/_dr/task/verifyEntityIntegrity",
method = POST,
auth = Auth.AUTH_INTERNAL_ONLY
)
public class VerifyEntityIntegrityAction implements Runnable {
private static final FormattingLogger logger = getLoggerForCallerClass();

View file

@ -10,6 +10,7 @@ java_library(
deps = [
"//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_appengine_api_1_0_sdk",

View file

@ -23,12 +23,17 @@ import com.google.common.base.Optional;
import google.registry.model.ofy.CommitLogBucket;
import google.registry.request.Action;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.util.TaskEnqueuer;
import java.util.Random;
import javax.inject.Inject;
/** Action for fanning out cron tasks for each commit log bucket. */
@Action(path = "/_dr/cron/commitLogFanout", automaticallyPrintOk = true)
@Action(
path = "/_dr/cron/commitLogFanout",
automaticallyPrintOk = true,
auth = Auth.AUTH_INTERNAL_ONLY
)
public final class CommitLogFanoutAction implements Runnable {
public static final String BUCKET_PARAM = "bucket";

View file

@ -41,6 +41,7 @@ import google.registry.request.Parameter;
import google.registry.request.ParameterMap;
import google.registry.request.RequestParameters;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.TaskEnqueuer;
import java.util.Random;
import java.util.Set;
@ -70,7 +71,11 @@ import javax.inject.Inject;
* This patharg is mostly useful for aesthetic purposes, since tasks are already namespaced.
* </ul>
*/
@Action(path = "/_dr/cron/fanout", automaticallyPrintOk = true)
@Action(
path = "/_dr/cron/fanout",
automaticallyPrintOk = true,
auth = Auth.AUTH_INTERNAL_ONLY
)
public final class TldFanoutAction implements Runnable {
private static final String ENDPOINT_PARAM = "endpoint";

View file

@ -25,6 +25,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",
"//third_party/java/objectify:objectify-v4_1",
"@com_google_appengine_api_1_0_sdk",

View file

@ -26,6 +26,7 @@ import google.registry.request.Action;
import google.registry.request.HttpException.ServiceUnavailableException;
import google.registry.request.Parameter;
import google.registry.request.RequestParameters;
import google.registry.request.auth.Auth;
import google.registry.util.DomainNameUtils;
import google.registry.util.FormattingLogger;
import java.util.Set;
@ -34,7 +35,12 @@ import javax.inject.Inject;
import org.joda.time.Duration;
/** Task that sends domain and host updates to the DNS server. */
@Action(path = PublishDnsUpdatesAction.PATH, method = POST, automaticallyPrintOk = true)
@Action(
path = PublishDnsUpdatesAction.PATH,
method = POST,
automaticallyPrintOk = true,
auth = Auth.AUTH_INTERNAL_ONLY
)
public final class PublishDnsUpdatesAction implements Runnable, Callable<Void> {
public static final String PATH = "/_dr/task/publishDnsUpdates";

View file

@ -40,6 +40,7 @@ import google.registry.model.registry.Registry;
import google.registry.request.Action;
import google.registry.request.Parameter;
import google.registry.request.RequestParameters;
import google.registry.request.auth.Auth;
import google.registry.util.FormattingLogger;
import google.registry.util.TaskEnqueuer;
import java.io.UnsupportedEncodingException;
@ -64,7 +65,11 @@ import org.joda.time.Duration;
* not.
* </ul>
*/
@Action(path = "/_dr/cron/readDnsQueue", automaticallyPrintOk = true)
@Action(
path = "/_dr/cron/readDnsQueue",
automaticallyPrintOk = true,
auth = Auth.AUTH_INTERNAL_ONLY
)
public final class ReadDnsQueueAction implements Runnable {
public static final String KEEP_TASKS_PARAM = "keepTasks";

View file

@ -26,11 +26,16 @@ 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.util.Clock;
import javax.inject.Inject;
/** Action that manually triggers refresh of DNS information. */
@Action(path = "/_dr/dnsRefresh", automaticallyPrintOk = true)
@Action(
path = "/_dr/dnsRefresh",
automaticallyPrintOk = true,
auth = Auth.AUTH_INTERNAL_ONLY
)
public final class RefreshDnsAction implements Runnable {
@Inject Clock clock;

View file

@ -31,6 +31,7 @@ import google.registry.request.Header;
import google.registry.request.HttpException.BadRequestException;
import google.registry.request.HttpException.NotModifiedException;
import google.registry.request.Payload;
import google.registry.request.auth.Auth;
import google.registry.util.FormattingLogger;
import google.registry.util.TaskEnqueuer;
import java.io.ByteArrayInputStream;
@ -48,7 +49,9 @@ import org.joda.time.Duration;
@Action(
path = BigqueryPollJobAction.PATH,
method = {Action.Method.GET, Action.Method.POST},
automaticallyPrintOk = true)
automaticallyPrintOk = true,
auth = Auth.AUTH_INTERNAL_ONLY
)
public class BigqueryPollJobAction implements Runnable {
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();

View file

@ -36,6 +36,7 @@ import google.registry.request.HttpException.NotModifiedException;
import google.registry.request.Parameter;
import google.registry.request.RequestMethod;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.FormattingLogger;
import java.util.Set;
import javax.inject.Inject;
@ -49,7 +50,8 @@ import org.joda.time.format.PeriodFormat;
@Action(
path = CheckSnapshotAction.PATH,
method = {POST, GET},
automaticallyPrintOk = true
automaticallyPrintOk = true,
auth = Auth.AUTH_INTERNAL_ONLY
)
public class CheckSnapshotAction implements Runnable {

View file

@ -37,6 +37,7 @@ import google.registry.model.domain.DomainResource;
import google.registry.model.registry.Registry.TldType;
import google.registry.request.Action;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.FormattingLogger;
import java.io.IOException;
import java.io.OutputStream;
@ -52,7 +53,11 @@ import org.joda.time.DateTime;
* Each TLD's active domain names are exported as a newline-delimited flat text file with the name
* TLD.txt into the domain-lists bucket. Note that this overwrites the files in place.
*/
@Action(path = "/_dr/task/exportDomainLists", method = POST)
@Action(
path = "/_dr/task/exportDomainLists",
method = POST,
auth = Auth.AUTH_INTERNAL_ONLY
)
public class ExportDomainListsAction implements Runnable {
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();

View file

@ -27,12 +27,17 @@ 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.storage.drive.DriveConnection;
import google.registry.util.FormattingLogger;
import javax.inject.Inject;
/** Action that exports the publicly viewable reserved terms list for a TLD to Google Drive. */
@Action(path = "/_dr/task/exportReservedTerms", method = POST)
@Action(
path = "/_dr/task/exportReservedTerms",
method = POST,
auth = Auth.AUTH_INTERNAL_ONLY
)
public class ExportReservedTermsAction implements Runnable {
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();

View file

@ -20,6 +20,7 @@ import static google.registry.request.Action.Method.POST;
import google.registry.config.RegistryConfig;
import google.registry.request.Action;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.Clock;
import google.registry.util.FormattingLogger;
import javax.inject.Inject;
@ -37,7 +38,12 @@ import javax.inject.Inject;
* <li>The {@link UpdateSnapshotViewAction} updates the view in latest_snapshot.
* </ol>
*/
@Action(path = ExportSnapshotAction.PATH, method = POST, automaticallyPrintOk = true)
@Action(
path = ExportSnapshotAction.PATH,
method = POST,
automaticallyPrintOk = true,
auth = Auth.AUTH_INTERNAL_ONLY
)
public class ExportSnapshotAction implements Runnable {
/** Queue to use for enqueuing the task that will actually launch the backup. */

View file

@ -43,6 +43,7 @@ import google.registry.request.Action;
import google.registry.request.HttpException.BadRequestException;
import google.registry.request.HttpException.InternalServerErrorException;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.util.Clock;
import google.registry.util.FormattingLogger;
import java.io.IOException;
@ -50,7 +51,11 @@ import javax.inject.Inject;
import org.joda.time.DateTime;
/** Action to load a Datastore snapshot from Google Cloud Storage into BigQuery. */
@Action(path = LoadSnapshotAction.PATH, method = POST)
@Action(
path = LoadSnapshotAction.PATH,
method = POST,
auth = Auth.AUTH_INTERNAL_ONLY
)
public class LoadSnapshotAction implements Runnable {
/** Parameter names for passing parameters into the servlet. */

View file

@ -29,9 +29,6 @@ import google.registry.request.HttpException.InternalServerErrorException;
import google.registry.request.JsonActionRunner;
import google.registry.request.JsonActionRunner.JsonAction;
import google.registry.request.auth.Auth;
import google.registry.request.auth.Auth.AuthMethod;
import google.registry.request.auth.Auth.UserPolicy;
import google.registry.request.auth.AuthLevel;
import google.registry.storage.drive.DriveConnection;
import google.registry.util.FormattingLogger;
import java.io.FileNotFoundException;
@ -43,12 +40,7 @@ import javax.inject.Inject;
@Action(
path = PublishDetailReportAction.PATH,
method = Action.Method.POST,
auth =
@Auth(
methods = {AuthMethod.INTERNAL, Auth.AuthMethod.API},
minimumLevel = AuthLevel.APP,
userPolicy = UserPolicy.ADMIN
)
auth = Auth.AUTH_INTERNAL_OR_ADMIN
)
public final class PublishDetailReportAction implements Runnable, JsonAction {

View file

@ -36,6 +36,7 @@ import google.registry.model.registrar.Registrar;
import google.registry.model.registrar.RegistrarContact;
import google.registry.request.Action;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.FormattingLogger;
import google.registry.util.Retrier;
import java.io.IOException;
@ -52,7 +53,11 @@ import javax.inject.Inject;
*
* <p>This uses the <a href="https://developers.google.com/admin-sdk/directory/">Directory API</a>.
*/
@Action(path = "/_dr/task/syncGroupMembers", method = POST)
@Action(
path = "/_dr/task/syncGroupMembers",
method = POST,
auth = Auth.AUTH_INTERNAL_ONLY
)
public final class SyncGroupMembersAction implements Runnable {
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();

View file

@ -13,6 +13,7 @@
// limitations under the License.
package google.registry.export;
import static google.registry.request.Action.Method.POST;
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
@ -27,13 +28,18 @@ import google.registry.config.RegistryConfig.Config;
import google.registry.request.Action;
import google.registry.request.HttpException.InternalServerErrorException;
import google.registry.request.Parameter;
import google.registry.request.auth.Auth;
import google.registry.util.FormattingLogger;
import google.registry.util.SqlTemplate;
import java.io.IOException;
import javax.inject.Inject;
/** Update a well-known view to point at a certain Datastore snapshot table in BigQuery. */
@Action(path = UpdateSnapshotViewAction.PATH, method = POST)
@Action(
path = UpdateSnapshotViewAction.PATH,
method = POST,
auth = Auth.AUTH_INTERNAL_ONLY
)
public class UpdateSnapshotViewAction implements Runnable {
/** Headers for passing parameters into the servlet. */

View file

@ -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_api_client",

View file

@ -34,6 +34,7 @@ import google.registry.model.server.Lock;
import google.registry.request.Action;
import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.FormattingLogger;
import google.registry.util.NonFinalForTesting;
import java.io.IOException;
@ -60,7 +61,11 @@ import org.joda.time.Duration;
*
* @see SyncRegistrarsSheet
*/
@Action(path = SyncRegistrarsSheetAction.PATH, method = POST)
@Action(
path = SyncRegistrarsSheetAction.PATH,
method = POST,
auth = Auth.AUTH_INTERNAL_ONLY
)
public class SyncRegistrarsSheetAction implements Runnable {
private enum Result {

View file

@ -46,7 +46,6 @@ 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;
@ -61,7 +60,7 @@ import javax.servlet.http.HttpServletRequest;
*/
@Action(
path = "/check",
auth = @Auth(minimumLevel = AuthLevel.NONE, userPolicy = Auth.UserPolicy.PUBLIC)
auth = Auth.AUTH_PUBLIC_ANONYMOUS
)
public class CheckApiAction implements Runnable {

View file

@ -19,7 +19,6 @@ 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;
@ -27,12 +26,7 @@ import javax.servlet.http.HttpSession;
@Action(
path = "/registrar-xhr",
method = Method.POST,
auth =
@Auth(
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API, Auth.AuthMethod.LEGACY},
minimumLevel = AuthLevel.USER,
userPolicy = Auth.UserPolicy.PUBLIC
)
auth = Auth.AUTH_PUBLIC_LOGGED_IN
)
public class EppConsoleAction implements Runnable {

View file

@ -18,7 +18,6 @@ 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;
@ -30,12 +29,7 @@ import javax.servlet.http.HttpSession;
@Action(
path = "/_dr/epp",
method = Method.POST,
auth =
@Auth(
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API},
minimumLevel = AuthLevel.APP,
userPolicy = Auth.UserPolicy.ADMIN
)
auth = Auth.AUTH_INTERNAL_OR_ADMIN
)
public class EppTlsAction implements Runnable {

View file

@ -25,7 +25,6 @@ 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;
@ -33,12 +32,7 @@ import javax.servlet.http.HttpServletRequest;
@Action(
path = "/_dr/epptool",
method = Method.POST,
auth =
@Auth(
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API},
minimumLevel = AuthLevel.APP,
userPolicy = Auth.UserPolicy.ADMIN
)
auth = Auth.AUTH_INTERNAL_OR_ADMIN
)
public class EppToolAction implements Runnable {

View file

@ -34,7 +34,6 @@ import google.registry.config.RegistryEnvironment;
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.security.XsrfTokenManager;
import google.registry.util.FormattingLogger;
import google.registry.util.TaskEnqueuer;
@ -58,12 +57,7 @@ import org.joda.time.DateTime;
path = LoadTestAction.PATH,
method = Action.Method.POST,
automaticallyPrintOk = true,
auth =
@Auth(
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API},
minimumLevel = AuthLevel.APP,
userPolicy = Auth.UserPolicy.ADMIN
)
auth = Auth.AUTH_INTERNAL_OR_ADMIN
)
public class LoadTestAction implements Runnable {

View file

@ -14,6 +14,7 @@ java_library(
"//java/google/registry/monitoring/metrics",
"//java/google/registry/monitoring/metrics/stackdriver",
"//java/google/registry/request",
"//java/google/registry/request/auth",
"//java/google/registry/util",
"//third_party/java/objectify:objectify-v4_1",
"@com_google_apis_google_api_services_bigquery",

View file

@ -36,13 +36,18 @@ import google.registry.config.RegistryConfig.Config;
import google.registry.request.Action;
import google.registry.request.Parameter;
import google.registry.request.ParameterMap;
import google.registry.request.auth.Auth;
import google.registry.util.FormattingLogger;
import java.io.IOException;
import java.util.Map;
import javax.inject.Inject;
/** Action for exporting metrics to BigQuery. */
@Action(path = MetricsExportAction.PATH, method = POST)
@Action(
path = MetricsExportAction.PATH,
method = POST,
auth = Auth.AUTH_INTERNAL_ONLY
)
public class MetricsExportAction implements Runnable {
public static final String PATH = "/_dr/task/metrics";

View file

@ -21,7 +21,6 @@ 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;
/**
@ -34,7 +33,7 @@ import javax.inject.Inject;
path = RdapAutnumAction.PATH,
method = {GET, HEAD},
isPrefix = true,
auth = @Auth(minimumLevel = AuthLevel.NONE, userPolicy = Auth.UserPolicy.PUBLIC)
auth = Auth.AUTH_PUBLIC_ANONYMOUS
)
public class RdapAutnumAction extends RdapActionBase {

View file

@ -24,7 +24,6 @@ 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;
@ -34,7 +33,7 @@ import org.joda.time.DateTime;
path = RdapDomainAction.PATH,
method = {GET, HEAD},
isPrefix = true,
auth = @Auth(minimumLevel = AuthLevel.NONE, userPolicy = Auth.UserPolicy.PUBLIC)
auth = Auth.AUTH_PUBLIC_ANONYMOUS
)
public class RdapDomainAction extends RdapActionBase {

View file

@ -42,7 +42,6 @@ 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 google.registry.util.FormattingLogger;
import google.registry.util.Idn;
@ -66,7 +65,7 @@ import org.joda.time.DateTime;
@Action(
path = RdapDomainSearchAction.PATH,
method = {GET, HEAD},
auth = @Auth(minimumLevel = AuthLevel.NONE, userPolicy = Auth.UserPolicy.PUBLIC)
auth = Auth.AUTH_PUBLIC_ANONYMOUS
)
public class RdapDomainSearchAction extends RdapActionBase {

View file

@ -32,7 +32,6 @@ 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;
@ -51,7 +50,7 @@ import org.joda.time.DateTime;
path = RdapEntityAction.PATH,
method = {GET, HEAD},
isPrefix = true,
auth = @Auth(minimumLevel = AuthLevel.NONE, userPolicy = Auth.UserPolicy.PUBLIC)
auth = Auth.AUTH_PUBLIC_ANONYMOUS
)
public class RdapEntityAction extends RdapActionBase {

View file

@ -41,7 +41,6 @@ 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;
@ -61,7 +60,7 @@ import org.joda.time.DateTime;
@Action(
path = RdapEntitySearchAction.PATH,
method = {GET, HEAD},
auth = @Auth(minimumLevel = AuthLevel.NONE, userPolicy = Auth.UserPolicy.PUBLIC)
auth = Auth.AUTH_PUBLIC_ANONYMOUS
)
public class RdapEntitySearchAction extends RdapActionBase {

View file

@ -22,7 +22,6 @@ 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;
@ -31,7 +30,7 @@ import javax.inject.Inject;
path = RdapHelpAction.PATH,
method = {GET, HEAD},
isPrefix = true,
auth = @Auth(minimumLevel = AuthLevel.NONE, userPolicy = Auth.UserPolicy.PUBLIC)
auth = Auth.AUTH_PUBLIC_ANONYMOUS
)
public class RdapHelpAction extends RdapActionBase {

View file

@ -21,7 +21,6 @@ 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;
/**
@ -34,7 +33,7 @@ import javax.inject.Inject;
path = RdapIpAction.PATH,
method = {GET, HEAD},
isPrefix = true,
auth = @Auth(minimumLevel = AuthLevel.NONE, userPolicy = Auth.UserPolicy.PUBLIC)
auth = Auth.AUTH_PUBLIC_ANONYMOUS
)
public class RdapIpAction extends RdapActionBase {

View file

@ -24,7 +24,6 @@ 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;
@ -34,7 +33,7 @@ import org.joda.time.DateTime;
path = RdapNameserverAction.PATH,
method = {GET, HEAD},
isPrefix = true,
auth = @Auth(minimumLevel = AuthLevel.NONE, userPolicy = Auth.UserPolicy.PUBLIC)
auth = Auth.AUTH_PUBLIC_ANONYMOUS
)
public class RdapNameserverAction extends RdapActionBase {

View file

@ -37,7 +37,6 @@ 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;
@ -58,7 +57,7 @@ import org.joda.time.DateTime;
@Action(
path = RdapNameserverSearchAction.PATH,
method = {GET, HEAD},
auth = @Auth(minimumLevel = AuthLevel.NONE, userPolicy = Auth.UserPolicy.PUBLIC)
auth = Auth.AUTH_PUBLIC_ANONYMOUS
)
public class RdapNameserverSearchAction extends RdapActionBase {

View file

@ -15,6 +15,7 @@ java_library(
"//java/google/registry/mapreduce/inputs",
"//java/google/registry/model",
"//java/google/registry/request",
"//java/google/registry/request/auth",
"//java/google/registry/tldconfig/idn",
"//java/google/registry/util",
"//java/google/registry/xjc",

View file

@ -27,6 +27,7 @@ import google.registry.model.rde.RdeNamingUtils;
import google.registry.request.Action;
import google.registry.request.Parameter;
import google.registry.request.RequestParameters;
import google.registry.request.auth.Auth;
import google.registry.util.FormattingLogger;
import java.io.BufferedInputStream;
import java.io.IOException;
@ -52,7 +53,12 @@ import org.joda.time.DateTime;
*
* @see <a href="http://newgtlds.icann.org/en/applicants/agb/agreement-approved-09jan14-en.htm">Registry Agreement</a>
*/
@Action(path = BrdaCopyAction.PATH, method = POST, automaticallyPrintOk = true)
@Action(
path = BrdaCopyAction.PATH,
method = POST,
automaticallyPrintOk = true,
auth = Auth.AUTH_INTERNAL_ONLY
)
public final class BrdaCopyAction implements Runnable {
static final String PATH = "/_dr/task/brdaCopy";

View file

@ -36,6 +36,7 @@ import google.registry.request.HttpException.NoContentException;
import google.registry.request.Parameter;
import google.registry.request.RequestParameters;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.FormattingLogger;
import java.io.IOException;
import java.io.InputStream;
@ -48,7 +49,11 @@ import org.joda.time.Duration;
/**
* Action that uploads a small XML RDE report to ICANN after {@link RdeUploadAction} has finished.
*/
@Action(path = RdeReportAction.PATH, method = POST)
@Action(
path = RdeReportAction.PATH,
method = POST,
auth = Auth.AUTH_INTERNAL_ONLY
)
public final class RdeReportAction implements Runnable, EscrowTask {
static final String PATH = "/_dr/task/rdeReport";

View file

@ -45,6 +45,7 @@ import google.registry.request.HttpException.BadRequestException;
import google.registry.request.Parameter;
import google.registry.request.RequestParameters;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.Clock;
import google.registry.util.FormattingLogger;
import javax.inject.Inject;
@ -187,7 +188,8 @@ import org.joda.time.Duration;
*/
@Action(
path = RdeStagingAction.PATH,
method = {GET, POST}
method = {GET, POST},
auth = Auth.AUTH_INTERNAL_ONLY
)
public final class RdeStagingAction implements Runnable {

View file

@ -48,6 +48,7 @@ import google.registry.request.HttpException.ServiceUnavailableException;
import google.registry.request.Parameter;
import google.registry.request.RequestParameters;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.Clock;
import google.registry.util.FormattingLogger;
import google.registry.util.Retrier;
@ -77,7 +78,11 @@ import org.joda.time.Duration;
* <p>Once this action completes, it rolls the cursor forward a day and triggers
* {@link RdeReportAction}.
*/
@Action(path = RdeUploadAction.PATH, method = POST)
@Action(
path = RdeUploadAction.PATH,
method = POST,
auth = Auth.AUTH_INTERNAL_ONLY
)
public final class RdeUploadAction implements Runnable, EscrowTask {
static final String PATH = "/_dr/task/rdeUpload";

View file

@ -16,6 +16,7 @@ java_library(
"//java/google/registry/model",
"//java/google/registry/pricing",
"//java/google/registry/request",
"//java/google/registry/request/auth",
"//java/google/registry/util",
"//java/google/registry/xjc",
"//java/google/registry/xml",

View file

@ -33,6 +33,7 @@ import google.registry.model.contact.ContactResource;
import google.registry.request.Action;
import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.FormattingLogger;
import google.registry.util.SystemClock;
import google.registry.xjc.JaxbFragment;
@ -45,7 +46,10 @@ import javax.inject.Inject;
*
* <p>Specify the escrow file to import with the "path" parameter.
*/
@Action(path = "/_dr/task/importRdeContacts")
@Action(
path = "/_dr/task/importRdeContacts",
auth = Auth.AUTH_INTERNAL_ONLY
)
public class RdeContactImportAction implements Runnable {
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();

View file

@ -50,6 +50,7 @@ import google.registry.model.transfer.TransferStatus;
import google.registry.request.Action;
import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.FormattingLogger;
import google.registry.util.SystemClock;
import google.registry.xjc.JaxbFragment;
@ -63,7 +64,10 @@ import org.joda.money.Money;
*
* <p>Specify the escrow file to import with the "path" parameter.
*/
@Action(path = "/_dr/task/importRdeDomains")
@Action(
path = "/_dr/task/importRdeDomains",
auth = Auth.AUTH_INTERNAL_ONLY
)
public class RdeDomainImportAction implements Runnable {
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();

View file

@ -33,6 +33,7 @@ import google.registry.model.host.HostResource;
import google.registry.request.Action;
import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.FormattingLogger;
import google.registry.util.SystemClock;
import google.registry.xjc.JaxbFragment;
@ -45,7 +46,10 @@ import javax.inject.Inject;
*
* <p>Specify the escrow file to import with the "path" parameter.
*/
@Action(path = "/_dr/task/importRdeHosts")
@Action(
path = "/_dr/task/importRdeHosts",
auth = Auth.AUTH_INTERNAL_ONLY
)
public class RdeHostImportAction implements Runnable {
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();

View file

@ -33,6 +33,7 @@ import google.registry.model.host.HostResource;
import google.registry.request.Action;
import google.registry.request.Parameter;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.FormattingLogger;
import google.registry.xjc.JaxbFragment;
import google.registry.xjc.rdehost.XjcRdeHost;
@ -51,7 +52,10 @@ import org.joda.time.DateTime;
*
* <p>Specify the escrow file to import with the "path" parameter.
*/
@Action(path = "/_dr/task/linkRdeHosts")
@Action(
path = "/_dr/task/linkRdeHosts",
auth = Auth.AUTH_INTERNAL_ONLY
)
public class RdeHostLinkAction implements Runnable {
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();

View file

@ -30,9 +30,6 @@ 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.Auth.AuthMethod;
import google.registry.request.auth.Auth.UserPolicy;
import google.registry.request.auth.AuthLevel;
import google.registry.util.FormattingLogger;
import google.registry.util.Retrier;
import google.registry.xml.XmlException;
@ -50,12 +47,7 @@ import javax.inject.Inject;
@Action(
path = IcannReportingUploadAction.PATH,
method = POST,
auth =
@Auth(
methods = {AuthMethod.INTERNAL, AuthMethod.API},
minimumLevel = AuthLevel.APP,
userPolicy = UserPolicy.ADMIN
)
auth = Auth.AUTH_INTERNAL_OR_ADMIN
)
public final class IcannReportingUploadAction implements Runnable {

View file

@ -47,5 +47,5 @@ public @interface Action {
boolean automaticallyPrintOk() default false;
/** Authentication settings. */
Auth auth() default @Auth;
Auth auth();
}

View file

@ -132,7 +132,7 @@ public class RequestHandler<C> {
return;
}
Optional<AuthResult> authResult =
requestAuthenticator.authorize(route.get().action().auth(), req);
requestAuthenticator.authorize(route.get().action().auth().authSettings(), req);
if (!authResult.isPresent()) {
rsp.sendError(SC_FORBIDDEN, "Not authorized");
return;

View file

@ -90,9 +90,9 @@ public class RouterDisplayHelper {
route.actionClass().getSimpleName(),
Joiner.on(",").join(route.action().method()),
route.action().automaticallyPrintOk() ? "y" : "n",
Joiner.on(",").join(route.action().auth().methods()),
route.action().auth().minimumLevel(),
route.action().auth().userPolicy());
Joiner.on(",").join(route.action().auth().authSettings().methods()),
route.action().auth().authSettings().minimumLevel(),
route.action().auth().authSettings().userPolicy());
}
private static String formatRoutes(Iterable<Route> routes) {
@ -119,11 +119,11 @@ public class RouterDisplayHelper {
if (len > methodsWidth) {
methodsWidth = len;
}
len = Joiner.on(",").join(route.action().auth().methods()).length();
len = Joiner.on(",").join(route.action().auth().authSettings().methods()).length();
if (len > authMethodsWidth) {
authMethodsWidth = len;
}
len = route.action().auth().minimumLevel().toString().length();
len = route.action().auth().authSettings().minimumLevel().toString().length();
if (len > minLevelWidth) {
minLevelWidth = len;
}

View file

@ -14,53 +14,74 @@
package google.registry.request.auth;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.google.common.collect.ImmutableList;
import google.registry.request.auth.RequestAuthenticator.AuthMethod;
import google.registry.request.auth.RequestAuthenticator.AuthSettings;
import google.registry.request.auth.RequestAuthenticator.UserPolicy;
/** Annotation used to configure authentication settings for Actions. */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Auth {
/** Available methods for authentication. */
public enum AuthMethod {
/** App Engine internal authentication. Must always be provided as the first method. */
INTERNAL,
/** Authentication methods suitable for API-style access, such as OAuth 2. */
API,
/** Legacy authentication using cookie-based App Engine Users API. Must come last if present. */
LEGACY
}
/** User authorization policy options. */
public enum UserPolicy {
/** This action ignores end users; the only configured auth method must be INTERNAL. */
IGNORED,
/** No user policy is enforced; anyone can access this action. */
PUBLIC,
/** Enum used to configure authentication settings for Actions. */
public enum Auth {
/**
* If there is a user, it must be an admin, as determined by isUserAdmin().
* Allows anyone access, doesn't attempt to authenticate user.
*
* <p>Note that, according to App Engine, anybody with access to the app in the GCP Console,
* including editors and viewers, is an admin.
* Will never return absent(), but only authenticates access from App Engine task-queues. For
* everyone else - returns NOT_AUTHENTICATED.
*/
ADMIN
AUTH_PUBLIC_ANONYMOUS(
ImmutableList.of(AuthMethod.INTERNAL),
AuthLevel.NONE,
UserPolicy.PUBLIC),
/**
* Allows anyone access, does attempt to authenticate user.
*
* If a user is logged in, will authenticate (and return) them. Otherwise, access is still
* granted, but NOT_AUTHENTICATED is returned.
*
* Will never return absent().
*/
AUTH_PUBLIC(
ImmutableList.of(AuthMethod.INTERNAL, AuthMethod.API, AuthMethod.LEGACY),
AuthLevel.NONE,
UserPolicy.PUBLIC),
/**
* Allows anyone access, as long as they are logged in.
*
* Does not allow access from App Engine task-queues.
*/
AUTH_PUBLIC_LOGGED_IN(
ImmutableList.of(AuthMethod.API, AuthMethod.LEGACY),
AuthLevel.USER,
UserPolicy.PUBLIC),
/**
* Allows only admins or App Engine task-queue access.
*/
AUTH_INTERNAL_OR_ADMIN(
ImmutableList.of(AuthMethod.INTERNAL, AuthMethod.API),
AuthLevel.APP,
UserPolicy.ADMIN),
/**
* Allows only internal (App Engine task-queue) access.
*/
AUTH_INTERNAL_ONLY(
ImmutableList.of(AuthMethod.INTERNAL),
AuthLevel.APP,
UserPolicy.IGNORED);
private final AuthSettings authSettings;
Auth(
ImmutableList<AuthMethod> methods,
AuthLevel minimumLevel,
UserPolicy userPolicy) {
authSettings = AuthSettings.create(methods, minimumLevel, userPolicy);
}
/** Enabled authentication methods for this action. */
AuthMethod[] methods() default { AuthMethod.INTERNAL };
/** Required minimum level of authentication for this action. */
AuthLevel minimumLevel() default AuthLevel.APP;
/** Required user authorization policy for this action. */
UserPolicy userPolicy() default UserPolicy.IGNORED;
public AuthSettings authSettings() {
return authSettings;
}
}

View file

@ -16,10 +16,12 @@ package google.registry.request.auth;
import static com.google.common.base.Preconditions.checkArgument;
import com.google.auto.value.AutoValue;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Ordering;
import com.google.errorprone.annotations.Immutable;
import google.registry.util.FormattingLogger;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
@ -44,6 +46,57 @@ public class RequestAuthenticator {
this.legacyAuthenticationMechanism = legacyAuthenticationMechanism;
}
/**
* Parameters used to configure the authenticator.
*
* AuthSettings shouldn't be used directly, instead - use one of the predefined {@link Auth} enum
* values.
*/
@Immutable
@AutoValue
public abstract static class AuthSettings {
public abstract ImmutableList<AuthMethod> methods();
public abstract AuthLevel minimumLevel();
public abstract UserPolicy userPolicy();
static AuthSettings create(
ImmutableList<AuthMethod> methods, AuthLevel minimumLevel, UserPolicy userPolicy) {
return new AutoValue_RequestAuthenticator_AuthSettings(methods, minimumLevel, userPolicy);
}
}
/** Available methods for authentication. */
public enum AuthMethod {
/** App Engine internal authentication. Must always be provided as the first method. */
INTERNAL,
/** Authentication methods suitable for API-style access, such as OAuth 2. */
API,
/** Legacy authentication using cookie-based App Engine Users API. Must come last if present. */
LEGACY
}
/** User authorization policy options. */
public enum UserPolicy {
/** This action ignores end users; the only configured auth method must be INTERNAL. */
IGNORED,
/** No user policy is enforced; anyone can access this action. */
PUBLIC,
/**
* If there is a user, it must be an admin, as determined by isUserAdmin().
*
* <p>Note that, according to App Engine, anybody with access to the app in the GCP Console,
* including editors and viewers, is an admin.
*/
ADMIN
}
/**
* Attempts to authenticate and authorize the user, according to the settings of the action.
*
@ -54,7 +107,7 @@ public class RequestAuthenticator {
* not; authentication can be "successful" even without any authentication if the action's
* auth settings are set to NONE -- in this case, NOT_AUTHENTICATED is returned
*/
public Optional<AuthResult> authorize(Auth auth, HttpServletRequest req) {
public Optional<AuthResult> authorize(AuthSettings auth, HttpServletRequest req) {
logger.infofmt("Action requires auth: %s", auth);
AuthResult authResult = authenticate(auth, req);
switch (auth.minimumLevel()) {
@ -104,15 +157,15 @@ public class RequestAuthenticator {
* @param req the {@link HttpServletRequest}; some authentication mechanisms use HTTP headers
* @return an authentication result; if no authentication was made, returns NOT_AUTHENTICATED
*/
private AuthResult authenticate(Auth auth, HttpServletRequest req) {
private AuthResult authenticate(AuthSettings auth, HttpServletRequest req) {
checkAuthConfig(auth);
for (Auth.AuthMethod authMethod : auth.methods()) {
for (AuthMethod authMethod : auth.methods()) {
switch (authMethod) {
// App Engine internal authentication, using the queue name header
case INTERNAL:
// checkAuthConfig will have insured that the user policy is not USER.
logger.info("Checking internal auth");
// INTERNAL should be skipped if a user is required.
if (auth.minimumLevel() != AuthLevel.USER) {
{
AuthResult authResult = appEngineInternalAuthenticationMechanism.authenticate(req);
if (authResult.isAuthenticated()) {
logger.infofmt("Authenticated: %s", authResult);
@ -148,25 +201,20 @@ public class RequestAuthenticator {
return AuthResult.NOT_AUTHENTICATED;
}
/** Validates an Auth object, checking for invalid setting combinations. */
void checkAuthConfig(Auth auth) {
ImmutableList<Auth.AuthMethod> authMethods = ImmutableList.copyOf(auth.methods());
/** Validates an AuthSettings object, checking for invalid setting combinations. */
static void checkAuthConfig(AuthSettings auth) {
ImmutableList<AuthMethod> authMethods = ImmutableList.copyOf(auth.methods());
checkArgument(!authMethods.isEmpty(), "Must specify at least one auth method");
checkArgument(
Ordering.explicit(Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API, Auth.AuthMethod.LEGACY)
Ordering.explicit(AuthMethod.INTERNAL, AuthMethod.API, AuthMethod.LEGACY)
.isStrictlyOrdered(authMethods),
"Auth methods must be unique and strictly in order - INTERNAL, API, LEGACY");
checkArgument(
authMethods.contains(Auth.AuthMethod.INTERNAL),
"Auth method INTERNAL must always be specified, and as the first auth method");
if (authMethods.equals(ImmutableList.of(Auth.AuthMethod.INTERNAL))) {
!(authMethods.contains(AuthMethod.INTERNAL) && auth.minimumLevel().equals(AuthLevel.USER)),
"Actions with INTERNAL auth method may not require USER auth level");
checkArgument(
!auth.minimumLevel().equals(AuthLevel.USER),
"Actions with only INTERNAL auth may not require USER auth level");
} else {
checkArgument(
!auth.userPolicy().equals(Auth.UserPolicy.IGNORED),
!(auth.userPolicy().equals(UserPolicy.IGNORED)
&& !authMethods.equals(ImmutableList.of(AuthMethod.INTERNAL))),
"Actions with auth methods beyond INTERNAL must not specify the IGNORED user policy");
}
}
}

View file

@ -17,6 +17,7 @@ java_library(
"//java/google/registry/keyring/api",
"//java/google/registry/model",
"//java/google/registry/request",
"//java/google/registry/request/auth",
"//java/google/registry/util",
"//java/google/registry/xml",
"//third_party/java/objectify:objectify-v4_1",

View file

@ -39,6 +39,7 @@ import google.registry.config.RegistryConfig.Config;
import google.registry.request.Action;
import google.registry.request.Parameter;
import google.registry.request.RequestParameters;
import google.registry.request.auth.Auth;
import google.registry.util.Clock;
import google.registry.util.FormattingLogger;
import google.registry.util.UrlFetchException;
@ -57,7 +58,12 @@ import org.joda.time.Duration;
*
* @see NordnVerifyAction
*/
@Action(path = NordnUploadAction.PATH, method = Action.Method.POST, automaticallyPrintOk = true)
@Action(
path = NordnUploadAction.PATH,
method = Action.Method.POST,
automaticallyPrintOk = true,
auth = Auth.AUTH_INTERNAL_ONLY
)
public final class NordnUploadAction implements Runnable {
static final String PATH = "/_dr/task/nordnUpload";

View file

@ -32,6 +32,7 @@ import google.registry.request.HttpException.ConflictException;
import google.registry.request.Parameter;
import google.registry.request.RequestParameters;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.util.FormattingLogger;
import google.registry.util.UrlFetchException;
import java.io.IOException;
@ -51,7 +52,12 @@ import javax.inject.Inject;
* @see <a href="http://tools.ietf.org/html/draft-lozano-tmch-func-spec-08#section-5.2.3.3">
* http://tools.ietf.org/html/draft-lozano-tmch-func-spec-08#section-5.2.3.3</a>
*/
@Action(path = NordnVerifyAction.PATH, method = Action.Method.POST, automaticallyPrintOk = true)
@Action(
path = NordnVerifyAction.PATH,
method = Action.Method.POST,
automaticallyPrintOk = true,
auth = Auth.AUTH_INTERNAL_ONLY
)
public final class NordnVerifyAction implements Runnable {
public static final String PARAM_CSV_DATA = "csvData";

View file

@ -20,13 +20,19 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.base.Optional;
import google.registry.config.RegistryConfig.Config;
import google.registry.request.Action;
import google.registry.request.auth.Auth;
import java.io.IOException;
import java.net.URL;
import java.security.GeneralSecurityException;
import javax.inject.Inject;
/** Action to download the latest ICANN TMCH CRL from MarksDB. */
@Action(path = "/_dr/task/tmchCrl", method = POST, automaticallyPrintOk = true)
@Action(
path = "/_dr/task/tmchCrl",
method = POST,
automaticallyPrintOk = true,
auth = Auth.AUTH_INTERNAL_ONLY
)
public final class TmchCrlAction implements Runnable {
@Inject Marksdb marksdb;

View file

@ -20,6 +20,7 @@ import com.google.common.base.Optional;
import google.registry.keyring.api.KeyModule.Key;
import google.registry.model.tmch.ClaimsListShard;
import google.registry.request.Action;
import google.registry.request.auth.Auth;
import google.registry.util.FormattingLogger;
import java.io.IOException;
import java.security.SignatureException;
@ -28,7 +29,12 @@ import javax.inject.Inject;
import org.bouncycastle.openpgp.PGPException;
/** Action to download the latest domain name list (aka claims list) from MarksDB. */
@Action(path = "/_dr/task/tmchDnl", method = POST, automaticallyPrintOk = true)
@Action(
path = "/_dr/task/tmchDnl",
method = POST,
automaticallyPrintOk = true,
auth = Auth.AUTH_INTERNAL_ONLY
)
public final class TmchDnlAction implements Runnable {
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();

View file

@ -20,6 +20,7 @@ import com.google.common.base.Optional;
import google.registry.keyring.api.KeyModule.Key;
import google.registry.model.smd.SignedMarkRevocationList;
import google.registry.request.Action;
import google.registry.request.auth.Auth;
import google.registry.util.FormattingLogger;
import java.io.IOException;
import java.security.SignatureException;
@ -28,7 +29,12 @@ import javax.inject.Inject;
import org.bouncycastle.openpgp.PGPException;
/** Action to download the latest signed mark revocation list from MarksDB. */
@Action(path = "/_dr/task/tmchSmdrl", method = POST, automaticallyPrintOk = true)
@Action(
path = "/_dr/task/tmchSmdrl",
method = POST,
automaticallyPrintOk = true,
auth = Auth.AUTH_INTERNAL_ONLY
)
public final class TmchSmdrlAction implements Runnable {
private static final FormattingLogger logger = FormattingLogger.getLoggerForCallerClass();

View file

@ -33,7 +33,6 @@ 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;
@ -46,12 +45,7 @@ import javax.inject.Inject;
@Action(
path = CreateGroupsAction.PATH,
method = POST,
auth =
@Auth(
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API},
minimumLevel = AuthLevel.APP,
userPolicy = Auth.UserPolicy.ADMIN
)
auth = Auth.AUTH_INTERNAL_OR_ADMIN
)
public class CreateGroupsAction implements Runnable {

View file

@ -26,7 +26,6 @@ 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;
@ -37,12 +36,7 @@ import javax.inject.Inject;
@Action(
path = CreatePremiumListAction.PATH,
method = POST,
auth =
@Auth(
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API},
minimumLevel = AuthLevel.APP,
userPolicy = Auth.UserPolicy.ADMIN
)
auth = Auth.AUTH_INTERNAL_OR_ADMIN
)
public class CreatePremiumListAction extends CreateOrUpdatePremiumListAction {

View file

@ -32,7 +32,6 @@ 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;
@ -51,12 +50,7 @@ import javax.inject.Inject;
*/
@Action(
path = DeleteEntityAction.PATH,
auth =
@Auth(
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API},
minimumLevel = AuthLevel.APP,
userPolicy = Auth.UserPolicy.ADMIN
)
auth = Auth.AUTH_INTERNAL_OR_ADMIN
)
public class DeleteEntityAction implements Runnable {

View file

@ -48,7 +48,6 @@ 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.util.Clock;
import java.io.IOException;
import java.io.OutputStream;
@ -74,12 +73,7 @@ import org.joda.time.Duration;
@Action(
path = GenerateZoneFilesAction.PATH,
method = POST,
auth =
@Auth(
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API},
minimumLevel = AuthLevel.APP,
userPolicy = Auth.UserPolicy.ADMIN
)
auth = Auth.AUTH_INTERNAL_OR_ADMIN
)
public class GenerateZoneFilesAction implements Runnable, JsonActionRunner.JsonAction {

View file

@ -32,6 +32,7 @@ import google.registry.model.ofy.CommitLogBucket;
import google.registry.model.ofy.CommitLogCheckpointRoot;
import google.registry.request.Action;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import java.util.Arrays;
import javax.inject.Inject;
@ -43,7 +44,11 @@ import javax.inject.Inject;
* which only admin users can do. That makes this command hard to use, which is appropriate, given
* the drastic consequences of accidental execution.
*/
@Action(path = "/_dr/task/killAllCommitLogs", method = POST)
@Action(
path = "/_dr/task/killAllCommitLogs",
method = POST,
auth = Auth.AUTH_INTERNAL_ONLY
)
public class KillAllCommitLogsAction implements Runnable {
@Inject MapreduceRunner mrRunner;

View file

@ -32,6 +32,7 @@ import google.registry.model.index.EppResourceIndex;
import google.registry.model.index.ForeignKeyIndex;
import google.registry.request.Action;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import javax.inject.Inject;
/**
@ -42,7 +43,11 @@ import javax.inject.Inject;
* which only admin users can do. That makes this command hard to use, which is appropriate, given
* the drastic consequences of accidental execution.
*/
@Action(path = "/_dr/task/killAllEppResources", method = POST)
@Action(
path = "/_dr/task/killAllEppResources",
method = POST,
auth = Auth.AUTH_INTERNAL_ONLY
)
public class KillAllEppResourcesAction implements Runnable {
@Inject MapreduceRunner mrRunner;

View file

@ -27,7 +27,6 @@ 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;
@ -37,12 +36,7 @@ import javax.inject.Inject;
@Action(
path = ListDomainsAction.PATH,
method = {GET, POST},
auth =
@Auth(
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API},
minimumLevel = AuthLevel.APP,
userPolicy = Auth.UserPolicy.ADMIN
)
auth = Auth.AUTH_INTERNAL_OR_ADMIN
)
public final class ListDomainsAction extends ListObjectsAction<DomainResource> {

View file

@ -25,7 +25,6 @@ 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;
@ -35,12 +34,7 @@ import org.joda.time.DateTime;
@Action(
path = ListHostsAction.PATH,
method = {GET, POST},
auth =
@Auth(
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API},
minimumLevel = AuthLevel.APP,
userPolicy = Auth.UserPolicy.ADMIN
)
auth = Auth.AUTH_INTERNAL_OR_ADMIN
)
public final class ListHostsAction extends ListObjectsAction<HostResource> {

View file

@ -23,7 +23,6 @@ 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;
/**
@ -32,12 +31,7 @@ import javax.inject.Inject;
@Action(
path = ListPremiumListsAction.PATH,
method = {GET, POST},
auth =
@Auth(
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API},
minimumLevel = AuthLevel.APP,
userPolicy = Auth.UserPolicy.ADMIN
)
auth = Auth.AUTH_INTERNAL_OR_ADMIN
)
public final class ListPremiumListsAction extends ListObjectsAction<PremiumList> {

View file

@ -23,19 +23,13 @@ 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},
auth =
@Auth(
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API},
minimumLevel = AuthLevel.APP,
userPolicy = Auth.UserPolicy.ADMIN
)
auth = Auth.AUTH_INTERNAL_OR_ADMIN
)
public final class ListRegistrarsAction extends ListObjectsAction<Registrar> {

View file

@ -23,19 +23,13 @@ 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},
auth =
@Auth(
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API},
minimumLevel = AuthLevel.APP,
userPolicy = Auth.UserPolicy.ADMIN
)
auth = Auth.AUTH_INTERNAL_OR_ADMIN
)
public final class ListReservedListsAction extends ListObjectsAction<ReservedList> {

View file

@ -26,7 +26,6 @@ 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;
@ -35,12 +34,7 @@ import org.joda.time.DateTime;
@Action(
path = ListTldsAction.PATH,
method = {GET, POST},
auth =
@Auth(
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API},
minimumLevel = AuthLevel.APP,
userPolicy = Auth.UserPolicy.ADMIN
)
auth = Auth.AUTH_INTERNAL_OR_ADMIN
)
public final class ListTldsAction extends ListObjectsAction<Registry> {

View file

@ -31,7 +31,6 @@ import google.registry.request.Action;
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 google.registry.util.NonFinalForTesting;
import javax.inject.Inject;
@ -51,12 +50,7 @@ import org.joda.time.DateTimeZone;
*/
@Action(
path = "/_dr/task/refreshDnsForAllDomains",
auth =
@Auth(
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API},
minimumLevel = AuthLevel.APP,
userPolicy = Auth.UserPolicy.ADMIN
)
auth = Auth.AUTH_INTERNAL_OR_ADMIN
)
public class RefreshDnsForAllDomainsAction implements Runnable {

View file

@ -27,7 +27,6 @@ import google.registry.model.EppResource;
import google.registry.request.Action;
import google.registry.request.Response;
import google.registry.request.auth.Auth;
import google.registry.request.auth.AuthLevel;
import javax.inject.Inject;
/**
@ -43,12 +42,7 @@ import javax.inject.Inject;
*/
@Action(
path = "/_dr/task/resaveAllEppResources",
auth =
@Auth(
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API},
minimumLevel = AuthLevel.APP,
userPolicy = Auth.UserPolicy.ADMIN
)
auth = Auth.AUTH_INTERNAL_OR_ADMIN
)
public class ResaveAllEppResourcesAction implements Runnable {

View file

@ -24,7 +24,6 @@ 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;
@ -35,12 +34,7 @@ import javax.inject.Inject;
@Action(
path = UpdatePremiumListAction.PATH,
method = POST,
auth =
@Auth(
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API},
minimumLevel = AuthLevel.APP,
userPolicy = Auth.UserPolicy.ADMIN
)
auth = Auth.AUTH_INTERNAL_OR_ADMIN
)
public class UpdatePremiumListAction extends CreateOrUpdatePremiumListAction {

View file

@ -47,7 +47,6 @@ 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;
@ -62,12 +61,7 @@ import javax.inject.Inject;
@Action(
path = VerifyOteAction.PATH,
method = Action.Method.POST,
auth =
@Auth(
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API},
minimumLevel = AuthLevel.APP,
userPolicy = Auth.UserPolicy.ADMIN
)
auth = Auth.AUTH_INTERNAL_OR_ADMIN
)
public class VerifyOteAction implements Runnable, JsonAction {

View file

@ -34,7 +34,6 @@ 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.request.auth.AuthResult;
import google.registry.security.XsrfTokenManager;
import google.registry.ui.server.SoyTemplateUtils;
@ -45,12 +44,7 @@ import javax.servlet.http.HttpServletRequest;
/** Action that serves Registrar Console single HTML page (SPA). */
@Action(
path = ConsoleUiAction.PATH,
auth =
@Auth(
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API, Auth.AuthMethod.LEGACY},
minimumLevel = AuthLevel.NONE,
userPolicy = Auth.UserPolicy.PUBLIC
)
auth = Auth.AUTH_PUBLIC
)
public final class ConsoleUiAction implements Runnable {

View file

@ -36,7 +36,6 @@ 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.request.auth.AuthResult;
import google.registry.security.JsonResponseHelper;
import google.registry.ui.forms.FormField;
@ -97,12 +96,7 @@ import org.joda.money.Money;
@Action(
path = "/registrar-payment",
method = Action.Method.POST,
auth =
@Auth(
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API, Auth.AuthMethod.LEGACY},
minimumLevel = AuthLevel.USER,
userPolicy = Auth.UserPolicy.PUBLIC
)
auth = Auth.AUTH_PUBLIC_LOGGED_IN
)
public final class RegistrarPaymentAction implements Runnable, JsonAction {

View file

@ -29,7 +29,6 @@ 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.request.auth.AuthResult;
import google.registry.security.JsonResponseHelper;
import java.util.Map;
@ -72,12 +71,7 @@ import org.joda.money.CurrencyUnit;
@Action(
path = "/registrar-payment-setup",
method = Action.Method.POST,
auth =
@Auth(
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API, Auth.AuthMethod.LEGACY},
minimumLevel = AuthLevel.USER,
userPolicy = Auth.UserPolicy.PUBLIC
)
auth = Auth.AUTH_PUBLIC_LOGGED_IN
)
public final class RegistrarPaymentSetupAction implements Runnable, JsonAction {

View file

@ -41,7 +41,6 @@ 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.request.auth.AuthResult;
import google.registry.security.JsonResponseHelper;
import google.registry.ui.forms.FormException;
@ -65,12 +64,7 @@ import javax.servlet.http.HttpServletRequest;
@Action(
path = RegistrarSettingsAction.PATH,
method = Action.Method.POST,
auth =
@Auth(
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API, Auth.AuthMethod.LEGACY},
minimumLevel = AuthLevel.USER,
userPolicy = Auth.UserPolicy.PUBLIC
)
auth = Auth.AUTH_PUBLIC_LOGGED_IN
)
public class RegistrarSettingsAction implements Runnable, JsonActionRunner.JsonAction {

View file

@ -33,7 +33,6 @@ 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;
@ -99,7 +98,7 @@ import org.joda.time.Duration;
@Action(
path = WhoisHttpServer.PATH,
isPrefix = true,
auth = @Auth(minimumLevel = AuthLevel.NONE, userPolicy = Auth.UserPolicy.PUBLIC)
auth = Auth.AUTH_PUBLIC_ANONYMOUS
)
public final class WhoisHttpServer implements Runnable {

View file

@ -25,7 +25,6 @@ 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.util.Retrier;
@ -54,12 +53,7 @@ import org.joda.time.DateTime;
@Action(
path = "/_dr/whois",
method = POST,
auth =
@Auth(
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API},
minimumLevel = AuthLevel.APP,
userPolicy = Auth.UserPolicy.ADMIN
)
auth = Auth.AUTH_INTERNAL_OR_ADMIN
)
public class WhoisServer implements Runnable {

View file

@ -12,8 +12,8 @@ PATH CLASS METHODS OK AUTH_METHODS
/rdap/nameserver/(*) RdapNameserverAction GET,HEAD n INTERNAL NONE PUBLIC
/rdap/nameservers RdapNameserverSearchAction GET,HEAD n INTERNAL NONE PUBLIC
/registrar ConsoleUiAction GET n INTERNAL,API,LEGACY NONE PUBLIC
/registrar-payment RegistrarPaymentAction POST n INTERNAL,API,LEGACY USER PUBLIC
/registrar-payment-setup RegistrarPaymentSetupAction POST n INTERNAL,API,LEGACY USER PUBLIC
/registrar-settings RegistrarSettingsAction POST n INTERNAL,API,LEGACY USER PUBLIC
/registrar-xhr EppConsoleAction POST n INTERNAL,API,LEGACY USER PUBLIC
/registrar-payment RegistrarPaymentAction POST n API,LEGACY USER PUBLIC
/registrar-payment-setup RegistrarPaymentSetupAction POST n API,LEGACY USER PUBLIC
/registrar-settings RegistrarSettingsAction POST n API,LEGACY USER PUBLIC
/registrar-xhr EppConsoleAction POST n API,LEGACY USER PUBLIC
/whois/(*) WhoisHttpServer GET n INTERNAL NONE PUBLIC

View file

@ -17,30 +17,23 @@ package google.registry.request;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.request.Action.Method.GET;
import static google.registry.request.Action.Method.POST;
import static google.registry.request.auth.Auth.AUTH_INTERNAL_OR_ADMIN;
import static google.registry.request.auth.Auth.AUTH_PUBLIC;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import com.google.appengine.api.oauth.OAuthServiceFactory;
import com.google.appengine.api.users.User;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.base.Optional;
import com.google.common.testing.NullPointerTester;
import google.registry.request.HttpException.ServiceUnavailableException;
import google.registry.request.auth.AppEngineInternalAuthenticationMechanism;
import google.registry.request.auth.Auth;
import google.registry.request.auth.AuthLevel;
import google.registry.request.auth.AuthResult;
import google.registry.request.auth.AuthenticationMechanism;
import google.registry.request.auth.LegacyAuthenticationMechanism;
import google.registry.request.auth.OAuthAuthenticationMechanism;
import google.registry.request.auth.RequestAuthenticator;
import google.registry.security.XsrfTokenManager;
import google.registry.request.auth.UserAuthInfo;
import google.registry.testing.AppEngineRule;
import google.registry.testing.FakeClock;
import google.registry.testing.FakeUserService;
import google.registry.testing.Providers;
import google.registry.testing.UserInfo;
import java.io.PrintWriter;
@ -69,7 +62,7 @@ public final class RequestHandlerTest {
path = "/bumblebee",
method = {GET, POST},
isPrefix = true,
auth = @Auth(minimumLevel = AuthLevel.NONE)
auth = AUTH_PUBLIC
)
public static class BumblebeeTask implements Runnable {
@Override
@ -80,7 +73,7 @@ public final class RequestHandlerTest {
path = "/sloth",
method = POST,
automaticallyPrintOk = true,
auth = @Auth(minimumLevel = AuthLevel.NONE)
auth = AUTH_PUBLIC
)
public static class SlothTask implements Runnable {
@Override
@ -90,14 +83,14 @@ public final class RequestHandlerTest {
@Action(
path = "/safe-sloth",
method = {GET, POST},
auth = @Auth(minimumLevel = AuthLevel.NONE)
auth = AUTH_PUBLIC
)
public static class SafeSlothTask implements Runnable {
@Override
public void run() {}
}
@Action(path = "/fail", auth = @Auth(minimumLevel = AuthLevel.NONE))
@Action(path = "/fail", auth = AUTH_PUBLIC)
public static final class FailTask implements Runnable {
@Override
public void run() {
@ -105,7 +98,7 @@ public final class RequestHandlerTest {
}
}
@Action(path = "/failAtConstruction", auth = @Auth(minimumLevel = AuthLevel.NONE))
@Action(path = "/failAtConstruction", auth = AUTH_PUBLIC)
public static final class FailAtConstructionTask implements Runnable {
public FailAtConstructionTask() {
throw new ServiceUnavailableException("Fail at construction");
@ -132,7 +125,7 @@ public final class RequestHandlerTest {
@Action(
path = "/auth/none",
auth = @Auth(minimumLevel = AuthLevel.NONE),
auth = AUTH_PUBLIC,
method = Action.Method.GET
)
public class AuthNoneAction extends AuthBase {
@ -142,14 +135,11 @@ public final class RequestHandlerTest {
}
@Action(
path = "/auth/adminUserAnyMethod",
auth = @Auth(
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API, Auth.AuthMethod.LEGACY},
minimumLevel = AuthLevel.USER,
userPolicy = Auth.UserPolicy.ADMIN),
path = "/auth/adminUser",
auth = AUTH_INTERNAL_OR_ADMIN,
method = Action.Method.GET)
public class AuthAdminUserAnyMethodAction extends AuthBase {
AuthAdminUserAnyMethodAction(AuthResult authResult) {
public class AuthAdminUserAction extends AuthBase {
AuthAdminUserAction(AuthResult authResult) {
super(authResult);
}
}
@ -190,8 +180,8 @@ public final class RequestHandlerTest {
return new AuthNoneAction(component.getRequestModule().provideAuthResult());
}
public AuthAdminUserAnyMethodAction authAdminUserAnyMethodAction() {
return new AuthAdminUserAnyMethodAction(component.getRequestModule().provideAuthResult());
public AuthAdminUserAction authAdminUserAction() {
return new AuthAdminUserAction(component.getRequestModule().provideAuthResult());
}
}
@ -209,30 +199,16 @@ public final class RequestHandlerTest {
private final BumblebeeTask bumblebeeTask = mock(BumblebeeTask.class);
private final SlothTask slothTask = mock(SlothTask.class);
private final SafeSlothTask safeSlothTask = mock(SafeSlothTask.class);
private final RequestAuthenticator requestAuthenticator = mock(RequestAuthenticator.class);
private final Component component = new Component();
private final StringWriter httpOutput = new StringWriter();
private RequestHandler<Component> handler;
private AuthResult providedAuthResult = null;
private final User testUser = new User("test@example.com", "test@example.com");
private RequestAuthenticator requestAuthenticator;
private XsrfTokenManager xsrfTokenManager;
private FakeUserService userService;
@Before
public void before() throws Exception {
userService = new FakeUserService();
xsrfTokenManager = new XsrfTokenManager(new FakeClock(), userService);
requestAuthenticator = new RequestAuthenticator(
new AppEngineInternalAuthenticationMechanism(),
ImmutableList.<AuthenticationMechanism>of(
new OAuthAuthenticationMechanism(
OAuthServiceFactory.getOAuthService(),
ImmutableSet.of("https://www.googleapis.com/auth/userinfo.email"),
ImmutableSet.of("https://www.googleapis.com/auth/userinfo.email"),
ImmutableSet.of("proxy-client-id", "regtool-client-id"))),
new LegacyAuthenticationMechanism(userService, xsrfTokenManager));
// Initialize here, not inline, so that we pick up the mocked UserService.
handler = RequestHandler.<Component>createForTest(
Component.class,
@ -256,19 +232,24 @@ public final class RequestHandlerTest {
public void testHandleRequest_normalRequest_works() throws Exception {
when(req.getMethod()).thenReturn("GET");
when(req.getRequestURI()).thenReturn("/bumblebee");
when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req))
.thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE)));
handler.handleRequest(req, rsp);
verifyZeroInteractions(rsp);
verify(bumblebeeTask).run();
}
@Test
public void testHandleRequest_multipleMethodMappings_works() throws Exception {
userService.setUser(testUser, false);
when(req.getMethod()).thenReturn("POST");
when(req.getHeader("X-CSRF-Token"))
.thenReturn(xsrfTokenManager.generateToken(testUser.getEmail()));
when(req.getRequestURI()).thenReturn("/bumblebee");
when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req))
.thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE)));
handler.handleRequest(req, rsp);
verify(bumblebeeTask).run();
}
@ -276,18 +257,23 @@ public final class RequestHandlerTest {
public void testHandleRequest_prefixEnabled_subpathsWork() throws Exception {
when(req.getMethod()).thenReturn("GET");
when(req.getRequestURI()).thenReturn("/bumblebee/hive");
when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req))
.thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE)));
handler.handleRequest(req, rsp);
verify(bumblebeeTask).run();
}
@Test
public void testHandleRequest_taskHasAutoPrintOk_printsOk() throws Exception {
userService.setUser(testUser, false);
when(req.getMethod()).thenReturn("POST");
when(req.getHeader("X-CSRF-Token"))
.thenReturn(xsrfTokenManager.generateToken(testUser.getEmail()));
when(req.getRequestURI()).thenReturn("/sloth");
when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req))
.thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE)));
handler.handleRequest(req, rsp);
verify(slothTask).run();
verify(rsp).setContentType("text/plain; charset=utf-8");
verify(rsp).getWriter();
@ -298,7 +284,11 @@ public final class RequestHandlerTest {
public void testHandleRequest_prefixDisabled_subpathsReturn404NotFound() throws Exception {
when(req.getMethod()).thenReturn("POST");
when(req.getRequestURI()).thenReturn("/sloth/nest");
when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req))
.thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE)));
handler.handleRequest(req, rsp);
verify(rsp).sendError(404);
}
@ -306,7 +296,11 @@ public final class RequestHandlerTest {
public void testHandleRequest_taskThrowsHttpException_getsHandledByHandler() throws Exception {
when(req.getMethod()).thenReturn("GET");
when(req.getRequestURI()).thenReturn("/fail");
when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req))
.thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE)));
handler.handleRequest(req, rsp);
verify(rsp).sendError(503, "Set sail for fail");
}
@ -316,7 +310,11 @@ public final class RequestHandlerTest {
throws Exception {
when(req.getMethod()).thenReturn("GET");
when(req.getRequestURI()).thenReturn("/failAtConstruction");
when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req))
.thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE)));
handler.handleRequest(req, rsp);
verify(rsp).sendError(503, "Fail at construction");
}
@ -324,7 +322,11 @@ public final class RequestHandlerTest {
public void testHandleRequest_notFound_returns404NotFound() throws Exception {
when(req.getMethod()).thenReturn("GET");
when(req.getRequestURI()).thenReturn("/bogus");
when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req))
.thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE)));
handler.handleRequest(req, rsp);
verify(rsp).sendError(404);
}
@ -332,7 +334,11 @@ public final class RequestHandlerTest {
public void testHandleRequest_methodNotAllowed_returns405MethodNotAllowed() throws Exception {
when(req.getMethod()).thenReturn("POST");
when(req.getRequestURI()).thenReturn("/fail");
when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req))
.thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE)));
handler.handleRequest(req, rsp);
verify(rsp).sendError(405);
}
@ -340,7 +346,11 @@ public final class RequestHandlerTest {
public void testHandleRequest_insaneMethod_returns405MethodNotAllowed() throws Exception {
when(req.getMethod()).thenReturn("FIREAWAY");
when(req.getRequestURI()).thenReturn("/fail");
when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req))
.thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE)));
handler.handleRequest(req, rsp);
verify(rsp).sendError(405);
}
@ -350,7 +360,11 @@ public final class RequestHandlerTest {
public void testHandleRequest_lowercaseMethod_notRecognized() throws Exception {
when(req.getMethod()).thenReturn("get");
when(req.getRequestURI()).thenReturn("/bumblebee");
when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req))
.thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE)));
handler.handleRequest(req, rsp);
verify(rsp).sendError(405);
}
@ -359,19 +373,19 @@ public final class RequestHandlerTest {
NullPointerTester tester = new NullPointerTester();
tester.setDefault(Class.class, Component.class);
tester.setDefault(RequestAuthenticator.class, requestAuthenticator);
tester.setDefault(XsrfTokenManager.class, xsrfTokenManager);
tester.testAllPublicStaticMethods(RequestHandler.class);
tester.testAllPublicInstanceMethods(handler);
}
@Test
public void testXsrfProtection_validTokenProvided_runsAction() throws Exception {
userService.setUser(testUser, false);
when(req.getMethod()).thenReturn("POST");
when(req.getHeader("X-CSRF-Token"))
.thenReturn(xsrfTokenManager.generateToken(testUser.getEmail()));
when(req.getRequestURI()).thenReturn("/safe-sloth");
when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req))
.thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE)));
handler.handleRequest(req, rsp);
verify(safeSlothTask).run();
}
@ -379,7 +393,11 @@ public final class RequestHandlerTest {
public void testXsrfProtection_GETMethodWithoutToken_doesntCheckToken() throws Exception {
when(req.getMethod()).thenReturn("GET");
when(req.getRequestURI()).thenReturn("/safe-sloth");
when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req))
.thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE)));
handler.handleRequest(req, rsp);
verify(safeSlothTask).run();
}
@ -387,6 +405,8 @@ public final class RequestHandlerTest {
public void testNoAuthNeeded_success() throws Exception {
when(req.getMethod()).thenReturn("GET");
when(req.getRequestURI()).thenReturn("/auth/none");
when(requestAuthenticator.authorize(AUTH_PUBLIC.authSettings(), req))
.thenReturn(Optional.of(AuthResult.create(AuthLevel.NONE)));
handler.handleRequest(req, rsp);
@ -396,22 +416,11 @@ public final class RequestHandlerTest {
}
@Test
public void testAuthNeeded_notLoggedIn() throws Exception {
public void testAuthNeeded_failure() throws Exception {
when(req.getMethod()).thenReturn("GET");
when(req.getRequestURI()).thenReturn("/auth/adminUserAnyMethod");
handler.handleRequest(req, rsp);
verify(rsp).sendError(403, "Not authorized");
assertThat(providedAuthResult).isNull();
assertThat(providedAuthResult).isNull();
}
@Test
public void testAuthNeeded_notAuthorized() throws Exception {
userService.setUser(testUser, false);
when(req.getMethod()).thenReturn("GET");
when(req.getRequestURI()).thenReturn("/auth/adminUserAnyMethod");
when(req.getRequestURI()).thenReturn("/auth/adminUser");
when(requestAuthenticator.authorize(AUTH_INTERNAL_OR_ADMIN.authSettings(), req))
.thenReturn(Optional.absent());
handler.handleRequest(req, rsp);
@ -421,9 +430,11 @@ public final class RequestHandlerTest {
@Test
public void testAuthNeeded_success() throws Exception {
userService.setUser(testUser, true);
when(req.getMethod()).thenReturn("GET");
when(req.getRequestURI()).thenReturn("/auth/adminUserAnyMethod");
when(req.getRequestURI()).thenReturn("/auth/adminUser");
when(requestAuthenticator.authorize(AUTH_INTERNAL_OR_ADMIN.authSettings(), req))
.thenReturn(
Optional.of(AuthResult.create(AuthLevel.USER, UserAuthInfo.create(testUser, true))));
handler.handleRequest(req, rsp);
@ -433,4 +444,5 @@ public final class RequestHandlerTest {
assertThat(providedAuthResult.userAuthInfo().get().user()).isEqualTo(testUser);
assertThat(providedAuthResult.userAuthInfo().get().oauthTokenInfo()).isAbsent();
}
}

View file

@ -15,6 +15,7 @@
package google.registry.request;
import static com.google.common.truth.Truth.assertThat;
import static google.registry.request.auth.Auth.AUTH_INTERNAL_ONLY;
import com.google.common.base.Function;
import com.google.common.base.Optional;
@ -46,7 +47,7 @@ public final class RouterTest {
////////////////////////////////////////////////////////////////////////////////////////////////
@Action(path = "/sloth")
@Action(path = "/sloth", auth = AUTH_INTERNAL_ONLY)
public static final class SlothTask implements Runnable {
@Override
public void run() {}
@ -76,7 +77,7 @@ public final class RouterTest {
////////////////////////////////////////////////////////////////////////////////////////////////
@Action(path = "/prefix", isPrefix = true)
@Action(path = "/prefix", isPrefix = true, auth = AUTH_INTERNAL_ONLY)
public static final class PrefixTask implements Runnable {
@Override
public void run() {}
@ -102,7 +103,7 @@ public final class RouterTest {
////////////////////////////////////////////////////////////////////////////////////////////////
@Action(path = "/prefix/long", isPrefix = true)
@Action(path = "/prefix/long", isPrefix = true, auth = AUTH_INTERNAL_ONLY)
public static final class LongTask implements Runnable {
@Override
public void run() {}
@ -151,13 +152,13 @@ public final class RouterTest {
////////////////////////////////////////////////////////////////////////////////////////////////
@Action(path = "/samePathAsOtherTask")
@Action(path = "/samePathAsOtherTask", auth = AUTH_INTERNAL_ONLY)
public static final class DuplicateTask1 implements Runnable {
@Override
public void run() {}
}
@Action(path = "/samePathAsOtherTask")
@Action(path = "/samePathAsOtherTask", auth = AUTH_INTERNAL_ONLY)
public static final class DuplicateTask2 implements Runnable {
@Override
public void run() {}

View file

@ -0,0 +1,31 @@
// Copyright 2017 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.request.auth;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link Auth}. */
@RunWith(JUnit4.class)
public final class AuthTest {
@Test
public void testAuthValues_validConfig() throws Exception {
for (Auth auth : Auth.values()) {
RequestAuthenticator.checkAuthConfig(auth.authSettings());
}
}
}

View file

@ -25,7 +25,9 @@ import com.google.appengine.api.users.UserService;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import google.registry.request.Action;
import google.registry.request.auth.RequestAuthenticator.AuthMethod;
import google.registry.request.auth.RequestAuthenticator.AuthSettings;
import google.registry.request.auth.RequestAuthenticator.UserPolicy;
import google.registry.security.XsrfTokenManager;
import google.registry.testing.AppEngineRule;
import google.registry.testing.ExceptionRule;
@ -49,79 +51,55 @@ public class RequestAuthenticatorTest {
@Rule
public final ExceptionRule thrown = new ExceptionRule();
@Action(
path = "/auth/none",
auth = @Auth(
methods = {Auth.AuthMethod.INTERNAL},
minimumLevel = AuthLevel.NONE,
userPolicy = Auth.UserPolicy.IGNORED),
method = Action.Method.GET)
public static class AuthNone {}
private static final AuthSettings AUTH_NONE = AuthSettings.create(
ImmutableList.of(AuthMethod.INTERNAL),
AuthLevel.NONE,
UserPolicy.IGNORED);
@Action(
path = "/auth/internalOnly",
auth = @Auth(minimumLevel = AuthLevel.APP),
method = Action.Method.GET)
public static class AuthInternalOnly {}
private static final AuthSettings AUTH_INTERNAL_ONLY = AuthSettings.create(
ImmutableList.of(AuthMethod.INTERNAL),
AuthLevel.APP,
UserPolicy.IGNORED);
@Action(
path = "/auth/anyUserAnyMethod",
auth = @Auth(
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API, Auth.AuthMethod.LEGACY},
minimumLevel = AuthLevel.USER,
userPolicy = Auth.UserPolicy.PUBLIC),
method = Action.Method.GET)
public static class AuthAnyUserAnyMethod {}
private static final AuthSettings AUTH_ANY_USER_ANY_METHOD = AuthSettings.create(
ImmutableList.of(AuthMethod.API, AuthMethod.LEGACY),
AuthLevel.USER,
UserPolicy.PUBLIC);
@Action(
path = "/auth/anyUserNoLegacy",
auth = @Auth(
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API},
minimumLevel = AuthLevel.USER,
userPolicy = Auth.UserPolicy.PUBLIC),
method = Action.Method.GET)
public static class AuthAnyUserNoLegacy {}
private static final AuthSettings AUTH_ANY_USER_NO_LEGACY = AuthSettings.create(
ImmutableList.of(AuthMethod.API),
AuthLevel.USER,
UserPolicy.PUBLIC);
@Action(
path = "/auth/adminUserAnyMethod",
auth = @Auth(
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API, Auth.AuthMethod.LEGACY},
minimumLevel = AuthLevel.USER,
userPolicy = Auth.UserPolicy.ADMIN),
method = Action.Method.GET)
public static class AuthAdminUserAnyMethod {}
private static final AuthSettings AUTH_ADMIN_USER_ANY_METHOD = AuthSettings.create(
ImmutableList.of(AuthMethod.API, AuthMethod.LEGACY),
AuthLevel.USER,
UserPolicy.ADMIN);
@Action(
path = "/auth/noMethods",
auth = @Auth(methods = {}))
public static class AuthNoMethods {}
private static final AuthSettings AUTH_NO_METHODS = AuthSettings.create(
ImmutableList.<AuthMethod>of(),
AuthLevel.APP,
UserPolicy.IGNORED);
@Action(
path = "/auth/missingInternal",
auth = @Auth(methods = {Auth.AuthMethod.API, Auth.AuthMethod.LEGACY}))
public static class AuthMissingInternal {}
private static final AuthSettings AUTH_WRONG_METHOD_ORDERING = AuthSettings.create(
ImmutableList.of(AuthMethod.API, AuthMethod.INTERNAL),
AuthLevel.APP,
UserPolicy.IGNORED);
@Action(
path = "/auth/wrongMethodOrdering",
auth = @Auth(methods = {Auth.AuthMethod.API, Auth.AuthMethod.INTERNAL}))
public static class AuthWrongMethodOrdering {}
private static final AuthSettings AUTH_DUPLICATE_METHODS = AuthSettings.create(
ImmutableList.of(AuthMethod.INTERNAL, AuthMethod.API, AuthMethod.API),
AuthLevel.APP,
UserPolicy.IGNORED);
@Action(
path = "/auth/duplicateMethods",
auth = @Auth(methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API, Auth.AuthMethod.API}))
public static class AuthDuplicateMethods {}
private static final AuthSettings AUTH_INTERNAL_WITH_USER = AuthSettings.create(
ImmutableList.of(AuthMethod.INTERNAL, AuthMethod.API),
AuthLevel.USER,
UserPolicy.IGNORED);
@Action(
path = "/auth/internalWithUser",
auth = @Auth(methods = {Auth.AuthMethod.INTERNAL}, minimumLevel = AuthLevel.USER))
public static class AuthInternalWithUser {}
@Action(
path = "/auth/wronglyIgnoringUser",
auth = @Auth(
methods = {Auth.AuthMethod.INTERNAL, Auth.AuthMethod.API},
userPolicy = Auth.UserPolicy.IGNORED))
public static class AuthWronglyIgnoringUser {}
private static final AuthSettings AUTH_WRONGLY_IGNORING_USER = AuthSettings.create(
ImmutableList.of(AuthMethod.INTERNAL, AuthMethod.API),
AuthLevel.APP,
UserPolicy.IGNORED);
private final UserService mockUserService = mock(UserService.class);
private final HttpServletRequest req = mock(HttpServletRequest.class);
@ -154,14 +132,14 @@ public class RequestAuthenticatorTest {
new LegacyAuthenticationMechanism(userService, xsrfTokenManager));
}
private Optional<AuthResult> runTest(UserService userService, Class<?> clazz) {
private Optional<AuthResult> runTest(UserService userService, AuthSettings auth) {
return createRequestAuthenticator(userService)
.authorize(clazz.getAnnotation(Action.class).auth(), req);
.authorize(auth, req);
}
@Test
public void testNoAuthNeeded_noneFound() throws Exception {
Optional<AuthResult> authResult = runTest(mockUserService, AuthNone.class);
Optional<AuthResult> authResult = runTest(mockUserService, AUTH_NONE);
verifyZeroInteractions(mockUserService);
assertThat(authResult).isPresent();
@ -172,7 +150,7 @@ public class RequestAuthenticatorTest {
public void testNoAuthNeeded_internalFound() throws Exception {
when(req.getHeader("X-AppEngine-QueueName")).thenReturn("__cron");
Optional<AuthResult> authResult = runTest(mockUserService, AuthNone.class);
Optional<AuthResult> authResult = runTest(mockUserService, AUTH_NONE);
verifyZeroInteractions(mockUserService);
assertThat(authResult).isPresent();
@ -182,7 +160,7 @@ public class RequestAuthenticatorTest {
@Test
public void testInternalAuth_notInvokedInternally() throws Exception {
Optional<AuthResult> authResult = runTest(mockUserService, AuthInternalOnly.class);
Optional<AuthResult> authResult = runTest(mockUserService, AUTH_INTERNAL_ONLY);
verifyZeroInteractions(mockUserService);
assertThat(authResult).isAbsent();
@ -192,7 +170,7 @@ public class RequestAuthenticatorTest {
public void testInternalAuth_success() throws Exception {
when(req.getHeader("X-AppEngine-QueueName")).thenReturn("__cron");
Optional<AuthResult> authResult = runTest(mockUserService, AuthInternalOnly.class);
Optional<AuthResult> authResult = runTest(mockUserService, AUTH_INTERNAL_ONLY);
verifyZeroInteractions(mockUserService);
assertThat(authResult).isPresent();
@ -202,7 +180,7 @@ public class RequestAuthenticatorTest {
@Test
public void testAnyUserAnyMethod_notLoggedIn() throws Exception {
Optional<AuthResult> authResult = runTest(fakeUserService, AuthAnyUserAnyMethod.class);
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ANY_USER_ANY_METHOD);
assertThat(authResult).isAbsent();
}
@ -211,7 +189,7 @@ public class RequestAuthenticatorTest {
public void testAnyUserAnyMethod_xsrfFailure() throws Exception {
fakeUserService.setUser(testUser, false);
Optional<AuthResult> authResult = runTest(fakeUserService, AuthAnyUserAnyMethod.class);
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ANY_USER_ANY_METHOD);
assertThat(authResult).isAbsent();
}
@ -222,7 +200,7 @@ public class RequestAuthenticatorTest {
when(req.getHeader(XsrfTokenManager.X_CSRF_TOKEN))
.thenReturn(xsrfTokenManager.generateToken(testUser.getEmail()));
Optional<AuthResult> authResult = runTest(fakeUserService, AuthAnyUserAnyMethod.class);
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ANY_USER_ANY_METHOD);
assertThat(authResult).isPresent();
assertThat(authResult.get().authLevel()).isEqualTo(AuthLevel.USER);
@ -237,7 +215,7 @@ public class RequestAuthenticatorTest {
fakeUserService.setUser(testUser, false);
when(req.getMethod()).thenReturn("GET");
Optional<AuthResult> authResult = runTest(fakeUserService, AuthAnyUserAnyMethod.class);
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ANY_USER_ANY_METHOD);
assertThat(authResult).isPresent();
assertThat(authResult.get().authLevel()).isEqualTo(AuthLevel.USER);
@ -248,7 +226,7 @@ public class RequestAuthenticatorTest {
@Test
public void testAdminUserAnyMethod_notLoggedIn() throws Exception {
Optional<AuthResult> authResult = runTest(fakeUserService, AuthAdminUserAnyMethod.class);
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ADMIN_USER_ANY_METHOD);
assertThat(authResult).isAbsent();
}
@ -257,7 +235,7 @@ public class RequestAuthenticatorTest {
public void testAdminUserAnyMethod_notAdminUser() throws Exception {
fakeUserService.setUser(testUser, false /* isAdmin */);
Optional<AuthResult> authResult = runTest(fakeUserService, AuthAdminUserAnyMethod.class);
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ADMIN_USER_ANY_METHOD);
assertThat(authResult).isAbsent();
}
@ -266,7 +244,7 @@ public class RequestAuthenticatorTest {
public void testAdminUserAnyMethod_xsrfFailure() throws Exception {
fakeUserService.setUser(testUser, true);
Optional<AuthResult> authResult = runTest(fakeUserService, AuthAdminUserAnyMethod.class);
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ADMIN_USER_ANY_METHOD);
assertThat(authResult).isAbsent();
}
@ -277,7 +255,7 @@ public class RequestAuthenticatorTest {
when(req.getHeader(XsrfTokenManager.X_CSRF_TOKEN))
.thenReturn(xsrfTokenManager.generateToken(testUser.getEmail()));
Optional<AuthResult> authResult = runTest(fakeUserService, AuthAdminUserAnyMethod.class);
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ADMIN_USER_ANY_METHOD);
assertThat(authResult).isPresent();
assertThat(authResult.get().authLevel()).isEqualTo(AuthLevel.USER);
@ -293,7 +271,7 @@ public class RequestAuthenticatorTest {
fakeOAuthService.setOAuthEnabled(true);
when(req.getHeader(AUTHORIZATION)).thenReturn("Bearer TOKEN");
Optional<AuthResult> authResult = runTest(fakeUserService, AuthAnyUserNoLegacy.class);
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ANY_USER_NO_LEGACY);
assertThat(authResult).isPresent();
assertThat(authResult.get().authLevel()).isEqualTo(AuthLevel.USER);
@ -316,7 +294,7 @@ public class RequestAuthenticatorTest {
fakeOAuthService.setOAuthEnabled(true);
when(req.getHeader(AUTHORIZATION)).thenReturn("Bearer TOKEN");
Optional<AuthResult> authResult = runTest(fakeUserService, AuthAnyUserNoLegacy.class);
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ANY_USER_NO_LEGACY);
assertThat(authResult).isPresent();
assertThat(authResult.get().authLevel()).isEqualTo(AuthLevel.USER);
@ -337,7 +315,7 @@ public class RequestAuthenticatorTest {
fakeOAuthService.setUser(testUser);
fakeOAuthService.setOAuthEnabled(true);
Optional<AuthResult> authResult = runTest(fakeUserService, AuthAnyUserNoLegacy.class);
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ANY_USER_NO_LEGACY);
assertThat(authResult).isAbsent();
}
@ -349,7 +327,7 @@ public class RequestAuthenticatorTest {
fakeOAuthService.setClientId("wrong-client-id");
when(req.getHeader(AUTHORIZATION)).thenReturn("Bearer TOKEN");
Optional<AuthResult> authResult = runTest(fakeUserService, AuthAnyUserNoLegacy.class);
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ANY_USER_NO_LEGACY);
assertThat(authResult).isAbsent();
}
@ -361,7 +339,7 @@ public class RequestAuthenticatorTest {
fakeOAuthService.setAuthorizedScopes();
when(req.getHeader(AUTHORIZATION)).thenReturn("Bearer TOKEN");
Optional<AuthResult> authResult = runTest(fakeUserService, AuthAnyUserNoLegacy.class);
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ANY_USER_NO_LEGACY);
assertThat(authResult).isAbsent();
}
@ -373,7 +351,7 @@ public class RequestAuthenticatorTest {
fakeOAuthService.setAuthorizedScopes("test-scope1", "test-scope3");
when(req.getHeader(AUTHORIZATION)).thenReturn("Bearer TOKEN");
Optional<AuthResult> authResult = runTest(fakeUserService, AuthAnyUserNoLegacy.class);
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ANY_USER_NO_LEGACY);
assertThat(authResult).isAbsent();
}
@ -385,7 +363,7 @@ public class RequestAuthenticatorTest {
fakeOAuthService.setAuthorizedScopes("test-scope1", "test-scope2", "test-scope3");
when(req.getHeader(AUTHORIZATION)).thenReturn("Bearer TOKEN");
Optional<AuthResult> authResult = runTest(fakeUserService, AuthAnyUserNoLegacy.class);
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ANY_USER_NO_LEGACY);
assertThat(authResult).isPresent();
assertThat(authResult.get().authLevel()).isEqualTo(AuthLevel.USER);
@ -405,55 +383,47 @@ public class RequestAuthenticatorTest {
public void testAnyUserNoLegacy_failureWithLegacyUser() throws Exception {
fakeUserService.setUser(testUser, false /* isAdmin */);
Optional<AuthResult> authResult = runTest(fakeUserService, AuthAnyUserNoLegacy.class);
Optional<AuthResult> authResult = runTest(fakeUserService, AUTH_ANY_USER_NO_LEGACY);
assertThat(authResult).isAbsent();
}
@Test
public void testNoMethods_failure() throws Exception {
public void testCheckAuthConfig_NoMethods_failure() throws Exception {
thrown.expect(IllegalArgumentException.class, "Must specify at least one auth method");
runTest(fakeUserService, AuthNoMethods.class);
RequestAuthenticator.checkAuthConfig(AUTH_NO_METHODS);
}
@Test
public void testMissingInternal_failure() throws Exception {
thrown.expect(IllegalArgumentException.class,
"Auth method INTERNAL must always be specified, and as the first auth method");
runTest(fakeUserService, AuthMissingInternal.class);
}
@Test
public void testWrongMethodOrdering_failure() throws Exception {
public void testCheckAuthConfig_WrongMethodOrdering_failure() throws Exception {
thrown.expect(IllegalArgumentException.class,
"Auth methods must be unique and strictly in order - INTERNAL, API, LEGACY");
runTest(fakeUserService, AuthWrongMethodOrdering.class);
RequestAuthenticator.checkAuthConfig(AUTH_WRONG_METHOD_ORDERING);
}
@Test
public void testDuplicateMethods_failure() throws Exception {
public void testCheckAuthConfig_DuplicateMethods_failure() throws Exception {
thrown.expect(IllegalArgumentException.class,
"Auth methods must be unique and strictly in order - INTERNAL, API, LEGACY");
runTest(fakeUserService, AuthDuplicateMethods.class);
RequestAuthenticator.checkAuthConfig(AUTH_DUPLICATE_METHODS);
}
@Test
public void testInternalWithUser_failure() throws Exception {
public void testCheckAuthConfig_InternalWithUser_failure() throws Exception {
thrown.expect(IllegalArgumentException.class,
"Actions with only INTERNAL auth may not require USER auth level");
"Actions with INTERNAL auth method may not require USER auth level");
runTest(fakeUserService, AuthInternalWithUser.class);
RequestAuthenticator.checkAuthConfig(AUTH_INTERNAL_WITH_USER);
}
@Test
public void testWronglyIgnoringUser_failure() throws Exception {
public void testCheckAuthConfig_WronglyIgnoringUser_failure() throws Exception {
thrown.expect(IllegalArgumentException.class,
"Actions with auth methods beyond INTERNAL must not specify the IGNORED user policy");
runTest(fakeUserService, AuthWronglyIgnoringUser.class);
RequestAuthenticator.checkAuthConfig(AUTH_WRONGLY_IGNORING_USER);
}
}