From 1c73f6eebf9c3ace623556fc2239d70f22a47374 Mon Sep 17 00:00:00 2001 From: Parag Jain Date: Thu, 29 Oct 2020 23:09:05 +0530 Subject: [PATCH 01/15] auth v2 model - fine grained permissions --- .../basic/BasicSecurityResourceFilter.java | 6 +- .../BasicRoleBasedAuthorizer.java | 2 +- .../hrtr/HttpRemoteTaskRunnerResource.java | 3 +- .../overlord/http/OverlordResource.java | 23 +- .../security/SupervisorResourceFilter.java | 6 +- .../http/security/TaskResourceFilter.java | 6 +- .../overlord/sampler/SamplerResource.java | 3 +- .../worker/http/TaskManagementResource.java | 3 +- .../indexing/worker/http/WorkerResource.java | 13 +- .../worker/shuffle/ShuffleResource.java | 3 +- .../HttpServerInventoryViewResource.java | 3 +- .../lookup/LookupIntrospectionResource.java | 29 ++- .../query/lookup/LookupListeningResource.java | 3 +- .../druid/server/BrokerQueryResource.java | 3 +- .../apache/druid/server/QueryLifecycle.java | 33 ++- .../druid/server/QueryLifecycleFactory.java | 5 +- .../apache/druid/server/StatusResource.java | 6 +- .../druid/server/http/ClusterResource.java | 3 +- .../CoordinatorCompactionConfigsResource.java | 79 ++++++- .../CoordinatorDynamicConfigsResource.java | 3 +- .../server/http/CoordinatorResource.java | 7 +- .../druid/server/http/HistoricalResource.java | 3 +- .../http/LookupCoordinatorResource.java | 158 ++++++++++++-- .../druid/server/http/RouterResource.java | 3 +- .../druid/server/http/RulesResource.java | 36 +++- .../server/http/SegmentListerResource.java | 3 +- .../server/http/SelfDiscoveryResource.java | 5 +- .../druid/server/http/ServersResource.java | 3 +- .../druid/server/http/TiersResource.java | 3 +- .../http/security/AbstractResourceFilter.java | 17 +- .../http/security/ConfigResourceFilter.java | 35 +-- .../security/DatasourceResourceFilter.java | 6 +- .../InternalInternalResourceFilter.java | 63 ++++++ .../LookupInternalResourceFilter.java | 63 ++++++ .../security/LookupStatusResourceFilter.java | 64 ++++++ .../http/security/RulesResourceFilter.java | 6 +- .../ServerPermissionsResourceFilter.java | 63 ++++++ .../security/ServerServerResourceFilter.java | 63 ++++++ .../security/ServerStatusResourceFilter.java | 63 ++++++ .../http/security/StateResourceFilter.java | 33 +-- .../cache/LookupCoordinatorManager.java | 23 ++ .../server/security/AllowAllAuthorizer.java | 6 + .../druid/server/security/AuthConfig.java | 31 ++- .../druid/server/security/Authorizer.java | 46 +++- .../druid/server/security/Resource.java | 7 + .../druid/server/security/ResourceName.java | 72 +++++++ .../druid/server/security/ResourceType.java | 7 +- .../LookupIntrospectionResourceTest.java | 17 +- .../http/LookupCoordinatorResourceTest.java | 199 ++++++++++++------ .../druid/server/http/RulesResourceTest.java | 12 +- .../security/ResourceFilterTestHelper.java | 22 +- .../sql/calcite/schema/SystemSchema.java | 39 ++-- .../sql/calcite/schema/SystemSchemaTest.java | 8 +- .../druid/sql/calcite/util/CalciteTests.java | 3 +- 54 files changed, 1217 insertions(+), 207 deletions(-) create mode 100644 server/src/main/java/org/apache/druid/server/http/security/InternalInternalResourceFilter.java create mode 100644 server/src/main/java/org/apache/druid/server/http/security/LookupInternalResourceFilter.java create mode 100644 server/src/main/java/org/apache/druid/server/http/security/LookupStatusResourceFilter.java create mode 100644 server/src/main/java/org/apache/druid/server/http/security/ServerPermissionsResourceFilter.java create mode 100644 server/src/main/java/org/apache/druid/server/http/security/ServerServerResourceFilter.java create mode 100644 server/src/main/java/org/apache/druid/server/http/security/ServerStatusResourceFilter.java create mode 100644 server/src/main/java/org/apache/druid/server/security/ResourceName.java diff --git a/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/BasicSecurityResourceFilter.java b/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/BasicSecurityResourceFilter.java index 8294ec1c6bd7..f64a89ab04fc 100644 --- a/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/BasicSecurityResourceFilter.java +++ b/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/BasicSecurityResourceFilter.java @@ -24,6 +24,7 @@ import org.apache.druid.java.util.common.StringUtils; import org.apache.druid.server.http.security.AbstractResourceFilter; import org.apache.druid.server.security.Access; +import org.apache.druid.server.security.AuthConfig; import org.apache.druid.server.security.AuthorizationUtils; import org.apache.druid.server.security.AuthorizerMapper; import org.apache.druid.server.security.Resource; @@ -39,10 +40,11 @@ public class BasicSecurityResourceFilter extends AbstractResourceFilter @Inject public BasicSecurityResourceFilter( - AuthorizerMapper authorizerMapper + AuthorizerMapper authorizerMapper, + AuthConfig authConfig ) { - super(authorizerMapper); + super(authorizerMapper, authConfig); } @Override diff --git a/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/BasicRoleBasedAuthorizer.java b/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/BasicRoleBasedAuthorizer.java index f307b9ea029e..72821effeb32 100644 --- a/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/BasicRoleBasedAuthorizer.java +++ b/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/BasicRoleBasedAuthorizer.java @@ -121,7 +121,7 @@ private boolean permissionCheck(Resource resource, Action action, BasicAuthorize } Pattern resourceNamePattern = permission.getResourceNamePattern(); - Matcher resourceNameMatcher = resourceNamePattern.matcher(resource.getName()); + Matcher resourceNameMatcher = resourceNamePattern.matcher(resource.getName().toString()); return resourceNameMatcher.matches(); } diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/hrtr/HttpRemoteTaskRunnerResource.java b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/hrtr/HttpRemoteTaskRunnerResource.java index fc2c5ced2542..f9a9676bb196 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/hrtr/HttpRemoteTaskRunnerResource.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/hrtr/HttpRemoteTaskRunnerResource.java @@ -24,6 +24,7 @@ import com.sun.jersey.spi.container.ResourceFilters; import org.apache.druid.indexing.overlord.TaskMaster; import org.apache.druid.indexing.overlord.TaskRunner; +import org.apache.druid.server.http.security.InternalInternalResourceFilter; import org.apache.druid.server.http.security.StateResourceFilter; import javax.ws.rs.GET; @@ -38,7 +39,7 @@ * {@link org.apache.druid.indexing.overlord.http.OverlordResource} */ @Path("/druid-internal/v1/httpRemoteTaskRunner") -@ResourceFilters(StateResourceFilter.class) +@ResourceFilters({ StateResourceFilter.class, InternalInternalResourceFilter.class }) public class HttpRemoteTaskRunnerResource { private final TaskMaster taskMaster; diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/http/OverlordResource.java b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/http/OverlordResource.java index bc01e55fe847..7c8f096ea0b6 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/http/OverlordResource.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/http/OverlordResource.java @@ -64,6 +64,9 @@ import org.apache.druid.server.http.HttpMediaType; import org.apache.druid.server.http.security.ConfigResourceFilter; import org.apache.druid.server.http.security.DatasourceResourceFilter; +import org.apache.druid.server.http.security.InternalInternalResourceFilter; +import org.apache.druid.server.http.security.ServerServerResourceFilter; +import org.apache.druid.server.http.security.ServerStatusResourceFilter; import org.apache.druid.server.http.security.StateResourceFilter; import org.apache.druid.server.security.Access; import org.apache.druid.server.security.Action; @@ -203,7 +206,7 @@ public Response apply(TaskQueue taskQueue) @GET @Path("/leader") - @ResourceFilters(StateResourceFilter.class) + @ResourceFilters({ StateResourceFilter.class, ServerStatusResourceFilter.class }) @Produces(MediaType.APPLICATION_JSON) public Response getLeader() { @@ -377,7 +380,7 @@ public Response apply(TaskQueue taskQueue) @POST @Path("/taskStatus") @Produces(MediaType.APPLICATION_JSON) - @ResourceFilters(StateResourceFilter.class) + @ResourceFilters({ StateResourceFilter.class, InternalInternalResourceFilter.class }) public Response getMultipleTaskStatuses(Set taskIds) { if (taskIds == null || taskIds.size() == 0) { @@ -398,7 +401,7 @@ public Response getMultipleTaskStatuses(Set taskIds) @GET @Path("/worker") @Produces(MediaType.APPLICATION_JSON) - @ResourceFilters(ConfigResourceFilter.class) + @ResourceFilters({ ConfigResourceFilter.class, ServerServerResourceFilter.class }) public Response getWorkerConfig() { if (workerConfigRef == null) { @@ -412,7 +415,7 @@ public Response getWorkerConfig() @POST @Path("/worker") @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(ConfigResourceFilter.class) + @ResourceFilters({ ConfigResourceFilter.class, ServerServerResourceFilter.class }) public Response setWorkerConfig( final WorkerBehaviorConfig workerBehaviorConfig, @HeaderParam(AuditManager.X_DRUID_AUTHOR) @DefaultValue("") final String author, @@ -437,7 +440,7 @@ public Response setWorkerConfig( @GET @Path("/worker/history") @Produces(MediaType.APPLICATION_JSON) - @ResourceFilters(ConfigResourceFilter.class) + @ResourceFilters({ ConfigResourceFilter.class, ServerServerResourceFilter.class }) public Response getWorkerConfigHistory( @QueryParam("interval") final String interval, @QueryParam("count") final Integer count @@ -470,7 +473,7 @@ public Response getWorkerConfigHistory( @POST @Path("/action") @Produces(MediaType.APPLICATION_JSON) - @ResourceFilters(StateResourceFilter.class) + @ResourceFilters({ StateResourceFilter.class, InternalInternalResourceFilter.class }) public Response doAction(final TaskActionHolder holder) { return asLeaderWith( @@ -711,7 +714,7 @@ public Response killPendingSegments( @GET @Path("/workers") @Produces(MediaType.APPLICATION_JSON) - @ResourceFilters(StateResourceFilter.class) + @ResourceFilters({ StateResourceFilter.class, ServerServerResourceFilter.class }) public Response getWorkers() { return asLeaderWith( @@ -741,7 +744,7 @@ public Response apply(TaskRunner taskRunner) @POST @Path("/worker/{host}/enable") @Produces(MediaType.APPLICATION_JSON) - @ResourceFilters(StateResourceFilter.class) + @ResourceFilters({ StateResourceFilter.class, ServerServerResourceFilter.class }) public Response enableWorker(@PathParam("host") final String host) { return changeWorkerStatus(host, WorkerTaskRunner.ActionType.ENABLE); @@ -750,7 +753,7 @@ public Response enableWorker(@PathParam("host") final String host) @POST @Path("/worker/{host}/disable") @Produces(MediaType.APPLICATION_JSON) - @ResourceFilters(StateResourceFilter.class) + @ResourceFilters({ StateResourceFilter.class, ServerServerResourceFilter.class }) public Response disableWorker(@PathParam("host") final String host) { return changeWorkerStatus(host, WorkerTaskRunner.ActionType.DISABLE); @@ -782,7 +785,7 @@ private Response changeWorkerStatus(String host, WorkerTaskRunner.ActionType act @GET @Path("/scaling") @Produces(MediaType.APPLICATION_JSON) - @ResourceFilters(StateResourceFilter.class) + @ResourceFilters({ StateResourceFilter.class, InternalInternalResourceFilter.class }) public Response getScalingState() { // Don't use asLeaderWith, since we want to return 200 instead of 503 when missing an autoscaler. diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/http/security/SupervisorResourceFilter.java b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/http/security/SupervisorResourceFilter.java index e834c2e875e5..f48d29022b28 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/http/security/SupervisorResourceFilter.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/http/security/SupervisorResourceFilter.java @@ -32,6 +32,7 @@ import org.apache.druid.server.http.security.AbstractResourceFilter; import org.apache.druid.server.security.Access; import org.apache.druid.server.security.Action; +import org.apache.druid.server.security.AuthConfig; import org.apache.druid.server.security.AuthorizationUtils; import org.apache.druid.server.security.AuthorizerMapper; import org.apache.druid.server.security.ForbiddenException; @@ -48,10 +49,11 @@ public class SupervisorResourceFilter extends AbstractResourceFilter @Inject public SupervisorResourceFilter( AuthorizerMapper authorizerMapper, - SupervisorManager supervisorManager + SupervisorManager supervisorManager, + AuthConfig authConfig ) { - super(authorizerMapper); + super(authorizerMapper, authConfig); this.supervisorManager = supervisorManager; } diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/http/security/TaskResourceFilter.java b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/http/security/TaskResourceFilter.java index 51b17f81def8..f38b1c24518d 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/http/security/TaskResourceFilter.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/http/security/TaskResourceFilter.java @@ -31,6 +31,7 @@ import org.apache.druid.java.util.common.StringUtils; import org.apache.druid.server.http.security.AbstractResourceFilter; import org.apache.druid.server.security.Access; +import org.apache.druid.server.security.AuthConfig; import org.apache.druid.server.security.AuthorizationUtils; import org.apache.druid.server.security.AuthorizerMapper; import org.apache.druid.server.security.ForbiddenException; @@ -55,10 +56,11 @@ public class TaskResourceFilter extends AbstractResourceFilter @Inject public TaskResourceFilter( TaskStorageQueryAdapter taskStorageQueryAdapter, - AuthorizerMapper authorizerMapper + AuthorizerMapper authorizerMapper, + AuthConfig authConfig ) { - super(authorizerMapper); + super(authorizerMapper, authConfig); this.taskStorageQueryAdapter = taskStorageQueryAdapter; } diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/sampler/SamplerResource.java b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/sampler/SamplerResource.java index 3b8276cd47ee..1c0d56c97c9f 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/sampler/SamplerResource.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/sampler/SamplerResource.java @@ -21,6 +21,7 @@ import com.google.common.base.Preconditions; import com.sun.jersey.spi.container.ResourceFilters; +import org.apache.druid.server.http.security.ServerPermissionsResourceFilter; import org.apache.druid.server.http.security.StateResourceFilter; import javax.ws.rs.Consumes; @@ -35,7 +36,7 @@ public class SamplerResource @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - @ResourceFilters(StateResourceFilter.class) + @ResourceFilters({ StateResourceFilter.class, ServerPermissionsResourceFilter.class }) public SamplerResponse post(final SamplerSpec sampler) { return Preconditions.checkNotNull(sampler, "Request body cannot be empty").sample(); diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/worker/http/TaskManagementResource.java b/indexing-service/src/main/java/org/apache/druid/indexing/worker/http/TaskManagementResource.java index 57046ff80a03..e22048dad2e4 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/worker/http/TaskManagementResource.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/worker/http/TaskManagementResource.java @@ -35,6 +35,7 @@ import org.apache.druid.java.util.emitter.EmittingLogger; import org.apache.druid.server.coordination.ChangeRequestHistory; import org.apache.druid.server.coordination.ChangeRequestsSnapshot; +import org.apache.druid.server.http.security.InternalInternalResourceFilter; import org.apache.druid.server.http.security.StateResourceFilter; import javax.servlet.AsyncContext; @@ -57,7 +58,7 @@ * Endpoints used by Overlord to Manage tasks on this Middle Manager. */ @Path("/druid-internal/v1/worker/") -@ResourceFilters(StateResourceFilter.class) +@ResourceFilters({ StateResourceFilter.class, InternalInternalResourceFilter.class }) public class TaskManagementResource { protected static final EmittingLogger log = new EmittingLogger(TaskManagementResource.class); diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/worker/http/WorkerResource.java b/indexing-service/src/main/java/org/apache/druid/indexing/worker/http/WorkerResource.java index 06c414b008dd..2e5a4f2bc842 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/worker/http/WorkerResource.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/worker/http/WorkerResource.java @@ -39,6 +39,7 @@ import org.apache.druid.java.util.common.logger.Logger; import org.apache.druid.server.http.HttpMediaType; import org.apache.druid.server.http.security.ConfigResourceFilter; +import org.apache.druid.server.http.security.InternalInternalResourceFilter; import org.apache.druid.server.http.security.StateResourceFilter; import org.apache.druid.tasklogs.TaskLogStreamer; @@ -95,7 +96,7 @@ public WorkerResource( @POST @Path("/disable") @Produces(MediaType.APPLICATION_JSON) - @ResourceFilters(ConfigResourceFilter.class) + @ResourceFilters({ ConfigResourceFilter.class, InternalInternalResourceFilter.class }) public Response doDisable() { try { @@ -121,7 +122,7 @@ public Response doDisable() @POST @Path("/enable") @Produces(MediaType.APPLICATION_JSON) - @ResourceFilters(ConfigResourceFilter.class) + @ResourceFilters({ ConfigResourceFilter.class, InternalInternalResourceFilter.class }) public Response doEnable() { try { @@ -139,7 +140,7 @@ public Response doEnable() @GET @Path("/enabled") @Produces(MediaType.APPLICATION_JSON) - @ResourceFilters(StateResourceFilter.class) + @ResourceFilters({ StateResourceFilter.class, InternalInternalResourceFilter.class }) public Response isEnabled() { try { @@ -153,7 +154,7 @@ public Response isEnabled() @GET @Path("/tasks") @Produces(MediaType.APPLICATION_JSON) - @ResourceFilters(StateResourceFilter.class) + @ResourceFilters({ StateResourceFilter.class, InternalInternalResourceFilter.class }) public Response getTasks() { try { @@ -181,7 +182,7 @@ public String apply(TaskRunnerWorkItem input) @POST @Path("/task/{taskid}/shutdown") @Produces(MediaType.APPLICATION_JSON) - @ResourceFilters(StateResourceFilter.class) + @ResourceFilters({ StateResourceFilter.class, InternalInternalResourceFilter.class }) public Response doShutdown(@PathParam("taskid") String taskid) { try { @@ -197,7 +198,7 @@ public Response doShutdown(@PathParam("taskid") String taskid) @GET @Path("/task/{taskid}/log") @Produces(HttpMediaType.TEXT_PLAIN_UTF8) - @ResourceFilters(StateResourceFilter.class) + @ResourceFilters({ StateResourceFilter.class, InternalInternalResourceFilter.class }) public Response doGetLog( @PathParam("taskid") String taskId, @QueryParam("offset") @DefaultValue("0") long offset diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/worker/shuffle/ShuffleResource.java b/indexing-service/src/main/java/org/apache/druid/indexing/worker/shuffle/ShuffleResource.java index dd885a2ab0d6..4b1064a106a8 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/worker/shuffle/ShuffleResource.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/worker/shuffle/ShuffleResource.java @@ -25,6 +25,7 @@ import org.apache.druid.java.util.common.DateTimes; import org.apache.druid.java.util.common.StringUtils; import org.apache.druid.java.util.common.logger.Logger; +import org.apache.druid.server.http.security.InternalInternalResourceFilter; import org.apache.druid.server.http.security.StateResourceFilter; import org.joda.time.Interval; @@ -54,7 +55,7 @@ * We could develop a new ResourceFileter in the future if needed. */ @Path("/druid/worker/v1/shuffle") -@ResourceFilters(StateResourceFilter.class) +@ResourceFilters({ StateResourceFilter.class, InternalInternalResourceFilter.class }) public class ShuffleResource { private static final Logger log = new Logger(ShuffleResource.class); diff --git a/server/src/main/java/org/apache/druid/client/HttpServerInventoryViewResource.java b/server/src/main/java/org/apache/druid/client/HttpServerInventoryViewResource.java index cda761e6f2bc..b40c64b5d5af 100644 --- a/server/src/main/java/org/apache/druid/client/HttpServerInventoryViewResource.java +++ b/server/src/main/java/org/apache/druid/client/HttpServerInventoryViewResource.java @@ -21,6 +21,7 @@ import com.google.inject.Inject; import com.sun.jersey.spi.container.ResourceFilters; +import org.apache.druid.server.http.security.InternalInternalResourceFilter; import org.apache.druid.server.http.security.StateResourceFilter; import javax.ws.rs.GET; @@ -33,7 +34,7 @@ * Collection of http endpoits to introspect state of HttpServerInventoryView instance for debugging. */ @Path("/druid-internal/v1/httpServerInventoryView") -@ResourceFilters(StateResourceFilter.class) +@ResourceFilters({ StateResourceFilter.class, InternalInternalResourceFilter.class }) public class HttpServerInventoryViewResource { private final HttpServerInventoryView httpServerInventoryView; diff --git a/server/src/main/java/org/apache/druid/query/lookup/LookupIntrospectionResource.java b/server/src/main/java/org/apache/druid/query/lookup/LookupIntrospectionResource.java index 8ccce127f0c0..52318d5957fd 100644 --- a/server/src/main/java/org/apache/druid/query/lookup/LookupIntrospectionResource.java +++ b/server/src/main/java/org/apache/druid/query/lookup/LookupIntrospectionResource.java @@ -23,7 +23,16 @@ import com.sun.jersey.spi.container.ResourceFilters; import org.apache.druid.java.util.common.logger.Logger; import org.apache.druid.server.http.security.ConfigResourceFilter; +import org.apache.druid.server.security.Access; +import org.apache.druid.server.security.Action; +import org.apache.druid.server.security.AuthConfig; +import org.apache.druid.server.security.AuthorizationUtils; +import org.apache.druid.server.security.AuthorizerMapper; +import org.apache.druid.server.security.Resource; +import org.apache.druid.server.security.ResourceAction; +import org.apache.druid.server.security.ResourceType; +import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.core.Context; @@ -37,18 +46,34 @@ public class LookupIntrospectionResource private static final Logger LOGGER = new Logger(LookupIntrospectionResource.class); private final LookupExtractorFactoryContainerProvider lookupExtractorFactoryContainerProvider; + private final AuthConfig authConfig; + private AuthorizerMapper authorizerMapper; @Inject public LookupIntrospectionResource( - @Context LookupExtractorFactoryContainerProvider lookupExtractorFactoryContainerProvider + @Context LookupExtractorFactoryContainerProvider lookupExtractorFactoryContainerProvider, + AuthConfig authConfig, + AuthorizerMapper authorizerMapper ) { this.lookupExtractorFactoryContainerProvider = lookupExtractorFactoryContainerProvider; + this.authConfig = authConfig; + this.authorizerMapper = authorizerMapper; } @Path("/{lookupId}") - public Object introspectLookup(@PathParam("lookupId") final String lookupId) + public Object introspectLookup(@PathParam("lookupId") final String lookupId, @Context HttpServletRequest request) { + if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + final Access access = AuthorizationUtils.authorizeResourceAction(request, + new ResourceAction(new Resource(lookupId, ResourceType.LOOKUP), Action.READ), + authorizerMapper + ); + if (!access.isAllowed()) { + return Response.status(Response.Status.FORBIDDEN).entity(access.getMessage()).build(); + } + } + final Optional maybeContainer = lookupExtractorFactoryContainerProvider.get(lookupId); diff --git a/server/src/main/java/org/apache/druid/query/lookup/LookupListeningResource.java b/server/src/main/java/org/apache/druid/query/lookup/LookupListeningResource.java index dd3f880583c6..c9352d5fc209 100644 --- a/server/src/main/java/org/apache/druid/query/lookup/LookupListeningResource.java +++ b/server/src/main/java/org/apache/druid/query/lookup/LookupListeningResource.java @@ -29,6 +29,7 @@ import org.apache.druid.guice.annotations.Smile; import org.apache.druid.java.util.common.logger.Logger; import org.apache.druid.server.http.security.ConfigResourceFilter; +import org.apache.druid.server.http.security.LookupInternalResourceFilter; import org.apache.druid.server.listener.resource.AbstractListenerHandler; import org.apache.druid.server.listener.resource.ListenerResource; import org.apache.druid.server.lookup.cache.LookupCoordinatorManager; @@ -41,7 +42,7 @@ import java.util.Map; @Path(ListenerResource.BASE_PATH + "/" + LookupCoordinatorManager.LOOKUP_LISTEN_ANNOUNCE_KEY) -@ResourceFilters(ConfigResourceFilter.class) +@ResourceFilters({ ConfigResourceFilter.class, LookupInternalResourceFilter.class }) class LookupListeningResource extends ListenerResource { private static final Logger LOG = new Logger(LookupListeningResource.class); diff --git a/server/src/main/java/org/apache/druid/server/BrokerQueryResource.java b/server/src/main/java/org/apache/druid/server/BrokerQueryResource.java index 9ef86af87330..bc7d80f592d1 100644 --- a/server/src/main/java/org/apache/druid/server/BrokerQueryResource.java +++ b/server/src/main/java/org/apache/druid/server/BrokerQueryResource.java @@ -29,6 +29,7 @@ import org.apache.druid.guice.annotations.Self; import org.apache.druid.guice.annotations.Smile; import org.apache.druid.query.Query; +import org.apache.druid.server.http.security.InternalInternalResourceFilter; import org.apache.druid.server.http.security.StateResourceFilter; import org.apache.druid.server.security.AuthConfig; import org.apache.druid.server.security.AuthorizerMapper; @@ -83,7 +84,7 @@ public BrokerQueryResource( @Path("/candidates") @Produces({MediaType.APPLICATION_JSON, SmileMediaTypes.APPLICATION_JACKSON_SMILE}) @Consumes({MediaType.APPLICATION_JSON, SmileMediaTypes.APPLICATION_JACKSON_SMILE, APPLICATION_SMILE}) - @ResourceFilters(StateResourceFilter.class) + @ResourceFilters({ StateResourceFilter.class, InternalInternalResourceFilter.class }) public Response getQueryTargets( InputStream in, @QueryParam("pretty") String pretty, diff --git a/server/src/main/java/org/apache/druid/server/QueryLifecycle.java b/server/src/main/java/org/apache/druid/server/QueryLifecycle.java index cf6cde5081c9..3368059c5fe0 100644 --- a/server/src/main/java/org/apache/druid/server/QueryLifecycle.java +++ b/server/src/main/java/org/apache/druid/server/QueryLifecycle.java @@ -35,6 +35,7 @@ import org.apache.druid.query.DefaultQueryConfig; import org.apache.druid.query.DruidMetrics; import org.apache.druid.query.GenericQueryMetricsFactory; +import org.apache.druid.query.LookupDataSource; import org.apache.druid.query.Query; import org.apache.druid.query.QueryInterruptedException; import org.apache.druid.query.QueryMetrics; @@ -45,12 +46,18 @@ import org.apache.druid.query.context.ResponseContext; import org.apache.druid.server.log.RequestLogger; import org.apache.druid.server.security.Access; +import org.apache.druid.server.security.Action; +import org.apache.druid.server.security.AuthConfig; import org.apache.druid.server.security.AuthenticationResult; import org.apache.druid.server.security.AuthorizationUtils; import org.apache.druid.server.security.AuthorizerMapper; +import org.apache.druid.server.security.Resource; +import org.apache.druid.server.security.ResourceAction; +import org.apache.druid.server.security.ResourceType; import javax.annotation.Nullable; import javax.servlet.http.HttpServletRequest; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import java.util.UUID; @@ -82,6 +89,7 @@ public class QueryLifecycle private final DefaultQueryConfig defaultQueryConfig; private final long startMs; private final long startNs; + private final AuthConfig authConfig; private State state = State.NEW; private AuthenticationResult authenticationResult; @@ -97,7 +105,8 @@ public QueryLifecycle( final AuthorizerMapper authorizerMapper, final DefaultQueryConfig defaultQueryConfig, final long startMs, - final long startNs + final long startNs, + final AuthConfig authConfig ) { this.warehouse = warehouse; @@ -109,6 +118,7 @@ public QueryLifecycle( this.defaultQueryConfig = defaultQueryConfig; this.startMs = startMs; this.startNs = startNs; + this.authConfig = authConfig; } /** @@ -134,7 +144,7 @@ public Sequence runSimple( final Sequence results; try { - final Access access = authorize(authenticationResult); + final Access access = authorize(authenticationResult, authConfig); if (!access.isAllowed()) { throw new ISE("Unauthorized"); } @@ -193,9 +203,26 @@ public void initialize(final Query baseQuery) * * @return authorization result */ - public Access authorize(final AuthenticationResult authenticationResult) + public Access authorize(final AuthenticationResult authenticationResult, final AuthConfig authConfig) { transition(State.INITIALIZED, State.AUTHORIZING); + if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + return doAuthorize( + authenticationResult, + AuthorizationUtils.authorizeAllResourceActions( + authenticationResult, + baseQuery.getDataSource() instanceof LookupDataSource ? + Collections.singleton(new ResourceAction(new Resource( + ((LookupDataSource) baseQuery.getDataSource()).getLookupName(), + ResourceType.LOOKUP + ), Action.READ)) : Iterables.transform( + baseQuery.getDataSource().getTableNames(), + AuthorizationUtils.DATASOURCE_READ_RA_GENERATOR + ), + authorizerMapper + ) + ); + } return doAuthorize( authenticationResult, AuthorizationUtils.authorizeAllResourceActions( diff --git a/server/src/main/java/org/apache/druid/server/QueryLifecycleFactory.java b/server/src/main/java/org/apache/druid/server/QueryLifecycleFactory.java index 106ffe9e5e9c..3b001f49f3e0 100644 --- a/server/src/main/java/org/apache/druid/server/QueryLifecycleFactory.java +++ b/server/src/main/java/org/apache/druid/server/QueryLifecycleFactory.java @@ -41,6 +41,7 @@ public class QueryLifecycleFactory private final RequestLogger requestLogger; private final AuthorizerMapper authorizerMapper; private final DefaultQueryConfig defaultQueryConfig; + private final AuthConfig authConfig; @Inject public QueryLifecycleFactory( @@ -61,6 +62,7 @@ public QueryLifecycleFactory( this.requestLogger = requestLogger; this.authorizerMapper = authorizerMapper; this.defaultQueryConfig = queryConfigSupplier.get(); + this.authConfig = authConfig; } public QueryLifecycle factorize() @@ -74,7 +76,8 @@ public QueryLifecycle factorize() authorizerMapper, defaultQueryConfig, System.currentTimeMillis(), - System.nanoTime() + System.nanoTime(), + authConfig ); } } diff --git a/server/src/main/java/org/apache/druid/server/StatusResource.java b/server/src/main/java/org/apache/druid/server/StatusResource.java index cf457f14ee3b..21bde41aad93 100644 --- a/server/src/main/java/org/apache/druid/server/StatusResource.java +++ b/server/src/main/java/org/apache/druid/server/StatusResource.java @@ -28,6 +28,8 @@ import org.apache.druid.initialization.Initialization; import org.apache.druid.java.util.common.StringUtils; import org.apache.druid.server.http.security.ConfigResourceFilter; +import org.apache.druid.server.http.security.ServerServerResourceFilter; +import org.apache.druid.server.http.security.ServerStatusResourceFilter; import org.apache.druid.server.http.security.StateResourceFilter; import org.apache.druid.utils.JvmUtils; import org.apache.druid.utils.RuntimeInfo; @@ -64,7 +66,7 @@ public StatusResource(Properties properties, DruidServerConfig druidServerConfig @GET @Path("/properties") - @ResourceFilters(ConfigResourceFilter.class) + @ResourceFilters({ ConfigResourceFilter.class, ServerServerResourceFilter.class }) @Produces(MediaType.APPLICATION_JSON) public Map getProperties() { @@ -74,7 +76,7 @@ public Map getProperties() } @GET - @ResourceFilters(StateResourceFilter.class) + @ResourceFilters({ StateResourceFilter.class, ServerStatusResourceFilter.class }) @Produces(MediaType.APPLICATION_JSON) public Status doGet( @Context final HttpServletRequest req diff --git a/server/src/main/java/org/apache/druid/server/http/ClusterResource.java b/server/src/main/java/org/apache/druid/server/http/ClusterResource.java index eabf51ec115e..779cc96939f8 100644 --- a/server/src/main/java/org/apache/druid/server/http/ClusterResource.java +++ b/server/src/main/java/org/apache/druid/server/http/ClusterResource.java @@ -31,6 +31,7 @@ import org.apache.druid.discovery.NodeRole; import org.apache.druid.guice.LazySingleton; import org.apache.druid.server.DruidNode; +import org.apache.druid.server.http.security.InternalInternalResourceFilter; import org.apache.druid.server.http.security.StateResourceFilter; import javax.ws.rs.GET; @@ -47,7 +48,7 @@ */ @Path("/druid/coordinator/v1/cluster") @LazySingleton -@ResourceFilters(StateResourceFilter.class) +@ResourceFilters({ StateResourceFilter.class, InternalInternalResourceFilter.class }) public class ClusterResource { private final DruidNodeDiscoveryProvider druidNodeDiscoveryProvider; diff --git a/server/src/main/java/org/apache/druid/server/http/CoordinatorCompactionConfigsResource.java b/server/src/main/java/org/apache/druid/server/http/CoordinatorCompactionConfigsResource.java index da66b333fcb8..362d0b3efb15 100644 --- a/server/src/main/java/org/apache/druid/server/http/CoordinatorCompactionConfigsResource.java +++ b/server/src/main/java/org/apache/druid/server/http/CoordinatorCompactionConfigsResource.java @@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableMap; import com.google.inject.Inject; import com.sun.jersey.spi.container.ResourceFilters; +import net.thisptr.jackson.jq.internal.misc.Lists; import org.apache.druid.audit.AuditInfo; import org.apache.druid.audit.AuditManager; import org.apache.druid.common.config.ConfigManager.SetResult; @@ -30,6 +31,14 @@ import org.apache.druid.server.coordinator.CoordinatorCompactionConfig; import org.apache.druid.server.coordinator.DataSourceCompactionConfig; import org.apache.druid.server.http.security.ConfigResourceFilter; +import org.apache.druid.server.http.security.ServerServerResourceFilter; +import org.apache.druid.server.security.Action; +import org.apache.druid.server.security.AuthConfig; +import org.apache.druid.server.security.AuthorizationUtils; +import org.apache.druid.server.security.AuthorizerMapper; +import org.apache.druid.server.security.Resource; +import org.apache.druid.server.security.ResourceAction; +import org.apache.druid.server.security.ResourceType; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Consumes; @@ -45,6 +54,7 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import java.util.Collections; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; @@ -54,23 +64,38 @@ public class CoordinatorCompactionConfigsResource { private final JacksonConfigManager manager; + private final AuthorizerMapper authorizerMapper; + private final AuthConfig authConfig; @Inject - public CoordinatorCompactionConfigsResource(JacksonConfigManager manager) + public CoordinatorCompactionConfigsResource( + JacksonConfigManager manager, + AuthorizerMapper authorizerMapper, + AuthConfig authConfig + ) { this.manager = manager; + this.authorizerMapper = authorizerMapper; + this.authConfig = authConfig; } @GET @Produces(MediaType.APPLICATION_JSON) - public Response getCompactionConfig() + public Response getCompactionConfig(@Context HttpServletRequest request) { - return Response.ok(CoordinatorCompactionConfig.current(manager)).build(); + if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + return Response + .ok(getFilteredConfigsByDatasource(CoordinatorCompactionConfig.current(manager), request, Action.READ)) + .build(); + } else { + return Response.ok(CoordinatorCompactionConfig.current(manager)).build(); + } } @POST @Path("/taskslots") @Consumes(MediaType.APPLICATION_JSON) + @ResourceFilters(ServerServerResourceFilter.class) public Response setCompactionTaskLimit( @QueryParam("ratio") Double compactionTaskSlotRatio, @QueryParam("max") Integer maxCompactionTaskSlots, @@ -111,6 +136,12 @@ public Response addOrUpdateCompactionConfig( @Context HttpServletRequest req ) { + if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + if (!AuthorizationUtils.authorizeResourceAction(req, AuthorizationUtils.DATASOURCE_WRITE_RA_GENERATOR.apply( + newConfig.getDataSource()), authorizerMapper).isAllowed()) { + return Response.status(Response.Status.FORBIDDEN).build(); + } + } final CoordinatorCompactionConfig current = CoordinatorCompactionConfig.current(manager); final CoordinatorCompactionConfig newCompactionConfig; final Map newConfigs = current @@ -136,8 +167,17 @@ public Response addOrUpdateCompactionConfig( @GET @Path("/{dataSource}") @Produces(MediaType.APPLICATION_JSON) - public Response getCompactionConfig(@PathParam("dataSource") String dataSource) + public Response getCompactionConfig(@PathParam("dataSource") String dataSource, @Context HttpServletRequest req) { + if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + if (!AuthorizationUtils.authorizeResourceAction( + req, + AuthorizationUtils.DATASOURCE_READ_RA_GENERATOR.apply(dataSource), + authorizerMapper + ).isAllowed()) { + return Response.status(Response.Status.FORBIDDEN).build(); + } + } final CoordinatorCompactionConfig current = CoordinatorCompactionConfig.current(manager); final Map configs = current .getCompactionConfigs() @@ -162,6 +202,15 @@ public Response deleteCompactionConfig( @Context HttpServletRequest req ) { + if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + if (!AuthorizationUtils.authorizeResourceAction( + req, + AuthorizationUtils.DATASOURCE_WRITE_RA_GENERATOR.apply(dataSource), + authorizerMapper + ).isAllowed()) { + return Response.status(Response.Status.FORBIDDEN).build(); + } + } final CoordinatorCompactionConfig current = CoordinatorCompactionConfig.current(manager); final Map configs = current .getCompactionConfigs() @@ -185,4 +234,26 @@ public Response deleteCompactionConfig( return Response.status(Response.Status.BAD_REQUEST).build(); } } + + private CoordinatorCompactionConfig getFilteredConfigsByDatasource( + CoordinatorCompactionConfig compactionConfig, + HttpServletRequest request, + Action action + ) + { + final Iterable dataSourceCompactionConfigs = AuthorizationUtils + .filterAuthorizedResources(request, compactionConfig.getCompactionConfigs(), + input -> Collections + .singleton(new ResourceAction(new Resource(input.getDataSource(), ResourceType.DATASOURCE), action)), + authorizerMapper + ); + // remove so that it does not complain about it when checking for server server permission + request.removeAttribute(AuthConfig.DRUID_AUTHORIZATION_CHECKED); + if (AuthorizationUtils + .authorizeResourceAction(request, new ResourceAction(Resource.SERVER_SERVER_RESOURCE, action), authorizerMapper) + .isAllowed()) { + return CoordinatorCompactionConfig.from(compactionConfig, Lists.newArrayList(dataSourceCompactionConfigs)); + } + return CoordinatorCompactionConfig.from(Lists.newArrayList(dataSourceCompactionConfigs)); + } } diff --git a/server/src/main/java/org/apache/druid/server/http/CoordinatorDynamicConfigsResource.java b/server/src/main/java/org/apache/druid/server/http/CoordinatorDynamicConfigsResource.java index 488b0e705722..ae24fac2ee77 100644 --- a/server/src/main/java/org/apache/druid/server/http/CoordinatorDynamicConfigsResource.java +++ b/server/src/main/java/org/apache/druid/server/http/CoordinatorDynamicConfigsResource.java @@ -28,6 +28,7 @@ import org.apache.druid.java.util.common.Intervals; import org.apache.druid.server.coordinator.CoordinatorDynamicConfig; import org.apache.druid.server.http.security.ConfigResourceFilter; +import org.apache.druid.server.http.security.ServerServerResourceFilter; import org.joda.time.Interval; import javax.inject.Inject; @@ -47,7 +48,7 @@ /** */ @Path("/druid/coordinator/v1/config") -@ResourceFilters(ConfigResourceFilter.class) +@ResourceFilters({ ConfigResourceFilter.class, ServerServerResourceFilter.class }) public class CoordinatorDynamicConfigsResource { private final JacksonConfigManager manager; diff --git a/server/src/main/java/org/apache/druid/server/http/CoordinatorResource.java b/server/src/main/java/org/apache/druid/server/http/CoordinatorResource.java index 92543e6a9c75..8042b3f102f4 100644 --- a/server/src/main/java/org/apache/druid/server/http/CoordinatorResource.java +++ b/server/src/main/java/org/apache/druid/server/http/CoordinatorResource.java @@ -27,6 +27,7 @@ import com.sun.jersey.spi.container.ResourceFilters; import org.apache.druid.server.coordinator.DruidCoordinator; import org.apache.druid.server.coordinator.LoadQueuePeon; +import org.apache.druid.server.http.security.ServerStatusResourceFilter; import org.apache.druid.server.http.security.StateResourceFilter; import org.apache.druid.timeline.DataSegment; @@ -55,7 +56,7 @@ public CoordinatorResource( @GET @Path("/leader") - @ResourceFilters(StateResourceFilter.class) + @ResourceFilters({ StateResourceFilter.class, ServerStatusResourceFilter.class }) @Produces(MediaType.APPLICATION_JSON) public Response getLeader() { @@ -81,7 +82,7 @@ public Response isLeader() @GET @Path("/loadstatus") - @ResourceFilters(StateResourceFilter.class) + @ResourceFilters({ StateResourceFilter.class, ServerStatusResourceFilter.class }) @Produces(MediaType.APPLICATION_JSON) public Response getLoadStatus( @QueryParam("simple") String simple, @@ -100,7 +101,7 @@ public Response getLoadStatus( @GET @Path("/loadqueue") - @ResourceFilters(StateResourceFilter.class) + @ResourceFilters({ StateResourceFilter.class, ServerStatusResourceFilter.class }) @Produces(MediaType.APPLICATION_JSON) public Response getLoadQueue( @QueryParam("simple") String simple, diff --git a/server/src/main/java/org/apache/druid/server/http/HistoricalResource.java b/server/src/main/java/org/apache/druid/server/http/HistoricalResource.java index 4bc48f444df1..d95e7aa9de93 100644 --- a/server/src/main/java/org/apache/druid/server/http/HistoricalResource.java +++ b/server/src/main/java/org/apache/druid/server/http/HistoricalResource.java @@ -22,6 +22,7 @@ import com.google.common.collect.ImmutableMap; import com.sun.jersey.spi.container.ResourceFilters; import org.apache.druid.server.coordination.SegmentLoadDropHandler; +import org.apache.druid.server.http.security.ServerStatusResourceFilter; import org.apache.druid.server.http.security.StateResourceFilter; import javax.inject.Inject; @@ -46,7 +47,7 @@ public HistoricalResource( @GET @Path("/loadstatus") - @ResourceFilters(StateResourceFilter.class) + @ResourceFilters({ StateResourceFilter.class, ServerStatusResourceFilter.class }) @Produces(MediaType.APPLICATION_JSON) public Response getLoadStatus() { diff --git a/server/src/main/java/org/apache/druid/server/http/LookupCoordinatorResource.java b/server/src/main/java/org/apache/druid/server/http/LookupCoordinatorResource.java index 8bc21caed54a..a582c216b0dd 100644 --- a/server/src/main/java/org/apache/druid/server/http/LookupCoordinatorResource.java +++ b/server/src/main/java/org/apache/druid/server/http/LookupCoordinatorResource.java @@ -39,8 +39,18 @@ import org.apache.druid.java.util.common.logger.Logger; import org.apache.druid.query.lookup.LookupsState; import org.apache.druid.server.http.security.ConfigResourceFilter; +import org.apache.druid.server.http.security.LookupStatusResourceFilter; +import org.apache.druid.server.http.security.ServerServerResourceFilter; import org.apache.druid.server.lookup.cache.LookupCoordinatorManager; import org.apache.druid.server.lookup.cache.LookupExtractorFactoryMapContainer; +import org.apache.druid.server.security.Access; +import org.apache.druid.server.security.Action; +import org.apache.druid.server.security.AuthConfig; +import org.apache.druid.server.security.AuthorizationUtils; +import org.apache.druid.server.security.AuthorizerMapper; +import org.apache.druid.server.security.Resource; +import org.apache.druid.server.security.ResourceAction; +import org.apache.druid.server.security.ResourceType; import javax.annotation.Nullable; import javax.servlet.http.HttpServletRequest; @@ -67,37 +77,45 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.function.BiFunction; import java.util.stream.Collectors; /** * Contains information about lookups exposed through the coordinator */ @Path("/druid/coordinator/v1/lookups") -@ResourceFilters(ConfigResourceFilter.class) public class LookupCoordinatorResource { private static final Logger LOG = new Logger(LookupCoordinatorResource.class); private final LookupCoordinatorManager lookupCoordinatorManager; private final ObjectMapper smileMapper; private final ObjectMapper jsonMapper; + private final AuthorizerMapper authorizerMapper; + private final AuthConfig authConfig; @Inject public LookupCoordinatorResource( final LookupCoordinatorManager lookupCoordinatorManager, final @Smile ObjectMapper smileMapper, - final @Json ObjectMapper jsonMapper + final @Json ObjectMapper jsonMapper, + AuthorizerMapper authorizerMapper, + AuthConfig authConfig ) { this.smileMapper = smileMapper; this.jsonMapper = jsonMapper; this.lookupCoordinatorManager = lookupCoordinatorManager; + this.authorizerMapper = authorizerMapper; + this.authConfig = authConfig; } @GET @Path("/config") @Produces({MediaType.APPLICATION_JSON, SmileMediaTypes.APPLICATION_JACKSON_SMILE}) + @ResourceFilters({ ConfigResourceFilter.class, LookupStatusResourceFilter.class }) public Response getTiers( - @DefaultValue("false") @QueryParam("discover") boolean discover + @DefaultValue("false") @QueryParam("discover") boolean discover, + @Context HttpServletRequest request ) { try { @@ -126,11 +144,15 @@ public Response getTiers( @GET @Produces({MediaType.APPLICATION_JSON}) @Path("/config/all") - public Response getAllLookupSpecs() + @ResourceFilters(ConfigResourceFilter.class) + public Response getAllLookupSpecs(@Context HttpServletRequest request) { try { - final Map> knownLookups = lookupCoordinatorManager + Map> knownLookups = lookupCoordinatorManager .getKnownLookups(); + if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + knownLookups = filterByLookupAccess(knownLookups, request, authorizerMapper, Action.READ); + } if (knownLookups == null) { return Response.status(Response.Status.NOT_FOUND).build(); } else { @@ -147,6 +169,7 @@ public Response getAllLookupSpecs() @Path("/config") @Produces({MediaType.APPLICATION_JSON, SmileMediaTypes.APPLICATION_JACKSON_SMILE}) @Consumes({MediaType.APPLICATION_JSON, SmileMediaTypes.APPLICATION_JACKSON_SMILE}) + @ResourceFilters(ConfigResourceFilter.class) public Response updateAllLookups( InputStream in, @HeaderParam(AuditManager.X_DRUID_AUTHOR) @DefaultValue("") final String author, @@ -166,6 +189,13 @@ public Response updateAllLookups( catch (IOException e) { return Response.status(Response.Status.BAD_REQUEST).entity(ServletResourceUtils.sanitizeException(e)).build(); } + if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + final Access access = AuthorizationUtils + .authorizeAllResourceActions(req, TIER_LOOKUP_RA_GENERATOR.apply(map, Action.WRITE), authorizerMapper); + if (!access.isAllowed()) { + return Response.status(Response.Status.FORBIDDEN).entity(access.getMessage()).build(); + } + } if (lookupCoordinatorManager.updateLookups(map, new AuditInfo(author, comment, req.getRemoteAddr()))) { return Response.status(Response.Status.ACCEPTED).entity(map).build(); } else { @@ -181,6 +211,7 @@ public Response updateAllLookups( @DELETE @Produces({MediaType.APPLICATION_JSON, SmileMediaTypes.APPLICATION_JACKSON_SMILE}) @Path("/config/{tier}") + @ResourceFilters(ConfigResourceFilter.class) public Response deleteTier( @PathParam("tier") String tier, @HeaderParam(AuditManager.X_DRUID_AUTHOR) @DefaultValue("") final String author, @@ -195,6 +226,15 @@ public Response deleteTier( .build(); } + if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + Map tierLookups = lookupCoordinatorManager.getTierLookups(tier); + final Access access = AuthorizationUtils + .authorizeAllResourceActions(req, LOOKUP_RA_GENERATOR.apply(tierLookups, Action.WRITE), authorizerMapper); + if (!access.isAllowed()) { + return Response.status(Response.Status.FORBIDDEN).entity(access.getMessage()).build(); + } + } + if (lookupCoordinatorManager.deleteTier(tier, new AuditInfo(author, comment, req.getRemoteAddr()))) { return Response.status(Response.Status.ACCEPTED).build(); } else { @@ -210,6 +250,7 @@ public Response deleteTier( @DELETE @Produces({MediaType.APPLICATION_JSON, SmileMediaTypes.APPLICATION_JACKSON_SMILE}) @Path("/config/{tier}/{lookup}") + @ResourceFilters(ConfigResourceFilter.class) public Response deleteLookup( @PathParam("tier") String tier, @PathParam("lookup") String lookup, @@ -231,6 +272,14 @@ public Response deleteLookup( .build(); } + if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + final Access access = AuthorizationUtils + .authorizeResourceAction(req, new ResourceAction(new Resource(lookup, ResourceType.LOOKUP), Action.WRITE), authorizerMapper); + if (!access.isAllowed()) { + return Response.status(Response.Status.FORBIDDEN).entity(access.getMessage()).build(); + } + } + if (lookupCoordinatorManager.deleteLookup(tier, lookup, new AuditInfo(author, comment, req.getRemoteAddr()))) { return Response.status(Response.Status.ACCEPTED).build(); } else { @@ -246,6 +295,7 @@ public Response deleteLookup( @POST @Produces({MediaType.APPLICATION_JSON, SmileMediaTypes.APPLICATION_JACKSON_SMILE}) @Path("/config/{tier}/{lookup}") + @ResourceFilters(ConfigResourceFilter.class) public Response createOrUpdateLookup( @PathParam("tier") String tier, @PathParam("lookup") String lookup, @@ -267,6 +317,15 @@ public Response createOrUpdateLookup( .entity(ServletResourceUtils.sanitizeException(new IAE("`lookup` required"))) .build(); } + + if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + final Access access = AuthorizationUtils + .authorizeResourceAction(req, new ResourceAction(new Resource(lookup, ResourceType.LOOKUP), Action.WRITE), authorizerMapper); + if (!access.isAllowed()) { + return Response.status(Response.Status.FORBIDDEN).entity(access.getMessage()).build(); + } + } + final boolean isSmile = SmileMediaTypes.APPLICATION_JACKSON_SMILE.equals(req.getContentType()); final ObjectMapper mapper = isSmile ? smileMapper : jsonMapper; final LookupExtractorFactoryMapContainer lookupSpec; @@ -296,9 +355,11 @@ public Response createOrUpdateLookup( @GET @Produces({MediaType.APPLICATION_JSON, SmileMediaTypes.APPLICATION_JACKSON_SMILE}) @Path("/config/{tier}/{lookup}") + @ResourceFilters(ConfigResourceFilter.class) public Response getSpecificLookup( @PathParam("tier") String tier, - @PathParam("lookup") String lookup + @PathParam("lookup") String lookup, + @Context HttpServletRequest req ) { try { @@ -312,6 +373,15 @@ public Response getSpecificLookup( .entity(ServletResourceUtils.sanitizeException(new NullPointerException("`lookup` required"))) .build(); } + + if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + final Access access = AuthorizationUtils + .authorizeResourceAction(req, new ResourceAction(new Resource(lookup, ResourceType.LOOKUP), Action.READ), authorizerMapper); + if (!access.isAllowed()) { + return Response.status(Response.Status.FORBIDDEN).entity(access.getMessage()).build(); + } + } + final LookupExtractorFactoryMapContainer map = lookupCoordinatorManager.getLookup(tier, lookup); if (map == null) { return Response.status(Response.Status.NOT_FOUND) @@ -329,10 +399,11 @@ public Response getSpecificLookup( @GET @Produces({MediaType.APPLICATION_JSON, SmileMediaTypes.APPLICATION_JACKSON_SMILE}) @Path("/config/{tier}") + @ResourceFilters(ConfigResourceFilter.class) public Response getSpecificTier( @PathParam("tier") String tier, - @DefaultValue("false") @QueryParam("detailed") boolean detailed - + @DefaultValue("false") @QueryParam("detailed") boolean detailed, + @Context HttpServletRequest request ) { try { @@ -341,12 +412,15 @@ public Response getSpecificTier( .entity(ServletResourceUtils.sanitizeException(new NullPointerException("`tier` required"))) .build(); } - final Map> map = lookupCoordinatorManager.getKnownLookups(); + Map> map = lookupCoordinatorManager.getKnownLookups(); if (map == null) { return Response.status(Response.Status.NOT_FOUND) .entity(ServletResourceUtils.sanitizeException(new RE("No lookups found"))) .build(); } + if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + map = filterByLookupAccess(map, request, authorizerMapper, Action.READ); + } final Map tierLookups = map.get(tier); if (tierLookups == null) { return Response.status(Response.Status.NOT_FOUND) @@ -368,8 +442,10 @@ public Response getSpecificTier( @GET @Produces({MediaType.APPLICATION_JSON}) @Path("/status") + @ResourceFilters(ConfigResourceFilter.class) public Response getAllLookupsStatus( - @QueryParam("detailed") boolean detailed + @QueryParam("detailed") boolean detailed, + @Context HttpServletRequest request ) { try { @@ -380,6 +456,9 @@ public Response getAllLookupsStatus( .entity(ServletResourceUtils.jsonize("No lookups found")) .build(); } + if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + configuredLookups = filterByLookupAccess(configuredLookups, request, authorizerMapper, Action.READ); + } Map> lookupsStateOnNodes = lookupCoordinatorManager .getLastKnownLookupsStateOnNodes(); @@ -417,9 +496,11 @@ public Response getAllLookupsStatus( @GET @Produces({MediaType.APPLICATION_JSON}) @Path("/status/{tier}") + @ResourceFilters(ConfigResourceFilter.class) public Response getLookupStatusForTier( @PathParam("tier") String tier, - @QueryParam("detailed") boolean detailed + @QueryParam("detailed") boolean detailed, + @Context HttpServletRequest request ) { try { @@ -430,6 +511,9 @@ public Response getLookupStatusForTier( .entity(ServletResourceUtils.jsonize("No lookups found")) .build(); } + if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + configuredLookups = filterByLookupAccess(configuredLookups, request, authorizerMapper, Action.READ); + } Map tierLookups = configuredLookups.get(tier); if (tierLookups == null) { @@ -463,10 +547,12 @@ public Response getLookupStatusForTier( @GET @Produces({MediaType.APPLICATION_JSON}) @Path("/status/{tier}/{lookup}") + @ResourceFilters(ConfigResourceFilter.class) public Response getSpecificLookupStatus( @PathParam("tier") String tier, @PathParam("lookup") String lookup, - @QueryParam("detailed") boolean detailed + @QueryParam("detailed") boolean detailed, + @Context HttpServletRequest request ) { try { @@ -477,6 +563,13 @@ public Response getSpecificLookupStatus( .entity(ServletResourceUtils.jsonize("No lookups found")) .build(); } + if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + final Access access = AuthorizationUtils + .authorizeResourceAction(request, new ResourceAction(new Resource(lookup, ResourceType.LOOKUP), Action.READ), authorizerMapper); + if (!access.isAllowed()) { + return Response.status(Response.Status.FORBIDDEN).entity(access.getMessage()).build(); + } + } Map tierLookups = configuredLookups.get(tier); if (tierLookups == null) { @@ -541,6 +634,7 @@ LookupStatus getLookupStatus( @GET @Produces({MediaType.APPLICATION_JSON}) @Path("/nodeStatus") + @ResourceFilters({ ConfigResourceFilter.class, ServerServerResourceFilter.class }) public Response getAllNodesStatus( @QueryParam("discover") boolean discover, @QueryParam("detailed") @Nullable Boolean detailed @@ -584,6 +678,7 @@ public Response getAllNodesStatus( @GET @Produces({MediaType.APPLICATION_JSON}) @Path("/nodeStatus/{tier}") + @ResourceFilters({ ConfigResourceFilter.class, ServerServerResourceFilter.class }) public Response getNodesStatusInTier( @PathParam("tier") String tier ) @@ -616,6 +711,7 @@ public Response getNodesStatusInTier( @GET @Produces({MediaType.APPLICATION_JSON}) @Path("/nodeStatus/{tier}/{hostAndPort}") + @ResourceFilters({ ConfigResourceFilter.class, ServerServerResourceFilter.class }) public Response getSpecificNodeStatus( @PathParam("tier") String tier, @PathParam("hostAndPort") HostAndPort hostAndPort @@ -753,4 +849,42 @@ public int hashCode() return Objects.hash(loaded, pendingNodes); } } + + private Map> filterByLookupAccess( + Map> lookups, + HttpServletRequest request, + AuthorizerMapper authorizerMapper, + Action action + ) + { + final Map> filteredLookups = new HashMap<>(lookups); + lookups.keySet().forEach(tier -> filteredLookups.compute(tier, (tier1, lookupFactory) -> { + final Map filteredMap = new HashMap<>(lookupFactory); + lookupFactory.keySet().forEach(loookupId -> { + // so that it does not complain about auth being done already, + // last call to authorizeResourceAction will set this attribute again + request.removeAttribute(AuthConfig.DRUID_AUTHORIZATION_CHECKED); + final Access access = AuthorizationUtils.authorizeResourceAction( + request, + new ResourceAction(new Resource(loookupId, ResourceType.LOOKUP), action), + authorizerMapper + ); + if (access.isAllowed()) { + filteredMap.remove(loookupId); + } + }); + return filteredMap; + } + )); + return filteredLookups; + } + + private BiFunction>, Action, Iterable> TIER_LOOKUP_RA_GENERATOR = (input, action) -> input + .values().stream().flatMap(lookupMap -> lookupMap.keySet().stream() + .map(lookupId -> new ResourceAction(new Resource(lookupId, ResourceType.LOOKUP), action))) + .collect(Collectors.toList()); + + private BiFunction, Action, Iterable> LOOKUP_RA_GENERATOR = (input, action) -> input + .keySet().stream().map(lookupId -> new ResourceAction(new Resource(lookupId, ResourceType.LOOKUP), action)) + .collect(Collectors.toList()); } diff --git a/server/src/main/java/org/apache/druid/server/http/RouterResource.java b/server/src/main/java/org/apache/druid/server/http/RouterResource.java index 994d8b5e9b98..40cdbe73f03f 100644 --- a/server/src/main/java/org/apache/druid/server/http/RouterResource.java +++ b/server/src/main/java/org/apache/druid/server/http/RouterResource.java @@ -23,6 +23,7 @@ import com.google.inject.Inject; import com.sun.jersey.spi.container.ResourceFilters; import org.apache.druid.client.selector.Server; +import org.apache.druid.server.http.security.InternalInternalResourceFilter; import org.apache.druid.server.http.security.StateResourceFilter; import org.apache.druid.server.router.TieredBrokerHostSelector; @@ -49,7 +50,7 @@ public RouterResource(TieredBrokerHostSelector tieredBrokerHostSelector) @GET @Path("/brokers") - @ResourceFilters(StateResourceFilter.class) + @ResourceFilters({ StateResourceFilter.class, InternalInternalResourceFilter.class }) @Produces(MediaType.APPLICATION_JSON) public Map> getBrokers() { diff --git a/server/src/main/java/org/apache/druid/server/http/RulesResource.java b/server/src/main/java/org/apache/druid/server/http/RulesResource.java index fdb5896c4a20..921b37dcc6aa 100644 --- a/server/src/main/java/org/apache/druid/server/http/RulesResource.java +++ b/server/src/main/java/org/apache/druid/server/http/RulesResource.java @@ -29,7 +29,11 @@ import org.apache.druid.metadata.MetadataRuleManager; import org.apache.druid.server.coordinator.rules.Rule; import org.apache.druid.server.http.security.RulesResourceFilter; +import org.apache.druid.server.http.security.ServerStatusResourceFilter; import org.apache.druid.server.http.security.StateResourceFilter; +import org.apache.druid.server.security.AuthConfig; +import org.apache.druid.server.security.AuthorizationUtils; +import org.apache.druid.server.security.AuthorizerMapper; import org.joda.time.Interval; import javax.servlet.http.HttpServletRequest; @@ -45,7 +49,10 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** */ @@ -53,26 +60,47 @@ public class RulesResource { public static final String RULES_ENDPOINT = "/druid/coordinator/v1/rules"; + public static final String DEFAULT_RULE_KEY = "_default"; private final MetadataRuleManager databaseRuleManager; private final AuditManager auditManager; + private final AuthorizerMapper authorizerMapper; + private final AuthConfig authConfig; @Inject public RulesResource( MetadataRuleManager databaseRuleManager, - AuditManager auditManager + AuditManager auditManager, + AuthorizerMapper authorizerMapper, + AuthConfig authConfig ) { this.databaseRuleManager = databaseRuleManager; this.auditManager = auditManager; + this.authorizerMapper = authorizerMapper; + this.authConfig = authConfig; } @GET @Produces(MediaType.APPLICATION_JSON) @ResourceFilters(StateResourceFilter.class) - public Response getRules() + public Response getRules(@Context HttpServletRequest request) { - return Response.ok(databaseRuleManager.getAllRules()).build(); + final Map> rules = databaseRuleManager.getAllRules(); + + if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + final Map> filteredRules = new HashMap<>(); + AuthorizationUtils.filterAuthorizedResources( + request, + rules.keySet(), + datasource -> Collections.singletonList(AuthorizationUtils.DATASOURCE_WRITE_RA_GENERATOR.apply(datasource)), + authorizerMapper + ).forEach(datasource -> filteredRules.put(datasource, rules.get(datasource))); + // add default rule + filteredRules.put(DEFAULT_RULE_KEY, rules.get(DEFAULT_RULE_KEY)); + return Response.ok(filteredRules).build(); + } + return Response.ok(rules).build(); } @GET @@ -138,7 +166,7 @@ public Response getDatasourceRuleHistory( @GET @Path("/history") @Produces(MediaType.APPLICATION_JSON) - @ResourceFilters(StateResourceFilter.class) + @ResourceFilters({ StateResourceFilter.class, ServerStatusResourceFilter.class }) public Response getDatasourceRuleHistory( @QueryParam("interval") final String interval, @QueryParam("count") final Integer count diff --git a/server/src/main/java/org/apache/druid/server/http/SegmentListerResource.java b/server/src/main/java/org/apache/druid/server/http/SegmentListerResource.java index 45dae0af0aa9..648910a1bad7 100644 --- a/server/src/main/java/org/apache/druid/server/http/SegmentListerResource.java +++ b/server/src/main/java/org/apache/druid/server/http/SegmentListerResource.java @@ -36,6 +36,7 @@ import org.apache.druid.server.coordination.DataSegmentChangeRequest; import org.apache.druid.server.coordination.SegmentLoadDropHandler; import org.apache.druid.server.coordinator.HttpLoadQueuePeon; +import org.apache.druid.server.http.security.InternalInternalResourceFilter; import org.apache.druid.server.http.security.StateResourceFilter; import javax.annotation.Nullable; @@ -59,7 +60,7 @@ * Endpoints exposed here are to be used only for druid internal management of segments by Coordinators, Brokers etc. */ @Path("/druid-internal/v1/segments/") -@ResourceFilters(StateResourceFilter.class) +@ResourceFilters({ StateResourceFilter.class, InternalInternalResourceFilter.class }) public class SegmentListerResource { protected static final EmittingLogger log = new EmittingLogger(SegmentListerResource.class); diff --git a/server/src/main/java/org/apache/druid/server/http/SelfDiscoveryResource.java b/server/src/main/java/org/apache/druid/server/http/SelfDiscoveryResource.java index 13b7bded496e..cb3a2b93c745 100644 --- a/server/src/main/java/org/apache/druid/server/http/SelfDiscoveryResource.java +++ b/server/src/main/java/org/apache/druid/server/http/SelfDiscoveryResource.java @@ -28,6 +28,7 @@ import org.apache.druid.guice.annotations.Self; import org.apache.druid.java.util.common.lifecycle.Lifecycle; import org.apache.druid.server.DruidNode; +import org.apache.druid.server.http.security.InternalInternalResourceFilter; import org.apache.druid.server.http.security.StateResourceFilter; import org.eclipse.jetty.http.HttpStatus; @@ -88,7 +89,7 @@ public void stop() @GET @Path("/status") @Produces(MediaType.APPLICATION_JSON) - @ResourceFilters(StateResourceFilter.class) + @ResourceFilters({ StateResourceFilter.class, InternalInternalResourceFilter.class }) public Response getSelfDiscoveredStatus() { return Response.ok(Collections.singletonMap("selfDiscovered", isDiscoveredAllRoles())).build(); @@ -97,7 +98,7 @@ public Response getSelfDiscoveredStatus() /** See the description of this endpoint in api-reference.md. */ @GET @Produces(MediaType.APPLICATION_JSON) - @ResourceFilters(StateResourceFilter.class) + @ResourceFilters({ StateResourceFilter.class, InternalInternalResourceFilter.class }) public Response getSelfDiscovered() { if (isDiscoveredAllRoles()) { diff --git a/server/src/main/java/org/apache/druid/server/http/ServersResource.java b/server/src/main/java/org/apache/druid/server/http/ServersResource.java index c6b105ad16c6..39bd827d51a5 100644 --- a/server/src/main/java/org/apache/druid/server/http/ServersResource.java +++ b/server/src/main/java/org/apache/druid/server/http/ServersResource.java @@ -27,6 +27,7 @@ import com.sun.jersey.spi.container.ResourceFilters; import org.apache.druid.client.DruidServer; import org.apache.druid.client.InventoryView; +import org.apache.druid.server.http.security.ServerServerResourceFilter; import org.apache.druid.server.http.security.StateResourceFilter; import org.apache.druid.timeline.DataSegment; import org.apache.druid.timeline.SegmentId; @@ -47,7 +48,7 @@ /** */ @Path("/druid/coordinator/v1/servers") -@ResourceFilters(StateResourceFilter.class) +@ResourceFilters({ StateResourceFilter.class, ServerServerResourceFilter.class }) public class ServersResource { private static Map makeSimpleServer(DruidServer input) diff --git a/server/src/main/java/org/apache/druid/server/http/TiersResource.java b/server/src/main/java/org/apache/druid/server/http/TiersResource.java index 51a0fdad1d80..bd83783ecb0f 100644 --- a/server/src/main/java/org/apache/druid/server/http/TiersResource.java +++ b/server/src/main/java/org/apache/druid/server/http/TiersResource.java @@ -24,6 +24,7 @@ import org.apache.druid.client.DruidDataSource; import org.apache.druid.client.DruidServer; import org.apache.druid.client.InventoryView; +import org.apache.druid.server.http.security.ServerServerResourceFilter; import org.apache.druid.server.http.security.StateResourceFilter; import org.apache.druid.timeline.DataSegment; import org.joda.time.Interval; @@ -44,7 +45,7 @@ /** */ @Path("/druid/coordinator/v1/tiers") -@ResourceFilters(StateResourceFilter.class) +@ResourceFilters({ StateResourceFilter.class, ServerServerResourceFilter.class }) public class TiersResource { private final InventoryView serverInventoryView; diff --git a/server/src/main/java/org/apache/druid/server/http/security/AbstractResourceFilter.java b/server/src/main/java/org/apache/druid/server/http/security/AbstractResourceFilter.java index 4119e0b9b747..dc09d54695ef 100644 --- a/server/src/main/java/org/apache/druid/server/http/security/AbstractResourceFilter.java +++ b/server/src/main/java/org/apache/druid/server/http/security/AbstractResourceFilter.java @@ -25,6 +25,7 @@ import com.sun.jersey.spi.container.ContainerResponseFilter; import com.sun.jersey.spi.container.ResourceFilter; import org.apache.druid.server.security.Action; +import org.apache.druid.server.security.AuthConfig; import org.apache.druid.server.security.AuthorizerMapper; import javax.servlet.http.HttpServletRequest; @@ -37,13 +38,13 @@ public abstract class AbstractResourceFilter implements ResourceFilter, Containe private HttpServletRequest req; private AuthorizerMapper authorizerMapper; + private AuthConfig authConfig; @Inject - public AbstractResourceFilter( - AuthorizerMapper authorizerMapper - ) + public AbstractResourceFilter(AuthorizerMapper authorizerMapper, AuthConfig authConfig) { this.authorizerMapper = authorizerMapper; + this.authConfig = authConfig; } @Override @@ -73,6 +74,16 @@ public void setAuthorizerMapper(AuthorizerMapper authorizerMapper) this.authorizerMapper = authorizerMapper; } + public AuthConfig getAuthConfig() + { + return authConfig; + } + + public void setAuthConfig(AuthConfig authConfig) + { + this.authConfig = authConfig; + } + public AbstractResourceFilter setReq(HttpServletRequest req) { this.req = req; diff --git a/server/src/main/java/org/apache/druid/server/http/security/ConfigResourceFilter.java b/server/src/main/java/org/apache/druid/server/http/security/ConfigResourceFilter.java index 7a45ca1d5bbb..d845a235bdbc 100644 --- a/server/src/main/java/org/apache/druid/server/http/security/ConfigResourceFilter.java +++ b/server/src/main/java/org/apache/druid/server/http/security/ConfigResourceFilter.java @@ -22,6 +22,7 @@ import com.google.inject.Inject; import com.sun.jersey.spi.container.ContainerRequest; import org.apache.druid.server.security.Access; +import org.apache.druid.server.security.AuthConfig; import org.apache.druid.server.security.AuthorizationUtils; import org.apache.druid.server.security.AuthorizerMapper; import org.apache.druid.server.security.ForbiddenException; @@ -37,35 +38,37 @@ * - druid/coordinator/v1/config * Note - Currently the resource name for all end points is set to "CONFIG" however if more fine grained access control * is required the resource name can be set to specific config properties. + * + * @deprecated Used only with {@link org.apache.druid.server.security.AuthConfig#AUTH_VERSION_1} */ +@Deprecated public class ConfigResourceFilter extends AbstractResourceFilter { @Inject - public ConfigResourceFilter( - AuthorizerMapper authorizerMapper - ) + public ConfigResourceFilter(AuthorizerMapper authorizerMapper, AuthConfig authConfig) { - super(authorizerMapper); + super(authorizerMapper, authConfig); } @Override public ContainerRequest filter(ContainerRequest request) { - final ResourceAction resourceAction = new ResourceAction( - new Resource("CONFIG", ResourceType.CONFIG), - getAction(request) - ); + if (getAuthConfig().getAuthVersion().equals(AuthConfig.AUTH_VERSION_1)) { + final ResourceAction resourceAction = new ResourceAction( + new Resource("CONFIG", ResourceType.CONFIG), + getAction(request) + ); - final Access authResult = AuthorizationUtils.authorizeResourceAction( - getReq(), - resourceAction, - getAuthorizerMapper() - ); + final Access authResult = AuthorizationUtils.authorizeResourceAction( + getReq(), + resourceAction, + getAuthorizerMapper() + ); - if (!authResult.isAllowed()) { - throw new ForbiddenException(authResult.toString()); + if (!authResult.isAllowed()) { + throw new ForbiddenException(authResult.toString()); + } } - return request; } } diff --git a/server/src/main/java/org/apache/druid/server/http/security/DatasourceResourceFilter.java b/server/src/main/java/org/apache/druid/server/http/security/DatasourceResourceFilter.java index e32446cfe5cb..dad25d93bf3c 100644 --- a/server/src/main/java/org/apache/druid/server/http/security/DatasourceResourceFilter.java +++ b/server/src/main/java/org/apache/druid/server/http/security/DatasourceResourceFilter.java @@ -25,6 +25,7 @@ import com.google.inject.Inject; import com.sun.jersey.spi.container.ContainerRequest; import org.apache.druid.server.security.Access; +import org.apache.druid.server.security.AuthConfig; import org.apache.druid.server.security.AuthorizationUtils; import org.apache.druid.server.security.AuthorizerMapper; import org.apache.druid.server.security.ForbiddenException; @@ -45,10 +46,11 @@ public class DatasourceResourceFilter extends AbstractResourceFilter { @Inject public DatasourceResourceFilter( - AuthorizerMapper authorizerMapper + AuthorizerMapper authorizerMapper, + AuthConfig authConfig ) { - super(authorizerMapper); + super(authorizerMapper, authConfig); } @Override diff --git a/server/src/main/java/org/apache/druid/server/http/security/InternalInternalResourceFilter.java b/server/src/main/java/org/apache/druid/server/http/security/InternalInternalResourceFilter.java new file mode 100644 index 000000000000..6d1ed1574e99 --- /dev/null +++ b/server/src/main/java/org/apache/druid/server/http/security/InternalInternalResourceFilter.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.druid.server.http.security; + +import com.google.inject.Inject; +import com.sun.jersey.spi.container.ContainerRequest; +import org.apache.druid.server.security.Access; +import org.apache.druid.server.security.AuthConfig; +import org.apache.druid.server.security.AuthorizationUtils; +import org.apache.druid.server.security.AuthorizerMapper; +import org.apache.druid.server.security.ForbiddenException; +import org.apache.druid.server.security.Resource; +import org.apache.druid.server.security.ResourceAction; + +public class InternalInternalResourceFilter extends AbstractResourceFilter +{ + @Inject + public InternalInternalResourceFilter(AuthorizerMapper authorizerMapper, + AuthConfig authConfig + ) + { + super(authorizerMapper, authConfig); + } + + @Override + public ContainerRequest filter(ContainerRequest request) + { + if (getAuthConfig().getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + final ResourceAction resourceAction = new ResourceAction( + Resource.INTERNAL_INTERNAL_RESOURCE, + getAction(request) + ); + + final Access authResult = AuthorizationUtils.authorizeResourceAction( + getReq(), + resourceAction, + getAuthorizerMapper() + ); + + if (!authResult.isAllowed()) { + throw new ForbiddenException(authResult.toString()); + } + } + return request; + } +} diff --git a/server/src/main/java/org/apache/druid/server/http/security/LookupInternalResourceFilter.java b/server/src/main/java/org/apache/druid/server/http/security/LookupInternalResourceFilter.java new file mode 100644 index 000000000000..d01f2d90d51d --- /dev/null +++ b/server/src/main/java/org/apache/druid/server/http/security/LookupInternalResourceFilter.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.druid.server.http.security; + +import com.google.inject.Inject; +import com.sun.jersey.spi.container.ContainerRequest; +import org.apache.druid.server.security.Access; +import org.apache.druid.server.security.AuthConfig; +import org.apache.druid.server.security.AuthorizationUtils; +import org.apache.druid.server.security.AuthorizerMapper; +import org.apache.druid.server.security.ForbiddenException; +import org.apache.druid.server.security.Resource; +import org.apache.druid.server.security.ResourceAction; + +public class LookupInternalResourceFilter extends AbstractResourceFilter +{ + @Inject + public LookupInternalResourceFilter(AuthorizerMapper authorizerMapper, + AuthConfig authConfig + ) + { + super(authorizerMapper, authConfig); + } + + @Override + public ContainerRequest filter(ContainerRequest request) + { + if (getAuthConfig().getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + final ResourceAction resourceAction = new ResourceAction( + Resource.LOOKUP_INTERNAL_RESOURCE, + getAction(request) + ); + + final Access authResult = AuthorizationUtils.authorizeResourceAction( + getReq(), + resourceAction, + getAuthorizerMapper() + ); + + if (!authResult.isAllowed()) { + throw new ForbiddenException(authResult.toString()); + } + } + return request; + } +} diff --git a/server/src/main/java/org/apache/druid/server/http/security/LookupStatusResourceFilter.java b/server/src/main/java/org/apache/druid/server/http/security/LookupStatusResourceFilter.java new file mode 100644 index 000000000000..037d1b66768b --- /dev/null +++ b/server/src/main/java/org/apache/druid/server/http/security/LookupStatusResourceFilter.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.druid.server.http.security; + +import com.google.inject.Inject; +import com.sun.jersey.spi.container.ContainerRequest; +import org.apache.druid.server.security.Access; +import org.apache.druid.server.security.AuthConfig; +import org.apache.druid.server.security.AuthorizationUtils; +import org.apache.druid.server.security.AuthorizerMapper; +import org.apache.druid.server.security.ForbiddenException; +import org.apache.druid.server.security.Resource; +import org.apache.druid.server.security.ResourceAction; + +public class LookupStatusResourceFilter extends AbstractResourceFilter +{ + @Inject + public LookupStatusResourceFilter( + AuthorizerMapper authorizerMapper, + AuthConfig authConfig + ) + { + super(authorizerMapper, authConfig); + } + + @Override + public ContainerRequest filter(ContainerRequest request) + { + if (getAuthConfig().getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + final ResourceAction resourceAction = new ResourceAction( + Resource.LOOKUP_STATUS_RESOURCE, + getAction(request) + ); + + final Access authResult = AuthorizationUtils.authorizeResourceAction( + getReq(), + resourceAction, + getAuthorizerMapper() + ); + + if (!authResult.isAllowed()) { + throw new ForbiddenException(authResult.toString()); + } + } + return request; + } +} diff --git a/server/src/main/java/org/apache/druid/server/http/security/RulesResourceFilter.java b/server/src/main/java/org/apache/druid/server/http/security/RulesResourceFilter.java index f314c77d7431..3458f62de1fb 100644 --- a/server/src/main/java/org/apache/druid/server/http/security/RulesResourceFilter.java +++ b/server/src/main/java/org/apache/druid/server/http/security/RulesResourceFilter.java @@ -25,6 +25,7 @@ import com.google.inject.Inject; import com.sun.jersey.spi.container.ContainerRequest; import org.apache.druid.server.security.Access; +import org.apache.druid.server.security.AuthConfig; import org.apache.druid.server.security.AuthorizationUtils; import org.apache.druid.server.security.AuthorizerMapper; import org.apache.druid.server.security.ForbiddenException; @@ -45,10 +46,11 @@ public class RulesResourceFilter extends AbstractResourceFilter { @Inject public RulesResourceFilter( - AuthorizerMapper authorizerMapper + AuthorizerMapper authorizerMapper, + AuthConfig authConfig ) { - super(authorizerMapper); + super(authorizerMapper, authConfig); } @Override diff --git a/server/src/main/java/org/apache/druid/server/http/security/ServerPermissionsResourceFilter.java b/server/src/main/java/org/apache/druid/server/http/security/ServerPermissionsResourceFilter.java new file mode 100644 index 000000000000..6026069a4bd9 --- /dev/null +++ b/server/src/main/java/org/apache/druid/server/http/security/ServerPermissionsResourceFilter.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.druid.server.http.security; + +import com.google.inject.Inject; +import com.sun.jersey.spi.container.ContainerRequest; +import org.apache.druid.server.security.Access; +import org.apache.druid.server.security.AuthConfig; +import org.apache.druid.server.security.AuthorizationUtils; +import org.apache.druid.server.security.AuthorizerMapper; +import org.apache.druid.server.security.ForbiddenException; +import org.apache.druid.server.security.Resource; +import org.apache.druid.server.security.ResourceAction; + +public class ServerPermissionsResourceFilter extends AbstractResourceFilter +{ + @Inject + public ServerPermissionsResourceFilter(AuthorizerMapper authorizerMapper, + AuthConfig authConfig + ) + { + super(authorizerMapper, authConfig); + } + + @Override + public ContainerRequest filter(ContainerRequest request) + { + if (getAuthConfig().getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + final ResourceAction resourceAction = new ResourceAction( + Resource.SERVER_PERMISSIONS_RESOURCE, + getAction(request) + ); + + final Access authResult = AuthorizationUtils.authorizeResourceAction( + getReq(), + resourceAction, + getAuthorizerMapper() + ); + + if (!authResult.isAllowed()) { + throw new ForbiddenException(authResult.toString()); + } + } + return request; + } +} diff --git a/server/src/main/java/org/apache/druid/server/http/security/ServerServerResourceFilter.java b/server/src/main/java/org/apache/druid/server/http/security/ServerServerResourceFilter.java new file mode 100644 index 000000000000..ee8132d1e509 --- /dev/null +++ b/server/src/main/java/org/apache/druid/server/http/security/ServerServerResourceFilter.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.druid.server.http.security; + +import com.google.inject.Inject; +import com.sun.jersey.spi.container.ContainerRequest; +import org.apache.druid.server.security.Access; +import org.apache.druid.server.security.AuthConfig; +import org.apache.druid.server.security.AuthorizationUtils; +import org.apache.druid.server.security.AuthorizerMapper; +import org.apache.druid.server.security.ForbiddenException; +import org.apache.druid.server.security.Resource; +import org.apache.druid.server.security.ResourceAction; + +public class ServerServerResourceFilter extends AbstractResourceFilter +{ + @Inject + public ServerServerResourceFilter(AuthorizerMapper authorizerMapper, + AuthConfig authConfig + ) + { + super(authorizerMapper, authConfig); + } + + @Override + public ContainerRequest filter(ContainerRequest request) + { + if (getAuthConfig().getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + final ResourceAction resourceAction = new ResourceAction( + Resource.SERVER_SERVER_RESOURCE, + getAction(request) + ); + + final Access authResult = AuthorizationUtils.authorizeResourceAction( + getReq(), + resourceAction, + getAuthorizerMapper() + ); + + if (!authResult.isAllowed()) { + throw new ForbiddenException(authResult.toString()); + } + } + return request; + } +} diff --git a/server/src/main/java/org/apache/druid/server/http/security/ServerStatusResourceFilter.java b/server/src/main/java/org/apache/druid/server/http/security/ServerStatusResourceFilter.java new file mode 100644 index 000000000000..f5e02712e3b0 --- /dev/null +++ b/server/src/main/java/org/apache/druid/server/http/security/ServerStatusResourceFilter.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.druid.server.http.security; + +import com.google.inject.Inject; +import com.sun.jersey.spi.container.ContainerRequest; +import org.apache.druid.server.security.Access; +import org.apache.druid.server.security.AuthConfig; +import org.apache.druid.server.security.AuthorizationUtils; +import org.apache.druid.server.security.AuthorizerMapper; +import org.apache.druid.server.security.ForbiddenException; +import org.apache.druid.server.security.Resource; +import org.apache.druid.server.security.ResourceAction; + +public class ServerStatusResourceFilter extends AbstractResourceFilter +{ + @Inject + public ServerStatusResourceFilter(AuthorizerMapper authorizerMapper, + AuthConfig authConfig + ) + { + super(authorizerMapper, authConfig); + } + + @Override + public ContainerRequest filter(ContainerRequest request) + { + if (getAuthConfig().getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + final ResourceAction resourceAction = new ResourceAction( + Resource.SERVER_STATUS_RESOURCE, + getAction(request) + ); + + final Access authResult = AuthorizationUtils.authorizeResourceAction( + getReq(), + resourceAction, + getAuthorizerMapper() + ); + + if (!authResult.isAllowed()) { + throw new ForbiddenException(authResult.toString()); + } + } + return request; + } +} diff --git a/server/src/main/java/org/apache/druid/server/http/security/StateResourceFilter.java b/server/src/main/java/org/apache/druid/server/http/security/StateResourceFilter.java index 3a2d0e3bf83c..b9e8e58ef1de 100644 --- a/server/src/main/java/org/apache/druid/server/http/security/StateResourceFilter.java +++ b/server/src/main/java/org/apache/druid/server/http/security/StateResourceFilter.java @@ -22,6 +22,7 @@ import com.google.inject.Inject; import com.sun.jersey.spi.container.ContainerRequest; import org.apache.druid.server.security.Access; +import org.apache.druid.server.security.AuthConfig; import org.apache.druid.server.security.AuthorizationUtils; import org.apache.druid.server.security.AuthorizerMapper; import org.apache.druid.server.security.ForbiddenException; @@ -42,33 +43,37 @@ * - status * Note - Currently the resource name for all end points is set to "STATE" however if more fine grained access control * is required the resource name can be set to specific state properties. + * + * @deprecated Used only with {@link AuthConfig#AUTH_VERSION_1} */ +@Deprecated public class StateResourceFilter extends AbstractResourceFilter { @Inject - public StateResourceFilter(AuthorizerMapper authorizerMapper) + public StateResourceFilter(AuthorizerMapper authorizerMapper, AuthConfig authConfig) { - super(authorizerMapper); + super(authorizerMapper, authConfig); } @Override public ContainerRequest filter(ContainerRequest request) { - final ResourceAction resourceAction = new ResourceAction( - Resource.STATE_RESOURCE, - getAction(request) - ); + if (getAuthConfig().getAuthVersion().equals(AuthConfig.AUTH_VERSION_1)) { + final ResourceAction resourceAction = new ResourceAction( + Resource.STATE_RESOURCE, + getAction(request) + ); - final Access authResult = AuthorizationUtils.authorizeResourceAction( - getReq(), - resourceAction, - getAuthorizerMapper() - ); + final Access authResult = AuthorizationUtils.authorizeResourceAction( + getReq(), + resourceAction, + getAuthorizerMapper() + ); - if (!authResult.isAllowed()) { - throw new ForbiddenException(authResult.toString()); + if (!authResult.isAllowed()) { + throw new ForbiddenException(authResult.toString()); + } } - return request; } } diff --git a/server/src/main/java/org/apache/druid/server/lookup/cache/LookupCoordinatorManager.java b/server/src/main/java/org/apache/druid/server/lookup/cache/LookupCoordinatorManager.java index 7526ecbcc10f..c8f8b7a1fb76 100644 --- a/server/src/main/java/org/apache/druid/server/lookup/cache/LookupCoordinatorManager.java +++ b/server/src/main/java/org/apache/druid/server/lookup/cache/LookupCoordinatorManager.java @@ -361,6 +361,29 @@ public LookupExtractorFactoryMapContainer getLookup(final String tier, final Str return tierLookups.get(lookupName); } + /** + * Try to get all lookups for the specified tier. + * + * @param tier tier + * + * @return The lookups for a tier if found or null if not found or if no lookups at all are found + */ + @Nullable + public Map getTierLookups(final String tier) + { + final Map> prior = getKnownLookups(); + if (prior == null) { + LOG.warn("Requested tier [%s]. But no lookups exist!", tier); + return null; + } + final Map tierLookups = prior.get(tier); + if (tierLookups == null) { + LOG.warn("Tier [%s] does not exist", tier); + return null; + } + return tierLookups; + } + public boolean isStarted() { return lifecycleLock.isStarted(); diff --git a/server/src/main/java/org/apache/druid/server/security/AllowAllAuthorizer.java b/server/src/main/java/org/apache/druid/server/security/AllowAllAuthorizer.java index 6271228b33de..b04a6367455b 100644 --- a/server/src/main/java/org/apache/druid/server/security/AllowAllAuthorizer.java +++ b/server/src/main/java/org/apache/druid/server/security/AllowAllAuthorizer.java @@ -26,4 +26,10 @@ public Access authorize(AuthenticationResult authenticationResult, Resource reso { return Access.OK; } + + @Override + public Access authorizeV2(AuthenticationResult authenticationResult, Resource resource, Action action) + { + return Access.OK; + } } diff --git a/server/src/main/java/org/apache/druid/server/security/AuthConfig.java b/server/src/main/java/org/apache/druid/server/security/AuthConfig.java index f588101a5b84..143ceb4e70cd 100644 --- a/server/src/main/java/org/apache/druid/server/security/AuthConfig.java +++ b/server/src/main/java/org/apache/druid/server/security/AuthConfig.java @@ -21,6 +21,8 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.base.Preconditions; +import org.apache.druid.java.util.common.StringUtils; import java.util.Collections; import java.util.List; @@ -28,6 +30,8 @@ public class AuthConfig { + public static final String AUTH_VERSION_1 = "v1"; + public static final String AUTH_VERSION_2 = "v2"; /** * HTTP attribute that holds an AuthenticationResult, with info about a successful authentication check. */ @@ -48,7 +52,7 @@ public class AuthConfig public AuthConfig() { - this(null, null, null, false); + this(null, null, null, false, AUTH_VERSION_1); } @JsonCreator @@ -56,13 +60,17 @@ public AuthConfig( @JsonProperty("authenticatorChain") List authenticatorChain, @JsonProperty("authorizers") List authorizers, @JsonProperty("unsecuredPaths") List unsecuredPaths, - @JsonProperty("allowUnauthenticatedHttpOptions") boolean allowUnauthenticatedHttpOptions + @JsonProperty("allowUnauthenticatedHttpOptions") boolean allowUnauthenticatedHttpOptions, + @JsonProperty("authVersion") String authVersion ) { this.authenticatorChain = authenticatorChain; this.authorizers = authorizers; this.unsecuredPaths = unsecuredPaths == null ? Collections.emptyList() : unsecuredPaths; this.allowUnauthenticatedHttpOptions = allowUnauthenticatedHttpOptions; + Preconditions.checkArgument(authVersion == null || (authVersion.equalsIgnoreCase(AUTH_VERSION_1) || authVersion + .equalsIgnoreCase(AUTH_VERSION_2)), "Unknown auth version [%s]", authVersion); + this.authVersion = authVersion == null ? AUTH_VERSION_1 : StringUtils.toLowerCase(authVersion); } @JsonProperty @@ -77,6 +85,9 @@ public AuthConfig( @JsonProperty private final boolean allowUnauthenticatedHttpOptions; + @JsonProperty + private final String authVersion; + public List getAuthenticatorChain() { return authenticatorChain; @@ -97,6 +108,11 @@ public boolean isAllowUnauthenticatedHttpOptions() return allowUnauthenticatedHttpOptions; } + public String getAuthVersion() + { + return authVersion; + } + @Override public boolean equals(Object o) { @@ -108,9 +124,10 @@ public boolean equals(Object o) } AuthConfig that = (AuthConfig) o; return isAllowUnauthenticatedHttpOptions() == that.isAllowUnauthenticatedHttpOptions() && - Objects.equals(getAuthenticatorChain(), that.getAuthenticatorChain()) && - Objects.equals(getAuthorizers(), that.getAuthorizers()) && - Objects.equals(getUnsecuredPaths(), that.getUnsecuredPaths()); + Objects.equals(getAuthenticatorChain(), that.getAuthenticatorChain()) && + Objects.equals(getAuthorizers(), that.getAuthorizers()) && + Objects.equals(getUnsecuredPaths(), that.getUnsecuredPaths()) && + Objects.equals(getAuthVersion(), that.getAuthVersion()); } @Override @@ -120,7 +137,8 @@ public int hashCode() getAuthenticatorChain(), getAuthorizers(), getUnsecuredPaths(), - isAllowUnauthenticatedHttpOptions() + isAllowUnauthenticatedHttpOptions(), + getAuthVersion() ); } @@ -132,6 +150,7 @@ public String toString() ", authorizers=" + authorizers + ", unsecuredPaths=" + unsecuredPaths + ", allowUnauthenticatedHttpOptions=" + allowUnauthenticatedHttpOptions + + ", authVersion=" + authVersion + '}'; } } diff --git a/server/src/main/java/org/apache/druid/server/security/Authorizer.java b/server/src/main/java/org/apache/druid/server/security/Authorizer.java index 8929c9045111..f7e3a95a7094 100644 --- a/server/src/main/java/org/apache/druid/server/security/Authorizer.java +++ b/server/src/main/java/org/apache/druid/server/security/Authorizer.java @@ -21,6 +21,9 @@ import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import org.apache.druid.java.util.common.IAE; +import org.apache.druid.java.util.common.ISE; +import org.apache.druid.java.util.common.StringUtils; @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") @JsonSubTypes(value = { @@ -40,13 +43,48 @@ public interface Authorizer { /** - * Check if the entity represented by {@code identity} is authorized to perform {@code action} on {@code resource}. + * Method to decide whether to use v1 or v2 model for authorization * - * @param authenticationResult The authentication result of the request - * @param resource The resource to be accessed - * @param action The action to perform on the resource + * @param authenticationResult The authentication result of the request + * @param resource The resource to be accessed + * @param action The action to perform on the resource + * @param authVersion Auth version to use + * @return An Access object representing the result of the authorization check. Must not be null. + */ + default Access authorize(AuthenticationResult authenticationResult, Resource resource, Action action, String authVersion) + { + switch (StringUtils.toLowerCase(authVersion)) { + case AuthConfig.AUTH_VERSION_1: + return authorize(authenticationResult, resource, action); + case AuthConfig.AUTH_VERSION_2: + return authorizeV2(authenticationResult, resource, action); + default: + throw new IAE("No such auth version [%s]", authVersion); + } + } + + /** + * Check if the entity represented by {@code identity} is authorized to perform {@code action} on {@code resource}. * + * @param authenticationResult The authentication result of the request + * @param resource The resource to be accessed + * @param action The action to perform on the resource * @return An Access object representing the result of the authorization check. Must not be null. */ Access authorize(AuthenticationResult authenticationResult, Resource resource, Action action); + + /** + * Check if the entity represented by {@code identity} is authorized to perform {@code action} on {@code resource}. + * This method will use Auth V2 model for resource name and types. + * + * @param authenticationResult The authentication result of the request + * @param resource The resource to be accessed + * @param action The action to perform on the resource + * @return An Access object representing the result of the authorization check. Must not be null. + */ + default Access authorizeV2(AuthenticationResult authenticationResult, Resource resource, Action action) + { + throw new ISE("Authorizer does not support V2 implementation, please implement it"); + } + } diff --git a/server/src/main/java/org/apache/druid/server/security/Resource.java b/server/src/main/java/org/apache/druid/server/security/Resource.java index 6770bdaf5cbc..a8d71186960a 100644 --- a/server/src/main/java/org/apache/druid/server/security/Resource.java +++ b/server/src/main/java/org/apache/druid/server/security/Resource.java @@ -26,6 +26,13 @@ public class Resource { public static final Resource STATE_RESOURCE = new Resource("STATE", ResourceType.STATE); + public static final Resource INTERNAL_INTERNAL_RESOURCE = new Resource("INTERNAL", ResourceType.INTERNAL); + public static final Resource SERVER_SERVER_RESOURCE = new Resource("SERVER", ResourceType.SERVER); + public static final Resource SERVER_PERMISSIONS_RESOURCE = new Resource("PERMISSIONS", ResourceType.SERVER); + public static final Resource SERVER_STATUS_RESOURCE = new Resource("STATUS", ResourceType.SERVER); + public static final Resource LOOKUP_INTERNAL_RESOURCE = new Resource("INTERNAL", ResourceType.LOOKUP); + public static final Resource LOOKUP_STATUS_RESOURCE = new Resource("STATUS", ResourceType.LOOKUP); + private final String name; private final ResourceType type; diff --git a/server/src/main/java/org/apache/druid/server/security/ResourceName.java b/server/src/main/java/org/apache/druid/server/security/ResourceName.java new file mode 100644 index 000000000000..e43206e24670 --- /dev/null +++ b/server/src/main/java/org/apache/druid/server/security/ResourceName.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.druid.server.security; + +import com.fasterxml.jackson.annotation.JsonCreator; +import org.apache.druid.java.util.common.StringUtils; + +import javax.validation.constraints.NotNull; +import java.util.Objects; + +public class ResourceName +{ + public static final ResourceName INTERNAL = new ResourceName("INTERNAL"); + public static final ResourceName LOOKUP = new ResourceName("LOOKUP"); + public static final ResourceName SERVER = new ResourceName("SERVER"); + public static final ResourceName STATUS = new ResourceName("STATUS"); + + private final String resourceName; + + public ResourceName(String name) + { + this.resourceName = StringUtils.toUpperCase(name); + } + + @JsonCreator + public static ResourceName fromString(@NotNull String name) + { + return new ResourceName(StringUtils.toUpperCase(name)); + } + + @Override + public String toString() + { + return this.resourceName; + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ResourceName that = (ResourceName) o; + return resourceName.equals(that.resourceName); + } + + @Override + public int hashCode() + { + return Objects.hash(resourceName); + } +} diff --git a/server/src/main/java/org/apache/druid/server/security/ResourceType.java b/server/src/main/java/org/apache/druid/server/security/ResourceType.java index 5debab216158..3304fc5e8d99 100644 --- a/server/src/main/java/org/apache/druid/server/security/ResourceType.java +++ b/server/src/main/java/org/apache/druid/server/security/ResourceType.java @@ -25,8 +25,11 @@ public enum ResourceType { DATASOURCE, - CONFIG, - STATE; + INTERNAL, + LOOKUP, + SERVER, + @Deprecated CONFIG, + @Deprecated STATE; @JsonCreator public static ResourceType fromString(String name) diff --git a/server/src/test/java/org/apache/druid/query/lookup/LookupIntrospectionResourceTest.java b/server/src/test/java/org/apache/druid/query/lookup/LookupIntrospectionResourceTest.java index dd8c84a96dcd..817e68d0cbbd 100644 --- a/server/src/test/java/org/apache/druid/query/lookup/LookupIntrospectionResourceTest.java +++ b/server/src/test/java/org/apache/druid/query/lookup/LookupIntrospectionResourceTest.java @@ -26,6 +26,9 @@ import com.sun.jersey.api.client.config.DefaultClientConfig; import org.apache.druid.query.extraction.MapLookupExtractor; import org.apache.druid.server.WebserverTestUtils; +import org.apache.druid.server.security.AllowAllAuthorizer; +import org.apache.druid.server.security.AuthConfig; +import org.apache.druid.server.security.AuthorizerMapper; import org.easymock.EasyMock; import org.glassfish.grizzly.http.server.HttpServer; import org.junit.After; @@ -33,6 +36,7 @@ import org.junit.Before; import org.junit.Test; +import javax.servlet.http.HttpServletRequest; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.net.URI; @@ -49,10 +53,14 @@ public class LookupIntrospectionResourceTest EasyMock.createMock(LookupIntrospectHandler.class); private LookupIntrospectionResource lookupIntrospectionResource = - new LookupIntrospectionResource(mockLookupExtractorFactoryContainerProvider); + new LookupIntrospectionResource(mockLookupExtractorFactoryContainerProvider, new AuthConfig(), + new AuthorizerMapper( + ImmutableMap.of(AuthConfig.ALLOW_ALL_NAME, new AllowAllAuthorizer())) + ); private URI baseUri; private HttpServer server; + private HttpServletRequest request; @Before public void setup() throws Exception @@ -98,6 +106,7 @@ public void setup() throws Exception } ); server.start(); + request = EasyMock.createMock(HttpServletRequest.class); } @After @@ -118,7 +127,7 @@ public void testNotImplementedIntrospectLookup() EasyMock.replay(mockLookupExtractorFactory); Assert.assertEquals( Response.status(Response.Status.NOT_FOUND).build().getStatus(), - ((Response) lookupIntrospectionResource.introspectLookup("lookupId")).getStatus() + ((Response) lookupIntrospectionResource.introspectLookup("lookupId", request)).getStatus() ); } @@ -127,7 +136,7 @@ public void testNotExistingLookup() { Assert.assertEquals( Response.status(Response.Status.NOT_FOUND).build().getStatus(), - ((Response) lookupIntrospectionResource.introspectLookup("not there")).getStatus() + ((Response) lookupIntrospectionResource.introspectLookup("not there", request)).getStatus() ); } @@ -139,7 +148,7 @@ public void testExistingLookup() .andReturn(new MapLookupExtractor(ImmutableMap.of(), false)) .anyTimes(); EasyMock.replay(mockLookupExtractorFactory); - Assert.assertEquals(mockLookupIntrospectHandler, lookupIntrospectionResource.introspectLookup("lookupId")); + Assert.assertEquals(mockLookupIntrospectHandler, lookupIntrospectionResource.introspectLookup("lookupId", request)); } @Test diff --git a/server/src/test/java/org/apache/druid/server/http/LookupCoordinatorResourceTest.java b/server/src/test/java/org/apache/druid/server/http/LookupCoordinatorResourceTest.java index 365eac6888ed..551f89e2b0fd 100644 --- a/server/src/test/java/org/apache/druid/server/http/LookupCoordinatorResourceTest.java +++ b/server/src/test/java/org/apache/druid/server/http/LookupCoordinatorResourceTest.java @@ -31,6 +31,7 @@ import org.apache.druid.query.lookup.LookupsState; import org.apache.druid.server.lookup.cache.LookupCoordinatorManager; import org.apache.druid.server.lookup.cache.LookupExtractorFactoryMapContainer; +import org.apache.druid.server.security.AuthConfig; import org.easymock.Capture; import org.easymock.EasyMock; import org.junit.Assert; @@ -101,9 +102,11 @@ public void testSimpleGet() final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); - final Response response = lookupCoordinatorResource.getTiers(false); + final Response response = lookupCoordinatorResource.getTiers(false, null); Assert.assertEquals(200, response.getStatus()); Assert.assertEquals(retVal.keySet(), response.getEntity()); EasyMock.verify(lookupCoordinatorManager); @@ -119,9 +122,11 @@ public void testMissingGet() final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); - final Response response = lookupCoordinatorResource.getTiers(false); + final Response response = lookupCoordinatorResource.getTiers(false, null); Assert.assertEquals(404, response.getStatus()); EasyMock.verify(lookupCoordinatorManager); } @@ -137,9 +142,11 @@ public void testExceptionalGet() final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); - final Response response = lookupCoordinatorResource.getTiers(false); + final Response response = lookupCoordinatorResource.getTiers(false, null); Assert.assertEquals(500, response.getStatus()); Assert.assertEquals(ImmutableMap.of("error", errMsg), response.getEntity()); EasyMock.verify(lookupCoordinatorManager); @@ -157,9 +164,11 @@ public void testDiscoveryGet() final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); - final Response response = lookupCoordinatorResource.getTiers(true); + final Response response = lookupCoordinatorResource.getTiers(true, null); Assert.assertEquals(200, response.getStatus()); Assert.assertEquals(ImmutableSet.of("lookupTier", "discoveredLookupTier"), response.getEntity()); @@ -179,9 +188,11 @@ public void testDiscoveryExceptionalGet() final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); - final Response response = lookupCoordinatorResource.getTiers(true); + final Response response = lookupCoordinatorResource.getTiers(true, null); Assert.assertEquals(500, response.getStatus()); Assert.assertEquals(ImmutableMap.of("error", errMsg), response.getEntity()); EasyMock.verify(lookupCoordinatorManager); @@ -203,9 +214,11 @@ public void testSimpleGetLookup() final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); - final Response response = lookupCoordinatorResource.getSpecificLookup(LOOKUP_TIER, LOOKUP_NAME); + final Response response = lookupCoordinatorResource.getSpecificLookup(LOOKUP_TIER, LOOKUP_NAME, null); Assert.assertEquals(200, response.getStatus()); Assert.assertEquals(container, response.getEntity()); EasyMock.verify(lookupCoordinatorManager); @@ -220,9 +233,11 @@ public void testDetailedGetLookup() final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); - final Response response = lookupCoordinatorResource.getSpecificTier(LOOKUP_TIER, true); + final Response response = lookupCoordinatorResource.getSpecificTier(LOOKUP_TIER, true, null); Assert.assertEquals(200, response.getStatus()); Assert.assertEquals(SINGLE_TIER_MAP.get(LOOKUP_TIER), response.getEntity()); EasyMock.verify(lookupCoordinatorManager); @@ -240,9 +255,11 @@ public void testMissingGetLookup() final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); - final Response response = lookupCoordinatorResource.getSpecificLookup(LOOKUP_TIER, LOOKUP_NAME); + final Response response = lookupCoordinatorResource.getSpecificLookup(LOOKUP_TIER, LOOKUP_NAME, null); Assert.assertEquals(404, response.getStatus()); EasyMock.verify(lookupCoordinatorManager); } @@ -256,12 +273,14 @@ public void testInvalidGetLookup() final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); - Assert.assertEquals(400, lookupCoordinatorResource.getSpecificLookup("foo", null).getStatus()); - Assert.assertEquals(400, lookupCoordinatorResource.getSpecificLookup("foo", "").getStatus()); - Assert.assertEquals(400, lookupCoordinatorResource.getSpecificLookup("", "foo").getStatus()); - Assert.assertEquals(400, lookupCoordinatorResource.getSpecificLookup(null, "foo").getStatus()); + Assert.assertEquals(400, lookupCoordinatorResource.getSpecificLookup("foo", null, null).getStatus()); + Assert.assertEquals(400, lookupCoordinatorResource.getSpecificLookup("foo", "", null).getStatus()); + Assert.assertEquals(400, lookupCoordinatorResource.getSpecificLookup("", "foo", null).getStatus()); + Assert.assertEquals(400, lookupCoordinatorResource.getSpecificLookup(null, "foo", null).getStatus()); EasyMock.verify(lookupCoordinatorManager); } @@ -278,9 +297,11 @@ public void testExceptionalGetLookup() final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); - final Response response = lookupCoordinatorResource.getSpecificLookup(LOOKUP_TIER, LOOKUP_NAME); + final Response response = lookupCoordinatorResource.getSpecificLookup(LOOKUP_TIER, LOOKUP_NAME, null); Assert.assertEquals(500, response.getStatus()); Assert.assertEquals(ImmutableMap.of("error", errMsg), response.getEntity()); EasyMock.verify(lookupCoordinatorManager); @@ -309,7 +330,9 @@ public void testSimpleDeleteTier() final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); final Response response = lookupCoordinatorResource.deleteTier( LOOKUP_TIER, @@ -352,7 +375,9 @@ public void testSimpleDelete() final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); final Response response = lookupCoordinatorResource.deleteLookup( LOOKUP_TIER, @@ -397,7 +422,9 @@ public void testMissingDelete() final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); final Response response = lookupCoordinatorResource.deleteLookup( LOOKUP_TIER, @@ -443,7 +470,9 @@ public void testExceptionalDelete() final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); final Response response = lookupCoordinatorResource.deleteLookup( LOOKUP_TIER, @@ -473,7 +502,9 @@ public void testInvalidDelete() final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); Assert.assertEquals(400, lookupCoordinatorResource.deleteLookup("foo", null, null, null, null).getStatus()); Assert.assertEquals(400, lookupCoordinatorResource.deleteLookup(null, null, null, null, null).getStatus()); @@ -505,7 +536,9 @@ public void testSimpleNew() throws Exception final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); final Response response = lookupCoordinatorResource.updateAllLookups( SINGLE_TIER_MAP_SOURCE.openStream(), @@ -549,7 +582,9 @@ public void testExceptionalNew() throws Exception final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); final Response response = lookupCoordinatorResource.updateAllLookups( SINGLE_TIER_MAP_SOURCE.openStream(), @@ -593,7 +628,9 @@ public void testFailedNew() throws Exception final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); final Response response = lookupCoordinatorResource.updateAllLookups( SINGLE_TIER_MAP_SOURCE.openStream(), @@ -639,7 +676,9 @@ public void testSimpleNewLookup() throws Exception final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); final Response response = lookupCoordinatorResource.createOrUpdateLookup( LOOKUP_TIER, @@ -687,7 +726,9 @@ public void testDBErrNewLookup() throws Exception final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); final Response response = lookupCoordinatorResource.createOrUpdateLookup( LOOKUP_TIER, @@ -736,7 +777,9 @@ public void testExceptionalNewLookup() throws Exception final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); final Response response = lookupCoordinatorResource.createOrUpdateLookup( LOOKUP_TIER, @@ -774,7 +817,9 @@ public void testNullValsNewLookup() throws Exception final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); EasyMock.replay(lookupCoordinatorManager, request); @@ -825,9 +870,11 @@ public void testSimpleGetTier() final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); - final Response response = lookupCoordinatorResource.getSpecificTier(LOOKUP_TIER, false); + final Response response = lookupCoordinatorResource.getSpecificTier(LOOKUP_TIER, false, null); Assert.assertEquals(200, response.getStatus()); Assert.assertEquals(SINGLE_TIER_MAP.get(LOOKUP_TIER).keySet(), response.getEntity()); EasyMock.verify(lookupCoordinatorManager); @@ -845,9 +892,11 @@ public void testMissingGetTier() final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); - final Response response = lookupCoordinatorResource.getSpecificTier(tier, false); + final Response response = lookupCoordinatorResource.getSpecificTier(tier, false, null); Assert.assertEquals(404, response.getStatus()); EasyMock.verify(lookupCoordinatorManager); } @@ -861,9 +910,11 @@ public void testNullGetTier() final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); - final Response response = lookupCoordinatorResource.getSpecificTier(tier, false); + final Response response = lookupCoordinatorResource.getSpecificTier(tier, false, null); Assert.assertEquals(400, response.getStatus()); Assert.assertEquals(ImmutableMap.of("error", "`tier` required"), response.getEntity()); EasyMock.verify(lookupCoordinatorManager); @@ -879,9 +930,11 @@ public void testNullLookupsGetTier() final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); - final Response response = lookupCoordinatorResource.getSpecificTier(tier, false); + final Response response = lookupCoordinatorResource.getSpecificTier(tier, false, null); Assert.assertEquals(404, response.getStatus()); Assert.assertEquals(ImmutableMap.of("error", "No lookups found"), response.getEntity()); EasyMock.verify(lookupCoordinatorManager); @@ -898,9 +951,11 @@ public void testExceptionalGetTier() final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); - final Response response = lookupCoordinatorResource.getSpecificTier(tier, false); + final Response response = lookupCoordinatorResource.getSpecificTier(tier, false, null); Assert.assertEquals(500, response.getStatus()); Assert.assertEquals(ImmutableMap.of("error", errMsg), response.getEntity()); EasyMock.verify(lookupCoordinatorManager); @@ -921,10 +976,12 @@ public void testGetAllLookupsStatus() final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); - final Response response = lookupCoordinatorResource.getAllLookupsStatus(false); + final Response response = lookupCoordinatorResource.getAllLookupsStatus(false, null); Assert.assertEquals(200, response.getStatus()); Assert.assertEquals( ImmutableMap.of( @@ -954,10 +1011,12 @@ public void testGetLookupStatusForTier() final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); - final Response response = lookupCoordinatorResource.getLookupStatusForTier(LOOKUP_TIER, false); + final Response response = lookupCoordinatorResource.getLookupStatusForTier(LOOKUP_TIER, false, null); Assert.assertEquals(200, response.getStatus()); Assert.assertEquals( ImmutableMap.of( @@ -984,10 +1043,12 @@ public void testGetSpecificLookupStatus() final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); - final Response response = lookupCoordinatorResource.getSpecificLookupStatus(LOOKUP_TIER, LOOKUP_NAME, false); + final Response response = lookupCoordinatorResource.getSpecificLookupStatus(LOOKUP_TIER, LOOKUP_NAME, false, null); Assert.assertEquals(200, response.getStatus()); Assert.assertEquals( new LookupCoordinatorResource.LookupStatus(true, null), response.getEntity() @@ -1002,7 +1063,9 @@ public void testGetLookupStatusDetailedTrue() final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( EasyMock.createStrictMock(LookupCoordinatorManager.class), MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); HostAndPort newNode = HostAndPort.fromParts("localhost", 4352); @@ -1024,7 +1087,9 @@ public void testGetLookupStatusDetailedFalse() final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( EasyMock.createStrictMock(LookupCoordinatorManager.class), MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); HostAndPort newNode = HostAndPort.fromParts("localhost", 4352); @@ -1055,7 +1120,9 @@ public void testGetAllNodesStatus() final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); final Response response = lookupCoordinatorResource.getAllNodesStatus(false, null); @@ -1089,7 +1156,9 @@ public void testGetAllNodesStatusDetailedFalse() final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); final Response response = lookupCoordinatorResource.getAllNodesStatus(false, false); @@ -1124,7 +1193,9 @@ public void testGetNodesStatusInTier() final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); final Response response = lookupCoordinatorResource.getNodesStatusInTier(LOOKUP_TIER); @@ -1152,7 +1223,9 @@ public void testGetSpecificNodeStatus() final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); final Response response = lookupCoordinatorResource.getSpecificNodeStatus(LOOKUP_TIER, LOOKUP_NODE); @@ -1198,9 +1271,11 @@ public void testGetAllLookupSpecs() final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); - final Response response = lookupCoordinatorResource.getAllLookupSpecs(); + final Response response = lookupCoordinatorResource.getAllLookupSpecs(null); Assert.assertEquals(Status.OK.getStatusCode(), response.getStatus()); Assert.assertEquals(lookups, response.getEntity()); EasyMock.verify(lookupCoordinatorManager); @@ -1219,9 +1294,11 @@ public void testGetEmptyAllLookupSpecs() final LookupCoordinatorResource lookupCoordinatorResource = new LookupCoordinatorResource( lookupCoordinatorManager, MAPPER, - MAPPER + MAPPER, + null, + new AuthConfig() ); - final Response response = lookupCoordinatorResource.getAllLookupSpecs(); + final Response response = lookupCoordinatorResource.getAllLookupSpecs(null); Assert.assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus()); EasyMock.verify(lookupCoordinatorManager); } diff --git a/server/src/test/java/org/apache/druid/server/http/RulesResourceTest.java b/server/src/test/java/org/apache/druid/server/http/RulesResourceTest.java index 5b52e3d30ee9..5bbfca314c42 100644 --- a/server/src/test/java/org/apache/druid/server/http/RulesResourceTest.java +++ b/server/src/test/java/org/apache/druid/server/http/RulesResourceTest.java @@ -78,7 +78,7 @@ public void testGetDatasourceRuleHistoryWithCount() .once(); EasyMock.replay(auditManager); - RulesResource rulesResource = new RulesResource(databaseRuleManager, auditManager); + RulesResource rulesResource = new RulesResource(databaseRuleManager, auditManager, null, null); Response response = rulesResource.getDatasourceRuleHistory("datasource1", null, 2); List rulesHistory = (List) response.getEntity(); @@ -121,7 +121,7 @@ public void testGetDatasourceRuleHistoryWithInterval() .once(); EasyMock.replay(auditManager); - RulesResource rulesResource = new RulesResource(databaseRuleManager, auditManager); + RulesResource rulesResource = new RulesResource(databaseRuleManager, auditManager, null, null); Response response = rulesResource.getDatasourceRuleHistory("datasource1", interval, null); List rulesHistory = (List) response.getEntity(); @@ -140,7 +140,7 @@ public void testGetDatasourceRuleHistoryWithWrongCount() .once(); EasyMock.replay(auditManager); - RulesResource rulesResource = new RulesResource(databaseRuleManager, auditManager); + RulesResource rulesResource = new RulesResource(databaseRuleManager, auditManager, null, null); Response response = rulesResource.getDatasourceRuleHistory("datasource1", null, -1); Map rulesHistory = (Map) response.getEntity(); @@ -181,7 +181,7 @@ public void testGetAllDatasourcesRuleHistoryWithCount() .once(); EasyMock.replay(auditManager); - RulesResource rulesResource = new RulesResource(databaseRuleManager, auditManager); + RulesResource rulesResource = new RulesResource(databaseRuleManager, auditManager, null, null); Response response = rulesResource.getDatasourceRuleHistory(null, 2); List rulesHistory = (List) response.getEntity(); @@ -224,7 +224,7 @@ public void testGetAllDatasourcesRuleHistoryWithInterval() .once(); EasyMock.replay(auditManager); - RulesResource rulesResource = new RulesResource(databaseRuleManager, auditManager); + RulesResource rulesResource = new RulesResource(databaseRuleManager, auditManager, null, null); Response response = rulesResource.getDatasourceRuleHistory(interval, null); List rulesHistory = (List) response.getEntity(); @@ -243,7 +243,7 @@ public void testGetAllDatasourcesRuleHistoryWithWrongCount() .once(); EasyMock.replay(auditManager); - RulesResource rulesResource = new RulesResource(databaseRuleManager, auditManager); + RulesResource rulesResource = new RulesResource(databaseRuleManager, auditManager, null, null); Response response = rulesResource.getDatasourceRuleHistory(null, -1); Map rulesHistory = (Map) response.getEntity(); diff --git a/server/src/test/java/org/apache/druid/server/http/security/ResourceFilterTestHelper.java b/server/src/test/java/org/apache/druid/server/http/security/ResourceFilterTestHelper.java index 95a0cc29e31a..ffb2409352e6 100644 --- a/server/src/test/java/org/apache/druid/server/http/security/ResourceFilterTestHelper.java +++ b/server/src/test/java/org/apache/druid/server/http/security/ResourceFilterTestHelper.java @@ -23,6 +23,7 @@ import com.google.common.base.Predicate; import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.inject.Binder; import com.google.inject.Guice; @@ -55,9 +56,18 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; public class ResourceFilterTestHelper { + private static Set> whitelistedForTestFilters = ImmutableSet.of( + ConfigResourceFilter.class, + DatasourceResourceFilter.class, + RulesResourceFilter.class, + StateResourceFilter.class + ); + public HttpServletRequest req; public AuthorizerMapper authorizerMapper; public ContainerRequest request; @@ -230,9 +240,10 @@ public Collection apply(final Method method) final List> resourceFilters = method.getAnnotation(ResourceFilters.class) == null ? baseResourceFilters : ImmutableList.copyOf(method.getAnnotation(ResourceFilters.class).value()); - + final List> enabledForTestResourceFilters = getEnabledForTestFilters( + resourceFilters); return Collections2.transform( - resourceFilters, + enabledForTestResourceFilters, new Function, Object[]>() { @Override @@ -271,4 +282,11 @@ private static String httpMethodFromAnnotation(Class i return method.getAnnotation(DELETE.class) != null ? "DELETE" : "POST"; } } + + private static List> getEnabledForTestFilters(List> filters) + { + List> filtered = filters.stream() + .filter(aClass -> whitelistedForTestFilters.contains(aClass)).collect(Collectors.toList()); + return filtered; + } } diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/schema/SystemSchema.java b/sql/src/main/java/org/apache/druid/sql/calcite/schema/SystemSchema.java index 19c6b22d7b98..4efa52c2e551 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/schema/SystemSchema.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/schema/SystemSchema.java @@ -70,6 +70,7 @@ import org.apache.druid.server.DruidNode; import org.apache.druid.server.security.Access; import org.apache.druid.server.security.Action; +import org.apache.druid.server.security.AuthConfig; import org.apache.druid.server.security.AuthenticationResult; import org.apache.druid.server.security.AuthorizationUtils; import org.apache.druid.server.security.AuthorizerMapper; @@ -209,14 +210,15 @@ public SystemSchema( final @Coordinator DruidLeaderClient coordinatorDruidLeaderClient, final @IndexingService DruidLeaderClient overlordDruidLeaderClient, final DruidNodeDiscoveryProvider druidNodeDiscoveryProvider, - final ObjectMapper jsonMapper + final ObjectMapper jsonMapper, + final AuthConfig authConfig ) { Preconditions.checkNotNull(serverView, "serverView"); this.tableMap = ImmutableMap.of( - SEGMENTS_TABLE, new SegmentsTable(druidSchema, metadataView, jsonMapper, authorizerMapper), - SERVERS_TABLE, new ServersTable(druidNodeDiscoveryProvider, serverInventoryView, authorizerMapper), - SERVER_SEGMENTS_TABLE, new ServerSegmentsTable(serverView, authorizerMapper), + SEGMENTS_TABLE, new SegmentsTable(druidSchema, metadataView, jsonMapper, authorizerMapper, authConfig), + SERVERS_TABLE, new ServersTable(druidNodeDiscoveryProvider, serverInventoryView, authorizerMapper, authConfig), + SERVER_SEGMENTS_TABLE, new ServerSegmentsTable(serverView, authorizerMapper, authConfig), TASKS_TABLE, new TasksTable(overlordDruidLeaderClient, jsonMapper, authorizerMapper), SUPERVISOR_TABLE, new SupervisorsTable(overlordDruidLeaderClient, jsonMapper, authorizerMapper) ); @@ -237,18 +239,21 @@ static class SegmentsTable extends AbstractTable implements ScannableTable private final ObjectMapper jsonMapper; private final AuthorizerMapper authorizerMapper; private final MetadataSegmentView metadataView; + private final AuthConfig authConfig; public SegmentsTable( DruidSchema druidSchemna, MetadataSegmentView metadataView, ObjectMapper jsonMapper, - AuthorizerMapper authorizerMapper + AuthorizerMapper authorizerMapper, + AuthConfig authConfig ) { this.druidSchema = druidSchemna; this.metadataView = metadataView; this.jsonMapper = jsonMapper; this.authorizerMapper = authorizerMapper; + this.authConfig = authConfig; } @Override @@ -479,16 +484,19 @@ static class ServersTable extends AbstractTable implements ScannableTable private final AuthorizerMapper authorizerMapper; private final DruidNodeDiscoveryProvider druidNodeDiscoveryProvider; private final InventoryView serverInventoryView; + private final AuthConfig authConfig; public ServersTable( DruidNodeDiscoveryProvider druidNodeDiscoveryProvider, InventoryView serverInventoryView, - AuthorizerMapper authorizerMapper + AuthorizerMapper authorizerMapper, + AuthConfig authConfig ) { this.authorizerMapper = authorizerMapper; this.druidNodeDiscoveryProvider = druidNodeDiscoveryProvider; this.serverInventoryView = serverInventoryView; + this.authConfig = authConfig; } @Override @@ -511,7 +519,7 @@ public Enumerable scan(DataContext root) root.get(PlannerContext.DATA_CTX_AUTHENTICATION_RESULT), "authenticationResult in dataContext" ); - checkStateReadAccessForServers(authenticationResult, authorizerMapper); + checkStateReadAccessForServers(authenticationResult, authorizerMapper, authConfig.getAuthVersion()); final FluentIterable results = FluentIterable .from(() -> druidServers) @@ -636,12 +644,14 @@ private static Iterator getDruidServers(DruidNodeDiscoveryPr static class ServerSegmentsTable extends AbstractTable implements ScannableTable { private final TimelineServerView serverView; - final AuthorizerMapper authorizerMapper; + private final AuthorizerMapper authorizerMapper; + private final AuthConfig authConfig; - public ServerSegmentsTable(TimelineServerView serverView, AuthorizerMapper authorizerMapper) + public ServerSegmentsTable(TimelineServerView serverView, AuthorizerMapper authorizerMapper, AuthConfig authConfig) { this.serverView = serverView; this.authorizerMapper = authorizerMapper; + this.authConfig = authConfig; } @Override @@ -663,7 +673,7 @@ public Enumerable scan(DataContext root) root.get(PlannerContext.DATA_CTX_AUTHENTICATION_RESULT), "authenticationResult in dataContext" ); - checkStateReadAccessForServers(authenticationResult, authorizerMapper); + checkStateReadAccessForServers(authenticationResult, authorizerMapper, authConfig.getAuthVersion()); final List rows = new ArrayList<>(); final List druidServers = serverView.getDruidServers(); @@ -1083,12 +1093,17 @@ private static String toStringOrNull(@Nullable final Object object) */ private static void checkStateReadAccessForServers( AuthenticationResult authenticationResult, - AuthorizerMapper authorizerMapper + AuthorizerMapper authorizerMapper, + String authVersion ) { final Access stateAccess = AuthorizationUtils.authorizeAllResourceActions( authenticationResult, - Collections.singletonList(new ResourceAction(Resource.STATE_RESOURCE, Action.READ)), + Collections.singletonList( + authVersion.equals(AuthConfig.AUTH_VERSION_2) ? new ResourceAction(Resource.SERVER_SERVER_RESOURCE, + Action.READ + ) : new ResourceAction(Resource.STATE_RESOURCE, Action.READ) + ), authorizerMapper ); if (!stateAccess.isAllowed()) { diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/schema/SystemSchemaTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/schema/SystemSchemaTest.java index 46aa3cca6785..3746fd7a01f5 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/schema/SystemSchemaTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/schema/SystemSchemaTest.java @@ -79,6 +79,7 @@ import org.apache.druid.server.coordination.ServerType; import org.apache.druid.server.coordinator.BytesAccumulatingResponseHandler; import org.apache.druid.server.security.Access; +import org.apache.druid.server.security.AuthConfig; import org.apache.druid.server.security.Authorizer; import org.apache.druid.server.security.AuthorizerMapper; import org.apache.druid.server.security.NoopEscalator; @@ -268,7 +269,8 @@ public Authorizer getAuthorizer(String name) client, client, druidNodeDiscoveryProvider, - mapper + mapper, + new AuthConfig() ); } @@ -524,7 +526,9 @@ public void testGetTableMap() @Test public void testSegmentsTable() throws Exception { - final SegmentsTable segmentsTable = new SegmentsTable(druidSchema, metadataView, new ObjectMapper(), authMapper); + final SegmentsTable segmentsTable = new SegmentsTable(druidSchema, metadataView, new ObjectMapper(), authMapper, + new AuthConfig() + ); final Set publishedSegments = new HashSet<>(Arrays.asList( new SegmentWithOvershadowedStatus(publishedCompactedSegment1, true), new SegmentWithOvershadowedStatus(publishedCompactedSegment2, false), diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java b/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java index d8088ae7ed87..a95fa7db16ff 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java @@ -1029,7 +1029,8 @@ NodeRole.COORDINATOR, new FakeDruidNodeDiscovery(ImmutableMap.of(NodeRole.COORDI druidLeaderClient, druidLeaderClient, provider, - getJsonMapper() + getJsonMapper(), + new AuthConfig() ); } From 60504eae854c3d9fe958405839ecb5b0c6e3655d Mon Sep 17 00:00:00 2001 From: Parag Jain Date: Fri, 30 Oct 2020 13:01:29 +0530 Subject: [PATCH 02/15] basic auth v2 implementation --- .../BasicRoleBasedAuthorizer.java | 8 ++ ...BasicAuthorizerMetadataStorageUpdater.java | 78 +++++++++++++++++-- .../BasicRoleBasedAuthorizerTest.java | 4 +- ...cAuthorizerMetadataStorageUpdaterTest.java | 4 +- ...oordinatorBasicAuthorizerResourceTest.java | 4 +- .../http/security/ConfigResourceFilter.java | 2 +- .../druid/server/security/Authorizer.java | 1 + 7 files changed, 91 insertions(+), 10 deletions(-) diff --git a/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/BasicRoleBasedAuthorizer.java b/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/BasicRoleBasedAuthorizer.java index 72821effeb32..67770d8eea56 100644 --- a/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/BasicRoleBasedAuthorizer.java +++ b/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/BasicRoleBasedAuthorizer.java @@ -109,6 +109,14 @@ public Access authorize(AuthenticationResult authenticationResult, Resource reso return new Access(false); } + @Override + public Access authorizeV2(AuthenticationResult authenticationResult, Resource resource, Action action) + { + // its same implementation as with v2 permissions for roles will be changed in the metadata store + // checking logic will remain the same + return this.authorize(authenticationResult, resource, action); + } + private boolean permissionCheck(Resource resource, Action action, BasicAuthorizerPermission permission) { if (action != permission.getResourceAction().getAction()) { diff --git a/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/db/updater/CoordinatorBasicAuthorizerMetadataStorageUpdater.java b/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/db/updater/CoordinatorBasicAuthorizerMetadataStorageUpdater.java index 01f409adf8ce..250f746f629c 100644 --- a/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/db/updater/CoordinatorBasicAuthorizerMetadataStorageUpdater.java +++ b/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/db/updater/CoordinatorBasicAuthorizerMetadataStorageUpdater.java @@ -53,6 +53,7 @@ import org.apache.druid.security.basic.authorization.entity.GroupMappingAndRoleMap; import org.apache.druid.security.basic.authorization.entity.UserAndRoleMap; import org.apache.druid.server.security.Action; +import org.apache.druid.server.security.AuthConfig; import org.apache.druid.server.security.Authorizer; import org.apache.druid.server.security.AuthorizerMapper; import org.apache.druid.server.security.Resource; @@ -86,7 +87,9 @@ public class CoordinatorBasicAuthorizerMetadataStorageUpdater implements BasicAu private static final String GROUP_MAPPINGS = "groupMappings"; private static final String ROLES = "roles"; + @Deprecated public static final List SUPERUSER_PERMISSIONS = makeSuperUserPermissions(); + public static final List SUPERUSER_PERMISSIONS_V2 = makeSuperUserPermissionsV2(); private final AuthorizerMapper authorizerMapper; private final MetadataStorageConnector connector; @@ -94,6 +97,7 @@ public class CoordinatorBasicAuthorizerMetadataStorageUpdater implements BasicAu private final BasicAuthorizerCacheNotifier cacheNotifier; private final BasicAuthCommonCacheConfig commonCacheConfig; private final ObjectMapper objectMapper; + private final AuthConfig authConfig; private final int numRetries = 5; private final Map cachedUserMaps; @@ -114,7 +118,8 @@ public CoordinatorBasicAuthorizerMetadataStorageUpdater( BasicAuthCommonCacheConfig commonCacheConfig, @Smile ObjectMapper objectMapper, BasicAuthorizerCacheNotifier cacheNotifier, - ConfigManager configManager // -V6022: ConfigManager creates the db table we need, set a dependency here + ConfigManager configManager, // -V6022: ConfigManager creates the db table we need, set a dependency here + AuthConfig authConfig ) { this.exec = Execs.scheduledSingleThreaded("CoordinatorBasicAuthorizerMetadataStorageUpdater-Exec--%d"); @@ -128,6 +133,7 @@ public CoordinatorBasicAuthorizerMetadataStorageUpdater( this.cachedGroupMappingMaps = new ConcurrentHashMap<>(); this.cachedRoleMaps = new ConcurrentHashMap<>(); this.authorizerNames = new HashSet<>(); + this.authConfig = authConfig; } @LifecycleStart @@ -175,7 +181,8 @@ public void start() initSuperUsersAndGroupMapping(authorizerName, userMap, roleMap, groupMappingMap, dbConfig.getInitialAdminUser(), dbConfig.getInitialAdminRole(), - dbConfig.getInitialAdminGroupMapping() + dbConfig.getInitialAdminGroupMapping(), + authConfig.getAuthVersion() ); } } @@ -1138,17 +1145,26 @@ private void initSuperUsersAndGroupMapping( Map groupMappingMap, String initialAdminUser, String initialAdminRole, - String initialAdminGroupMapping + String initialAdminGroupMapping, + String authVersion ) { if (!roleMap.containsKey(BasicAuthUtils.ADMIN_NAME)) { createRoleInternal(authorizerName, BasicAuthUtils.ADMIN_NAME); - setPermissionsInternal(authorizerName, BasicAuthUtils.ADMIN_NAME, SUPERUSER_PERMISSIONS); + setPermissionsInternal( + authorizerName, + BasicAuthUtils.ADMIN_NAME, + authVersion.equals(AuthConfig.AUTH_VERSION_2) ? SUPERUSER_PERMISSIONS_V2 : SUPERUSER_PERMISSIONS + ); } if (!roleMap.containsKey(BasicAuthUtils.INTERNAL_USER_NAME)) { createRoleInternal(authorizerName, BasicAuthUtils.INTERNAL_USER_NAME); - setPermissionsInternal(authorizerName, BasicAuthUtils.INTERNAL_USER_NAME, SUPERUSER_PERMISSIONS); + setPermissionsInternal( + authorizerName, + BasicAuthUtils.INTERNAL_USER_NAME, + authVersion.equals(AuthConfig.AUTH_VERSION_2) ? SUPERUSER_PERMISSIONS_V2 : SUPERUSER_PERMISSIONS + ); } if (!userMap.containsKey(BasicAuthUtils.ADMIN_NAME)) { @@ -1165,7 +1181,11 @@ private void initSuperUsersAndGroupMapping( && !(initialAdminRole.equals(BasicAuthUtils.ADMIN_NAME) || initialAdminRole.equals(BasicAuthUtils.INTERNAL_USER_NAME)) && !roleMap.containsKey(initialAdminRole)) { createRoleInternal(authorizerName, initialAdminRole); - setPermissionsInternal(authorizerName, initialAdminRole, SUPERUSER_PERMISSIONS); + setPermissionsInternal( + authorizerName, + initialAdminRole, + authVersion.equals(AuthConfig.AUTH_VERSION_2) ? SUPERUSER_PERMISSIONS_V2 : SUPERUSER_PERMISSIONS + ); } if (initialAdminUser != null @@ -1186,6 +1206,7 @@ private void initSuperUsersAndGroupMapping( } } + @Deprecated private static List makeSuperUserPermissions() { ResourceAction datasourceR = new ResourceAction( @@ -1220,4 +1241,49 @@ private static List makeSuperUserPermissions() return Lists.newArrayList(datasourceR, datasourceW, configR, configW, stateR, stateW); } + + private static List makeSuperUserPermissionsV2() + { + ResourceAction datasourceR = new ResourceAction( + new Resource(".*", ResourceType.DATASOURCE), + Action.READ + ); + + ResourceAction datasourceW = new ResourceAction( + new Resource(".*", ResourceType.DATASOURCE), + Action.WRITE + ); + + ResourceAction internalR = new ResourceAction( + new Resource(".*", ResourceType.INTERNAL), + Action.READ + ); + + ResourceAction internalW = new ResourceAction( + new Resource(".*", ResourceType.INTERNAL), + Action.WRITE + ); + + ResourceAction lookupR = new ResourceAction( + new Resource(".*", ResourceType.LOOKUP), + Action.READ + ); + + ResourceAction lookupW = new ResourceAction( + new Resource(".*", ResourceType.LOOKUP), + Action.WRITE + ); + + ResourceAction serverR = new ResourceAction( + new Resource(".*", ResourceType.SERVER), + Action.READ + ); + + ResourceAction serverW = new ResourceAction( + new Resource(".*", ResourceType.SERVER), + Action.WRITE + ); + + return Lists.newArrayList(datasourceR, datasourceW, internalR, internalW, lookupR, lookupW, serverR, serverW); + } } diff --git a/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/BasicRoleBasedAuthorizerTest.java b/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/BasicRoleBasedAuthorizerTest.java index acdb0ab9dd5b..19172ba4a7f2 100644 --- a/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/BasicRoleBasedAuthorizerTest.java +++ b/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/BasicRoleBasedAuthorizerTest.java @@ -34,6 +34,7 @@ import org.apache.druid.security.basic.authorization.entity.BasicAuthorizerGroupMapping; import org.apache.druid.server.security.Access; import org.apache.druid.server.security.Action; +import org.apache.druid.server.security.AuthConfig; import org.apache.druid.server.security.AuthenticationResult; import org.apache.druid.server.security.AuthorizerMapper; import org.apache.druid.server.security.Resource; @@ -122,7 +123,8 @@ public void setUp() new BasicAuthCommonCacheConfig(null, null, null, null), new ObjectMapper(new SmileFactory()), new NoopBasicAuthorizerCacheNotifier(), - null + null, + new AuthConfig() ); updater.start(); diff --git a/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/CoordinatorBasicAuthorizerMetadataStorageUpdaterTest.java b/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/CoordinatorBasicAuthorizerMetadataStorageUpdaterTest.java index 7a322acce6e0..5cb09106df5d 100644 --- a/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/CoordinatorBasicAuthorizerMetadataStorageUpdaterTest.java +++ b/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/CoordinatorBasicAuthorizerMetadataStorageUpdaterTest.java @@ -36,6 +36,7 @@ import org.apache.druid.security.basic.authorization.entity.BasicAuthorizerRole; import org.apache.druid.security.basic.authorization.entity.BasicAuthorizerUser; import org.apache.druid.server.security.Action; +import org.apache.druid.server.security.AuthConfig; import org.apache.druid.server.security.AuthorizerMapper; import org.apache.druid.server.security.Resource; import org.apache.druid.server.security.ResourceAction; @@ -113,7 +114,8 @@ public void setUp() new BasicAuthCommonCacheConfig(null, null, null, null), objectMapper, new NoopBasicAuthorizerCacheNotifier(), - null + null, + new AuthConfig() ); updater.start(); diff --git a/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/CoordinatorBasicAuthorizerResourceTest.java b/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/CoordinatorBasicAuthorizerResourceTest.java index 746d3339f974..51ea886f7b73 100644 --- a/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/CoordinatorBasicAuthorizerResourceTest.java +++ b/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/CoordinatorBasicAuthorizerResourceTest.java @@ -45,6 +45,7 @@ import org.apache.druid.security.basic.authorization.entity.BasicAuthorizerUserFull; import org.apache.druid.security.basic.authorization.entity.BasicAuthorizerUserFullSimplifiedPermissions; import org.apache.druid.server.security.Action; +import org.apache.druid.server.security.AuthConfig; import org.apache.druid.server.security.AuthValidator; import org.apache.druid.server.security.AuthorizerMapper; import org.apache.druid.server.security.Resource; @@ -145,7 +146,8 @@ public void setUp() new BasicAuthCommonCacheConfig(null, null, null, null), new ObjectMapper(new SmileFactory()), new NoopBasicAuthorizerCacheNotifier(), - null + null, + new AuthConfig() ); resource = new BasicAuthorizerResource( diff --git a/server/src/main/java/org/apache/druid/server/http/security/ConfigResourceFilter.java b/server/src/main/java/org/apache/druid/server/http/security/ConfigResourceFilter.java index d845a235bdbc..10a083950e7a 100644 --- a/server/src/main/java/org/apache/druid/server/http/security/ConfigResourceFilter.java +++ b/server/src/main/java/org/apache/druid/server/http/security/ConfigResourceFilter.java @@ -39,7 +39,7 @@ * Note - Currently the resource name for all end points is set to "CONFIG" however if more fine grained access control * is required the resource name can be set to specific config properties. * - * @deprecated Used only with {@link org.apache.druid.server.security.AuthConfig#AUTH_VERSION_1} + * @deprecated Used only with {@link AuthConfig#AUTH_VERSION_1} */ @Deprecated public class ConfigResourceFilter extends AbstractResourceFilter diff --git a/server/src/main/java/org/apache/druid/server/security/Authorizer.java b/server/src/main/java/org/apache/druid/server/security/Authorizer.java index f7e3a95a7094..9c1e47dd6d2f 100644 --- a/server/src/main/java/org/apache/druid/server/security/Authorizer.java +++ b/server/src/main/java/org/apache/druid/server/security/Authorizer.java @@ -71,6 +71,7 @@ default Access authorize(AuthenticationResult authenticationResult, Resource res * @param action The action to perform on the resource * @return An Access object representing the result of the authorization check. Must not be null. */ + @Deprecated Access authorize(AuthenticationResult authenticationResult, Resource resource, Action action); /** From 8acc45a9ae5c1361b5bac3952505947473e9d9ea Mon Sep 17 00:00:00 2001 From: Parag Jain Date: Tue, 3 Nov 2020 18:40:35 +0530 Subject: [PATCH 03/15] minor changes and documentation more docs, minor change standalone page for auth model more docs fix link in doc --- docs/design/auth-model.md | 326 ++++++++++++++++++ docs/design/auth.md | 4 + .../extensions-core/druid-basic-security.md | 76 +--- .../basic/BasicSecurityResourceFilter.java | 16 +- .../overlord/sampler/SamplerResource.java | 4 +- .../query/lookup/LookupListeningResource.java | 4 +- .../druid/server/http/BrokerResource.java | 3 +- .../server/http/CoordinatorResource.java | 5 +- .../druid/server/http/RulesResource.java | 8 +- .../ServerPermissionsResourceFilter.java | 63 ---- ...ter.java => ServerUserResourceFilter.java} | 6 +- .../druid/server/security/Resource.java | 2 +- 12 files changed, 359 insertions(+), 158 deletions(-) create mode 100644 docs/design/auth-model.md delete mode 100644 server/src/main/java/org/apache/druid/server/http/security/ServerPermissionsResourceFilter.java rename server/src/main/java/org/apache/druid/server/http/security/{LookupInternalResourceFilter.java => ServerUserResourceFilter.java} (91%) diff --git a/docs/design/auth-model.md b/docs/design/auth-model.md new file mode 100644 index 000000000000..4ba9c6e4441c --- /dev/null +++ b/docs/design/auth-model.md @@ -0,0 +1,326 @@ +--- +id: auth-model +title: "Authorization model" +--- + + + +## Authorization model + +There are two versions of auth model in Druid, auth v1 model (legacy) and newer auth v2 model. Auth v1 is default and switch +to v2 can be made by setting the flag `druid.auth.authVersion=v2` . + +Here are the details below - + +## Auth V1 Model (Legacy) +There are two action types in Druid: READ and WRITE + +There are three resource types in Druid: DATASOURCE, CONFIG, and STATE. + +### DATASOURCE +Resource names for this type are datasource names. Specifying a datasource permission allows the administrator to grant users access to specific datasources. + +### CONFIG +There are two possible resource names for the "CONFIG" resource type, "CONFIG" and "security". Granting a user access to CONFIG resources allows them to access the following endpoints. + +"CONFIG" resource name covers the following endpoints: + +|Endpoint|Process Type| +|--------|---------| +|`/druid/coordinator/v1/config`|coordinator| +|`/druid/indexer/v1/worker`|overlord| +|`/druid/indexer/v1/worker/history`|overlord| +|`/druid/worker/v1/disable`|middleManager| +|`/druid/worker/v1/enable`|middleManager| + +"security" resource name covers the following endpoint: + +|Endpoint|Process Type| +|--------|---------| +|`/druid-ext/basic-security/authentication`|coordinator| +|`/druid-ext/basic-security/authorization`|coordinator| + +### STATE +There is only one possible resource name for the "STATE" config resource type, "STATE". Granting a user access to STATE resources allows them to access the following endpoints. + +"STATE" resource name covers the following endpoints: + +|Endpoint|Process Type| +|--------|---------| +|`/druid/coordinator/v1`|coordinator| +|`/druid/coordinator/v1/rules`|coordinator| +|`/druid/coordinator/v1/rules/history`|coordinator| +|`/druid/coordinator/v1/servers`|coordinator| +|`/druid/coordinator/v1/tiers`|coordinator| +|`/druid/broker/v1`|broker| +|`/druid/v2/candidates`|broker| +|`/druid/indexer/v1/leader`|overlord| +|`/druid/indexer/v1/isLeader`|overlord| +|`/druid/indexer/v1/action`|overlord| +|`/druid/indexer/v1/workers`|overlord| +|`/druid/indexer/v1/scaling`|overlord| +|`/druid/worker/v1/enabled`|middleManager| +|`/druid/worker/v1/tasks`|middleManager| +|`/druid/worker/v1/task/{taskid}/shutdown`|middleManager| +|`/druid/worker/v1/task/{taskid}/log`|middleManager| +|`/druid/historical/v1`|historical| +|`/druid-internal/v1/segments/`|historical| +|`/druid-internal/v1/segments/`|peon| +|`/druid-internal/v1/segments/`|realtime| +|`/status`|all process types| + +### HTTP methods + +For information on what HTTP methods are supported on a particular request endpoint, please refer to the [API documentation](../operations/api-reference.md). + +GET requires READ permission, while POST and DELETE require WRITE permission. + +### SQL Permissions + +Queries on Druid datasources require DATASOURCE READ permissions for the specified datasource. + +Queries on the [INFORMATION_SCHEMA tables](../querying/sql.html#information-schema) will +return information about datasources that the caller has DATASOURCE READ access to. Other +datasources will be omitted. + +Queries on the [system schema tables](../querying/sql.html#system-schema) require the following permissions: +- `segments`: Segments will be filtered based on DATASOURCE READ permissions. +- `servers`: The user requires STATE READ permissions. +- `server_segments`: The user requires STATE READ permissions and segments will be filtered based on DATASOURCE READ permissions. +- `tasks`: Tasks will be filtered based on DATASOURCE READ permissions. + +## Auth V2 Model + +This model can be enabled by setting the flag `druid.auth.authVersion=v2`. The idea behind this model is to support user +personas like admin, viewer etc. in an easy manner. + +There are two action types in Druid: READ and WRITE. Depending on HTTP method used for endpoints action is decided, for +GET and HEAD method actoin is `READ`, for all other methods action is `WRITE`. + +There are 4 resource types in Druid: DATASOURCE, INTERNAL, LOOKUP and SERVER. + +1. `DATASOURCE` resource type is concerned with all the actions that can be taken for querying, indexing, setting retention, +compaction rules for a dataset. Thus, if a user has read/write action on datasource they can control the lifecycle of that datasouce. + +1. `SERVER` resource type covers all the cluster administration endpoints. + +1. `LOOKUP` resource type covers lookups similar to `DATASOURCE` resource type. + +1. `INTERNAL` resource type covers all the resources/endpoints that druid internally uses to communicate among nodes. + +Examples - A server admin role can have read/write on `DATASOURCE`, `SERVER` and `LOOKUP` resource types. A viewer can have +just read on all or specific `DATASOURCE`. Many times users may want to create roles with custom permissions, all these +is supported using `Resource Name`, each `Resource type` can have mulitple of them. Authorizer uses `Action`, `Resource Type` +and `Resource Name` to authorize users action. For example, while querying a datasource named `ds` a `READ` action on +resource type `DATASOURCE` with resource name `ds` is required. Below are the details - + +### DATASOURCE + +Resource names for this type are datasource names. Specifying a datasource permission allows the administrator to grant users access to specific datasources. +When users get `READ` permission on a datasource, they will be able to query that datasource, will be able to see retention/compaction rules +for them, get schema details and vice versa with `WRITE` permissions. + +Below are the endpoints protected/filtered using datasource permissions. These permissions are enforced in SQL queries as well, see `SQL Permissions` section below. + +|Endpoint|Process Type| +|--------|---------| +|`GET/POST /druid/coordinator/v1/datasources/...`|coordinator| +|`GET /druid/coordinator/v1/rules`|coordinator| +|`GET/POST /druid/coordinator/v1/rules/{dataSourceName}`|coordinator| +|`GET /druid/coordinator/v1/rules/{dataSourceName}/history`|coordinator| +|`GET/POST /druid/v2/datasources/...`|broker| +|`GET/POST /druid/coordinator/v1/metadata/...`|coordinator| +|`POST /druid/indexer/v1/task`|overlord| +|`GET /druid/indexer/v1/task/{taskid}`|overlord| +|`GET /druid/indexer/v1/task/{taskid}/status`|overlord| +|`GET /druid/indexer/v1/task/{taskid}/segments`|overlord| +|`POST /druid/indexer/v1/task/{taskid}/shutdown`|overlord| +|`POST /druid/indexer/v1/datasources/{dataSource}/shutdownAllTasks`|overlord| +|`GET /druid/indexer/v1/waitingTasks`|overlord| +|`GET /druid/indexer/v1/pendingTasks`|overlord| +|`GET /druid/indexer/v1/runningTasks`|overlord| +|`GET /druid/indexer/v1/completeTasks`|overlord| +|`GET /druid/indexer/v1/tasks`|overlord| +|`DELETE /druid/indexer/v1/pendingSegments/{dataSource}`|overlord| +|`GET /druid/indexer/v1/task/{taskid}/log`|overlord| +|`GET /druid/indexer/v1/task/{taskid}/reports`|overlord| + + +### SERVER + +There are 3 possible resource name for the "SERVER" config resource type, `SERVER`, `STATUS` and `USER`. + +`SERVER` resoruce name covers following the end points, these are relevant for managing the cluster and can be used by +cluster managers. This resource name is used in SQL queries asking for server information see `SQL Permissions` section below. + +|Endpoint|Process Type| +|--------|---------| +|`GET /druid/indexer/v1/workers`|overlord| +|`POST /druid/indexer/v1/worker/{host}/enable`|overlord| +|`POST /druid/indexer/v1/worker/{host}/disable`|overlord| +|`GET /druid/coordinator/v1/loadstatus`|coordinator| +|`GET /druid/coordinator/v1/loadqueue`|coordinator| +|`GET /druid/coordinator/v1/rules/history`|coordinator| +|`GET /druid/coordinator/v1/servers`|coordinator| +|`GET /druid/coordinator/v1/servers/{serverName}`|coordinator| +|`GET /druid/coordinator/v1/servers/{serverName}/segments`|coordinator| +|`GET /druid/coordinator/v1/servers/{serverName}/segments/{segmentId}`|coordinator| +|`GET /druid/coordinator/v1/tiers`|coordinator| +|`GET /druid/coordinator/v1/tiers/{tierName}`|coordinator| +|`GET /druid/indexer/v1/worker`|overlord| +|`POST /druid/indexer/v1/worker`|overlord| +|`GET /druid/indexer/v1/worker/history`|overlord| +|`GET /status/properties`|all| +|`POST /druid/coordinator/v1/config/compaction/taskslots`|coordinator| +|`GET /druid/coordinator/v1/config`|coordinator| +|`POST /druid/coordinator/v1/config`|coordinator| +|`GET /druid/coordinator/v1/config/history`|coordinator| +|`GET /druid/coordinator/v1/lookups/nodeStatus`|coordinator| +|`GET /druid/coordinator/v1/lookups/nodeStatus/{tier}`|coordinator| +|`GET /druid/coordinator/v1/lookups/config/{tier}/{hostAndPort}`|coordinator| +|`GET/POST /druid-ext/basic-security/authentication`|coordinator| +|`GET/POST /druid-ext/basic-security/authorization`|coordinator| + +`STATUS` resource name covers following the end points and are relevant to check status of various processes and can be +used by cluster managers and systems checking status of different drudi processes. + +|Endpoint|Process Type| +|--------|---------| +|`GET /druid/indexer/v1/leader`|overlord| +|`GET /druid/coordinator/v1/leader`|coordinator| +|`GET /druid/coordinator/v1/isLeader`|coordinator| +|`GET /druid/historical/v1/loadstatus`|broker,historical,peon| +|`GET /druid/broker/v1/loadstatus`|broker| +|`GET /status`|all| + +`USER` resource name covers following the end points and are relevant to general users using the cluster needing access +to some `SERVER` resources during routine work. + +|Endpoint|Process Type| +|--------|---------| +|`POST /druid/indexer/v1/sampler`|overlord| +|`GET /druid/coordinator/v1/lookups/config`|coordinator| + +### INTERNAL + +There is only one possible resource names for the `INTERNAL` resource type - `INTERNAL`. These permissions mostly makes sense +for druid nodes talking to each other and cluster admins. + +|Endpoint|Process Type| +|--------|---------| +|`GET /druid-internal/v1/httpRemoteTaskRunner/knownTasks `|overlord| +|`GET /druid-internal/v1/httpRemoteTaskRunner/pendingTasksQueue`|overlord| +|`GET /druid-internal/v1/httpRemoteTaskRunner/workerSyncerDebugInfo`|overlord| +|`GET /druid-internal/v1/httpRemoteTaskRunner/blacklistedWorkers`|overlord| +|`GET /druid-internal/v1/httpRemoteTaskRunner/lazyWorker`|overlord| +|`GET /druid-internal/v1/httpRemoteTaskRunner/workersWithUnacknowledgedTasks`|overlord| +|`GET /druid-internal/v1/httpRemoteTaskRunner/workersEilgibleToRunTasks`|overlord| +|`POST /druid/indexer/v1/taskStatus`|overlord| +|`POST /druid/indexer/v1/action`|overlord| +|`GET /druid/indexer/v1/scaling`|overlord| +|`GET /druid/worker/v1/shuffle/task/{supervisorTaskId}/{subTaskId}/partition`|middleManager| +|`DELETE /druid/worker/v1/shuffle/task/{supervisorTaskId}`|middleManager| +|`GET /druid-internal/v1/worker/`|middleManager| +|`POST /druid-internal/v1/worker/assignTask`|middleManager| +|`GET /druid/worker/v1/enabled`|middleManager| +|`GET /druid/worker/v1/tasks`|middleManager| +|`POST /druid/worker/v1/task/{taskid}/shutdown`|middleManager| +|`GET /druid/worker/v1/task/{taskid}/log`|middleManager| +|`POST /druid/worker/v1/disable`|middleManager| +|`POST /druid/worker/v1/enable`|middleManager| +|`GET /druid-internal/v1/httpServerInventoryView`|broker,coordinator| +|`POST /druid/v2/candidates`|broker| +|`GET /druid/coordinator/v1/cluster`|coordinator| +|`GET /druid/coordinator/v1/cluster/{nodeRole}`|coordinator| +|`GET /druid/router/v1/brokers`|router| +|`GET /druid-internal/v1/segments/`|broker,historical,peon| +|`GET /druid-internal/v1/segments/changeRequests`|broker,historical,peon| +|`GET /status/selfDiscovered/status`|all| +|`GET /status/selfDiscovered`|all| +|`POST /druid/listen/v1/lookups`|broker,historical,peon| +|`POST /druid/listen/v1/lookups/updates`|broker,historical,peon| +|`GET /druid/listen/v1/lookups`|broker,historical,peon| +|`GET /druid/listen/v1/lookups/{id}`|broker,historical,peon| +|`POST /druid/listen/v1/lookups/{id}`|broker,historical,peon| +|`DELETE /druid/listen/v1/lookups/{id}`|broker,historical,peon| + +### LOOKUP + +`LOOKUP` is similar to `DATASOURCE` resource type having lookup name as resource name and specifying a lookup permission +allows the administrator to grant users access to specific lookup name. Endpoints that return list of lookpus will return +filtered list by the granted permissions. It covers the following endpoints. + +|Endpoint|Process Type| +|--------|---------| +|`POST /druid/coordinator/v1/lookups/config`|coordinator| +|`GET /druid/coordinator/v1/lookups/config/all`|coordinator| +|`DELETE /druid/coordinator/v1/lookups/config/{tier}`|coordinator| +|`DELETE /druid/coordinator/v1/lookups/config/{tier}/{lookup}`|coordinator| +|`POST /druid/coordinator/v1/lookups/config/{tier}/{lookup}`|coordinator| +|`GET /druid/coordinator/v1/lookups/config/{tier}/{lookup}`|coordinator| +|`GET /druid/coordinator/v1/lookups/config/{tier}`|coordinator| +|`GET /druid/coordinator/v1/lookups/status`|coordinator| +|`GET /druid/coordinator/v1/lookups/status/{tier}`|coordinator| +|`GET /druid/coordinator/v1/lookups/status/{tier}/{lookup}`|coordinator| + +### EXAMPLE ROLES-PERMISSION MAPPING + +These are some example policies that be used. Policies are of form `ACTION:RESOURCE_TYPE:RESOURCE_NAME` +1. Cluster admin (super user/druid system) can have following policies - `READ:DATASOURCE:*`, `WRITE:DATASOURCE:*`, +`READ:INTERNAL:*`, `WRITE:INTERNAL:*`, `READ:SERVER:*`, `WRITE:SERVER:*`, `READ:LOOKUP:*` and `WRITE:LOOKUP:*`. +1. Cluster manager can have following policies - `READ:DATASOURCE:*`, `WRITE:DATASOURCE:*`, `READ:SERVER:*`, `WRITE:SERVER:*`, + `READ:LOOKUP:*` and `WRITE:LOOKUP:*`. +1. A user just wanting to read/write to specific datasource and lookup can have following policies - `READ:DATASOURCE:`, +`WRITE:DATASOURCE:`, `READ:LOOKUP:`, `WRITE:LOOKUP:`, `READ:SERVER:USER` and `WRITE:SERVER:USER`. +1. A user just wanting to read from specific datasource and lookup can have following policies - `READ:DATASOURCE:`, +`READ:LOOKUP:`, and `READ:SERVER:USER`. +1. An external monitoring system can just have `READ:SERVER:STATUS` permission. + +### HTTP methods + +For information on what HTTP methods are supported on a particular request endpoint, please refer to the [API documentation](../operations/api-reference.md). + +GET and HEAD requires READ permission, while POST and DELETE require WRITE permission. + +### SQL Permissions + +Queries on Druid datasources require DATASOURCE READ permissions for the specified datasource. + +Queries on the [INFORMATION_SCHEMA tables](../querying/sql.html#information-schema) will +return information about datasources that the caller has DATASOURCE READ access to. Other +datasources will be omitted. + +Queries on the [system schema tables](../querying/sql.html#system-schema) require the following permissions: +- `segments`: Segments will be filtered based on DATASOURCE READ permissions. +- `servers`: The user requires `READ` on `SERVER` resource type with `SERVER` resource name. +- `server_segments`: The user requires `READ` on `SERVER` resource type with `SERVER` resource name permissions and segments +will be filtered based on DATASOURCE READ permissions. +- `tasks`: Tasks will be filtered based on DATASOURCE READ permissions. + +### Backwards compatibility and rolling upgrade to auth v2 model from v1 +Unless the flag `druid.auth.authVersion` is set to `v2`, older model will be used. For rolling upgradw to v2 model +there are few prerequisites - +1. Define the new permissions and policies in the auth system you are using. +1. Make sure the auth extension you are using supports the newer model, you need to make sure the `Authorizer` implementation +has implemented `authorizeV2` method implemented. +1. Please make sure both newer and older set of permissions/roles or policies exist in the auth system till all the nodes are upgraded. + +Once all the above things are in place follow the rolling upgrade [guide](../operations/rolling-updates.html) and set `druid.auth.authVersion=v2` +on each node before upgrading. \ No newline at end of file diff --git a/docs/design/auth.md b/docs/design/auth.md index 9701f632ddba..c23f0efeb58d 100644 --- a/docs/design/auth.md +++ b/docs/design/auth.md @@ -194,3 +194,7 @@ druid.auth.authorizer..name= ``` These properties provide the authenticator and authorizer names to the implementations as @JsonProperty parameters, potentially useful when multiple authenticators or authorizers of the same type are configured. + +## Defining permissions + +Please see [authorization model](../design/auth-model.md). diff --git a/docs/development/extensions-core/druid-basic-security.md b/docs/development/extensions-core/druid-basic-security.md index 892306e4a07b..248d57252f46 100644 --- a/docs/development/extensions-core/druid-basic-security.md +++ b/docs/development/extensions-core/druid-basic-security.md @@ -457,81 +457,7 @@ Each Authorizer will always have a default "admin" and "druid_system" user with ## Defining permissions -There are two action types in Druid: READ and WRITE - -There are three resource types in Druid: DATASOURCE, CONFIG, and STATE. - -### DATASOURCE -Resource names for this type are datasource names. Specifying a datasource permission allows the administrator to grant users access to specific datasources. - -### CONFIG -There are two possible resource names for the "CONFIG" resource type, "CONFIG" and "security". Granting a user access to CONFIG resources allows them to access the following endpoints. - -"CONFIG" resource name covers the following endpoints: - -|Endpoint|Process Type| -|--------|---------| -|`/druid/coordinator/v1/config`|coordinator| -|`/druid/indexer/v1/worker`|overlord| -|`/druid/indexer/v1/worker/history`|overlord| -|`/druid/worker/v1/disable`|middleManager| -|`/druid/worker/v1/enable`|middleManager| - -"security" resource name covers the following endpoint: - -|Endpoint|Process Type| -|--------|---------| -|`/druid-ext/basic-security/authentication`|coordinator| -|`/druid-ext/basic-security/authorization`|coordinator| - -### STATE -There is only one possible resource name for the "STATE" config resource type, "STATE". Granting a user access to STATE resources allows them to access the following endpoints. - -"STATE" resource name covers the following endpoints: - -|Endpoint|Process Type| -|--------|---------| -|`/druid/coordinator/v1`|coordinator| -|`/druid/coordinator/v1/rules`|coordinator| -|`/druid/coordinator/v1/rules/history`|coordinator| -|`/druid/coordinator/v1/servers`|coordinator| -|`/druid/coordinator/v1/tiers`|coordinator| -|`/druid/broker/v1`|broker| -|`/druid/v2/candidates`|broker| -|`/druid/indexer/v1/leader`|overlord| -|`/druid/indexer/v1/isLeader`|overlord| -|`/druid/indexer/v1/action`|overlord| -|`/druid/indexer/v1/workers`|overlord| -|`/druid/indexer/v1/scaling`|overlord| -|`/druid/worker/v1/enabled`|middleManager| -|`/druid/worker/v1/tasks`|middleManager| -|`/druid/worker/v1/task/{taskid}/shutdown`|middleManager| -|`/druid/worker/v1/task/{taskid}/log`|middleManager| -|`/druid/historical/v1`|historical| -|`/druid-internal/v1/segments/`|historical| -|`/druid-internal/v1/segments/`|peon| -|`/druid-internal/v1/segments/`|realtime| -|`/status`|all process types| - -### HTTP methods - -For information on what HTTP methods are supported on a particular request endpoint, please refer to the [API documentation](../../operations/api-reference.md). - -GET requires READ permission, while POST and DELETE require WRITE permission. - -### SQL Permissions - -Queries on Druid datasources require DATASOURCE READ permissions for the specified datasource. - -Queries on the [INFORMATION_SCHEMA tables](../../querying/sql.html#information-schema) will -return information about datasources that the caller has DATASOURCE READ access to. Other -datasources will be omitted. - -Queries on the [system schema tables](../../querying/sql.html#system-schema) require the following permissions: -- `segments`: Segments will be filtered based on DATASOURCE READ permissions. -- `servers`: The user requires STATE READ permissions. -- `server_segments`: The user requires STATE READ permissions and segments will be filtered based on DATASOURCE READ permissions. -- `tasks`: Tasks will be filtered based on DATASOURCE READ permissions. +Please see [authorization model](../../design/auth-model.md). ## Configuration Propagation diff --git a/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/BasicSecurityResourceFilter.java b/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/BasicSecurityResourceFilter.java index f64a89ab04fc..170a4626a0f7 100644 --- a/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/BasicSecurityResourceFilter.java +++ b/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/BasicSecurityResourceFilter.java @@ -50,10 +50,18 @@ public BasicSecurityResourceFilter( @Override public ContainerRequest filter(ContainerRequest request) { - final ResourceAction resourceAction = new ResourceAction( - new Resource(SECURITY_RESOURCE_NAME, ResourceType.CONFIG), - getAction(request) - ); + final ResourceAction resourceAction; + if (getAuthConfig().getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + resourceAction = new ResourceAction( + Resource.SERVER_SERVER_RESOURCE, + getAction(request) + ); + } else { + resourceAction = new ResourceAction( + new Resource(SECURITY_RESOURCE_NAME, ResourceType.CONFIG), + getAction(request) + ); + } final Access authResult = AuthorizationUtils.authorizeResourceAction( getReq(), diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/sampler/SamplerResource.java b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/sampler/SamplerResource.java index 1c0d56c97c9f..9453acef522a 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/sampler/SamplerResource.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/sampler/SamplerResource.java @@ -21,7 +21,7 @@ import com.google.common.base.Preconditions; import com.sun.jersey.spi.container.ResourceFilters; -import org.apache.druid.server.http.security.ServerPermissionsResourceFilter; +import org.apache.druid.server.http.security.ServerUserResourceFilter; import org.apache.druid.server.http.security.StateResourceFilter; import javax.ws.rs.Consumes; @@ -36,7 +36,7 @@ public class SamplerResource @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - @ResourceFilters({ StateResourceFilter.class, ServerPermissionsResourceFilter.class }) + @ResourceFilters({ StateResourceFilter.class, ServerUserResourceFilter.class }) public SamplerResponse post(final SamplerSpec sampler) { return Preconditions.checkNotNull(sampler, "Request body cannot be empty").sample(); diff --git a/server/src/main/java/org/apache/druid/query/lookup/LookupListeningResource.java b/server/src/main/java/org/apache/druid/query/lookup/LookupListeningResource.java index c9352d5fc209..5eb967764467 100644 --- a/server/src/main/java/org/apache/druid/query/lookup/LookupListeningResource.java +++ b/server/src/main/java/org/apache/druid/query/lookup/LookupListeningResource.java @@ -29,7 +29,7 @@ import org.apache.druid.guice.annotations.Smile; import org.apache.druid.java.util.common.logger.Logger; import org.apache.druid.server.http.security.ConfigResourceFilter; -import org.apache.druid.server.http.security.LookupInternalResourceFilter; +import org.apache.druid.server.http.security.InternalInternalResourceFilter; import org.apache.druid.server.listener.resource.AbstractListenerHandler; import org.apache.druid.server.listener.resource.ListenerResource; import org.apache.druid.server.lookup.cache.LookupCoordinatorManager; @@ -42,7 +42,7 @@ import java.util.Map; @Path(ListenerResource.BASE_PATH + "/" + LookupCoordinatorManager.LOOKUP_LISTEN_ANNOUNCE_KEY) -@ResourceFilters({ ConfigResourceFilter.class, LookupInternalResourceFilter.class }) +@ResourceFilters({ ConfigResourceFilter.class, InternalInternalResourceFilter.class }) class LookupListeningResource extends ListenerResource { private static final Logger LOG = new Logger(LookupListeningResource.class); diff --git a/server/src/main/java/org/apache/druid/server/http/BrokerResource.java b/server/src/main/java/org/apache/druid/server/http/BrokerResource.java index e50b04edf8f0..b45154adae85 100644 --- a/server/src/main/java/org/apache/druid/server/http/BrokerResource.java +++ b/server/src/main/java/org/apache/druid/server/http/BrokerResource.java @@ -23,6 +23,7 @@ import com.google.inject.Inject; import com.sun.jersey.spi.container.ResourceFilters; import org.apache.druid.client.BrokerServerView; +import org.apache.druid.server.http.security.ServerStatusResourceFilter; import org.apache.druid.server.http.security.StateResourceFilter; import javax.ws.rs.GET; @@ -44,7 +45,7 @@ public BrokerResource(BrokerServerView brokerServerView) @GET @Path("/loadstatus") - @ResourceFilters(StateResourceFilter.class) + @ResourceFilters({ StateResourceFilter.class, ServerStatusResourceFilter.class }) @Produces(MediaType.APPLICATION_JSON) public Response getLoadStatus() { diff --git a/server/src/main/java/org/apache/druid/server/http/CoordinatorResource.java b/server/src/main/java/org/apache/druid/server/http/CoordinatorResource.java index 8042b3f102f4..960c98ebc763 100644 --- a/server/src/main/java/org/apache/druid/server/http/CoordinatorResource.java +++ b/server/src/main/java/org/apache/druid/server/http/CoordinatorResource.java @@ -27,6 +27,7 @@ import com.sun.jersey.spi.container.ResourceFilters; import org.apache.druid.server.coordinator.DruidCoordinator; import org.apache.druid.server.coordinator.LoadQueuePeon; +import org.apache.druid.server.http.security.ServerServerResourceFilter; import org.apache.druid.server.http.security.ServerStatusResourceFilter; import org.apache.druid.server.http.security.StateResourceFilter; import org.apache.druid.timeline.DataSegment; @@ -82,7 +83,7 @@ public Response isLeader() @GET @Path("/loadstatus") - @ResourceFilters({ StateResourceFilter.class, ServerStatusResourceFilter.class }) + @ResourceFilters({ StateResourceFilter.class, ServerServerResourceFilter.class }) @Produces(MediaType.APPLICATION_JSON) public Response getLoadStatus( @QueryParam("simple") String simple, @@ -101,7 +102,7 @@ public Response getLoadStatus( @GET @Path("/loadqueue") - @ResourceFilters({ StateResourceFilter.class, ServerStatusResourceFilter.class }) + @ResourceFilters({ StateResourceFilter.class, ServerServerResourceFilter.class }) @Produces(MediaType.APPLICATION_JSON) public Response getLoadQueue( @QueryParam("simple") String simple, diff --git a/server/src/main/java/org/apache/druid/server/http/RulesResource.java b/server/src/main/java/org/apache/druid/server/http/RulesResource.java index 921b37dcc6aa..f844593239f1 100644 --- a/server/src/main/java/org/apache/druid/server/http/RulesResource.java +++ b/server/src/main/java/org/apache/druid/server/http/RulesResource.java @@ -29,7 +29,7 @@ import org.apache.druid.metadata.MetadataRuleManager; import org.apache.druid.server.coordinator.rules.Rule; import org.apache.druid.server.http.security.RulesResourceFilter; -import org.apache.druid.server.http.security.ServerStatusResourceFilter; +import org.apache.druid.server.http.security.ServerServerResourceFilter; import org.apache.druid.server.http.security.StateResourceFilter; import org.apache.druid.server.security.AuthConfig; import org.apache.druid.server.security.AuthorizationUtils; @@ -93,11 +93,9 @@ public Response getRules(@Context HttpServletRequest request) AuthorizationUtils.filterAuthorizedResources( request, rules.keySet(), - datasource -> Collections.singletonList(AuthorizationUtils.DATASOURCE_WRITE_RA_GENERATOR.apply(datasource)), + datasource -> Collections.singletonList(AuthorizationUtils.DATASOURCE_READ_RA_GENERATOR.apply(datasource)), authorizerMapper ).forEach(datasource -> filteredRules.put(datasource, rules.get(datasource))); - // add default rule - filteredRules.put(DEFAULT_RULE_KEY, rules.get(DEFAULT_RULE_KEY)); return Response.ok(filteredRules).build(); } return Response.ok(rules).build(); @@ -166,7 +164,7 @@ public Response getDatasourceRuleHistory( @GET @Path("/history") @Produces(MediaType.APPLICATION_JSON) - @ResourceFilters({ StateResourceFilter.class, ServerStatusResourceFilter.class }) + @ResourceFilters({ StateResourceFilter.class, ServerServerResourceFilter.class }) public Response getDatasourceRuleHistory( @QueryParam("interval") final String interval, @QueryParam("count") final Integer count diff --git a/server/src/main/java/org/apache/druid/server/http/security/ServerPermissionsResourceFilter.java b/server/src/main/java/org/apache/druid/server/http/security/ServerPermissionsResourceFilter.java deleted file mode 100644 index 6026069a4bd9..000000000000 --- a/server/src/main/java/org/apache/druid/server/http/security/ServerPermissionsResourceFilter.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.druid.server.http.security; - -import com.google.inject.Inject; -import com.sun.jersey.spi.container.ContainerRequest; -import org.apache.druid.server.security.Access; -import org.apache.druid.server.security.AuthConfig; -import org.apache.druid.server.security.AuthorizationUtils; -import org.apache.druid.server.security.AuthorizerMapper; -import org.apache.druid.server.security.ForbiddenException; -import org.apache.druid.server.security.Resource; -import org.apache.druid.server.security.ResourceAction; - -public class ServerPermissionsResourceFilter extends AbstractResourceFilter -{ - @Inject - public ServerPermissionsResourceFilter(AuthorizerMapper authorizerMapper, - AuthConfig authConfig - ) - { - super(authorizerMapper, authConfig); - } - - @Override - public ContainerRequest filter(ContainerRequest request) - { - if (getAuthConfig().getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { - final ResourceAction resourceAction = new ResourceAction( - Resource.SERVER_PERMISSIONS_RESOURCE, - getAction(request) - ); - - final Access authResult = AuthorizationUtils.authorizeResourceAction( - getReq(), - resourceAction, - getAuthorizerMapper() - ); - - if (!authResult.isAllowed()) { - throw new ForbiddenException(authResult.toString()); - } - } - return request; - } -} diff --git a/server/src/main/java/org/apache/druid/server/http/security/LookupInternalResourceFilter.java b/server/src/main/java/org/apache/druid/server/http/security/ServerUserResourceFilter.java similarity index 91% rename from server/src/main/java/org/apache/druid/server/http/security/LookupInternalResourceFilter.java rename to server/src/main/java/org/apache/druid/server/http/security/ServerUserResourceFilter.java index d01f2d90d51d..f53cf5a7a22d 100644 --- a/server/src/main/java/org/apache/druid/server/http/security/LookupInternalResourceFilter.java +++ b/server/src/main/java/org/apache/druid/server/http/security/ServerUserResourceFilter.java @@ -29,10 +29,10 @@ import org.apache.druid.server.security.Resource; import org.apache.druid.server.security.ResourceAction; -public class LookupInternalResourceFilter extends AbstractResourceFilter +public class ServerUserResourceFilter extends AbstractResourceFilter { @Inject - public LookupInternalResourceFilter(AuthorizerMapper authorizerMapper, + public ServerUserResourceFilter(AuthorizerMapper authorizerMapper, AuthConfig authConfig ) { @@ -44,7 +44,7 @@ public ContainerRequest filter(ContainerRequest request) { if (getAuthConfig().getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { final ResourceAction resourceAction = new ResourceAction( - Resource.LOOKUP_INTERNAL_RESOURCE, + Resource.SERVER_USER_RESOURCE, getAction(request) ); diff --git a/server/src/main/java/org/apache/druid/server/security/Resource.java b/server/src/main/java/org/apache/druid/server/security/Resource.java index a8d71186960a..bef25a55473f 100644 --- a/server/src/main/java/org/apache/druid/server/security/Resource.java +++ b/server/src/main/java/org/apache/druid/server/security/Resource.java @@ -28,7 +28,7 @@ public class Resource public static final Resource INTERNAL_INTERNAL_RESOURCE = new Resource("INTERNAL", ResourceType.INTERNAL); public static final Resource SERVER_SERVER_RESOURCE = new Resource("SERVER", ResourceType.SERVER); - public static final Resource SERVER_PERMISSIONS_RESOURCE = new Resource("PERMISSIONS", ResourceType.SERVER); + public static final Resource SERVER_USER_RESOURCE = new Resource("USER", ResourceType.SERVER); public static final Resource SERVER_STATUS_RESOURCE = new Resource("STATUS", ResourceType.SERVER); public static final Resource LOOKUP_INTERNAL_RESOURCE = new Resource("INTERNAL", ResourceType.LOOKUP); public static final Resource LOOKUP_STATUS_RESOURCE = new Resource("STATUS", ResourceType.LOOKUP); From 497da0140220fd06a2571bfc710a82da18b38719 Mon Sep 17 00:00:00 2001 From: Parag Jain Date: Wed, 4 Nov 2020 13:38:34 +0530 Subject: [PATCH 04/15] use auth v2 method compilation --- .../basic/BasicSecurityResourceFilter.java | 9 +- ...BasicAuthorizerMetadataStorageUpdater.java | 7 +- .../BasicRoleBasedAuthorizerTest.java | 65 ++++++---- ...cAuthorizerMetadataStorageUpdaterTest.java | 7 +- ...oordinatorBasicAuthorizerResourceTest.java | 6 +- .../authorizer/RangerAuthorizerTest.java | 17 +-- .../security/SupervisorResourceFilter.java | 6 +- .../http/security/TaskResourceFilter.java | 6 +- .../druid/indexing/common/TestUtils.java | 2 +- ...stractParallelIndexSupervisorTaskTest.java | 2 +- .../overlord/http/OverlordResourceTest.java | 2 +- .../supervisor/SupervisorResourceTest.java | 2 +- .../lookup/LookupIntrospectionResource.java | 5 +- .../apache/druid/server/QueryLifecycle.java | 6 +- .../CoordinatorCompactionConfigsResource.java | 13 +- .../http/LookupCoordinatorResource.java | 25 ++-- .../druid/server/http/RulesResource.java | 7 +- .../http/security/AbstractResourceFilter.java | 14 +-- .../http/security/ConfigResourceFilter.java | 6 +- .../security/DatasourceResourceFilter.java | 8 +- .../InternalInternalResourceFilter.java | 8 +- .../security/LookupStatusResourceFilter.java | 9 +- .../http/security/RulesResourceFilter.java | 8 +- .../security/ServerServerResourceFilter.java | 8 +- .../security/ServerStatusResourceFilter.java | 8 +- .../security/ServerUserResourceFilter.java | 8 +- .../http/security/StateResourceFilter.java | 6 +- .../AuthorizerMapperModule.java | 4 +- .../druid/server/security/AuthTestUtils.java | 2 +- .../server/security/AuthorizationUtils.java | 6 +- .../server/security/AuthorizerMapper.java | 11 +- .../LookupIntrospectionResourceTest.java | 6 +- .../AsyncQueryForwardingServletTest.java | 2 +- .../druid/server/QueryResourceTest.java | 6 +- .../server/http/DataSourcesResourceTest.java | 2 +- .../http/LookupCoordinatorResourceTest.java | 116 ++++++------------ .../druid/server/http/RulesResourceTest.java | 13 +- .../sql/calcite/schema/SystemSchema.java | 24 ++-- .../sql/calcite/schema/SystemSchemaTest.java | 6 +- .../druid/sql/calcite/util/CalciteTests.java | 2 +- 40 files changed, 200 insertions(+), 270 deletions(-) diff --git a/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/BasicSecurityResourceFilter.java b/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/BasicSecurityResourceFilter.java index 170a4626a0f7..fa628599c909 100644 --- a/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/BasicSecurityResourceFilter.java +++ b/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/BasicSecurityResourceFilter.java @@ -39,19 +39,16 @@ public class BasicSecurityResourceFilter extends AbstractResourceFilter private static final String SECURITY_RESOURCE_NAME = "security"; @Inject - public BasicSecurityResourceFilter( - AuthorizerMapper authorizerMapper, - AuthConfig authConfig - ) + public BasicSecurityResourceFilter(AuthorizerMapper authorizerMapper) { - super(authorizerMapper, authConfig); + super(authorizerMapper); } @Override public ContainerRequest filter(ContainerRequest request) { final ResourceAction resourceAction; - if (getAuthConfig().getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + if (getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { resourceAction = new ResourceAction( Resource.SERVER_SERVER_RESOURCE, getAction(request) diff --git a/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/db/updater/CoordinatorBasicAuthorizerMetadataStorageUpdater.java b/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/db/updater/CoordinatorBasicAuthorizerMetadataStorageUpdater.java index 250f746f629c..c70c259d03ac 100644 --- a/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/db/updater/CoordinatorBasicAuthorizerMetadataStorageUpdater.java +++ b/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/db/updater/CoordinatorBasicAuthorizerMetadataStorageUpdater.java @@ -97,7 +97,6 @@ public class CoordinatorBasicAuthorizerMetadataStorageUpdater implements BasicAu private final BasicAuthorizerCacheNotifier cacheNotifier; private final BasicAuthCommonCacheConfig commonCacheConfig; private final ObjectMapper objectMapper; - private final AuthConfig authConfig; private final int numRetries = 5; private final Map cachedUserMaps; @@ -118,8 +117,7 @@ public CoordinatorBasicAuthorizerMetadataStorageUpdater( BasicAuthCommonCacheConfig commonCacheConfig, @Smile ObjectMapper objectMapper, BasicAuthorizerCacheNotifier cacheNotifier, - ConfigManager configManager, // -V6022: ConfigManager creates the db table we need, set a dependency here - AuthConfig authConfig + ConfigManager configManager // -V6022: ConfigManager creates the db table we need, set a dependency here ) { this.exec = Execs.scheduledSingleThreaded("CoordinatorBasicAuthorizerMetadataStorageUpdater-Exec--%d"); @@ -133,7 +131,6 @@ public CoordinatorBasicAuthorizerMetadataStorageUpdater( this.cachedGroupMappingMaps = new ConcurrentHashMap<>(); this.cachedRoleMaps = new ConcurrentHashMap<>(); this.authorizerNames = new HashSet<>(); - this.authConfig = authConfig; } @LifecycleStart @@ -182,7 +179,7 @@ public void start() dbConfig.getInitialAdminUser(), dbConfig.getInitialAdminRole(), dbConfig.getInitialAdminGroupMapping(), - authConfig.getAuthVersion() + authorizerMapper.getAuthVersion() ); } } diff --git a/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/BasicRoleBasedAuthorizerTest.java b/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/BasicRoleBasedAuthorizerTest.java index 19172ba4a7f2..c6d0cdc09746 100644 --- a/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/BasicRoleBasedAuthorizerTest.java +++ b/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/BasicRoleBasedAuthorizerTest.java @@ -116,15 +116,14 @@ public void setUp() null, new LDAPRoleProvider(null, groupFilters) ) - ) + ), null ), connector, tablesConfig, new BasicAuthCommonCacheConfig(null, null, null, null), new ObjectMapper(new SmileFactory()), new NoopBasicAuthorizerCacheNotifier(), - null, - new AuthConfig() + null ); updater.start(); @@ -173,14 +172,16 @@ public void testAuth() Access access = authorizer.authorize( authenticationResult, new Resource("testResource", ResourceType.DATASOURCE), - Action.WRITE + Action.WRITE, + AuthConfig.AUTH_VERSION_1 ); Assert.assertTrue(access.isAllowed()); access = authorizer.authorize( authenticationResult, new Resource("wrongResource", ResourceType.DATASOURCE), - Action.WRITE + Action.WRITE, + AuthConfig.AUTH_VERSION_1 ); Assert.assertFalse(access.isAllowed()); } @@ -207,14 +208,16 @@ public void testAuthGroupMapping() Access access = ldapAuthorizer.authorize( authenticationResult, new Resource("testResource", ResourceType.DATASOURCE), - Action.WRITE + Action.WRITE, + AuthConfig.AUTH_VERSION_1 ); Assert.assertTrue(access.isAllowed()); access = ldapAuthorizer.authorize( authenticationResult, new Resource("wrongResource", ResourceType.DATASOURCE), - Action.WRITE + Action.WRITE, + AuthConfig.AUTH_VERSION_1 ); Assert.assertFalse(access.isAllowed()); } @@ -252,21 +255,24 @@ public void testAuthGroupMappingPatternRightMask() Access access = ldapAuthorizer.authorize( authenticationResult, new Resource("testResource", ResourceType.DATASOURCE), - Action.READ + Action.READ, + AuthConfig.AUTH_VERSION_1 ); Assert.assertTrue(access.isAllowed()); access = ldapAuthorizer.authorize( authenticationResult, new Resource("testResource", ResourceType.DATASOURCE), - Action.WRITE + Action.WRITE, + AuthConfig.AUTH_VERSION_1 ); Assert.assertTrue(access.isAllowed()); access = ldapAuthorizer.authorize( authenticationResult, new Resource("wrongResource", ResourceType.DATASOURCE), - Action.WRITE + Action.WRITE, + AuthConfig.AUTH_VERSION_1 ); Assert.assertFalse(access.isAllowed()); @@ -277,21 +283,24 @@ public void testAuthGroupMappingPatternRightMask() access = ldapAuthorizer.authorize( authenticationResult, new Resource("testResource", ResourceType.DATASOURCE), - Action.WRITE + Action.WRITE, + AuthConfig.AUTH_VERSION_1 ); Assert.assertFalse(access.isAllowed()); access = ldapAuthorizer.authorize( authenticationResult, new Resource("testResource", ResourceType.DATASOURCE), - Action.READ + Action.READ, + AuthConfig.AUTH_VERSION_1 ); Assert.assertTrue(access.isAllowed()); access = ldapAuthorizer.authorize( authenticationResult, new Resource("wrongResource", ResourceType.DATASOURCE), - Action.READ + Action.READ, + AuthConfig.AUTH_VERSION_1 ); Assert.assertFalse(access.isAllowed()); } @@ -330,21 +339,24 @@ public void testAuthGroupMappingPatternLeftMask() Access access = ldapAuthorizer.authorize( authenticationResult, new Resource("testResource", ResourceType.DATASOURCE), - Action.READ + Action.READ, + AuthConfig.AUTH_VERSION_1 ); Assert.assertTrue(access.isAllowed()); access = ldapAuthorizer.authorize( authenticationResult, new Resource("testResource", ResourceType.DATASOURCE), - Action.WRITE + Action.WRITE, + AuthConfig.AUTH_VERSION_1 ); Assert.assertTrue(access.isAllowed()); access = ldapAuthorizer.authorize( authenticationResult, new Resource("wrongResource", ResourceType.DATASOURCE), - Action.WRITE + Action.WRITE, + AuthConfig.AUTH_VERSION_1 ); Assert.assertFalse(access.isAllowed()); @@ -355,21 +367,24 @@ public void testAuthGroupMappingPatternLeftMask() access = ldapAuthorizer.authorize( authenticationResult, new Resource("testResource", ResourceType.DATASOURCE), - Action.WRITE + Action.WRITE, + AuthConfig.AUTH_VERSION_1 ); Assert.assertFalse(access.isAllowed()); access = ldapAuthorizer.authorize( authenticationResult, new Resource("testResource", ResourceType.DATASOURCE), - Action.READ + Action.READ, + AuthConfig.AUTH_VERSION_1 ); Assert.assertTrue(access.isAllowed()); access = ldapAuthorizer.authorize( authenticationResult, new Resource("wrongResource", ResourceType.DATASOURCE), - Action.READ + Action.READ, + AuthConfig.AUTH_VERSION_1 ); Assert.assertFalse(access.isAllowed()); } @@ -397,28 +412,32 @@ public void testAuthMissingGroupMapping() Access access = ldapAuthorizer.authorize( authenticationResult, new Resource("testResource", ResourceType.DATASOURCE), - Action.WRITE + Action.WRITE, + AuthConfig.AUTH_VERSION_1 ); Assert.assertFalse(access.isAllowed()); access = ldapAuthorizer.authorize( authenticationResult, new Resource("testResource", ResourceType.DATASOURCE), - Action.READ + Action.READ, + AuthConfig.AUTH_VERSION_1 ); Assert.assertFalse(access.isAllowed()); access = ldapAuthorizer.authorize( authenticationResult, new Resource("wrongResource", ResourceType.DATASOURCE), - Action.WRITE + Action.WRITE, + AuthConfig.AUTH_VERSION_1 ); Assert.assertFalse(access.isAllowed()); access = ldapAuthorizer.authorize( authenticationResult, new Resource("wrongResource", ResourceType.DATASOURCE), - Action.READ + Action.READ, + AuthConfig.AUTH_VERSION_1 ); Assert.assertFalse(access.isAllowed()); } diff --git a/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/CoordinatorBasicAuthorizerMetadataStorageUpdaterTest.java b/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/CoordinatorBasicAuthorizerMetadataStorageUpdaterTest.java index 5cb09106df5d..d857a302035c 100644 --- a/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/CoordinatorBasicAuthorizerMetadataStorageUpdaterTest.java +++ b/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/CoordinatorBasicAuthorizerMetadataStorageUpdaterTest.java @@ -36,7 +36,6 @@ import org.apache.druid.security.basic.authorization.entity.BasicAuthorizerRole; import org.apache.druid.security.basic.authorization.entity.BasicAuthorizerUser; import org.apache.druid.server.security.Action; -import org.apache.druid.server.security.AuthConfig; import org.apache.druid.server.security.AuthorizerMapper; import org.apache.druid.server.security.Resource; import org.apache.druid.server.security.ResourceAction; @@ -107,15 +106,15 @@ public void setUp() null, null ) - ) + ), + null ), connector, tablesConfig, new BasicAuthCommonCacheConfig(null, null, null, null), objectMapper, new NoopBasicAuthorizerCacheNotifier(), - null, - new AuthConfig() + null ); updater.start(); diff --git a/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/CoordinatorBasicAuthorizerResourceTest.java b/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/CoordinatorBasicAuthorizerResourceTest.java index 51ea886f7b73..3d6f22487f1e 100644 --- a/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/CoordinatorBasicAuthorizerResourceTest.java +++ b/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/CoordinatorBasicAuthorizerResourceTest.java @@ -45,7 +45,6 @@ import org.apache.druid.security.basic.authorization.entity.BasicAuthorizerUserFull; import org.apache.druid.security.basic.authorization.entity.BasicAuthorizerUserFullSimplifiedPermissions; import org.apache.druid.server.security.Action; -import org.apache.druid.server.security.AuthConfig; import org.apache.druid.server.security.AuthValidator; import org.apache.druid.server.security.AuthorizerMapper; import org.apache.druid.server.security.Resource; @@ -136,7 +135,7 @@ public void setUp() null, null ) - ) + ), null ); storageUpdater = new CoordinatorBasicAuthorizerMetadataStorageUpdater( @@ -146,8 +145,7 @@ public void setUp() new BasicAuthCommonCacheConfig(null, null, null, null), new ObjectMapper(new SmileFactory()), new NoopBasicAuthorizerCacheNotifier(), - null, - new AuthConfig() + null ); resource = new BasicAuthorizerResource( diff --git a/extensions-core/druid-ranger-security/src/test/java/org/apache/druid/security/ranger/authorizer/RangerAuthorizerTest.java b/extensions-core/druid-ranger-security/src/test/java/org/apache/druid/security/ranger/authorizer/RangerAuthorizerTest.java index 46fef6ca77c8..3a6bb7372a34 100644 --- a/extensions-core/druid-ranger-security/src/test/java/org/apache/druid/security/ranger/authorizer/RangerAuthorizerTest.java +++ b/extensions-core/druid-ranger-security/src/test/java/org/apache/druid/security/ranger/authorizer/RangerAuthorizerTest.java @@ -20,6 +20,7 @@ package org.apache.druid.security.ranger.authorizer; import org.apache.druid.server.security.Action; +import org.apache.druid.server.security.AuthConfig; import org.apache.druid.server.security.AuthenticationResult; import org.apache.druid.server.security.Resource; import org.apache.druid.server.security.ResourceType; @@ -48,13 +49,13 @@ public static void setupBeforeClass() @Test public void testOperations() { - Assert.assertTrue(rangerAuthorizer.authorize(alice, aliceDatasource, Action.READ).isAllowed()); - Assert.assertTrue(rangerAuthorizer.authorize(alice, aliceDatasource, Action.READ).isAllowed()); - Assert.assertTrue(rangerAuthorizer.authorize(alice, aliceConfig, Action.READ).isAllowed()); - Assert.assertTrue(rangerAuthorizer.authorize(alice, aliceConfig, Action.WRITE).isAllowed()); - Assert.assertTrue(rangerAuthorizer.authorize(alice, aliceState, Action.READ).isAllowed()); - Assert.assertTrue(rangerAuthorizer.authorize(alice, aliceState, Action.WRITE).isAllowed()); - - Assert.assertFalse(rangerAuthorizer.authorize(bob, aliceDatasource, Action.READ).isAllowed()); + Assert.assertTrue(rangerAuthorizer.authorize(alice, aliceDatasource, Action.READ, AuthConfig.AUTH_VERSION_1).isAllowed()); + Assert.assertTrue(rangerAuthorizer.authorize(alice, aliceDatasource, Action.READ, AuthConfig.AUTH_VERSION_1).isAllowed()); + Assert.assertTrue(rangerAuthorizer.authorize(alice, aliceConfig, Action.READ, AuthConfig.AUTH_VERSION_1).isAllowed()); + Assert.assertTrue(rangerAuthorizer.authorize(alice, aliceConfig, Action.WRITE, AuthConfig.AUTH_VERSION_1).isAllowed()); + Assert.assertTrue(rangerAuthorizer.authorize(alice, aliceState, Action.READ, AuthConfig.AUTH_VERSION_1).isAllowed()); + Assert.assertTrue(rangerAuthorizer.authorize(alice, aliceState, Action.WRITE, AuthConfig.AUTH_VERSION_1).isAllowed()); + + Assert.assertFalse(rangerAuthorizer.authorize(bob, aliceDatasource, Action.READ, AuthConfig.AUTH_VERSION_1).isAllowed()); } } diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/http/security/SupervisorResourceFilter.java b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/http/security/SupervisorResourceFilter.java index f48d29022b28..e834c2e875e5 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/http/security/SupervisorResourceFilter.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/http/security/SupervisorResourceFilter.java @@ -32,7 +32,6 @@ import org.apache.druid.server.http.security.AbstractResourceFilter; import org.apache.druid.server.security.Access; import org.apache.druid.server.security.Action; -import org.apache.druid.server.security.AuthConfig; import org.apache.druid.server.security.AuthorizationUtils; import org.apache.druid.server.security.AuthorizerMapper; import org.apache.druid.server.security.ForbiddenException; @@ -49,11 +48,10 @@ public class SupervisorResourceFilter extends AbstractResourceFilter @Inject public SupervisorResourceFilter( AuthorizerMapper authorizerMapper, - SupervisorManager supervisorManager, - AuthConfig authConfig + SupervisorManager supervisorManager ) { - super(authorizerMapper, authConfig); + super(authorizerMapper); this.supervisorManager = supervisorManager; } diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/http/security/TaskResourceFilter.java b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/http/security/TaskResourceFilter.java index f38b1c24518d..51b17f81def8 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/http/security/TaskResourceFilter.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/http/security/TaskResourceFilter.java @@ -31,7 +31,6 @@ import org.apache.druid.java.util.common.StringUtils; import org.apache.druid.server.http.security.AbstractResourceFilter; import org.apache.druid.server.security.Access; -import org.apache.druid.server.security.AuthConfig; import org.apache.druid.server.security.AuthorizationUtils; import org.apache.druid.server.security.AuthorizerMapper; import org.apache.druid.server.security.ForbiddenException; @@ -56,11 +55,10 @@ public class TaskResourceFilter extends AbstractResourceFilter @Inject public TaskResourceFilter( TaskStorageQueryAdapter taskStorageQueryAdapter, - AuthorizerMapper authorizerMapper, - AuthConfig authConfig + AuthorizerMapper authorizerMapper ) { - super(authorizerMapper, authConfig); + super(authorizerMapper); this.taskStorageQueryAdapter = taskStorageQueryAdapter; } diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/TestUtils.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/TestUtils.java index c36a072b34eb..a831d70ad5d8 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/TestUtils.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/common/TestUtils.java @@ -95,7 +95,7 @@ public TestUtils() .addValue(RowIngestionMetersFactory.class, rowIngestionMetersFactory) .addValue(PruneSpecsHolder.class, PruneSpecsHolder.DEFAULT) .addValue(IndexingServiceClient.class, INDEXING_SERVICE_CLIENT) - .addValue(AuthorizerMapper.class, new AuthorizerMapper(ImmutableMap.of())) + .addValue(AuthorizerMapper.class, new AuthorizerMapper(ImmutableMap.of(), null)) .addValue(AppenderatorsManager.class, APPENDERATORS_MANAGER) .addValue(LocalDataSegmentPuller.class, new LocalDataSegmentPuller()) .addValue(IndexTaskClientFactory.class, TASK_CLIENT_FACTORY) diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/AbstractParallelIndexSupervisorTaskTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/AbstractParallelIndexSupervisorTaskTest.java index 45b0f383b110..caab0d6e9e04 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/AbstractParallelIndexSupervisorTaskTest.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/common/task/batch/parallel/AbstractParallelIndexSupervisorTaskTest.java @@ -524,7 +524,7 @@ public void prepareObjectMapper(ObjectMapper objectMapper, IndexIO indexIO) .addValue(AuthorizerMapper.class, null) .addValue(RowIngestionMetersFactory.class, new DropwizardRowIngestionMetersFactory()) .addValue(DataSegment.PruneSpecsHolder.class, DataSegment.PruneSpecsHolder.DEFAULT) - .addValue(AuthorizerMapper.class, new AuthorizerMapper(ImmutableMap.of())) + .addValue(AuthorizerMapper.class, new AuthorizerMapper(ImmutableMap.of(), null)) .addValue(AppenderatorsManager.class, TestUtils.APPENDERATORS_MANAGER) .addValue(LocalDataSegmentPuller.class, new LocalDataSegmentPuller()) .addValue(CoordinatorClient.class, coordinatorClient) diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/overlord/http/OverlordResourceTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/overlord/http/OverlordResourceTest.java index e41a6c9b462e..03d596be279e 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/overlord/http/OverlordResourceTest.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/overlord/http/OverlordResourceTest.java @@ -102,7 +102,7 @@ public void setUp() Optional.of(taskRunner) ).anyTimes(); - AuthorizerMapper authMapper = new AuthorizerMapper(null) + AuthorizerMapper authMapper = new AuthorizerMapper(null, null) { @Override public Authorizer getAuthorizer(String name) diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/overlord/supervisor/SupervisorResourceTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/overlord/supervisor/SupervisorResourceTest.java index 1390e7acc98c..710791f217e1 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/overlord/supervisor/SupervisorResourceTest.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/overlord/supervisor/SupervisorResourceTest.java @@ -86,7 +86,7 @@ public void setUp() { supervisorResource = new SupervisorResource( taskMaster, - new AuthorizerMapper(null) + new AuthorizerMapper(null, null) { @Override public Authorizer getAuthorizer(String name) diff --git a/server/src/main/java/org/apache/druid/query/lookup/LookupIntrospectionResource.java b/server/src/main/java/org/apache/druid/query/lookup/LookupIntrospectionResource.java index 52318d5957fd..1d409d8b14d8 100644 --- a/server/src/main/java/org/apache/druid/query/lookup/LookupIntrospectionResource.java +++ b/server/src/main/java/org/apache/druid/query/lookup/LookupIntrospectionResource.java @@ -46,25 +46,22 @@ public class LookupIntrospectionResource private static final Logger LOGGER = new Logger(LookupIntrospectionResource.class); private final LookupExtractorFactoryContainerProvider lookupExtractorFactoryContainerProvider; - private final AuthConfig authConfig; private AuthorizerMapper authorizerMapper; @Inject public LookupIntrospectionResource( @Context LookupExtractorFactoryContainerProvider lookupExtractorFactoryContainerProvider, - AuthConfig authConfig, AuthorizerMapper authorizerMapper ) { this.lookupExtractorFactoryContainerProvider = lookupExtractorFactoryContainerProvider; - this.authConfig = authConfig; this.authorizerMapper = authorizerMapper; } @Path("/{lookupId}") public Object introspectLookup(@PathParam("lookupId") final String lookupId, @Context HttpServletRequest request) { - if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + if (authorizerMapper.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { final Access access = AuthorizationUtils.authorizeResourceAction(request, new ResourceAction(new Resource(lookupId, ResourceType.LOOKUP), Action.READ), authorizerMapper diff --git a/server/src/main/java/org/apache/druid/server/QueryLifecycle.java b/server/src/main/java/org/apache/druid/server/QueryLifecycle.java index 3368059c5fe0..23e76663340e 100644 --- a/server/src/main/java/org/apache/druid/server/QueryLifecycle.java +++ b/server/src/main/java/org/apache/druid/server/QueryLifecycle.java @@ -144,7 +144,7 @@ public Sequence runSimple( final Sequence results; try { - final Access access = authorize(authenticationResult, authConfig); + final Access access = authorize(authenticationResult); if (!access.isAllowed()) { throw new ISE("Unauthorized"); } @@ -203,10 +203,10 @@ public void initialize(final Query baseQuery) * * @return authorization result */ - public Access authorize(final AuthenticationResult authenticationResult, final AuthConfig authConfig) + public Access authorize(final AuthenticationResult authenticationResult) { transition(State.INITIALIZED, State.AUTHORIZING); - if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + if (authorizerMapper.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { return doAuthorize( authenticationResult, AuthorizationUtils.authorizeAllResourceActions( diff --git a/server/src/main/java/org/apache/druid/server/http/CoordinatorCompactionConfigsResource.java b/server/src/main/java/org/apache/druid/server/http/CoordinatorCompactionConfigsResource.java index 362d0b3efb15..715ce6b92580 100644 --- a/server/src/main/java/org/apache/druid/server/http/CoordinatorCompactionConfigsResource.java +++ b/server/src/main/java/org/apache/druid/server/http/CoordinatorCompactionConfigsResource.java @@ -65,25 +65,22 @@ public class CoordinatorCompactionConfigsResource { private final JacksonConfigManager manager; private final AuthorizerMapper authorizerMapper; - private final AuthConfig authConfig; @Inject public CoordinatorCompactionConfigsResource( JacksonConfigManager manager, - AuthorizerMapper authorizerMapper, - AuthConfig authConfig + AuthorizerMapper authorizerMapper ) { this.manager = manager; this.authorizerMapper = authorizerMapper; - this.authConfig = authConfig; } @GET @Produces(MediaType.APPLICATION_JSON) public Response getCompactionConfig(@Context HttpServletRequest request) { - if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + if (authorizerMapper.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { return Response .ok(getFilteredConfigsByDatasource(CoordinatorCompactionConfig.current(manager), request, Action.READ)) .build(); @@ -136,7 +133,7 @@ public Response addOrUpdateCompactionConfig( @Context HttpServletRequest req ) { - if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + if (authorizerMapper.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { if (!AuthorizationUtils.authorizeResourceAction(req, AuthorizationUtils.DATASOURCE_WRITE_RA_GENERATOR.apply( newConfig.getDataSource()), authorizerMapper).isAllowed()) { return Response.status(Response.Status.FORBIDDEN).build(); @@ -169,7 +166,7 @@ public Response addOrUpdateCompactionConfig( @Produces(MediaType.APPLICATION_JSON) public Response getCompactionConfig(@PathParam("dataSource") String dataSource, @Context HttpServletRequest req) { - if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + if (authorizerMapper.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { if (!AuthorizationUtils.authorizeResourceAction( req, AuthorizationUtils.DATASOURCE_READ_RA_GENERATOR.apply(dataSource), @@ -202,7 +199,7 @@ public Response deleteCompactionConfig( @Context HttpServletRequest req ) { - if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + if (authorizerMapper.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { if (!AuthorizationUtils.authorizeResourceAction( req, AuthorizationUtils.DATASOURCE_WRITE_RA_GENERATOR.apply(dataSource), diff --git a/server/src/main/java/org/apache/druid/server/http/LookupCoordinatorResource.java b/server/src/main/java/org/apache/druid/server/http/LookupCoordinatorResource.java index a582c216b0dd..df3ef588c37a 100644 --- a/server/src/main/java/org/apache/druid/server/http/LookupCoordinatorResource.java +++ b/server/src/main/java/org/apache/druid/server/http/LookupCoordinatorResource.java @@ -91,22 +91,19 @@ public class LookupCoordinatorResource private final ObjectMapper smileMapper; private final ObjectMapper jsonMapper; private final AuthorizerMapper authorizerMapper; - private final AuthConfig authConfig; @Inject public LookupCoordinatorResource( final LookupCoordinatorManager lookupCoordinatorManager, final @Smile ObjectMapper smileMapper, final @Json ObjectMapper jsonMapper, - AuthorizerMapper authorizerMapper, - AuthConfig authConfig + AuthorizerMapper authorizerMapper ) { this.smileMapper = smileMapper; this.jsonMapper = jsonMapper; this.lookupCoordinatorManager = lookupCoordinatorManager; this.authorizerMapper = authorizerMapper; - this.authConfig = authConfig; } @GET @@ -150,7 +147,7 @@ public Response getAllLookupSpecs(@Context HttpServletRequest request) try { Map> knownLookups = lookupCoordinatorManager .getKnownLookups(); - if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + if (authorizerMapper.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { knownLookups = filterByLookupAccess(knownLookups, request, authorizerMapper, Action.READ); } if (knownLookups == null) { @@ -189,7 +186,7 @@ public Response updateAllLookups( catch (IOException e) { return Response.status(Response.Status.BAD_REQUEST).entity(ServletResourceUtils.sanitizeException(e)).build(); } - if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + if (authorizerMapper.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { final Access access = AuthorizationUtils .authorizeAllResourceActions(req, TIER_LOOKUP_RA_GENERATOR.apply(map, Action.WRITE), authorizerMapper); if (!access.isAllowed()) { @@ -226,7 +223,7 @@ public Response deleteTier( .build(); } - if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + if (authorizerMapper.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { Map tierLookups = lookupCoordinatorManager.getTierLookups(tier); final Access access = AuthorizationUtils .authorizeAllResourceActions(req, LOOKUP_RA_GENERATOR.apply(tierLookups, Action.WRITE), authorizerMapper); @@ -272,7 +269,7 @@ public Response deleteLookup( .build(); } - if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + if (authorizerMapper.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { final Access access = AuthorizationUtils .authorizeResourceAction(req, new ResourceAction(new Resource(lookup, ResourceType.LOOKUP), Action.WRITE), authorizerMapper); if (!access.isAllowed()) { @@ -318,7 +315,7 @@ public Response createOrUpdateLookup( .build(); } - if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + if (authorizerMapper.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { final Access access = AuthorizationUtils .authorizeResourceAction(req, new ResourceAction(new Resource(lookup, ResourceType.LOOKUP), Action.WRITE), authorizerMapper); if (!access.isAllowed()) { @@ -374,7 +371,7 @@ public Response getSpecificLookup( .build(); } - if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + if (authorizerMapper.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { final Access access = AuthorizationUtils .authorizeResourceAction(req, new ResourceAction(new Resource(lookup, ResourceType.LOOKUP), Action.READ), authorizerMapper); if (!access.isAllowed()) { @@ -418,7 +415,7 @@ public Response getSpecificTier( .entity(ServletResourceUtils.sanitizeException(new RE("No lookups found"))) .build(); } - if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + if (authorizerMapper.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { map = filterByLookupAccess(map, request, authorizerMapper, Action.READ); } final Map tierLookups = map.get(tier); @@ -456,7 +453,7 @@ public Response getAllLookupsStatus( .entity(ServletResourceUtils.jsonize("No lookups found")) .build(); } - if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + if (authorizerMapper.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { configuredLookups = filterByLookupAccess(configuredLookups, request, authorizerMapper, Action.READ); } @@ -511,7 +508,7 @@ public Response getLookupStatusForTier( .entity(ServletResourceUtils.jsonize("No lookups found")) .build(); } - if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + if (authorizerMapper.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { configuredLookups = filterByLookupAccess(configuredLookups, request, authorizerMapper, Action.READ); } @@ -563,7 +560,7 @@ public Response getSpecificLookupStatus( .entity(ServletResourceUtils.jsonize("No lookups found")) .build(); } - if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + if (authorizerMapper.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { final Access access = AuthorizationUtils .authorizeResourceAction(request, new ResourceAction(new Resource(lookup, ResourceType.LOOKUP), Action.READ), authorizerMapper); if (!access.isAllowed()) { diff --git a/server/src/main/java/org/apache/druid/server/http/RulesResource.java b/server/src/main/java/org/apache/druid/server/http/RulesResource.java index f844593239f1..ec78e6e5327a 100644 --- a/server/src/main/java/org/apache/druid/server/http/RulesResource.java +++ b/server/src/main/java/org/apache/druid/server/http/RulesResource.java @@ -65,20 +65,17 @@ public class RulesResource private final MetadataRuleManager databaseRuleManager; private final AuditManager auditManager; private final AuthorizerMapper authorizerMapper; - private final AuthConfig authConfig; @Inject public RulesResource( MetadataRuleManager databaseRuleManager, AuditManager auditManager, - AuthorizerMapper authorizerMapper, - AuthConfig authConfig + AuthorizerMapper authorizerMapper ) { this.databaseRuleManager = databaseRuleManager; this.auditManager = auditManager; this.authorizerMapper = authorizerMapper; - this.authConfig = authConfig; } @GET @@ -88,7 +85,7 @@ public Response getRules(@Context HttpServletRequest request) { final Map> rules = databaseRuleManager.getAllRules(); - if (authConfig.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + if (authorizerMapper.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { final Map> filteredRules = new HashMap<>(); AuthorizationUtils.filterAuthorizedResources( request, diff --git a/server/src/main/java/org/apache/druid/server/http/security/AbstractResourceFilter.java b/server/src/main/java/org/apache/druid/server/http/security/AbstractResourceFilter.java index dc09d54695ef..c8087c2346a2 100644 --- a/server/src/main/java/org/apache/druid/server/http/security/AbstractResourceFilter.java +++ b/server/src/main/java/org/apache/druid/server/http/security/AbstractResourceFilter.java @@ -25,7 +25,6 @@ import com.sun.jersey.spi.container.ContainerResponseFilter; import com.sun.jersey.spi.container.ResourceFilter; import org.apache.druid.server.security.Action; -import org.apache.druid.server.security.AuthConfig; import org.apache.druid.server.security.AuthorizerMapper; import javax.servlet.http.HttpServletRequest; @@ -38,13 +37,11 @@ public abstract class AbstractResourceFilter implements ResourceFilter, Containe private HttpServletRequest req; private AuthorizerMapper authorizerMapper; - private AuthConfig authConfig; @Inject - public AbstractResourceFilter(AuthorizerMapper authorizerMapper, AuthConfig authConfig) + public AbstractResourceFilter(AuthorizerMapper authorizerMapper) { this.authorizerMapper = authorizerMapper; - this.authConfig = authConfig; } @Override @@ -74,14 +71,9 @@ public void setAuthorizerMapper(AuthorizerMapper authorizerMapper) this.authorizerMapper = authorizerMapper; } - public AuthConfig getAuthConfig() + public String getAuthVersion() { - return authConfig; - } - - public void setAuthConfig(AuthConfig authConfig) - { - this.authConfig = authConfig; + return authorizerMapper.getAuthVersion(); } public AbstractResourceFilter setReq(HttpServletRequest req) diff --git a/server/src/main/java/org/apache/druid/server/http/security/ConfigResourceFilter.java b/server/src/main/java/org/apache/druid/server/http/security/ConfigResourceFilter.java index 10a083950e7a..960cd5a37bb0 100644 --- a/server/src/main/java/org/apache/druid/server/http/security/ConfigResourceFilter.java +++ b/server/src/main/java/org/apache/druid/server/http/security/ConfigResourceFilter.java @@ -45,15 +45,15 @@ public class ConfigResourceFilter extends AbstractResourceFilter { @Inject - public ConfigResourceFilter(AuthorizerMapper authorizerMapper, AuthConfig authConfig) + public ConfigResourceFilter(AuthorizerMapper authorizerMapper) { - super(authorizerMapper, authConfig); + super(authorizerMapper); } @Override public ContainerRequest filter(ContainerRequest request) { - if (getAuthConfig().getAuthVersion().equals(AuthConfig.AUTH_VERSION_1)) { + if (getAuthVersion().equals(AuthConfig.AUTH_VERSION_1)) { final ResourceAction resourceAction = new ResourceAction( new Resource("CONFIG", ResourceType.CONFIG), getAction(request) diff --git a/server/src/main/java/org/apache/druid/server/http/security/DatasourceResourceFilter.java b/server/src/main/java/org/apache/druid/server/http/security/DatasourceResourceFilter.java index dad25d93bf3c..64ba228adbba 100644 --- a/server/src/main/java/org/apache/druid/server/http/security/DatasourceResourceFilter.java +++ b/server/src/main/java/org/apache/druid/server/http/security/DatasourceResourceFilter.java @@ -25,7 +25,6 @@ import com.google.inject.Inject; import com.sun.jersey.spi.container.ContainerRequest; import org.apache.druid.server.security.Access; -import org.apache.druid.server.security.AuthConfig; import org.apache.druid.server.security.AuthorizationUtils; import org.apache.druid.server.security.AuthorizerMapper; import org.apache.druid.server.security.ForbiddenException; @@ -45,12 +44,9 @@ public class DatasourceResourceFilter extends AbstractResourceFilter { @Inject - public DatasourceResourceFilter( - AuthorizerMapper authorizerMapper, - AuthConfig authConfig - ) + public DatasourceResourceFilter(AuthorizerMapper authorizerMapper) { - super(authorizerMapper, authConfig); + super(authorizerMapper); } @Override diff --git a/server/src/main/java/org/apache/druid/server/http/security/InternalInternalResourceFilter.java b/server/src/main/java/org/apache/druid/server/http/security/InternalInternalResourceFilter.java index 6d1ed1574e99..f5f855700b7e 100644 --- a/server/src/main/java/org/apache/druid/server/http/security/InternalInternalResourceFilter.java +++ b/server/src/main/java/org/apache/druid/server/http/security/InternalInternalResourceFilter.java @@ -32,17 +32,15 @@ public class InternalInternalResourceFilter extends AbstractResourceFilter { @Inject - public InternalInternalResourceFilter(AuthorizerMapper authorizerMapper, - AuthConfig authConfig - ) + public InternalInternalResourceFilter(AuthorizerMapper authorizerMapper) { - super(authorizerMapper, authConfig); + super(authorizerMapper); } @Override public ContainerRequest filter(ContainerRequest request) { - if (getAuthConfig().getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + if (getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { final ResourceAction resourceAction = new ResourceAction( Resource.INTERNAL_INTERNAL_RESOURCE, getAction(request) diff --git a/server/src/main/java/org/apache/druid/server/http/security/LookupStatusResourceFilter.java b/server/src/main/java/org/apache/druid/server/http/security/LookupStatusResourceFilter.java index 037d1b66768b..a29890f56965 100644 --- a/server/src/main/java/org/apache/druid/server/http/security/LookupStatusResourceFilter.java +++ b/server/src/main/java/org/apache/druid/server/http/security/LookupStatusResourceFilter.java @@ -32,18 +32,15 @@ public class LookupStatusResourceFilter extends AbstractResourceFilter { @Inject - public LookupStatusResourceFilter( - AuthorizerMapper authorizerMapper, - AuthConfig authConfig - ) + public LookupStatusResourceFilter(AuthorizerMapper authorizerMapper) { - super(authorizerMapper, authConfig); + super(authorizerMapper); } @Override public ContainerRequest filter(ContainerRequest request) { - if (getAuthConfig().getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + if (getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { final ResourceAction resourceAction = new ResourceAction( Resource.LOOKUP_STATUS_RESOURCE, getAction(request) diff --git a/server/src/main/java/org/apache/druid/server/http/security/RulesResourceFilter.java b/server/src/main/java/org/apache/druid/server/http/security/RulesResourceFilter.java index 3458f62de1fb..dc5ddecfb3c8 100644 --- a/server/src/main/java/org/apache/druid/server/http/security/RulesResourceFilter.java +++ b/server/src/main/java/org/apache/druid/server/http/security/RulesResourceFilter.java @@ -25,7 +25,6 @@ import com.google.inject.Inject; import com.sun.jersey.spi.container.ContainerRequest; import org.apache.druid.server.security.Access; -import org.apache.druid.server.security.AuthConfig; import org.apache.druid.server.security.AuthorizationUtils; import org.apache.druid.server.security.AuthorizerMapper; import org.apache.druid.server.security.ForbiddenException; @@ -45,12 +44,9 @@ public class RulesResourceFilter extends AbstractResourceFilter { @Inject - public RulesResourceFilter( - AuthorizerMapper authorizerMapper, - AuthConfig authConfig - ) + public RulesResourceFilter(AuthorizerMapper authorizerMapper) { - super(authorizerMapper, authConfig); + super(authorizerMapper); } @Override diff --git a/server/src/main/java/org/apache/druid/server/http/security/ServerServerResourceFilter.java b/server/src/main/java/org/apache/druid/server/http/security/ServerServerResourceFilter.java index ee8132d1e509..c1799278ca8d 100644 --- a/server/src/main/java/org/apache/druid/server/http/security/ServerServerResourceFilter.java +++ b/server/src/main/java/org/apache/druid/server/http/security/ServerServerResourceFilter.java @@ -32,17 +32,15 @@ public class ServerServerResourceFilter extends AbstractResourceFilter { @Inject - public ServerServerResourceFilter(AuthorizerMapper authorizerMapper, - AuthConfig authConfig - ) + public ServerServerResourceFilter(AuthorizerMapper authorizerMapper) { - super(authorizerMapper, authConfig); + super(authorizerMapper); } @Override public ContainerRequest filter(ContainerRequest request) { - if (getAuthConfig().getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + if (getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { final ResourceAction resourceAction = new ResourceAction( Resource.SERVER_SERVER_RESOURCE, getAction(request) diff --git a/server/src/main/java/org/apache/druid/server/http/security/ServerStatusResourceFilter.java b/server/src/main/java/org/apache/druid/server/http/security/ServerStatusResourceFilter.java index f5e02712e3b0..eb260771884e 100644 --- a/server/src/main/java/org/apache/druid/server/http/security/ServerStatusResourceFilter.java +++ b/server/src/main/java/org/apache/druid/server/http/security/ServerStatusResourceFilter.java @@ -32,17 +32,15 @@ public class ServerStatusResourceFilter extends AbstractResourceFilter { @Inject - public ServerStatusResourceFilter(AuthorizerMapper authorizerMapper, - AuthConfig authConfig - ) + public ServerStatusResourceFilter(AuthorizerMapper authorizerMapper) { - super(authorizerMapper, authConfig); + super(authorizerMapper); } @Override public ContainerRequest filter(ContainerRequest request) { - if (getAuthConfig().getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + if (getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { final ResourceAction resourceAction = new ResourceAction( Resource.SERVER_STATUS_RESOURCE, getAction(request) diff --git a/server/src/main/java/org/apache/druid/server/http/security/ServerUserResourceFilter.java b/server/src/main/java/org/apache/druid/server/http/security/ServerUserResourceFilter.java index f53cf5a7a22d..d4331c287c6a 100644 --- a/server/src/main/java/org/apache/druid/server/http/security/ServerUserResourceFilter.java +++ b/server/src/main/java/org/apache/druid/server/http/security/ServerUserResourceFilter.java @@ -32,17 +32,15 @@ public class ServerUserResourceFilter extends AbstractResourceFilter { @Inject - public ServerUserResourceFilter(AuthorizerMapper authorizerMapper, - AuthConfig authConfig - ) + public ServerUserResourceFilter(AuthorizerMapper authorizerMapper) { - super(authorizerMapper, authConfig); + super(authorizerMapper); } @Override public ContainerRequest filter(ContainerRequest request) { - if (getAuthConfig().getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + if (getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { final ResourceAction resourceAction = new ResourceAction( Resource.SERVER_USER_RESOURCE, getAction(request) diff --git a/server/src/main/java/org/apache/druid/server/http/security/StateResourceFilter.java b/server/src/main/java/org/apache/druid/server/http/security/StateResourceFilter.java index b9e8e58ef1de..28abe2f438e8 100644 --- a/server/src/main/java/org/apache/druid/server/http/security/StateResourceFilter.java +++ b/server/src/main/java/org/apache/druid/server/http/security/StateResourceFilter.java @@ -50,15 +50,15 @@ public class StateResourceFilter extends AbstractResourceFilter { @Inject - public StateResourceFilter(AuthorizerMapper authorizerMapper, AuthConfig authConfig) + public StateResourceFilter(AuthorizerMapper authorizerMapper) { - super(authorizerMapper, authConfig); + super(authorizerMapper); } @Override public ContainerRequest filter(ContainerRequest request) { - if (getAuthConfig().getAuthVersion().equals(AuthConfig.AUTH_VERSION_1)) { + if (getAuthVersion().equals(AuthConfig.AUTH_VERSION_1)) { final ResourceAction resourceAction = new ResourceAction( Resource.STATE_RESOURCE, getAction(request) diff --git a/server/src/main/java/org/apache/druid/server/initialization/AuthorizerMapperModule.java b/server/src/main/java/org/apache/druid/server/initialization/AuthorizerMapperModule.java index f547d14c5f33..cef8480faa25 100644 --- a/server/src/main/java/org/apache/druid/server/initialization/AuthorizerMapperModule.java +++ b/server/src/main/java/org/apache/druid/server/initialization/AuthorizerMapperModule.java @@ -97,7 +97,7 @@ public AuthorizerMapper get() AllowAllAuthorizer allowAllAuthorizer = new AllowAllAuthorizer(); authorizerMap.put(AuthConfig.ALLOW_ALL_NAME, allowAllAuthorizer); - return new AuthorizerMapper(null) + return new AuthorizerMapper(null, authConfig.getAuthVersion()) { @Override public Authorizer getAuthorizer(String name) @@ -138,7 +138,7 @@ public Map getAuthorizerMap() authorizerMap.put(authorizerName, authorizer); } - return new AuthorizerMapper(authorizerMap); + return new AuthorizerMapper(authorizerMap, authConfig.getAuthVersion()); } } diff --git a/server/src/main/java/org/apache/druid/server/security/AuthTestUtils.java b/server/src/main/java/org/apache/druid/server/security/AuthTestUtils.java index 5922a5d234c4..9a9b35c8a30e 100644 --- a/server/src/main/java/org/apache/druid/server/security/AuthTestUtils.java +++ b/server/src/main/java/org/apache/druid/server/security/AuthTestUtils.java @@ -32,7 +32,7 @@ public class AuthTestUtils defaultMap.put(AuthConfig.ALLOW_ALL_NAME, new AllowAllAuthenticator()); TEST_AUTHENTICATOR_MAPPER = new AuthenticatorMapper(defaultMap); - TEST_AUTHORIZER_MAPPER = new AuthorizerMapper(null) { + TEST_AUTHORIZER_MAPPER = new AuthorizerMapper(null, null) { @Override public Authorizer getAuthorizer(String name) { diff --git a/server/src/main/java/org/apache/druid/server/security/AuthorizationUtils.java b/server/src/main/java/org/apache/druid/server/security/AuthorizationUtils.java index afb62eb8e3bb..f0e360bf5480 100644 --- a/server/src/main/java/org/apache/druid/server/security/AuthorizationUtils.java +++ b/server/src/main/java/org/apache/druid/server/security/AuthorizationUtils.java @@ -121,7 +121,8 @@ public static Access authorizeAllResourceActions( final Access access = authorizer.authorize( authenticationResult, resourceAction.getResource(), - resourceAction.getAction() + resourceAction.getAction(), + authorizerMapper.getAuthVersion() ); if (!access.isAllowed()) { return access; @@ -277,7 +278,8 @@ public static Iterable filterAuthorizedResources( ra -> authorizer.authorize( authenticationResult, ra.getResource(), - ra.getAction() + ra.getAction(), + authorizerMapper.getAuthVersion() ) ); if (!access.isAllowed()) { diff --git a/server/src/main/java/org/apache/druid/server/security/AuthorizerMapper.java b/server/src/main/java/org/apache/druid/server/security/AuthorizerMapper.java index e888cc14f9df..709bb655575a 100644 --- a/server/src/main/java/org/apache/druid/server/security/AuthorizerMapper.java +++ b/server/src/main/java/org/apache/druid/server/security/AuthorizerMapper.java @@ -27,12 +27,12 @@ public class AuthorizerMapper { private Map authorizerMap; + private String authVersion; - public AuthorizerMapper( - Map authorizerMap - ) + public AuthorizerMapper(Map authorizerMap, String authVersion) { this.authorizerMap = authorizerMap; + this.authVersion = authVersion == null ? AuthConfig.AUTH_VERSION_1 : authVersion; } public Authorizer getAuthorizer(String name) @@ -44,4 +44,9 @@ public Map getAuthorizerMap() { return authorizerMap; } + + public String getAuthVersion() + { + return authVersion; + } } diff --git a/server/src/test/java/org/apache/druid/query/lookup/LookupIntrospectionResourceTest.java b/server/src/test/java/org/apache/druid/query/lookup/LookupIntrospectionResourceTest.java index 817e68d0cbbd..bc85ba379a31 100644 --- a/server/src/test/java/org/apache/druid/query/lookup/LookupIntrospectionResourceTest.java +++ b/server/src/test/java/org/apache/druid/query/lookup/LookupIntrospectionResourceTest.java @@ -53,9 +53,11 @@ public class LookupIntrospectionResourceTest EasyMock.createMock(LookupIntrospectHandler.class); private LookupIntrospectionResource lookupIntrospectionResource = - new LookupIntrospectionResource(mockLookupExtractorFactoryContainerProvider, new AuthConfig(), + new LookupIntrospectionResource(mockLookupExtractorFactoryContainerProvider, new AuthorizerMapper( - ImmutableMap.of(AuthConfig.ALLOW_ALL_NAME, new AllowAllAuthorizer())) + ImmutableMap.of(AuthConfig.ALLOW_ALL_NAME, new AllowAllAuthorizer()), + null + ) ); private URI baseUri; diff --git a/server/src/test/java/org/apache/druid/server/AsyncQueryForwardingServletTest.java b/server/src/test/java/org/apache/druid/server/AsyncQueryForwardingServletTest.java index 066088c0eac4..beb4577daadb 100644 --- a/server/src/test/java/org/apache/druid/server/AsyncQueryForwardingServletTest.java +++ b/server/src/test/java/org/apache/druid/server/AsyncQueryForwardingServletTest.java @@ -132,7 +132,7 @@ public void configure(Binder binder) ); binder.bind(JettyServerInitializer.class).to(ProxyJettyServerInit.class).in(LazySingleton.class); binder.bind(AuthorizerMapper.class).toInstance( - new AuthorizerMapper(null) + new AuthorizerMapper(null, null) { @Override diff --git a/server/src/test/java/org/apache/druid/server/QueryResourceTest.java b/server/src/test/java/org/apache/druid/server/QueryResourceTest.java index 0a24b0edb8ad..2ed826640c46 100644 --- a/server/src/test/java/org/apache/druid/server/QueryResourceTest.java +++ b/server/src/test/java/org/apache/druid/server/QueryResourceTest.java @@ -550,7 +550,7 @@ public void testSecuredQuery() throws Exception EasyMock.replay(testServletRequest); - AuthorizerMapper authMapper = new AuthorizerMapper(null) + AuthorizerMapper authMapper = new AuthorizerMapper(null, null) { @Override public Authorizer getAuthorizer(String name) @@ -651,7 +651,7 @@ public void testSecuredCancelQuery() throws Exception EasyMock.replay(testServletRequest); - AuthorizerMapper authMapper = new AuthorizerMapper(null) + AuthorizerMapper authMapper = new AuthorizerMapper(null, null) { @Override public Authorizer getAuthorizer(String name) @@ -781,7 +781,7 @@ public void testDenySecuredCancelQuery() throws Exception EasyMock.replay(testServletRequest); - AuthorizerMapper authMapper = new AuthorizerMapper(null) + AuthorizerMapper authMapper = new AuthorizerMapper(null, null) { @Override public Authorizer getAuthorizer(String name) diff --git a/server/src/test/java/org/apache/druid/server/http/DataSourcesResourceTest.java b/server/src/test/java/org/apache/druid/server/http/DataSourcesResourceTest.java index 02747a354970..5c3e88821ce9 100644 --- a/server/src/test/java/org/apache/druid/server/http/DataSourcesResourceTest.java +++ b/server/src/test/java/org/apache/druid/server/http/DataSourcesResourceTest.java @@ -234,7 +234,7 @@ public void testSecuredGetFullQueryableDataSources() EasyMock.expect(inventoryView.getInventory()).andReturn(ImmutableList.of(server)).once(); EasyMock.replay(inventoryView, server, request); - AuthorizerMapper authMapper = new AuthorizerMapper(null) { + AuthorizerMapper authMapper = new AuthorizerMapper(null, null) { @Override public Authorizer getAuthorizer(String name) { diff --git a/server/src/test/java/org/apache/druid/server/http/LookupCoordinatorResourceTest.java b/server/src/test/java/org/apache/druid/server/http/LookupCoordinatorResourceTest.java index 551f89e2b0fd..bcf1c7960707 100644 --- a/server/src/test/java/org/apache/druid/server/http/LookupCoordinatorResourceTest.java +++ b/server/src/test/java/org/apache/druid/server/http/LookupCoordinatorResourceTest.java @@ -31,7 +31,7 @@ import org.apache.druid.query.lookup.LookupsState; import org.apache.druid.server.lookup.cache.LookupCoordinatorManager; import org.apache.druid.server.lookup.cache.LookupExtractorFactoryMapContainer; -import org.apache.druid.server.security.AuthConfig; +import org.apache.druid.server.security.AuthorizerMapper; import org.easymock.Capture; import org.easymock.EasyMock; import org.junit.Assert; @@ -103,8 +103,7 @@ public void testSimpleGet() lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.getTiers(false, null); Assert.assertEquals(200, response.getStatus()); @@ -123,8 +122,7 @@ public void testMissingGet() lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.getTiers(false, null); Assert.assertEquals(404, response.getStatus()); @@ -143,8 +141,7 @@ public void testExceptionalGet() lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.getTiers(false, null); Assert.assertEquals(500, response.getStatus()); @@ -165,8 +162,7 @@ public void testDiscoveryGet() lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.getTiers(true, null); Assert.assertEquals(200, response.getStatus()); @@ -189,8 +185,7 @@ public void testDiscoveryExceptionalGet() lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.getTiers(true, null); Assert.assertEquals(500, response.getStatus()); @@ -215,8 +210,7 @@ public void testSimpleGetLookup() lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.getSpecificLookup(LOOKUP_TIER, LOOKUP_NAME, null); Assert.assertEquals(200, response.getStatus()); @@ -234,8 +228,7 @@ public void testDetailedGetLookup() lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.getSpecificTier(LOOKUP_TIER, true, null); Assert.assertEquals(200, response.getStatus()); @@ -256,8 +249,7 @@ public void testMissingGetLookup() lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.getSpecificLookup(LOOKUP_TIER, LOOKUP_NAME, null); Assert.assertEquals(404, response.getStatus()); @@ -274,8 +266,7 @@ public void testInvalidGetLookup() lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); Assert.assertEquals(400, lookupCoordinatorResource.getSpecificLookup("foo", null, null).getStatus()); Assert.assertEquals(400, lookupCoordinatorResource.getSpecificLookup("foo", "", null).getStatus()); @@ -298,8 +289,7 @@ public void testExceptionalGetLookup() lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.getSpecificLookup(LOOKUP_TIER, LOOKUP_NAME, null); Assert.assertEquals(500, response.getStatus()); @@ -331,8 +321,7 @@ public void testSimpleDeleteTier() lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.deleteTier( LOOKUP_TIER, @@ -376,8 +365,7 @@ public void testSimpleDelete() lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.deleteLookup( LOOKUP_TIER, @@ -423,8 +411,7 @@ public void testMissingDelete() lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.deleteLookup( LOOKUP_TIER, @@ -471,8 +458,7 @@ public void testExceptionalDelete() lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.deleteLookup( LOOKUP_TIER, @@ -503,8 +489,7 @@ public void testInvalidDelete() lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); Assert.assertEquals(400, lookupCoordinatorResource.deleteLookup("foo", null, null, null, null).getStatus()); Assert.assertEquals(400, lookupCoordinatorResource.deleteLookup(null, null, null, null, null).getStatus()); @@ -537,8 +522,7 @@ public void testSimpleNew() throws Exception lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.updateAllLookups( SINGLE_TIER_MAP_SOURCE.openStream(), @@ -583,8 +567,7 @@ public void testExceptionalNew() throws Exception lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.updateAllLookups( SINGLE_TIER_MAP_SOURCE.openStream(), @@ -629,8 +612,7 @@ public void testFailedNew() throws Exception lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.updateAllLookups( SINGLE_TIER_MAP_SOURCE.openStream(), @@ -677,8 +659,7 @@ public void testSimpleNewLookup() throws Exception lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.createOrUpdateLookup( LOOKUP_TIER, @@ -727,8 +708,7 @@ public void testDBErrNewLookup() throws Exception lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.createOrUpdateLookup( LOOKUP_TIER, @@ -778,8 +758,7 @@ public void testExceptionalNewLookup() throws Exception lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.createOrUpdateLookup( LOOKUP_TIER, @@ -818,8 +797,7 @@ public void testNullValsNewLookup() throws Exception lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); EasyMock.replay(lookupCoordinatorManager, request); @@ -871,8 +849,7 @@ public void testSimpleGetTier() lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.getSpecificTier(LOOKUP_TIER, false, null); Assert.assertEquals(200, response.getStatus()); @@ -893,8 +870,7 @@ public void testMissingGetTier() lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.getSpecificTier(tier, false, null); Assert.assertEquals(404, response.getStatus()); @@ -911,8 +887,7 @@ public void testNullGetTier() lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.getSpecificTier(tier, false, null); Assert.assertEquals(400, response.getStatus()); @@ -931,8 +906,7 @@ public void testNullLookupsGetTier() lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.getSpecificTier(tier, false, null); Assert.assertEquals(404, response.getStatus()); @@ -952,8 +926,7 @@ public void testExceptionalGetTier() lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.getSpecificTier(tier, false, null); Assert.assertEquals(500, response.getStatus()); @@ -977,8 +950,7 @@ public void testGetAllLookupsStatus() lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.getAllLookupsStatus(false, null); @@ -1012,8 +984,7 @@ public void testGetLookupStatusForTier() lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.getLookupStatusForTier(LOOKUP_TIER, false, null); @@ -1044,8 +1015,7 @@ public void testGetSpecificLookupStatus() lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.getSpecificLookupStatus(LOOKUP_TIER, LOOKUP_NAME, false, null); @@ -1064,8 +1034,7 @@ public void testGetLookupStatusDetailedTrue() EasyMock.createStrictMock(LookupCoordinatorManager.class), MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); HostAndPort newNode = HostAndPort.fromParts("localhost", 4352); @@ -1088,8 +1057,7 @@ public void testGetLookupStatusDetailedFalse() EasyMock.createStrictMock(LookupCoordinatorManager.class), MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); HostAndPort newNode = HostAndPort.fromParts("localhost", 4352); @@ -1121,8 +1089,7 @@ public void testGetAllNodesStatus() lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.getAllNodesStatus(false, null); @@ -1157,8 +1124,7 @@ public void testGetAllNodesStatusDetailedFalse() lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.getAllNodesStatus(false, false); @@ -1194,8 +1160,7 @@ public void testGetNodesStatusInTier() lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.getNodesStatusInTier(LOOKUP_TIER); @@ -1224,8 +1189,7 @@ public void testGetSpecificNodeStatus() lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.getSpecificNodeStatus(LOOKUP_TIER, LOOKUP_NODE); @@ -1272,8 +1236,7 @@ public void testGetAllLookupSpecs() lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.getAllLookupSpecs(null); Assert.assertEquals(Status.OK.getStatusCode(), response.getStatus()); @@ -1295,8 +1258,7 @@ public void testGetEmptyAllLookupSpecs() lookupCoordinatorManager, MAPPER, MAPPER, - null, - new AuthConfig() + new AuthorizerMapper(null, null) ); final Response response = lookupCoordinatorResource.getAllLookupSpecs(null); Assert.assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus()); diff --git a/server/src/test/java/org/apache/druid/server/http/RulesResourceTest.java b/server/src/test/java/org/apache/druid/server/http/RulesResourceTest.java index 5bbfca314c42..72812a9af6bf 100644 --- a/server/src/test/java/org/apache/druid/server/http/RulesResourceTest.java +++ b/server/src/test/java/org/apache/druid/server/http/RulesResourceTest.java @@ -26,6 +26,7 @@ import org.apache.druid.java.util.common.DateTimes; import org.apache.druid.java.util.common.Intervals; import org.apache.druid.metadata.MetadataRuleManager; +import org.apache.druid.server.security.AuthorizerMapper; import org.easymock.EasyMock; import org.joda.time.Interval; import org.junit.Assert; @@ -78,7 +79,7 @@ public void testGetDatasourceRuleHistoryWithCount() .once(); EasyMock.replay(auditManager); - RulesResource rulesResource = new RulesResource(databaseRuleManager, auditManager, null, null); + RulesResource rulesResource = new RulesResource(databaseRuleManager, auditManager, new AuthorizerMapper(null, null)); Response response = rulesResource.getDatasourceRuleHistory("datasource1", null, 2); List rulesHistory = (List) response.getEntity(); @@ -121,7 +122,7 @@ public void testGetDatasourceRuleHistoryWithInterval() .once(); EasyMock.replay(auditManager); - RulesResource rulesResource = new RulesResource(databaseRuleManager, auditManager, null, null); + RulesResource rulesResource = new RulesResource(databaseRuleManager, auditManager, new AuthorizerMapper(null, null)); Response response = rulesResource.getDatasourceRuleHistory("datasource1", interval, null); List rulesHistory = (List) response.getEntity(); @@ -140,7 +141,7 @@ public void testGetDatasourceRuleHistoryWithWrongCount() .once(); EasyMock.replay(auditManager); - RulesResource rulesResource = new RulesResource(databaseRuleManager, auditManager, null, null); + RulesResource rulesResource = new RulesResource(databaseRuleManager, auditManager, new AuthorizerMapper(null, null)); Response response = rulesResource.getDatasourceRuleHistory("datasource1", null, -1); Map rulesHistory = (Map) response.getEntity(); @@ -181,7 +182,7 @@ public void testGetAllDatasourcesRuleHistoryWithCount() .once(); EasyMock.replay(auditManager); - RulesResource rulesResource = new RulesResource(databaseRuleManager, auditManager, null, null); + RulesResource rulesResource = new RulesResource(databaseRuleManager, auditManager, new AuthorizerMapper(null, null)); Response response = rulesResource.getDatasourceRuleHistory(null, 2); List rulesHistory = (List) response.getEntity(); @@ -224,7 +225,7 @@ public void testGetAllDatasourcesRuleHistoryWithInterval() .once(); EasyMock.replay(auditManager); - RulesResource rulesResource = new RulesResource(databaseRuleManager, auditManager, null, null); + RulesResource rulesResource = new RulesResource(databaseRuleManager, auditManager, new AuthorizerMapper(null, null)); Response response = rulesResource.getDatasourceRuleHistory(interval, null); List rulesHistory = (List) response.getEntity(); @@ -243,7 +244,7 @@ public void testGetAllDatasourcesRuleHistoryWithWrongCount() .once(); EasyMock.replay(auditManager); - RulesResource rulesResource = new RulesResource(databaseRuleManager, auditManager, null, null); + RulesResource rulesResource = new RulesResource(databaseRuleManager, auditManager, new AuthorizerMapper(null, null)); Response response = rulesResource.getDatasourceRuleHistory(null, -1); Map rulesHistory = (Map) response.getEntity(); diff --git a/sql/src/main/java/org/apache/druid/sql/calcite/schema/SystemSchema.java b/sql/src/main/java/org/apache/druid/sql/calcite/schema/SystemSchema.java index 4efa52c2e551..5bf22e0e2c77 100644 --- a/sql/src/main/java/org/apache/druid/sql/calcite/schema/SystemSchema.java +++ b/sql/src/main/java/org/apache/druid/sql/calcite/schema/SystemSchema.java @@ -216,9 +216,9 @@ public SystemSchema( { Preconditions.checkNotNull(serverView, "serverView"); this.tableMap = ImmutableMap.of( - SEGMENTS_TABLE, new SegmentsTable(druidSchema, metadataView, jsonMapper, authorizerMapper, authConfig), - SERVERS_TABLE, new ServersTable(druidNodeDiscoveryProvider, serverInventoryView, authorizerMapper, authConfig), - SERVER_SEGMENTS_TABLE, new ServerSegmentsTable(serverView, authorizerMapper, authConfig), + SEGMENTS_TABLE, new SegmentsTable(druidSchema, metadataView, jsonMapper, authorizerMapper), + SERVERS_TABLE, new ServersTable(druidNodeDiscoveryProvider, serverInventoryView, authorizerMapper), + SERVER_SEGMENTS_TABLE, new ServerSegmentsTable(serverView, authorizerMapper), TASKS_TABLE, new TasksTable(overlordDruidLeaderClient, jsonMapper, authorizerMapper), SUPERVISOR_TABLE, new SupervisorsTable(overlordDruidLeaderClient, jsonMapper, authorizerMapper) ); @@ -239,21 +239,18 @@ static class SegmentsTable extends AbstractTable implements ScannableTable private final ObjectMapper jsonMapper; private final AuthorizerMapper authorizerMapper; private final MetadataSegmentView metadataView; - private final AuthConfig authConfig; public SegmentsTable( DruidSchema druidSchemna, MetadataSegmentView metadataView, ObjectMapper jsonMapper, - AuthorizerMapper authorizerMapper, - AuthConfig authConfig + AuthorizerMapper authorizerMapper ) { this.druidSchema = druidSchemna; this.metadataView = metadataView; this.jsonMapper = jsonMapper; this.authorizerMapper = authorizerMapper; - this.authConfig = authConfig; } @Override @@ -484,19 +481,16 @@ static class ServersTable extends AbstractTable implements ScannableTable private final AuthorizerMapper authorizerMapper; private final DruidNodeDiscoveryProvider druidNodeDiscoveryProvider; private final InventoryView serverInventoryView; - private final AuthConfig authConfig; public ServersTable( DruidNodeDiscoveryProvider druidNodeDiscoveryProvider, InventoryView serverInventoryView, - AuthorizerMapper authorizerMapper, - AuthConfig authConfig + AuthorizerMapper authorizerMapper ) { this.authorizerMapper = authorizerMapper; this.druidNodeDiscoveryProvider = druidNodeDiscoveryProvider; this.serverInventoryView = serverInventoryView; - this.authConfig = authConfig; } @Override @@ -519,7 +513,7 @@ public Enumerable scan(DataContext root) root.get(PlannerContext.DATA_CTX_AUTHENTICATION_RESULT), "authenticationResult in dataContext" ); - checkStateReadAccessForServers(authenticationResult, authorizerMapper, authConfig.getAuthVersion()); + checkStateReadAccessForServers(authenticationResult, authorizerMapper, authorizerMapper.getAuthVersion()); final FluentIterable results = FluentIterable .from(() -> druidServers) @@ -645,13 +639,11 @@ static class ServerSegmentsTable extends AbstractTable implements ScannableTable { private final TimelineServerView serverView; private final AuthorizerMapper authorizerMapper; - private final AuthConfig authConfig; - public ServerSegmentsTable(TimelineServerView serverView, AuthorizerMapper authorizerMapper, AuthConfig authConfig) + public ServerSegmentsTable(TimelineServerView serverView, AuthorizerMapper authorizerMapper) { this.serverView = serverView; this.authorizerMapper = authorizerMapper; - this.authConfig = authConfig; } @Override @@ -673,7 +665,7 @@ public Enumerable scan(DataContext root) root.get(PlannerContext.DATA_CTX_AUTHENTICATION_RESULT), "authenticationResult in dataContext" ); - checkStateReadAccessForServers(authenticationResult, authorizerMapper, authConfig.getAuthVersion()); + checkStateReadAccessForServers(authenticationResult, authorizerMapper, authorizerMapper.getAuthVersion()); final List rows = new ArrayList<>(); final List druidServers = serverView.getDruidServers(); diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/schema/SystemSchemaTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/schema/SystemSchemaTest.java index 3746fd7a01f5..435da11999d5 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/schema/SystemSchemaTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/schema/SystemSchemaTest.java @@ -192,7 +192,7 @@ public void setUp() throws Exception .addMockedMethod("getStatus") .createMock(); request = EasyMock.createMock(Request.class); - authMapper = new AuthorizerMapper(null) + authMapper = new AuthorizerMapper(null, null) { @Override public Authorizer getAuthorizer(String name) @@ -526,9 +526,7 @@ public void testGetTableMap() @Test public void testSegmentsTable() throws Exception { - final SegmentsTable segmentsTable = new SegmentsTable(druidSchema, metadataView, new ObjectMapper(), authMapper, - new AuthConfig() - ); + final SegmentsTable segmentsTable = new SegmentsTable(druidSchema, metadataView, new ObjectMapper(), authMapper); final Set publishedSegments = new HashSet<>(Arrays.asList( new SegmentWithOvershadowedStatus(publishedCompactedSegment1, true), new SegmentWithOvershadowedStatus(publishedCompactedSegment2, false), diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java b/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java index a95fa7db16ff..6139ba796c82 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/util/CalciteTests.java @@ -165,7 +165,7 @@ public class CalciteTests public static final String LOOKUP_SCHEMA_NAME = "lookup"; public static final String TEST_SUPERUSER_NAME = "testSuperuser"; - public static final AuthorizerMapper TEST_AUTHORIZER_MAPPER = new AuthorizerMapper(null) + public static final AuthorizerMapper TEST_AUTHORIZER_MAPPER = new AuthorizerMapper(null, null) { @Override public Authorizer getAuthorizer(String name) From 8f6752d099b7431d5f4a3994658182372b742ddd Mon Sep 17 00:00:00 2001 From: Parag Jain Date: Thu, 5 Nov 2020 02:11:01 +0530 Subject: [PATCH 05/15] Resource filter auth v2 tests, deprecate BasicSecurityResourceFilter --- .../basic/BasicSecurityResourceFilter.java | 40 +++--- .../endpoint/BasicAuthenticatorResource.java | 19 +-- .../endpoint/BasicAuthorizerResource.java | 51 ++++---- .../OverlordSecurityResourceFilterTest.java | 20 ++- .../http/LookupCoordinatorResource.java | 4 +- .../security/LookupStatusResourceFilter.java | 61 --------- .../security/ResourceFilterTestHelper.java | 117 ++++++++++++------ .../security/SecurityResourceFilterTest.java | 18 ++- 8 files changed, 164 insertions(+), 166 deletions(-) delete mode 100644 server/src/main/java/org/apache/druid/server/http/security/LookupStatusResourceFilter.java diff --git a/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/BasicSecurityResourceFilter.java b/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/BasicSecurityResourceFilter.java index fa628599c909..7ac60e0b3b3a 100644 --- a/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/BasicSecurityResourceFilter.java +++ b/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/BasicSecurityResourceFilter.java @@ -34,12 +34,15 @@ import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; +@Deprecated public class BasicSecurityResourceFilter extends AbstractResourceFilter { private static final String SECURITY_RESOURCE_NAME = "security"; @Inject - public BasicSecurityResourceFilter(AuthorizerMapper authorizerMapper) + public BasicSecurityResourceFilter( + AuthorizerMapper authorizerMapper + ) { super(authorizerMapper); } @@ -47,33 +50,26 @@ public BasicSecurityResourceFilter(AuthorizerMapper authorizerMapper) @Override public ContainerRequest filter(ContainerRequest request) { - final ResourceAction resourceAction; - if (getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { - resourceAction = new ResourceAction( - Resource.SERVER_SERVER_RESOURCE, - getAction(request) - ); - } else { - resourceAction = new ResourceAction( + if (getAuthVersion().equals(AuthConfig.AUTH_VERSION_1)) { + final ResourceAction resourceAction = new ResourceAction( new Resource(SECURITY_RESOURCE_NAME, ResourceType.CONFIG), getAction(request) ); - } - final Access authResult = AuthorizationUtils.authorizeResourceAction( - getReq(), - resourceAction, - getAuthorizerMapper() - ); - - if (!authResult.isAllowed()) { - throw new WebApplicationException( - Response.status(Response.Status.FORBIDDEN) - .entity(StringUtils.format("Access-Check-Result: %s", authResult.toString())) - .build() + final Access authResult = AuthorizationUtils.authorizeResourceAction( + getReq(), + resourceAction, + getAuthorizerMapper() ); - } + if (!authResult.isAllowed()) { + throw new WebApplicationException( + Response.status(Response.Status.FORBIDDEN) + .entity(StringUtils.format("Access-Check-Result: %s", authResult.toString())) + .build() + ); + } + } return request; } } diff --git a/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authentication/endpoint/BasicAuthenticatorResource.java b/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authentication/endpoint/BasicAuthenticatorResource.java index 23d8316771c4..4eb93f84a641 100644 --- a/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authentication/endpoint/BasicAuthenticatorResource.java +++ b/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authentication/endpoint/BasicAuthenticatorResource.java @@ -25,6 +25,7 @@ import org.apache.druid.guice.LazySingleton; import org.apache.druid.security.basic.BasicSecurityResourceFilter; import org.apache.druid.security.basic.authentication.entity.BasicAuthenticatorCredentialUpdate; +import org.apache.druid.server.http.security.ServerServerResourceFilter; import org.apache.druid.server.security.AuthValidator; import javax.servlet.http.HttpServletRequest; @@ -65,7 +66,7 @@ public BasicAuthenticatorResource( @Path("/loadStatus") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response getLoadStatus( @Context HttpServletRequest req ) @@ -83,7 +84,7 @@ public Response getLoadStatus( @Path("/refreshAll") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response refreshAll( @Context HttpServletRequest req ) @@ -100,7 +101,7 @@ public Response refreshAll( @Path("/db/{authenticatorName}/users") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response getAllUsers( @Context HttpServletRequest req, @PathParam("authenticatorName") final String authenticatorName @@ -120,7 +121,7 @@ public Response getAllUsers( @Path("/db/{authenticatorName}/users/{userName}") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response getUser( @Context HttpServletRequest req, @PathParam("authenticatorName") final String authenticatorName, @@ -143,7 +144,7 @@ public Response getUser( @Path("/db/{authenticatorName}/users/{userName}") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response createUser( @Context HttpServletRequest req, @PathParam("authenticatorName") final String authenticatorName, @@ -166,7 +167,7 @@ public Response createUser( @Path("/db/{authenticatorName}/users/{userName}") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response deleteUser( @Context HttpServletRequest req, @PathParam("authenticatorName") final String authenticatorName, @@ -189,7 +190,7 @@ public Response deleteUser( @Path("/db/{authenticatorName}/users/{userName}/credentials") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response updateUserCredentials( @Context HttpServletRequest req, @PathParam("authenticatorName") final String authenticatorName, @@ -210,7 +211,7 @@ public Response updateUserCredentials( @Path("/db/{authenticatorName}/cachedSerializedUserMap") @Produces(SmileMediaTypes.APPLICATION_JACKSON_SMILE) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response getCachedSerializedUserMap( @Context HttpServletRequest req, @PathParam("authenticatorName") final String authenticatorName @@ -227,7 +228,7 @@ public Response getCachedSerializedUserMap( @Path("/listen/{authenticatorName}") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response authenticatorUpdateListener( @Context HttpServletRequest req, @PathParam("authenticatorName") final String authenticatorName, diff --git a/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/endpoint/BasicAuthorizerResource.java b/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/endpoint/BasicAuthorizerResource.java index cb8a9fa2a240..a7bb2447ab9e 100644 --- a/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/endpoint/BasicAuthorizerResource.java +++ b/extensions-core/druid-basic-security/src/main/java/org/apache/druid/security/basic/authorization/endpoint/BasicAuthorizerResource.java @@ -25,6 +25,7 @@ import org.apache.druid.guice.LazySingleton; import org.apache.druid.security.basic.BasicSecurityResourceFilter; import org.apache.druid.security.basic.authorization.entity.BasicAuthorizerGroupMapping; +import org.apache.druid.server.http.security.ServerServerResourceFilter; import org.apache.druid.server.security.AuthValidator; import org.apache.druid.server.security.ResourceAction; @@ -68,7 +69,7 @@ public BasicAuthorizerResource( @Path("/loadStatus") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response getLoadStatus( @Context HttpServletRequest req ) @@ -86,7 +87,7 @@ public Response getLoadStatus( @Path("/refreshAll") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response refreshAll( @Context HttpServletRequest req ) @@ -104,7 +105,7 @@ public Response refreshAll( @Path("/db/{authorizerName}/users") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response getAllUsers( @Context HttpServletRequest req, @PathParam("authorizerName") final String authorizerName @@ -123,7 +124,7 @@ public Response getAllUsers( @Path("/db/{authorizerName}/groupMappings") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response getAllGroupMappings( @Context HttpServletRequest req, @PathParam("authorizerName") final String authorizerName @@ -143,7 +144,7 @@ public Response getAllGroupMappings( @Path("/db/{authorizerName}/users/{userName}") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response getUser( @Context HttpServletRequest req, @PathParam("authorizerName") final String authorizerName, @@ -166,7 +167,7 @@ public Response getUser( @Path("/db/{authorizerName}/groupMappings/{groupMappingName}") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response getGroupMapping( @Context HttpServletRequest req, @PathParam("authorizerName") final String authorizerName, @@ -190,7 +191,7 @@ public Response getGroupMapping( @Path("/db/{authorizerName}/users/{userName}") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response createUser( @Context HttpServletRequest req, @PathParam("authorizerName") final String authorizerName, @@ -213,7 +214,7 @@ public Response createUser( @Path("/db/{authorizerName}/users/{userName}") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response deleteUser( @Context HttpServletRequest req, @PathParam("authorizerName") final String authorizerName, @@ -236,7 +237,7 @@ public Response deleteUser( @Path("/db/{authorizerName}/groupMappings/{groupMappingName}") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response createGroupMapping( @Context HttpServletRequest req, @PathParam("authorizerName") final String authorizerName, @@ -263,7 +264,7 @@ public Response createGroupMapping( @Path("/db/{authorizerName}/groupMappings/{groupMappingName}") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response deleteGroupMapping( @Context HttpServletRequest req, @PathParam("authorizerName") final String authorizerName, @@ -283,7 +284,7 @@ public Response deleteGroupMapping( @Path("/db/{authorizerName}/roles") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response getAllRoles( @Context HttpServletRequest req, @PathParam("authorizerName") final String authorizerName @@ -305,7 +306,7 @@ public Response getAllRoles( @Path("/db/{authorizerName}/roles/{roleName}") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response getRole( @Context HttpServletRequest req, @PathParam("authorizerName") final String authorizerName, @@ -330,7 +331,7 @@ public Response getRole( @Path("/db/{authorizerName}/roles/{roleName}") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response createRole( @Context HttpServletRequest req, @PathParam("authorizerName") final String authorizerName, @@ -353,7 +354,7 @@ public Response createRole( @Path("/db/{authorizerName}/roles/{roleName}") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response deleteRole( @Context HttpServletRequest req, @PathParam("authorizerName") final String authorizerName, @@ -377,7 +378,7 @@ public Response deleteRole( @Path("/db/{authorizerName}/users/{userName}/roles/{roleName}") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response assignRoleToUser( @Context HttpServletRequest req, @PathParam("authorizerName") final String authorizerName, @@ -402,7 +403,7 @@ public Response assignRoleToUser( @Path("/db/{authorizerName}/users/{userName}/roles/{roleName}") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response unassignRoleFromUser( @Context HttpServletRequest req, @PathParam("authorizerName") final String authorizerName, @@ -427,7 +428,7 @@ public Response unassignRoleFromUser( @Path("/db/{authorizerName}/groupMappings/{groupMappingName}/roles/{roleName}") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response assignRoleToGroupMapping( @Context HttpServletRequest req, @PathParam("authorizerName") final String authorizerName, @@ -452,7 +453,7 @@ public Response assignRoleToGroupMapping( @Path("/db/{authorizerName}/groupMappings/{groupMappingName}/roles/{roleName}") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response unassignRoleFromGroupMapping( @Context HttpServletRequest req, @PathParam("authorizerName") final String authorizerName, @@ -477,7 +478,7 @@ public Response unassignRoleFromGroupMapping( @Path("/db/{authorizerName}/roles/{roleName}/permissions") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response setRolePermissions( @Context HttpServletRequest req, @PathParam("authorizerName") final String authorizerName, @@ -501,7 +502,7 @@ public Response setRolePermissions( @Path("/db/{authorizerName}/roles/{roleName}/permissions") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response getRolePermissions( @Context HttpServletRequest req, @PathParam("authorizerName") final String authorizerName, @@ -521,7 +522,7 @@ public Response getRolePermissions( @Path("/db/{authorizerName}/cachedSerializedUserMap") @Produces(SmileMediaTypes.APPLICATION_JACKSON_SMILE) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response getCachedSerializedUserMap( @Context HttpServletRequest req, @PathParam("authorizerName") final String authorizerName @@ -540,7 +541,7 @@ public Response getCachedSerializedUserMap( @Path("/db/{authorizerName}/cachedSerializedGroupMappingMap") @Produces(SmileMediaTypes.APPLICATION_JACKSON_SMILE) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response getCachedSerializedGroupMap( @Context HttpServletRequest req, @PathParam("authorizerName") final String authorizerName @@ -560,7 +561,7 @@ public Response getCachedSerializedGroupMap( @Path("/listen/{authorizerName}") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) @Deprecated public Response authorizerUpdateListener( @Context HttpServletRequest req, @@ -579,7 +580,7 @@ public Response authorizerUpdateListener( @Path("/listen/users/{authorizerName}") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response authorizerUserUpdateListener( @Context HttpServletRequest req, @PathParam("authorizerName") final String authorizerName, @@ -597,7 +598,7 @@ public Response authorizerUserUpdateListener( @Path("/listen/groupMappings/{authorizerName}") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(BasicSecurityResourceFilter.class) + @ResourceFilters({ BasicSecurityResourceFilter.class, ServerServerResourceFilter.class }) public Response authorizerGroupMappingUpdateListener( @Context HttpServletRequest req, @PathParam("authorizerName") final String authorizerName, diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/overlord/http/security/OverlordSecurityResourceFilterTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/overlord/http/security/OverlordSecurityResourceFilterTest.java index e603a553e244..80a7fcefc44d 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/overlord/http/security/OverlordSecurityResourceFilterTest.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/overlord/http/security/OverlordSecurityResourceFilterTest.java @@ -52,9 +52,11 @@ public class OverlordSecurityResourceFilterTest extends ResourceFilterTestHelper { private static final Pattern WORD = Pattern.compile("\\w+"); - @Parameterized.Parameters(name = "{index}: requestPath={0}, requestMethod={1}, resourceFilter={2}") + @Parameterized.Parameters(name = "{index}: requestPath={0}, requestMethod={1}, resourceFilter={2}, authVersion={4}, performAuth={5}") public static Collection data() { + ResourceFilterTestHelper.currentFilters.add(TaskResourceFilter.class); + ResourceFilterTestHelper.currentFilters.add(SupervisorResourceFilter.class); return ImmutableList.copyOf( Iterables.concat( getRequestPaths(OverlordResource.class, ImmutableList.of( @@ -79,6 +81,8 @@ public static Collection data() private final ResourceFilter resourceFilter; private final Injector injector; private final Task noopTask = NoopTask.create(); + private final String authVersion; + private final boolean performAuth; private static boolean mockedOnceTsqa; private static boolean mockedOnceSM; @@ -89,13 +93,17 @@ public OverlordSecurityResourceFilterTest( String requestPath, String requestMethod, ResourceFilter resourceFilter, - Injector injector + Injector injector, + String authVersion, + boolean performAuth ) { this.requestPath = requestPath; this.requestMethod = requestMethod; this.resourceFilter = resourceFilter; this.injector = injector; + this.authVersion = authVersion; + this.performAuth = performAuth; } @Before @@ -174,7 +182,7 @@ public String getSource() @Test public void testResourcesFilteringAccess() { - setUpMockExpectations(requestPath, true, requestMethod); + setUpMockExpectations(requestPath, true, requestMethod, authVersion, performAuth); EasyMock.expect(request.getEntity(Task.class)).andReturn(noopTask).anyTimes(); // As request object is a strict mock the ordering of expected calls matters // therefore adding the expectation below again as getEntity is called before getMethod @@ -186,11 +194,15 @@ public void testResourcesFilteringAccess() @Test(expected = ForbiddenException.class) public void testDatasourcesResourcesFilteringNoAccess() { - setUpMockExpectations(requestPath, false, requestMethod); + setUpMockExpectations(requestPath, false, requestMethod, authVersion, performAuth); EasyMock.expect(request.getEntity(Task.class)).andReturn(noopTask).anyTimes(); EasyMock.replay(req, request, authorizerMapper); try { resourceFilter.getRequestFilter().filter(request); + if (!performAuth) { + // if auth does not need to be performed, exception will not be thrown so do it manually + throw new ForbiddenException(); + } } catch (ForbiddenException e) { throw e; diff --git a/server/src/main/java/org/apache/druid/server/http/LookupCoordinatorResource.java b/server/src/main/java/org/apache/druid/server/http/LookupCoordinatorResource.java index df3ef588c37a..71b481a41063 100644 --- a/server/src/main/java/org/apache/druid/server/http/LookupCoordinatorResource.java +++ b/server/src/main/java/org/apache/druid/server/http/LookupCoordinatorResource.java @@ -39,8 +39,8 @@ import org.apache.druid.java.util.common.logger.Logger; import org.apache.druid.query.lookup.LookupsState; import org.apache.druid.server.http.security.ConfigResourceFilter; -import org.apache.druid.server.http.security.LookupStatusResourceFilter; import org.apache.druid.server.http.security.ServerServerResourceFilter; +import org.apache.druid.server.http.security.ServerUserResourceFilter; import org.apache.druid.server.lookup.cache.LookupCoordinatorManager; import org.apache.druid.server.lookup.cache.LookupExtractorFactoryMapContainer; import org.apache.druid.server.security.Access; @@ -109,7 +109,7 @@ public LookupCoordinatorResource( @GET @Path("/config") @Produces({MediaType.APPLICATION_JSON, SmileMediaTypes.APPLICATION_JACKSON_SMILE}) - @ResourceFilters({ ConfigResourceFilter.class, LookupStatusResourceFilter.class }) + @ResourceFilters({ ConfigResourceFilter.class, ServerUserResourceFilter.class }) public Response getTiers( @DefaultValue("false") @QueryParam("discover") boolean discover, @Context HttpServletRequest request diff --git a/server/src/main/java/org/apache/druid/server/http/security/LookupStatusResourceFilter.java b/server/src/main/java/org/apache/druid/server/http/security/LookupStatusResourceFilter.java deleted file mode 100644 index a29890f56965..000000000000 --- a/server/src/main/java/org/apache/druid/server/http/security/LookupStatusResourceFilter.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.druid.server.http.security; - -import com.google.inject.Inject; -import com.sun.jersey.spi.container.ContainerRequest; -import org.apache.druid.server.security.Access; -import org.apache.druid.server.security.AuthConfig; -import org.apache.druid.server.security.AuthorizationUtils; -import org.apache.druid.server.security.AuthorizerMapper; -import org.apache.druid.server.security.ForbiddenException; -import org.apache.druid.server.security.Resource; -import org.apache.druid.server.security.ResourceAction; - -public class LookupStatusResourceFilter extends AbstractResourceFilter -{ - @Inject - public LookupStatusResourceFilter(AuthorizerMapper authorizerMapper) - { - super(authorizerMapper); - } - - @Override - public ContainerRequest filter(ContainerRequest request) - { - if (getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { - final ResourceAction resourceAction = new ResourceAction( - Resource.LOOKUP_STATUS_RESOURCE, - getAction(request) - ); - - final Access authResult = AuthorizationUtils.authorizeResourceAction( - getReq(), - resourceAction, - getAuthorizerMapper() - ); - - if (!authResult.isAllowed()) { - throw new ForbiddenException(authResult.toString()); - } - } - return request; - } -} diff --git a/server/src/test/java/org/apache/druid/server/http/security/ResourceFilterTestHelper.java b/server/src/test/java/org/apache/druid/server/http/security/ResourceFilterTestHelper.java index ffb2409352e6..ca0aaa534f11 100644 --- a/server/src/test/java/org/apache/druid/server/http/security/ResourceFilterTestHelper.java +++ b/server/src/test/java/org/apache/druid/server/http/security/ResourceFilterTestHelper.java @@ -25,6 +25,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; import com.google.inject.Binder; import com.google.inject.Guice; import com.google.inject.Injector; @@ -33,6 +34,7 @@ import com.sun.jersey.spi.container.ContainerRequest; import com.sun.jersey.spi.container.ResourceFilter; import com.sun.jersey.spi.container.ResourceFilters; +import org.apache.druid.java.util.common.Pair; import org.apache.druid.java.util.common.StringUtils; import org.apache.druid.server.security.Access; import org.apache.druid.server.security.Action; @@ -58,16 +60,27 @@ import java.util.List; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; public class ResourceFilterTestHelper { - private static Set> whitelistedForTestFilters = ImmutableSet.of( + private static final Set> LEGACY_FILTERS = ImmutableSet.of( ConfigResourceFilter.class, - DatasourceResourceFilter.class, - RulesResourceFilter.class, StateResourceFilter.class ); + public static Set> currentFilters = Sets.newHashSet( + DatasourceResourceFilter.class, + RulesResourceFilter.class + ); + + private static final Set> NEW_FILTERS = ImmutableSet.of( + InternalInternalResourceFilter.class, + ServerServerResourceFilter.class, + ServerStatusResourceFilter.class, + ServerUserResourceFilter.class + ); + public HttpServletRequest req; public AuthorizerMapper authorizerMapper; public ContainerRequest request; @@ -76,7 +89,7 @@ public void setUp(ResourceFilter resourceFilter) { req = EasyMock.createStrictMock(HttpServletRequest.class); request = EasyMock.createStrictMock(ContainerRequest.class); - authorizerMapper = EasyMock.createStrictMock(AuthorizerMapper.class); + authorizerMapper = EasyMock.createMock(AuthorizerMapper.class); // Memory barrier synchronized (this) { @@ -88,7 +101,9 @@ public void setUp(ResourceFilter resourceFilter) public void setUpMockExpectations( String requestPath, boolean authCheckResult, - String requestMethod + String requestMethod, + String authVersion, + boolean performAuth ) { EasyMock.expect(request.getPath()).andReturn(requestPath).anyTimes(); @@ -124,24 +139,32 @@ public MultivaluedMap getMatrixParameters() EasyMock.expect(req.getAttribute(AuthConfig.DRUID_ALLOW_UNSECURED_PATH)).andReturn(null).anyTimes(); EasyMock.expect(req.getAttribute(AuthConfig.DRUID_AUTHORIZATION_CHECKED)).andReturn(null).anyTimes(); AuthenticationResult authenticationResult = new AuthenticationResult("druid", "druid", null, null); - EasyMock.expect(req.getAttribute(AuthConfig.DRUID_AUTHENTICATION_RESULT)) - .andReturn(authenticationResult) - .atLeastOnce(); - req.setAttribute(AuthConfig.DRUID_AUTHORIZATION_CHECKED, authCheckResult); - EasyMock.expectLastCall().anyTimes(); - EasyMock.expect(authorizerMapper.getAuthorizer( - EasyMock.anyString() - )).andReturn( - new Authorizer() - { - @Override - public Access authorize(AuthenticationResult authenticationResult1, Resource resource, Action action) + + EasyMock.expect(authorizerMapper.getAuthVersion()).andReturn(authVersion).anyTimes(); + if (performAuth) { + EasyMock.expect(req.getAttribute(AuthConfig.DRUID_AUTHENTICATION_RESULT)) + .andReturn(authenticationResult) + .atLeastOnce(); + EasyMock.expect(authorizerMapper.getAuthorizer( + EasyMock.anyString() + )).andReturn( + new Authorizer() { - return new Access(authCheckResult); + @Override + public Access authorize(AuthenticationResult authenticationResult1, Resource resource, Action action) + { + return new Access(authCheckResult); + } + @Override + public Access authorizeV2(AuthenticationResult authenticationResult1, Resource resource, Action action) + { + return new Access(authCheckResult); + } } - - } - ).atLeastOnce(); + ).atLeastOnce(); + req.setAttribute(AuthConfig.DRUID_AUTHORIZATION_CHECKED, authCheckResult); + EasyMock.expectLastCall().anyTimes(); + } } public static Collection getRequestPathsWithAuthorizer(final AnnotatedElement classOrMethod) @@ -239,29 +262,38 @@ public Collection apply(final Method method) { final List> resourceFilters = method.getAnnotation(ResourceFilters.class) == null ? baseResourceFilters : - ImmutableList.copyOf(method.getAnnotation(ResourceFilters.class).value()); - final List> enabledForTestResourceFilters = getEnabledForTestFilters( - resourceFilters); + ImmutableList.copyOf(method.getAnnotation(ResourceFilters.class).value()); + final List, String>> filterVersionPairs = resourceFilters + .stream().flatMap( + (java.util.function.Function, Stream, String>>>) filter -> + Stream.of( + new Pair<>(filter, AuthConfig.AUTH_VERSION_1), + new Pair<>(filter, AuthConfig.AUTH_VERSION_2) + )).collect(Collectors.toList()); return Collections2.transform( - enabledForTestResourceFilters, - new Function, Object[]>() + filterVersionPairs, + new Function, String>, Object[]>() { @Override - public Object[] apply(Class input) + public Object[] apply(Pair, String> filterVersionPair) { if (method.getAnnotation(Path.class) != null) { - return new Object[]{ + return new Object[] { StringUtils.format("%s%s", basepath, method.getAnnotation(Path.class).value()), - httpMethodFromAnnotation(input, method), - injector.getInstance(input), - injector + httpMethodFromAnnotation(filterVersionPair.lhs, method), + injector.getInstance(filterVersionPair.lhs), + injector, + filterVersionPair.rhs, + performAuth(filterVersionPair.lhs, filterVersionPair.rhs) }; } else { - return new Object[]{ + return new Object[] { basepath, - httpMethodFromAnnotation(input, method), - injector.getInstance(input), - injector + httpMethodFromAnnotation(filterVersionPair.lhs, method), + injector.getInstance(filterVersionPair.lhs), + injector, + filterVersionPair.rhs, + performAuth(filterVersionPair.lhs, filterVersionPair.rhs) }; } } @@ -283,10 +315,17 @@ private static String httpMethodFromAnnotation(Class i } } - private static List> getEnabledForTestFilters(List> filters) + // the new filters will perform authorization only if auth version is v2 + // current filters mostly datasource realted ones will work same for both v1 and v2 + // legacy filters will perform authorization only if auth version is v1 + public static boolean performAuth(Class filter, String authVersion) { - List> filtered = filters.stream() - .filter(aClass -> whitelistedForTestFilters.contains(aClass)).collect(Collectors.toList()); - return filtered; + if (NEW_FILTERS.contains(filter) && authVersion.equals(AuthConfig.AUTH_VERSION_2)) { + return true; + } else if (currentFilters.contains(filter)) { + return true; + } else { + return LEGACY_FILTERS.contains(filter) && authVersion.equals(AuthConfig.AUTH_VERSION_1); + } } } diff --git a/server/src/test/java/org/apache/druid/server/http/security/SecurityResourceFilterTest.java b/server/src/test/java/org/apache/druid/server/http/security/SecurityResourceFilterTest.java index c0b83145d352..0bbdc63223c7 100644 --- a/server/src/test/java/org/apache/druid/server/http/security/SecurityResourceFilterTest.java +++ b/server/src/test/java/org/apache/druid/server/http/security/SecurityResourceFilterTest.java @@ -53,7 +53,7 @@ @RunWith(Parameterized.class) public class SecurityResourceFilterTest extends ResourceFilterTestHelper { - @Parameterized.Parameters(name = "{index}: requestPath={0}, requestMethod={1}, resourceFilter={2}") + @Parameterized.Parameters(name = "{index}: requestPath={0}, requestMethod={1}, resourceFilter={2}, authVersion={4}, performAuth={5}") public static Collection data() { return ImmutableList.copyOf( @@ -83,18 +83,24 @@ public static Collection data() private final String requestMethod; private final ResourceFilter resourceFilter; private final Injector injector; + private final String authVersion; + private final boolean performAuth; public SecurityResourceFilterTest( String requestPath, String requestMethod, ResourceFilter resourceFilter, - Injector injector + Injector injector, + String authVersion, + boolean performAuth ) { this.requestPath = requestPath; this.requestMethod = requestMethod; this.resourceFilter = resourceFilter; this.injector = injector; + this.authVersion = authVersion; + this.performAuth = performAuth; } @Before @@ -106,7 +112,7 @@ public void setUp() @Test public void testResourcesFilteringAccess() { - setUpMockExpectations(requestPath, true, requestMethod); + setUpMockExpectations(requestPath, true, requestMethod, authVersion, performAuth); EasyMock.replay(req, request, authorizerMapper); resourceFilter.getRequestFilter().filter(request); EasyMock.verify(req, request, authorizerMapper); @@ -115,10 +121,14 @@ public void testResourcesFilteringAccess() @Test(expected = ForbiddenException.class) public void testResourcesFilteringNoAccess() { - setUpMockExpectations(requestPath, false, requestMethod); + setUpMockExpectations(requestPath, false, requestMethod, authVersion, performAuth); EasyMock.replay(req, request, authorizerMapper); try { resourceFilter.getRequestFilter().filter(request); + if (!performAuth) { + // if auth does not need to be performed, exception will not be thrown so do it manually + throw new ForbiddenException(); + } Assert.fail(); } catch (ForbiddenException e) { From 90bbe294e6b2f76434cdb490b43357b290de2305 Mon Sep 17 00:00:00 2001 From: Parag Jain Date: Thu, 5 Nov 2020 14:02:44 +0530 Subject: [PATCH 06/15] secure sampler --- .../sampler/IndexTaskSamplerSpec.java | 5 ++ .../overlord/sampler/SamplerResource.java | 48 +++++++++++++++++-- .../druid/server/security/Resource.java | 2 - 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/sampler/IndexTaskSamplerSpec.java b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/sampler/IndexTaskSamplerSpec.java index be73cc7b5509..6282a93b7ecb 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/sampler/IndexTaskSamplerSpec.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/sampler/IndexTaskSamplerSpec.java @@ -93,4 +93,9 @@ public SamplerResponse sample() { return inputSourceSampler.sample(inputSource, inputFormat, dataSchema, samplerConfig); } + + public InputSource getInputSource() + { + return inputSource; + } } diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/sampler/SamplerResource.java b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/sampler/SamplerResource.java index 9453acef522a..4939950b2f61 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/sampler/SamplerResource.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/sampler/SamplerResource.java @@ -20,25 +20,67 @@ package org.apache.druid.indexing.overlord.sampler; import com.google.common.base.Preconditions; +import com.google.inject.Inject; import com.sun.jersey.spi.container.ResourceFilters; -import org.apache.druid.server.http.security.ServerUserResourceFilter; +import org.apache.druid.indexing.input.DruidInputSource; import org.apache.druid.server.http.security.StateResourceFilter; +import org.apache.druid.server.security.Access; +import org.apache.druid.server.security.Action; +import org.apache.druid.server.security.AuthConfig; +import org.apache.druid.server.security.AuthorizationUtils; +import org.apache.druid.server.security.AuthorizerMapper; +import org.apache.druid.server.security.ForbiddenException; +import org.apache.druid.server.security.Resource; +import org.apache.druid.server.security.ResourceAction; +import org.apache.druid.server.security.ResourceType; +import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Consumes; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; +import java.util.ArrayList; +import java.util.List; @Path("/druid/indexer/v1/sampler") public class SamplerResource { + private final AuthorizerMapper authorizerMapper; + + @Inject + public SamplerResource(AuthorizerMapper authorizerMapper) + { + this.authorizerMapper = authorizerMapper; + } + @POST @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) - @ResourceFilters({ StateResourceFilter.class, ServerUserResourceFilter.class }) - public SamplerResponse post(final SamplerSpec sampler) + @ResourceFilters(StateResourceFilter.class) + public SamplerResponse post(final SamplerSpec sampler, @Context final HttpServletRequest req) { + if (authorizerMapper.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + final List resourceActions = new ArrayList<>(); + resourceActions.add(new ResourceAction(Resource.SERVER_USER_RESOURCE, Action.WRITE)); + // make sure the user has read permissions on the druid datasource + if (sampler instanceof IndexTaskSamplerSpec + && ((IndexTaskSamplerSpec) sampler).getInputSource() != null + && ((IndexTaskSamplerSpec) sampler).getInputSource() instanceof DruidInputSource) { + final String dataSource = ((DruidInputSource) ((IndexTaskSamplerSpec) sampler).getInputSource()) + .getDataSource(); + resourceActions.add(new ResourceAction(new Resource(dataSource, ResourceType.DATASOURCE), Action.READ)); + } + final Access authResult = AuthorizationUtils.authorizeAllResourceActions( + req, + resourceActions, + authorizerMapper + ); + if (!authResult.isAllowed()) { + throw new ForbiddenException(authResult.getMessage()); + } + } return Preconditions.checkNotNull(sampler, "Request body cannot be empty").sample(); } } diff --git a/server/src/main/java/org/apache/druid/server/security/Resource.java b/server/src/main/java/org/apache/druid/server/security/Resource.java index bef25a55473f..c99eb5d71895 100644 --- a/server/src/main/java/org/apache/druid/server/security/Resource.java +++ b/server/src/main/java/org/apache/druid/server/security/Resource.java @@ -30,8 +30,6 @@ public class Resource public static final Resource SERVER_SERVER_RESOURCE = new Resource("SERVER", ResourceType.SERVER); public static final Resource SERVER_USER_RESOURCE = new Resource("USER", ResourceType.SERVER); public static final Resource SERVER_STATUS_RESOURCE = new Resource("STATUS", ResourceType.SERVER); - public static final Resource LOOKUP_INTERNAL_RESOURCE = new Resource("INTERNAL", ResourceType.LOOKUP); - public static final Resource LOOKUP_STATUS_RESOURCE = new Resource("STATUS", ResourceType.LOOKUP); private final String name; private final ResourceType type; From c7cc6fea696ccec504f67d557f3528151ef808a4 Mon Sep 17 00:00:00 2001 From: Parag Jain Date: Thu, 5 Nov 2020 18:17:28 +0530 Subject: [PATCH 07/15] add missing endpoints to doc --- docs/design/auth-model.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/design/auth-model.md b/docs/design/auth-model.md index 4ba9c6e4441c..7a694c19cdec 100644 --- a/docs/design/auth-model.md +++ b/docs/design/auth-model.md @@ -147,6 +147,8 @@ Below are the endpoints protected/filtered using datasource permissions. These p |`GET /druid/coordinator/v1/rules/{dataSourceName}/history`|coordinator| |`GET/POST /druid/v2/datasources/...`|broker| |`GET/POST /druid/coordinator/v1/metadata/...`|coordinator| +|`GET/POST /druid/coordinator/v1/config/compaction`|coordinator| +|`GET/DELETE /druid/coordinator/v1/config/compaction/{dataSource}`|coordinator| |`POST /druid/indexer/v1/task`|overlord| |`GET /druid/indexer/v1/task/{taskid}`|overlord| |`GET /druid/indexer/v1/task/{taskid}/status`|overlord| @@ -199,7 +201,7 @@ cluster managers. This resource name is used in SQL queries asking for server in |`GET/POST /druid-ext/basic-security/authorization`|coordinator| `STATUS` resource name covers following the end points and are relevant to check status of various processes and can be -used by cluster managers and systems checking status of different drudi processes. +used by cluster managers and systems checking status of different druid processes. |Endpoint|Process Type| |--------|---------| From 613872b744b44b28b41bc1f4527483e5218ce1c6 Mon Sep 17 00:00:00 2001 From: Parag Jain Date: Thu, 5 Nov 2020 20:03:21 +0530 Subject: [PATCH 08/15] refactoring --- docs/design/auth-model.md | 7 ++-- .../CoordinatorCompactionConfigsResource.java | 10 +++--- .../http/LookupCoordinatorResource.java | 11 ++++--- .../druid/server/http/TiersResource.java | 32 ++++++++++++++++--- 4 files changed, 43 insertions(+), 17 deletions(-) diff --git a/docs/design/auth-model.md b/docs/design/auth-model.md index 7a694c19cdec..9531c05f433f 100644 --- a/docs/design/auth-model.md +++ b/docs/design/auth-model.md @@ -145,6 +145,7 @@ Below are the endpoints protected/filtered using datasource permissions. These p |`GET /druid/coordinator/v1/rules`|coordinator| |`GET/POST /druid/coordinator/v1/rules/{dataSourceName}`|coordinator| |`GET /druid/coordinator/v1/rules/{dataSourceName}/history`|coordinator| +|`GET /druid/coordinator/v1/tiers/{tierName}`|coordinator| |`GET/POST /druid/v2/datasources/...`|broker| |`GET/POST /druid/coordinator/v1/metadata/...`|coordinator| |`GET/POST /druid/coordinator/v1/config/compaction`|coordinator| @@ -184,8 +185,6 @@ cluster managers. This resource name is used in SQL queries asking for server in |`GET /druid/coordinator/v1/servers/{serverName}`|coordinator| |`GET /druid/coordinator/v1/servers/{serverName}/segments`|coordinator| |`GET /druid/coordinator/v1/servers/{serverName}/segments/{segmentId}`|coordinator| -|`GET /druid/coordinator/v1/tiers`|coordinator| -|`GET /druid/coordinator/v1/tiers/{tierName}`|coordinator| |`GET /druid/indexer/v1/worker`|overlord| |`POST /druid/indexer/v1/worker`|overlord| |`GET /druid/indexer/v1/worker/history`|overlord| @@ -219,6 +218,8 @@ to some `SERVER` resources during routine work. |--------|---------| |`POST /druid/indexer/v1/sampler`|overlord| |`GET /druid/coordinator/v1/lookups/config`|coordinator| +|`GET /druid/coordinator/v1/tiers`|coordinator| +|`GET /druid/coordinator/v1/tiers/{tierName}`|coordinator| ### INTERNAL @@ -290,7 +291,7 @@ These are some example policies that be used. Policies are of form `ACTION:RESOU 1. Cluster manager can have following policies - `READ:DATASOURCE:*`, `WRITE:DATASOURCE:*`, `READ:SERVER:*`, `WRITE:SERVER:*`, `READ:LOOKUP:*` and `WRITE:LOOKUP:*`. 1. A user just wanting to read/write to specific datasource and lookup can have following policies - `READ:DATASOURCE:`, -`WRITE:DATASOURCE:`, `READ:LOOKUP:`, `WRITE:LOOKUP:`, `READ:SERVER:USER` and `WRITE:SERVER:USER`. +`WRITE:DATASOURCE:`, `READ:LOOKUP:`, `WRITE:LOOKUP:`, `READ:SERVER:USER`, `WRITE:SERVER:USER` AND `READ:SERVER:STATUS`. 1. A user just wanting to read from specific datasource and lookup can have following policies - `READ:DATASOURCE:`, `READ:LOOKUP:`, and `READ:SERVER:USER`. 1. An external monitoring system can just have `READ:SERVER:STATUS` permission. diff --git a/server/src/main/java/org/apache/druid/server/http/CoordinatorCompactionConfigsResource.java b/server/src/main/java/org/apache/druid/server/http/CoordinatorCompactionConfigsResource.java index 715ce6b92580..64680fe99e8f 100644 --- a/server/src/main/java/org/apache/druid/server/http/CoordinatorCompactionConfigsResource.java +++ b/server/src/main/java/org/apache/druid/server/http/CoordinatorCompactionConfigsResource.java @@ -244,11 +244,13 @@ private CoordinatorCompactionConfig getFilteredConfigsByDatasource( .singleton(new ResourceAction(new Resource(input.getDataSource(), ResourceType.DATASOURCE), action)), authorizerMapper ); - // remove so that it does not complain about it when checking for server server permission - request.removeAttribute(AuthConfig.DRUID_AUTHORIZATION_CHECKED); + // add base compaction configs, if user has server server permissions if (AuthorizationUtils - .authorizeResourceAction(request, new ResourceAction(Resource.SERVER_SERVER_RESOURCE, action), authorizerMapper) - .isAllowed()) { + .authorizeAllResourceActions( + AuthorizationUtils.authenticationResultFromRequest(request), + Collections.singleton(new ResourceAction(Resource.SERVER_SERVER_RESOURCE, action)), + authorizerMapper + ).isAllowed()) { return CoordinatorCompactionConfig.from(compactionConfig, Lists.newArrayList(dataSourceCompactionConfigs)); } return CoordinatorCompactionConfig.from(Lists.newArrayList(dataSourceCompactionConfigs)); diff --git a/server/src/main/java/org/apache/druid/server/http/LookupCoordinatorResource.java b/server/src/main/java/org/apache/druid/server/http/LookupCoordinatorResource.java index 71b481a41063..b821397ae01d 100644 --- a/server/src/main/java/org/apache/druid/server/http/LookupCoordinatorResource.java +++ b/server/src/main/java/org/apache/druid/server/http/LookupCoordinatorResource.java @@ -71,6 +71,7 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -858,12 +859,9 @@ private Map> filterByLoo lookups.keySet().forEach(tier -> filteredLookups.compute(tier, (tier1, lookupFactory) -> { final Map filteredMap = new HashMap<>(lookupFactory); lookupFactory.keySet().forEach(loookupId -> { - // so that it does not complain about auth being done already, - // last call to authorizeResourceAction will set this attribute again - request.removeAttribute(AuthConfig.DRUID_AUTHORIZATION_CHECKED); - final Access access = AuthorizationUtils.authorizeResourceAction( + final Access access = AuthorizationUtils.authorizeAllResourceActions( request, - new ResourceAction(new Resource(loookupId, ResourceType.LOOKUP), action), + Collections.singleton(new ResourceAction(new Resource(loookupId, ResourceType.LOOKUP), action)), authorizerMapper ); if (access.isAllowed()) { @@ -873,6 +871,9 @@ private Map> filterByLoo return filteredMap; } )); + // We're filtering, so having access to none of the objects isn't an authorization failure (in terms of whether + // to send an error response or not.) + request.setAttribute(AuthConfig.DRUID_AUTHORIZATION_CHECKED, true); return filteredLookups; } diff --git a/server/src/main/java/org/apache/druid/server/http/TiersResource.java b/server/src/main/java/org/apache/druid/server/http/TiersResource.java index bd83783ecb0f..97641132b3de 100644 --- a/server/src/main/java/org/apache/druid/server/http/TiersResource.java +++ b/server/src/main/java/org/apache/druid/server/http/TiersResource.java @@ -24,18 +24,27 @@ import org.apache.druid.client.DruidDataSource; import org.apache.druid.client.DruidServer; import org.apache.druid.client.InventoryView; -import org.apache.druid.server.http.security.ServerServerResourceFilter; +import org.apache.druid.server.http.security.ServerUserResourceFilter; import org.apache.druid.server.http.security.StateResourceFilter; +import org.apache.druid.server.security.Action; +import org.apache.druid.server.security.AuthorizationUtils; +import org.apache.druid.server.security.AuthorizerMapper; +import org.apache.druid.server.security.Resource; +import org.apache.druid.server.security.ResourceAction; +import org.apache.druid.server.security.ResourceType; import org.apache.druid.timeline.DataSegment; import org.joda.time.Interval; +import javax.servlet.http.HttpServletRequest; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import java.util.Collections; import java.util.EnumMap; import java.util.HashMap; import java.util.Map; @@ -45,15 +54,17 @@ /** */ @Path("/druid/coordinator/v1/tiers") -@ResourceFilters({ StateResourceFilter.class, ServerServerResourceFilter.class }) +@ResourceFilters({ StateResourceFilter.class, ServerUserResourceFilter.class }) public class TiersResource { private final InventoryView serverInventoryView; + private final AuthorizerMapper authorizerMapper; @Inject - public TiersResource(InventoryView serverInventoryView) + public TiersResource(InventoryView serverInventoryView, AuthorizerMapper authorizerMapper) { this.serverInventoryView = serverInventoryView; + this.authorizerMapper = authorizerMapper; } private enum TierMetadataKeys @@ -99,13 +110,24 @@ private enum IntervalProperties @GET @Path("/{tierName}") @Produces(MediaType.APPLICATION_JSON) - public Response getTierDataSources(@PathParam("tierName") String tierName, @QueryParam("simple") String simple) + public Response getTierDataSources( + @PathParam("tierName") String tierName, + @QueryParam("simple") String simple, + @Context HttpServletRequest request + ) { if (simple != null) { Map>> tierToStatsPerInterval = new HashMap<>(); for (DruidServer druidServer : serverInventoryView.getInventory()) { if (druidServer.getTier().equalsIgnoreCase(tierName)) { - for (DataSegment dataSegment : druidServer.iterateAllSegments()) { + Iterable filteredSegments = AuthorizationUtils.filterAuthorizedResources( + AuthorizationUtils.authenticationResultFromRequest(request), + druidServer.iterateAllSegments(), + input -> Collections.singleton( + new ResourceAction(new Resource(input.getDataSource(), ResourceType.DATASOURCE), Action.READ)), + authorizerMapper + ); + for (DataSegment dataSegment : filteredSegments) { Map properties = tierToStatsPerInterval .computeIfAbsent(dataSegment.getDataSource(), dsName -> new HashMap<>()) .computeIfAbsent(dataSegment.getInterval(), interval -> new EnumMap<>(IntervalProperties.class)); From 0f578623a1219dda19fcc9233c4309019f0580ed Mon Sep 17 00:00:00 2001 From: Parag Jain Date: Mon, 9 Nov 2020 22:02:16 +0530 Subject: [PATCH 09/15] reindexing from datasource permissions --- docs/design/auth-model.md | 4 +++- .../overlord/http/OverlordResource.java | 21 +++++++++++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/docs/design/auth-model.md b/docs/design/auth-model.md index 9531c05f433f..b5c653cbbf6b 100644 --- a/docs/design/auth-model.md +++ b/docs/design/auth-model.md @@ -165,6 +165,8 @@ Below are the endpoints protected/filtered using datasource permissions. These p |`GET /druid/indexer/v1/task/{taskid}/log`|overlord| |`GET /druid/indexer/v1/task/{taskid}/reports`|overlord| +Note - When reindexing from an existing datasource, user needs read on source datasource permission in addition to +write on destination datasource. ### SERVER @@ -318,7 +320,7 @@ will be filtered based on DATASOURCE READ permissions. - `tasks`: Tasks will be filtered based on DATASOURCE READ permissions. ### Backwards compatibility and rolling upgrade to auth v2 model from v1 -Unless the flag `druid.auth.authVersion` is set to `v2`, older model will be used. For rolling upgradw to v2 model +Unless the flag `druid.auth.authVersion` is set to `v2`, older model will be used. For rolling upgrade to v2 model there are few prerequisites - 1. Define the new permissions and policies in the auth system you are using. 1. Make sure the auth extension you are using supports the newer model, you need to make sure the `Authorizer` implementation diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/http/OverlordResource.java b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/http/OverlordResource.java index 7c8f096ea0b6..7d279a323a78 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/http/OverlordResource.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/http/OverlordResource.java @@ -44,7 +44,9 @@ import org.apache.druid.indexer.TaskStatusPlus; import org.apache.druid.indexing.common.actions.TaskActionClient; import org.apache.druid.indexing.common.actions.TaskActionHolder; +import org.apache.druid.indexing.common.task.IndexTask; import org.apache.druid.indexing.common.task.Task; +import org.apache.druid.indexing.input.DruidInputSource; import org.apache.druid.indexing.overlord.IndexerMetadataStorageAdapter; import org.apache.druid.indexing.overlord.TaskMaster; import org.apache.druid.indexing.overlord.TaskQueue; @@ -163,14 +165,25 @@ public OverlordResource( public Response taskPost(final Task task, @Context final HttpServletRequest req) { final String dataSource = task.getDataSource(); - final ResourceAction resourceAction = new ResourceAction( + final List resourceActions = new ArrayList<>(); + + resourceActions.add(new ResourceAction( new Resource(dataSource, ResourceType.DATASOURCE), Action.WRITE - ); + )); + + // if its a reindex task from druid, make sure the user has read permissions on the source druid datasource + if (task instanceof IndexTask && ((IndexTask) task).getIngestionSchema().getIOConfig() + .getInputSource() instanceof DruidInputSource) { + final String readFromDataSource = ((DruidInputSource) ((IndexTask) task).getIngestionSchema() + .getIOConfig() + .getInputSource()).getDataSource(); + resourceActions.add(new ResourceAction(new Resource(readFromDataSource, ResourceType.DATASOURCE), Action.READ)); + } - Access authResult = AuthorizationUtils.authorizeResourceAction( + final Access authResult = AuthorizationUtils.authorizeAllResourceActions( req, - resourceAction, + resourceActions, authorizerMapper ); From 3489957683a54590c823fc66aa9ac682ead852a5 Mon Sep 17 00:00:00 2001 From: Parag Jain Date: Tue, 10 Nov 2020 15:45:18 +0530 Subject: [PATCH 10/15] lookup doc clarification --- docs/design/auth-model.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/design/auth-model.md b/docs/design/auth-model.md index b5c653cbbf6b..6c72885051f0 100644 --- a/docs/design/auth-model.md +++ b/docs/design/auth-model.md @@ -285,6 +285,9 @@ filtered list by the granted permissions. It covers the following endpoints. |`GET /druid/coordinator/v1/lookups/status/{tier}`|coordinator| |`GET /druid/coordinator/v1/lookups/status/{tier}/{lookup}`|coordinator| +Note - Since lookups are protected using lookup ids, users have to make sure the lookup ids are unique across different +lookup tiers to avoid conflicts. + ### EXAMPLE ROLES-PERMISSION MAPPING These are some example policies that be used. Policies are of form `ACTION:RESOURCE_TYPE:RESOURCE_NAME` From 85e33304768c25b969e32e95cfdd52d7d63e3685 Mon Sep 17 00:00:00 2001 From: Parag Jain Date: Wed, 11 Nov 2020 14:07:09 +0530 Subject: [PATCH 11/15] - reindexing druid datasource permission check - add auth v2 tests for basic auth ext, system schema - fix doc - fix Lists dependency --- docs/querying/sql.md | 2 +- .../BasicRoleBasedAuthorizerTest.java | 55 ++++++----- ...cAuthorizerMetadataStorageUpdaterTest.java | 93 ++++++++++++------- .../overlord/http/OverlordResource.java | 5 +- .../CoordinatorCompactionConfigsResource.java | 2 +- .../sql/calcite/schema/SystemSchemaTest.java | 86 ++++++++++++++++- 6 files changed, 182 insertions(+), 61 deletions(-) diff --git a/docs/querying/sql.md b/docs/querying/sql.md index 861397265bf8..aeaed07926a3 100644 --- a/docs/querying/sql.md +++ b/docs/querying/sql.md @@ -1221,5 +1221,5 @@ Druid SQL planning occurs on the Broker and is configured by ## Security -Please see [Defining SQL permissions](../development/extensions-core/druid-basic-security.html#sql-permissions) in the +Please see [Defining SQL permissions](../design/auth-model.html#sql-permissions) in the basic security documentation for information on what permissions are needed for making SQL queries. diff --git a/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/BasicRoleBasedAuthorizerTest.java b/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/BasicRoleBasedAuthorizerTest.java index c6d0cdc09746..c7af45962eec 100644 --- a/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/BasicRoleBasedAuthorizerTest.java +++ b/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/BasicRoleBasedAuthorizerTest.java @@ -45,6 +45,8 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; import javax.naming.directory.BasicAttribute; import javax.naming.directory.BasicAttributes; @@ -55,6 +57,7 @@ import java.util.List; import java.util.Map; +@RunWith(Parameterized.class) public class BasicRoleBasedAuthorizerTest { private static final String DB_AUTHORIZER_NAME = "metadata"; @@ -74,6 +77,16 @@ public class BasicRoleBasedAuthorizerTest private SearchResult userSearchResult; private SearchResult adminSearchResult; + private final String authVersion; + + @Parameterized.Parameters(name = "{index}: authVersion={0}") + public static Iterable data() { + return Arrays.asList(AuthConfig.AUTH_VERSION_1, AuthConfig.AUTH_VERSION_2); + } + + public BasicRoleBasedAuthorizerTest(String authVersion) { + this.authVersion = authVersion; + } @Before public void setUp() @@ -116,7 +129,7 @@ public void setUp() null, new LDAPRoleProvider(null, groupFilters) ) - ), null + ), authVersion ), connector, tablesConfig, @@ -173,7 +186,7 @@ public void testAuth() authenticationResult, new Resource("testResource", ResourceType.DATASOURCE), Action.WRITE, - AuthConfig.AUTH_VERSION_1 + authVersion ); Assert.assertTrue(access.isAllowed()); @@ -181,7 +194,7 @@ public void testAuth() authenticationResult, new Resource("wrongResource", ResourceType.DATASOURCE), Action.WRITE, - AuthConfig.AUTH_VERSION_1 + authVersion ); Assert.assertFalse(access.isAllowed()); } @@ -209,7 +222,7 @@ public void testAuthGroupMapping() authenticationResult, new Resource("testResource", ResourceType.DATASOURCE), Action.WRITE, - AuthConfig.AUTH_VERSION_1 + authVersion ); Assert.assertTrue(access.isAllowed()); @@ -217,7 +230,7 @@ public void testAuthGroupMapping() authenticationResult, new Resource("wrongResource", ResourceType.DATASOURCE), Action.WRITE, - AuthConfig.AUTH_VERSION_1 + authVersion ); Assert.assertFalse(access.isAllowed()); } @@ -256,7 +269,7 @@ public void testAuthGroupMappingPatternRightMask() authenticationResult, new Resource("testResource", ResourceType.DATASOURCE), Action.READ, - AuthConfig.AUTH_VERSION_1 + authVersion ); Assert.assertTrue(access.isAllowed()); @@ -264,7 +277,7 @@ public void testAuthGroupMappingPatternRightMask() authenticationResult, new Resource("testResource", ResourceType.DATASOURCE), Action.WRITE, - AuthConfig.AUTH_VERSION_1 + authVersion ); Assert.assertTrue(access.isAllowed()); @@ -272,7 +285,7 @@ public void testAuthGroupMappingPatternRightMask() authenticationResult, new Resource("wrongResource", ResourceType.DATASOURCE), Action.WRITE, - AuthConfig.AUTH_VERSION_1 + authVersion ); Assert.assertFalse(access.isAllowed()); @@ -284,7 +297,7 @@ public void testAuthGroupMappingPatternRightMask() authenticationResult, new Resource("testResource", ResourceType.DATASOURCE), Action.WRITE, - AuthConfig.AUTH_VERSION_1 + authVersion ); Assert.assertFalse(access.isAllowed()); @@ -292,7 +305,7 @@ public void testAuthGroupMappingPatternRightMask() authenticationResult, new Resource("testResource", ResourceType.DATASOURCE), Action.READ, - AuthConfig.AUTH_VERSION_1 + authVersion ); Assert.assertTrue(access.isAllowed()); @@ -300,7 +313,7 @@ public void testAuthGroupMappingPatternRightMask() authenticationResult, new Resource("wrongResource", ResourceType.DATASOURCE), Action.READ, - AuthConfig.AUTH_VERSION_1 + authVersion ); Assert.assertFalse(access.isAllowed()); } @@ -340,7 +353,7 @@ public void testAuthGroupMappingPatternLeftMask() authenticationResult, new Resource("testResource", ResourceType.DATASOURCE), Action.READ, - AuthConfig.AUTH_VERSION_1 + authVersion ); Assert.assertTrue(access.isAllowed()); @@ -348,7 +361,7 @@ public void testAuthGroupMappingPatternLeftMask() authenticationResult, new Resource("testResource", ResourceType.DATASOURCE), Action.WRITE, - AuthConfig.AUTH_VERSION_1 + authVersion ); Assert.assertTrue(access.isAllowed()); @@ -356,7 +369,7 @@ public void testAuthGroupMappingPatternLeftMask() authenticationResult, new Resource("wrongResource", ResourceType.DATASOURCE), Action.WRITE, - AuthConfig.AUTH_VERSION_1 + authVersion ); Assert.assertFalse(access.isAllowed()); @@ -368,7 +381,7 @@ public void testAuthGroupMappingPatternLeftMask() authenticationResult, new Resource("testResource", ResourceType.DATASOURCE), Action.WRITE, - AuthConfig.AUTH_VERSION_1 + authVersion ); Assert.assertFalse(access.isAllowed()); @@ -376,7 +389,7 @@ public void testAuthGroupMappingPatternLeftMask() authenticationResult, new Resource("testResource", ResourceType.DATASOURCE), Action.READ, - AuthConfig.AUTH_VERSION_1 + authVersion ); Assert.assertTrue(access.isAllowed()); @@ -384,7 +397,7 @@ public void testAuthGroupMappingPatternLeftMask() authenticationResult, new Resource("wrongResource", ResourceType.DATASOURCE), Action.READ, - AuthConfig.AUTH_VERSION_1 + authVersion ); Assert.assertFalse(access.isAllowed()); } @@ -413,7 +426,7 @@ public void testAuthMissingGroupMapping() authenticationResult, new Resource("testResource", ResourceType.DATASOURCE), Action.WRITE, - AuthConfig.AUTH_VERSION_1 + authVersion ); Assert.assertFalse(access.isAllowed()); @@ -421,7 +434,7 @@ public void testAuthMissingGroupMapping() authenticationResult, new Resource("testResource", ResourceType.DATASOURCE), Action.READ, - AuthConfig.AUTH_VERSION_1 + authVersion ); Assert.assertFalse(access.isAllowed()); @@ -429,7 +442,7 @@ public void testAuthMissingGroupMapping() authenticationResult, new Resource("wrongResource", ResourceType.DATASOURCE), Action.WRITE, - AuthConfig.AUTH_VERSION_1 + authVersion ); Assert.assertFalse(access.isAllowed()); @@ -437,7 +450,7 @@ public void testAuthMissingGroupMapping() authenticationResult, new Resource("wrongResource", ResourceType.DATASOURCE), Action.READ, - AuthConfig.AUTH_VERSION_1 + authVersion ); Assert.assertFalse(access.isAllowed()); } diff --git a/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/CoordinatorBasicAuthorizerMetadataStorageUpdaterTest.java b/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/CoordinatorBasicAuthorizerMetadataStorageUpdaterTest.java index d857a302035c..5a46013015d2 100644 --- a/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/CoordinatorBasicAuthorizerMetadataStorageUpdaterTest.java +++ b/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/CoordinatorBasicAuthorizerMetadataStorageUpdaterTest.java @@ -36,6 +36,7 @@ import org.apache.druid.security.basic.authorization.entity.BasicAuthorizerRole; import org.apache.druid.security.basic.authorization.entity.BasicAuthorizerUser; import org.apache.druid.server.security.Action; +import org.apache.druid.server.security.AuthConfig; import org.apache.druid.server.security.AuthorizerMapper; import org.apache.druid.server.security.Resource; import org.apache.druid.server.security.ResourceAction; @@ -45,11 +46,15 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +@RunWith(Parameterized.class) public class CoordinatorBasicAuthorizerMetadataStorageUpdaterTest { private static final String AUTHORIZER_NAME = "test"; @@ -62,18 +67,7 @@ public class CoordinatorBasicAuthorizerMetadataStorageUpdaterTest BasicAuthUtils.INTERNAL_USER_NAME)) ); - private static final Map BASE_ROLE_MAP = ImmutableMap.of( - BasicAuthUtils.ADMIN_NAME, - new BasicAuthorizerRole( - BasicAuthUtils.ADMIN_NAME, - BasicAuthorizerPermission.makePermissionList(CoordinatorBasicAuthorizerMetadataStorageUpdater.SUPERUSER_PERMISSIONS) - ), - BasicAuthUtils.INTERNAL_USER_NAME, - new BasicAuthorizerRole( - BasicAuthUtils.INTERNAL_USER_NAME, - BasicAuthorizerPermission.makePermissionList(CoordinatorBasicAuthorizerMetadataStorageUpdater.SUPERUSER_PERMISSIONS) - ) - ); + private final Map baseRoleMap; @Rule public ExpectedException expectedException = ExpectedException.none(); @@ -83,6 +77,52 @@ public class CoordinatorBasicAuthorizerMetadataStorageUpdaterTest private CoordinatorBasicAuthorizerMetadataStorageUpdater updater; private ObjectMapper objectMapper; + private final AuthorizerMapper authorizerMapper; + + @Parameterized.Parameters(name = "{index}: authVersion={0}") + public static Iterable data() { + return Arrays.asList(AuthConfig.AUTH_VERSION_1, AuthConfig.AUTH_VERSION_2); + } + + public CoordinatorBasicAuthorizerMetadataStorageUpdaterTest(String authVersion) + { + this.authorizerMapper = new AuthorizerMapper( + ImmutableMap.of( + AUTHORIZER_NAME, + new BasicRoleBasedAuthorizer( + null, + AUTHORIZER_NAME, + null, + null, + null, + null, + null, + null + ) + ), + authVersion + ); + baseRoleMap = ImmutableMap.of( + BasicAuthUtils.ADMIN_NAME, + new BasicAuthorizerRole( + BasicAuthUtils.ADMIN_NAME, + authVersion.equals(AuthConfig.AUTH_VERSION_2) + ? BasicAuthorizerPermission + .makePermissionList(CoordinatorBasicAuthorizerMetadataStorageUpdater.SUPERUSER_PERMISSIONS_V2) + : BasicAuthorizerPermission + .makePermissionList(CoordinatorBasicAuthorizerMetadataStorageUpdater.SUPERUSER_PERMISSIONS) + ), + BasicAuthUtils.INTERNAL_USER_NAME, + new BasicAuthorizerRole( + BasicAuthUtils.INTERNAL_USER_NAME, + authVersion.equals(AuthConfig.AUTH_VERSION_2) + ? BasicAuthorizerPermission + .makePermissionList(CoordinatorBasicAuthorizerMetadataStorageUpdater.SUPERUSER_PERMISSIONS_V2) + : BasicAuthorizerPermission + .makePermissionList(CoordinatorBasicAuthorizerMetadataStorageUpdater.SUPERUSER_PERMISSIONS) + ) + ); + } @Before public void setUp() @@ -93,22 +133,7 @@ public void setUp() connector.createConfigTable(); updater = new CoordinatorBasicAuthorizerMetadataStorageUpdater( - new AuthorizerMapper( - ImmutableMap.of( - AUTHORIZER_NAME, - new BasicRoleBasedAuthorizer( - null, - AUTHORIZER_NAME, - null, - null, - null, - null, - null, - null - ) - ), - null - ), + authorizerMapper, connector, tablesConfig, new BasicAuthCommonCacheConfig(null, null, null, null), @@ -202,7 +227,7 @@ public void testCreateDuplicateGroupMapping() public void testCreateDeleteRole() { updater.createRole(AUTHORIZER_NAME, "druid"); - Map expectedRoleMap = new HashMap<>(BASE_ROLE_MAP); + Map expectedRoleMap = new HashMap<>(baseRoleMap); expectedRoleMap.put("druid", new BasicAuthorizerRole("druid", ImmutableList.of())); Map actualRoleMap = BasicAuthUtils.deserializeAuthorizerRoleMap( objectMapper, @@ -247,7 +272,7 @@ public void testAddAndRemoveRoleToUser() Map expectedUserMap = new HashMap<>(BASE_USER_MAP); expectedUserMap.put("druid", new BasicAuthorizerUser("druid", ImmutableSet.of("druidRole"))); - Map expectedRoleMap = new HashMap<>(BASE_ROLE_MAP); + Map expectedRoleMap = new HashMap<>(baseRoleMap); expectedRoleMap.put("druidRole", new BasicAuthorizerRole("druidRole", ImmutableList.of())); Map actualUserMap = BasicAuthUtils.deserializeAuthorizerUserMap( @@ -285,7 +310,7 @@ public void testAddAndRemoveRoleToGroupMapping() Map expectedGroupMappingMap = new HashMap<>(); expectedGroupMappingMap.put("druid", new BasicAuthorizerGroupMapping("druid", "CN=test", ImmutableSet.of("druidRole"))); - Map expectedRoleMap = new HashMap<>(BASE_ROLE_MAP); + Map expectedRoleMap = new HashMap<>(baseRoleMap); expectedRoleMap.put("druidRole", new BasicAuthorizerRole("druidRole", ImmutableList.of())); Map actualGroupMappingMap = BasicAuthUtils.deserializeAuthorizerGroupMappingMap( @@ -392,7 +417,7 @@ public void testUnassignInvalidRoleAssignmentToUserFails() Map expectedUserMap = new HashMap<>(BASE_USER_MAP); expectedUserMap.put("druid", new BasicAuthorizerUser("druid", ImmutableSet.of())); - Map expectedRoleMap = new HashMap<>(BASE_ROLE_MAP); + Map expectedRoleMap = new HashMap<>(baseRoleMap); expectedRoleMap.put("druidRole", new BasicAuthorizerRole("druidRole", ImmutableList.of())); Map actualUserMap = BasicAuthUtils.deserializeAuthorizerUserMap( @@ -424,7 +449,7 @@ public void testUnassignInvalidRoleAssignmentToGroupMappingFails() Map expectedGroupMappingMap = new HashMap<>(); expectedGroupMappingMap.put("druid", new BasicAuthorizerGroupMapping("druid", "CN=test", null)); - Map expectedRoleMap = new HashMap<>(BASE_ROLE_MAP); + Map expectedRoleMap = new HashMap<>(baseRoleMap); expectedRoleMap.put("druidRole", new BasicAuthorizerRole("druidRole", ImmutableList.of())); Map actualGroupMappingMap = BasicAuthUtils.deserializeAuthorizerGroupMappingMap( @@ -464,7 +489,7 @@ public void testSetRolePermissions() Map expectedUserMap = new HashMap<>(BASE_USER_MAP); expectedUserMap.put("druid", new BasicAuthorizerUser("druid", ImmutableSet.of("druidRole"))); - Map expectedRoleMap = new HashMap<>(BASE_ROLE_MAP); + Map expectedRoleMap = new HashMap<>(baseRoleMap); expectedRoleMap.put( "druidRole", new BasicAuthorizerRole("druidRole", BasicAuthorizerPermission.makePermissionList(permsToAdd)) diff --git a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/http/OverlordResource.java b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/http/OverlordResource.java index 7d279a323a78..ab79ce453643 100644 --- a/indexing-service/src/main/java/org/apache/druid/indexing/overlord/http/OverlordResource.java +++ b/indexing-service/src/main/java/org/apache/druid/indexing/overlord/http/OverlordResource.java @@ -72,6 +72,7 @@ import org.apache.druid.server.http.security.StateResourceFilter; import org.apache.druid.server.security.Access; import org.apache.druid.server.security.Action; +import org.apache.druid.server.security.AuthConfig; import org.apache.druid.server.security.AuthorizationUtils; import org.apache.druid.server.security.AuthorizerMapper; import org.apache.druid.server.security.ForbiddenException; @@ -173,8 +174,8 @@ public Response taskPost(final Task task, @Context final HttpServletRequest req) )); // if its a reindex task from druid, make sure the user has read permissions on the source druid datasource - if (task instanceof IndexTask && ((IndexTask) task).getIngestionSchema().getIOConfig() - .getInputSource() instanceof DruidInputSource) { + if (authorizerMapper.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2) && task instanceof IndexTask + && ((IndexTask) task).getIngestionSchema().getIOConfig().getInputSource() instanceof DruidInputSource) { final String readFromDataSource = ((DruidInputSource) ((IndexTask) task).getIngestionSchema() .getIOConfig() .getInputSource()).getDataSource(); diff --git a/server/src/main/java/org/apache/druid/server/http/CoordinatorCompactionConfigsResource.java b/server/src/main/java/org/apache/druid/server/http/CoordinatorCompactionConfigsResource.java index 64680fe99e8f..02968d2fb758 100644 --- a/server/src/main/java/org/apache/druid/server/http/CoordinatorCompactionConfigsResource.java +++ b/server/src/main/java/org/apache/druid/server/http/CoordinatorCompactionConfigsResource.java @@ -21,9 +21,9 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; import com.google.inject.Inject; import com.sun.jersey.spi.container.ResourceFilters; -import net.thisptr.jackson.jq.internal.misc.Lists; import org.apache.druid.audit.AuditInfo; import org.apache.druid.audit.AuditManager; import org.apache.druid.common.config.ConfigManager.SetResult; diff --git a/sql/src/test/java/org/apache/druid/sql/calcite/schema/SystemSchemaTest.java b/sql/src/test/java/org/apache/druid/sql/calcite/schema/SystemSchemaTest.java index 435da11999d5..bdb4413f68ca 100644 --- a/sql/src/test/java/org/apache/druid/sql/calcite/schema/SystemSchemaTest.java +++ b/sql/src/test/java/org/apache/druid/sql/calcite/schema/SystemSchemaTest.java @@ -24,6 +24,8 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; import junitparams.converters.Nullable; import org.apache.calcite.DataContext; import org.apache.calcite.adapter.java.JavaTypeFactory; @@ -79,10 +81,15 @@ import org.apache.druid.server.coordination.ServerType; import org.apache.druid.server.coordinator.BytesAccumulatingResponseHandler; import org.apache.druid.server.security.Access; +import org.apache.druid.server.security.Action; import org.apache.druid.server.security.AuthConfig; +import org.apache.druid.server.security.AuthenticationResult; import org.apache.druid.server.security.Authorizer; import org.apache.druid.server.security.AuthorizerMapper; import org.apache.druid.server.security.NoopEscalator; +import org.apache.druid.server.security.Resource; +import org.apache.druid.server.security.ResourceAction; +import org.apache.druid.server.security.ResourceType; import org.apache.druid.sql.calcite.planner.PlannerConfig; import org.apache.druid.sql.calcite.schema.SystemSchema.SegmentsTable; import org.apache.druid.sql.calcite.table.RowSignatures; @@ -109,6 +116,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; import java.io.File; import java.io.IOException; @@ -122,6 +130,7 @@ import java.util.Map; import java.util.Set; +@RunWith(JUnitParamsRunner.class) public class SystemSchemaTest extends CalciteTestBase { private static final PlannerConfig PLANNER_CONFIG_DEFAULT = new PlannerConfig(); @@ -731,8 +740,33 @@ private void verifyRow( } @Test - public void testServersTable() + @Parameters({ AuthConfig.AUTH_VERSION_1, AuthConfig.AUTH_VERSION_2 }) + public void testServersTable(String authVersion) { + final List expectedResourceActions = new ArrayList<>(); + authMapper = new AuthorizerMapper(null, authVersion) + { + @Override + public Authorizer getAuthorizer(String name) + { + return new Authorizer() + { + @Override + public Access authorize(AuthenticationResult authenticationResult, Resource resource, Action action) + { + expectedResourceActions.add(new ResourceAction(resource, action)); + return new Access(true); + } + + @Override + public Access authorizeV2(AuthenticationResult authenticationResult, Resource resource, Action action) + { + expectedResourceActions.add(new ResourceAction(resource, action)); + return new Access(true); + } + }; + } + }; SystemSchema.ServersTable serversTable = EasyMock.createMockBuilder(SystemSchema.ServersTable.class) .withConstructor( @@ -944,6 +978,10 @@ public Object get(String name) Assert.assertArrayEquals(expectedRows.get(i), rows.get(i)); } + final List actualResourceActions = authVersion.equals(AuthConfig.AUTH_VERSION_2) ? + ImmutableList.of(new ResourceAction(Resource.SERVER_SERVER_RESOURCE, Action.READ)) : + ImmutableList.of(new ResourceAction(Resource.STATE_RESOURCE, Action.READ)); + Assert.assertEquals(expectedResourceActions, actualResourceActions); // Verify value types. verifyTypes(rows, SystemSchema.SERVERS_SIGNATURE); } @@ -984,8 +1022,34 @@ private Object[] createExpectedRow( } @Test - public void testServerSegmentsTable() + @Parameters({ AuthConfig.AUTH_VERSION_1, AuthConfig.AUTH_VERSION_2 }) + public void testServerSegmentsTable(String authVersion) { + final List expectedResourceActions = new ArrayList<>(); + authMapper = new AuthorizerMapper(null, authVersion) + { + @Override + public Authorizer getAuthorizer(String name) + { + return new Authorizer() + { + @Override + public Access authorize(AuthenticationResult authenticationResult, Resource resource, Action action) + { + expectedResourceActions.add(new ResourceAction(resource, action)); + return new Access(true); + } + + @Override + public Access authorizeV2(AuthenticationResult authenticationResult, Resource resource, Action action) + { + expectedResourceActions.add(new ResourceAction(resource, action)); + return new Access(true); + } + }; + } + }; + SystemSchema.ServerSegmentsTable serverSegmentsTable = EasyMock .createMockBuilder(SystemSchema.ServerSegmentsTable.class) .withConstructor(serverView, authMapper) @@ -1053,6 +1117,24 @@ public Object get(String name) Assert.assertEquals("server2:1234", row4[0]); Assert.assertEquals("test5_2015-01-01T00:00:00.000Z_2016-01-01T00:00:00.000Z_version5", row4[1].toString()); + final List actualResourceActions = authVersion.equals(AuthConfig.AUTH_VERSION_2) ? + ImmutableList.of( + new ResourceAction(Resource.SERVER_SERVER_RESOURCE, Action.READ), + new ResourceAction(new Resource("test1", ResourceType.DATASOURCE), Action.READ), + new ResourceAction(new Resource("test2", ResourceType.DATASOURCE), Action.READ), + new ResourceAction(new Resource("test3", ResourceType.DATASOURCE), Action.READ), + new ResourceAction(new Resource("test4", ResourceType.DATASOURCE), Action.READ), + new ResourceAction(new Resource("test5", ResourceType.DATASOURCE), Action.READ) + ) : + ImmutableList.of( + new ResourceAction(Resource.STATE_RESOURCE, Action.READ), + new ResourceAction(new Resource("test1", ResourceType.DATASOURCE), Action.READ), + new ResourceAction(new Resource("test2", ResourceType.DATASOURCE), Action.READ), + new ResourceAction(new Resource("test3", ResourceType.DATASOURCE), Action.READ), + new ResourceAction(new Resource("test4", ResourceType.DATASOURCE), Action.READ), + new ResourceAction(new Resource("test5", ResourceType.DATASOURCE), Action.READ) + ); + Assert.assertEquals(expectedResourceActions, actualResourceActions); // Verify value types. verifyTypes(rows, SystemSchema.SERVER_SEGMENTS_SIGNATURE); } From 97efb0560cfb1bbabdd40dd7a309251b24a4217f Mon Sep 17 00:00:00 2001 From: Parag Jain Date: Wed, 11 Nov 2020 23:27:40 +0530 Subject: [PATCH 12/15] secure compaction resource with auth v2 --- docs/design/auth-model.md | 3 + .../druid/server/http/CompactionResource.java | 64 +++++++++++++++-- .../druid/server/security/ResourceName.java | 72 ------------------- .../server/http/CompactionResourceTest.java | 63 ++++++++++++++-- .../druid/server/http/RulesResourceTest.java | 66 +++++++++++++++++ 5 files changed, 186 insertions(+), 82 deletions(-) delete mode 100644 server/src/main/java/org/apache/druid/server/security/ResourceName.java diff --git a/docs/design/auth-model.md b/docs/design/auth-model.md index 6c72885051f0..4c6653426e95 100644 --- a/docs/design/auth-model.md +++ b/docs/design/auth-model.md @@ -150,6 +150,8 @@ Below are the endpoints protected/filtered using datasource permissions. These p |`GET/POST /druid/coordinator/v1/metadata/...`|coordinator| |`GET/POST /druid/coordinator/v1/config/compaction`|coordinator| |`GET/DELETE /druid/coordinator/v1/config/compaction/{dataSource}`|coordinator| +|`GET /druid/coordinator/v1/compaction/progress`|coordinator| +|`GET /druid/coordinator/v1/compaction/status`|coordinator| |`POST /druid/indexer/v1/task`|overlord| |`GET /druid/indexer/v1/task/{taskid}`|overlord| |`GET /druid/indexer/v1/task/{taskid}/status`|overlord| @@ -254,6 +256,7 @@ for druid nodes talking to each other and cluster admins. |`POST /druid/v2/candidates`|broker| |`GET /druid/coordinator/v1/cluster`|coordinator| |`GET /druid/coordinator/v1/cluster/{nodeRole}`|coordinator| +|`GET /druid/coordinator/v1/compaction/compact`|coordinator| |`GET /druid/router/v1/brokers`|router| |`GET /druid-internal/v1/segments/`|broker,historical,peon| |`GET /druid-internal/v1/segments/changeRequests`|broker,historical,peon| diff --git a/server/src/main/java/org/apache/druid/server/http/CompactionResource.java b/server/src/main/java/org/apache/druid/server/http/CompactionResource.java index 8c995144d8f9..1da42fb2b201 100644 --- a/server/src/main/java/org/apache/druid/server/http/CompactionResource.java +++ b/server/src/main/java/org/apache/druid/server/http/CompactionResource.java @@ -27,28 +27,45 @@ import org.apache.druid.server.coordinator.AutoCompactionSnapshot; import org.apache.druid.server.coordinator.DruidCoordinator; import org.apache.druid.server.http.security.ConfigResourceFilter; +import org.apache.druid.server.http.security.InternalInternalResourceFilter; import org.apache.druid.server.http.security.StateResourceFilter; +import org.apache.druid.server.security.Access; +import org.apache.druid.server.security.Action; +import org.apache.druid.server.security.AuthConfig; +import org.apache.druid.server.security.AuthorizationUtils; +import org.apache.druid.server.security.AuthorizerMapper; +import org.apache.druid.server.security.ForbiddenException; +import org.apache.druid.server.security.Resource; +import org.apache.druid.server.security.ResourceAction; +import org.apache.druid.server.security.ResourceType; +import javax.servlet.http.HttpServletRequest; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; @Path("/druid/coordinator/v1/compaction") public class CompactionResource { private final DruidCoordinator coordinator; + private final AuthorizerMapper authorizerMapper; @Inject public CompactionResource( - DruidCoordinator coordinator + DruidCoordinator coordinator, + AuthorizerMapper authorizerMapper ) { this.coordinator = coordinator; + this.authorizerMapper = authorizerMapper; } /** @@ -56,7 +73,7 @@ public CompactionResource( */ @POST @Path("/compact") - @ResourceFilters(ConfigResourceFilter.class) + @ResourceFilters({ ConfigResourceFilter.class, InternalInternalResourceFilter.class }) @VisibleForTesting public Response forceTriggerCompaction() { @@ -69,9 +86,22 @@ public Response forceTriggerCompaction() @Produces(MediaType.APPLICATION_JSON) @ResourceFilters(StateResourceFilter.class) public Response getCompactionProgress( - @QueryParam("dataSource") String dataSource + @QueryParam("dataSource") String dataSource, + @Context HttpServletRequest request ) { + if (authorizerMapper.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + final Access authResult = AuthorizationUtils.authorizeResourceAction( + request, + new ResourceAction(new Resource(dataSource, ResourceType.DATASOURCE), Action.READ), + authorizerMapper + ); + + if (!authResult.isAllowed()) { + throw new ForbiddenException(authResult.getMessage()); + } + } + final Long notCompactedSegmentSizeBytes = coordinator.getTotalSizeOfSegmentsAwaitingCompaction(dataSource); if (notCompactedSegmentSizeBytes == null) { return Response.status(Response.Status.BAD_REQUEST).entity(ImmutableMap.of("error", "unknown dataSource")).build(); @@ -85,13 +115,37 @@ public Response getCompactionProgress( @Produces(MediaType.APPLICATION_JSON) @ResourceFilters(StateResourceFilter.class) public Response getCompactionSnapshotForDataSource( - @QueryParam("dataSource") String dataSource + @QueryParam("dataSource") String dataSource, + @Context HttpServletRequest request ) { final Collection snapshots; if (dataSource == null || dataSource.isEmpty()) { - snapshots = coordinator.getAutoCompactionSnapshot().values(); + if (authorizerMapper.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + snapshots = new HashSet<>(); + AuthorizationUtils.filterAuthorizedResources( + request, + coordinator.getAutoCompactionSnapshot().values(), + input -> Collections.singleton( + new ResourceAction(new Resource(input.getDataSource(), ResourceType.DATASOURCE), Action.READ)), + authorizerMapper + ).forEach(snapshots::add); + } else { + snapshots = coordinator.getAutoCompactionSnapshot().values(); + } } else { + if (authorizerMapper.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + final Access authResult = AuthorizationUtils.authorizeResourceAction( + request, + new ResourceAction(new Resource(dataSource, ResourceType.DATASOURCE), Action.READ), + authorizerMapper + ); + + if (!authResult.isAllowed()) { + throw new ForbiddenException(authResult.getMessage()); + } + } + AutoCompactionSnapshot autoCompactionSnapshot = coordinator.getAutoCompactionSnapshotForDataSource(dataSource); if (autoCompactionSnapshot == null) { return Response.status(Response.Status.BAD_REQUEST).entity(ImmutableMap.of("error", "unknown dataSource")).build(); diff --git a/server/src/main/java/org/apache/druid/server/security/ResourceName.java b/server/src/main/java/org/apache/druid/server/security/ResourceName.java deleted file mode 100644 index e43206e24670..000000000000 --- a/server/src/main/java/org/apache/druid/server/security/ResourceName.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.druid.server.security; - -import com.fasterxml.jackson.annotation.JsonCreator; -import org.apache.druid.java.util.common.StringUtils; - -import javax.validation.constraints.NotNull; -import java.util.Objects; - -public class ResourceName -{ - public static final ResourceName INTERNAL = new ResourceName("INTERNAL"); - public static final ResourceName LOOKUP = new ResourceName("LOOKUP"); - public static final ResourceName SERVER = new ResourceName("SERVER"); - public static final ResourceName STATUS = new ResourceName("STATUS"); - - private final String resourceName; - - public ResourceName(String name) - { - this.resourceName = StringUtils.toUpperCase(name); - } - - @JsonCreator - public static ResourceName fromString(@NotNull String name) - { - return new ResourceName(StringUtils.toUpperCase(name)); - } - - @Override - public String toString() - { - return this.resourceName; - } - - @Override - public boolean equals(Object o) - { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - ResourceName that = (ResourceName) o; - return resourceName.equals(that.resourceName); - } - - @Override - public int hashCode() - { - return Objects.hash(resourceName); - } -} diff --git a/server/src/test/java/org/apache/druid/server/http/CompactionResourceTest.java b/server/src/test/java/org/apache/druid/server/http/CompactionResourceTest.java index 9e7cfd601eae..c3a2a8627d31 100644 --- a/server/src/test/java/org/apache/druid/server/http/CompactionResourceTest.java +++ b/server/src/test/java/org/apache/druid/server/http/CompactionResourceTest.java @@ -23,15 +23,27 @@ import com.google.common.collect.ImmutableMap; import org.apache.druid.server.coordinator.AutoCompactionSnapshot; import org.apache.druid.server.coordinator.DruidCoordinator; +import org.apache.druid.server.security.Access; +import org.apache.druid.server.security.Action; +import org.apache.druid.server.security.AuthConfig; +import org.apache.druid.server.security.AuthenticationResult; +import org.apache.druid.server.security.Authorizer; +import org.apache.druid.server.security.AuthorizerMapper; +import org.apache.druid.server.security.Resource; import org.easymock.EasyMock; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import javax.servlet.http.HttpServletRequest; import javax.ws.rs.core.Response; +import java.util.Arrays; import java.util.Map; +@RunWith(Parameterized.class) public class CompactionResourceTest { private DruidCoordinator mock; @@ -49,6 +61,47 @@ public class CompactionResourceTest 1, 1 ); + private AuthorizerMapper authorizerMapper; + private HttpServletRequest request; + + @Parameterized.Parameters(name = "{index}: authVersion={0}") + public static Iterable data() + { + return Arrays.asList(AuthConfig.AUTH_VERSION_1, AuthConfig.AUTH_VERSION_2); + } + + public CompactionResourceTest(String authVersion) + { + authorizerMapper = new AuthorizerMapper( + ImmutableMap.of("auth1", + new Authorizer() + { + @Override + public Access authorize(AuthenticationResult authenticationResult, Resource resource, Action action) + { + return new Access(true); + } + + @Override + public Access authorizeV2(AuthenticationResult authenticationResult, Resource resource, Action action) + { + return new Access(true); + } + } + ), + authVersion + ); + request = EasyMock.createMock(HttpServletRequest.class); + if (authVersion.equals(AuthConfig.AUTH_VERSION_2)) { + EasyMock.expect(request.getAttribute(AuthConfig.DRUID_ALLOW_UNSECURED_PATH)).andReturn(null).once(); + EasyMock.expect(request.getAttribute(AuthConfig.DRUID_AUTHORIZATION_CHECKED)).andReturn(null).once(); + EasyMock.expect(request.getAttribute(AuthConfig.DRUID_AUTHENTICATION_RESULT)) + .andReturn(new AuthenticationResult("", "auth1", "", null)).once(); + request.setAttribute(EasyMock.anyString(), EasyMock.anyObject()); + EasyMock.expectLastCall().once(); + } + EasyMock.replay(request); + } @Before public void setUp() @@ -59,7 +112,7 @@ public void setUp() @After public void tearDown() { - EasyMock.verify(mock); + EasyMock.verify(mock, request); } @Test @@ -73,7 +126,7 @@ public void testGetCompactionSnapshotForDataSourceWithEmptyQueryParameter() EasyMock.expect(mock.getAutoCompactionSnapshot()).andReturn(expected).once(); EasyMock.replay(mock); - final Response response = new CompactionResource(mock).getCompactionSnapshotForDataSource(""); + final Response response = new CompactionResource(mock, authorizerMapper).getCompactionSnapshotForDataSource("", request); Assert.assertEquals(ImmutableMap.of("latestStatus", expected.values()), response.getEntity()); Assert.assertEquals(200, response.getStatus()); } @@ -90,7 +143,7 @@ public void testGetCompactionSnapshotForDataSourceWithNullQueryParameter() EasyMock.expect(mock.getAutoCompactionSnapshot()).andReturn(expected).once(); EasyMock.replay(mock); - final Response response = new CompactionResource(mock).getCompactionSnapshotForDataSource(null); + final Response response = new CompactionResource(mock, authorizerMapper).getCompactionSnapshotForDataSource(null, request); Assert.assertEquals(ImmutableMap.of("latestStatus", expected.values()), response.getEntity()); Assert.assertEquals(200, response.getStatus()); } @@ -103,7 +156,7 @@ public void testGetCompactionSnapshotForDataSourceWithValidQueryParameter() EasyMock.expect(mock.getAutoCompactionSnapshotForDataSource(dataSourceName)).andReturn(expectedSnapshot).once(); EasyMock.replay(mock); - final Response response = new CompactionResource(mock).getCompactionSnapshotForDataSource(dataSourceName); + final Response response = new CompactionResource(mock, authorizerMapper).getCompactionSnapshotForDataSource(dataSourceName, request); Assert.assertEquals(ImmutableMap.of("latestStatus", ImmutableList.of(expectedSnapshot)), response.getEntity()); Assert.assertEquals(200, response.getStatus()); } @@ -116,7 +169,7 @@ public void testGetCompactionSnapshotForDataSourceWithInvalidQueryParameter() EasyMock.expect(mock.getAutoCompactionSnapshotForDataSource(dataSourceName)).andReturn(null).once(); EasyMock.replay(mock); - final Response response = new CompactionResource(mock).getCompactionSnapshotForDataSource(dataSourceName); + final Response response = new CompactionResource(mock, authorizerMapper).getCompactionSnapshotForDataSource(dataSourceName, request); Assert.assertEquals(400, response.getStatus()); } } diff --git a/server/src/test/java/org/apache/druid/server/http/RulesResourceTest.java b/server/src/test/java/org/apache/druid/server/http/RulesResourceTest.java index 72812a9af6bf..ca3edfa7bac5 100644 --- a/server/src/test/java/org/apache/druid/server/http/RulesResourceTest.java +++ b/server/src/test/java/org/apache/druid/server/http/RulesResourceTest.java @@ -20,19 +20,29 @@ package org.apache.druid.server.http; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import org.apache.druid.audit.AuditEntry; import org.apache.druid.audit.AuditInfo; import org.apache.druid.audit.AuditManager; import org.apache.druid.java.util.common.DateTimes; import org.apache.druid.java.util.common.Intervals; import org.apache.druid.metadata.MetadataRuleManager; +import org.apache.druid.server.coordinator.rules.ForeverLoadRule; +import org.apache.druid.server.coordinator.rules.Rule; +import org.apache.druid.server.security.Access; +import org.apache.druid.server.security.Action; +import org.apache.druid.server.security.AuthConfig; +import org.apache.druid.server.security.AuthenticationResult; +import org.apache.druid.server.security.Authorizer; import org.apache.druid.server.security.AuthorizerMapper; +import org.apache.druid.server.security.Resource; import org.easymock.EasyMock; import org.joda.time.Interval; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import javax.servlet.http.HttpServletRequest; import javax.ws.rs.core.Response; import java.util.List; import java.util.Map; @@ -49,6 +59,62 @@ public void setUp() auditManager = EasyMock.createStrictMock(AuditManager.class); } + @Test + public void testGetRules() + { + final String authorizerName = "testAuthorizer"; + + EasyMock.expect(databaseRuleManager.getAllRules()).andReturn( + ImmutableMap.of( + "ds1", + ImmutableList.of(new ForeverLoadRule(null)), + "ds2", + ImmutableList.of(new ForeverLoadRule(null)) + ) + ).once(); + + RulesResource rulesResource = new RulesResource(databaseRuleManager, auditManager, new AuthorizerMapper( + ImmutableMap.of(authorizerName, + new Authorizer() + { + @Override + public Access authorize(AuthenticationResult authenticationResult, Resource resource, + Action action + ) + { + return null; + } + + @Override + public Access authorizeV2(AuthenticationResult authenticationResult, Resource resource, Action action) + { + if (resource.getName().equals("ds1")) { + return new Access(true); + } + return new Access(false); + } + } + ), AuthConfig.AUTH_VERSION_2)); + + final HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class); + EasyMock.expect(request.getAttribute(AuthConfig.DRUID_ALLOW_UNSECURED_PATH)).andReturn(null).once(); + EasyMock.expect(request.getAttribute(AuthConfig.DRUID_AUTHORIZATION_CHECKED)).andReturn(null).once(); + EasyMock.expect(request.getAttribute(AuthConfig.DRUID_AUTHENTICATION_RESULT)) + .andReturn(new AuthenticationResult("", authorizerName, "", null)).once(); + request.setAttribute(EasyMock.anyString(), EasyMock.anyObject()); + EasyMock.expectLastCall().once(); + + EasyMock.replay(databaseRuleManager, request); + + Response response = rulesResource.getRules(request); + final Map> rules = (Map>) response.getEntity(); + Assert.assertEquals(1, rules.size()); + Assert.assertEquals(1, rules.get("ds1").size()); + Assert.assertEquals(new ForeverLoadRule(null), rules.get("ds1").get(0)); + + EasyMock.verify(databaseRuleManager, request); + } + @Test public void testGetDatasourceRuleHistoryWithCount() { From 7e412f3a457531c4a0fd97f73b64882f5c870307 Mon Sep 17 00:00:00 2001 From: Parag Jain Date: Thu, 12 Nov 2020 14:37:02 +0530 Subject: [PATCH 13/15] add more tests, fix getAllLookupSpecs endpoint --- .../BasicRoleBasedAuthorizerTest.java | 6 +- ...cAuthorizerMetadataStorageUpdaterTest.java | 3 +- .../overlord/http/OverlordResourceTest.java | 112 +++++++- .../overlord/sampler/SamplerResourceTest.java | 125 ++++++++ .../http/LookupCoordinatorResource.java | 13 +- .../http/LookupCoordinatorResourceTest.java | 266 ++++++++++++------ 6 files changed, 422 insertions(+), 103 deletions(-) create mode 100644 indexing-service/src/test/java/org/apache/druid/indexing/overlord/sampler/SamplerResourceTest.java diff --git a/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/BasicRoleBasedAuthorizerTest.java b/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/BasicRoleBasedAuthorizerTest.java index c7af45962eec..c28ae7028ac1 100644 --- a/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/BasicRoleBasedAuthorizerTest.java +++ b/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/BasicRoleBasedAuthorizerTest.java @@ -80,11 +80,13 @@ public class BasicRoleBasedAuthorizerTest private final String authVersion; @Parameterized.Parameters(name = "{index}: authVersion={0}") - public static Iterable data() { + public static Iterable data() + { return Arrays.asList(AuthConfig.AUTH_VERSION_1, AuthConfig.AUTH_VERSION_2); } - public BasicRoleBasedAuthorizerTest(String authVersion) { + public BasicRoleBasedAuthorizerTest(String authVersion) + { this.authVersion = authVersion; } diff --git a/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/CoordinatorBasicAuthorizerMetadataStorageUpdaterTest.java b/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/CoordinatorBasicAuthorizerMetadataStorageUpdaterTest.java index 5a46013015d2..bf7f6b4fcbec 100644 --- a/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/CoordinatorBasicAuthorizerMetadataStorageUpdaterTest.java +++ b/extensions-core/druid-basic-security/src/test/java/org/apache/druid/security/authorization/CoordinatorBasicAuthorizerMetadataStorageUpdaterTest.java @@ -80,7 +80,8 @@ public class CoordinatorBasicAuthorizerMetadataStorageUpdaterTest private final AuthorizerMapper authorizerMapper; @Parameterized.Parameters(name = "{index}: authVersion={0}") - public static Iterable data() { + public static Iterable data() + { return Arrays.asList(AuthConfig.AUTH_VERSION_1, AuthConfig.AUTH_VERSION_2); } diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/overlord/http/OverlordResourceTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/overlord/http/OverlordResourceTest.java index 03d596be279e..4d39db6059fe 100644 --- a/indexing-service/src/test/java/org/apache/druid/indexing/overlord/http/OverlordResourceTest.java +++ b/indexing-service/src/test/java/org/apache/druid/indexing/overlord/http/OverlordResourceTest.java @@ -22,6 +22,7 @@ import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; import com.google.common.util.concurrent.ListenableFuture; import org.apache.druid.indexer.RunnerTaskState; import org.apache.druid.indexer.TaskInfo; @@ -33,8 +34,11 @@ import org.apache.druid.indexing.common.actions.TaskActionClient; import org.apache.druid.indexing.common.config.TaskConfig; import org.apache.druid.indexing.common.task.AbstractTask; +import org.apache.druid.indexing.common.task.IndexTask; import org.apache.druid.indexing.common.task.NoopTask; import org.apache.druid.indexing.common.task.Task; +import org.apache.druid.indexing.common.task.TaskResource; +import org.apache.druid.indexing.input.DruidInputSource; import org.apache.druid.indexing.overlord.IndexerMetadataStorageAdapter; import org.apache.druid.indexing.overlord.TaskMaster; import org.apache.druid.indexing.overlord.TaskQueue; @@ -43,8 +47,10 @@ import org.apache.druid.indexing.overlord.TaskStorageQueryAdapter; import org.apache.druid.indexing.overlord.WorkerTaskRunnerQueryAdapter; import org.apache.druid.java.util.common.DateTimes; +import org.apache.druid.java.util.common.ISE; import org.apache.druid.java.util.common.RE; import org.apache.druid.segment.TestHelper; +import org.apache.druid.segment.indexing.DataSchema; import org.apache.druid.server.security.Access; import org.apache.druid.server.security.Action; import org.apache.druid.server.security.AuthConfig; @@ -53,6 +59,8 @@ import org.apache.druid.server.security.AuthorizerMapper; import org.apache.druid.server.security.ForbiddenException; import org.apache.druid.server.security.Resource; +import org.apache.druid.server.security.ResourceAction; +import org.apache.druid.server.security.ResourceType; import org.easymock.EasyMock; import org.jboss.netty.handler.codec.http.HttpResponseStatus; import org.joda.time.DateTime; @@ -66,15 +74,20 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; +@RunWith(Parameterized.class) public class OverlordResourceTest { private OverlordResource overlordResource; @@ -88,21 +101,18 @@ public class OverlordResourceTest @Rule public ExpectedException expectedException = ExpectedException.none(); - @Before - public void setUp() + @Parameterized.Parameters(name = "{index}: authVersion={0}") + public static Iterable data() { - taskRunner = EasyMock.createMock(TaskRunner.class); - taskMaster = EasyMock.createStrictMock(TaskMaster.class); - taskStorageQueryAdapter = EasyMock.createStrictMock(TaskStorageQueryAdapter.class); - indexerMetadataStorageAdapter = EasyMock.createStrictMock(IndexerMetadataStorageAdapter.class); - req = EasyMock.createStrictMock(HttpServletRequest.class); - workerTaskRunnerQueryAdapter = EasyMock.createStrictMock(WorkerTaskRunnerQueryAdapter.class); + return Arrays.asList(AuthConfig.AUTH_VERSION_1, AuthConfig.AUTH_VERSION_2); + } - EasyMock.expect(taskMaster.getTaskRunner()).andReturn( - Optional.of(taskRunner) - ).anyTimes(); + private AuthorizerMapper authMapper; + private List actualResourceActions = new ArrayList<>(); - AuthorizerMapper authMapper = new AuthorizerMapper(null, null) + public OverlordResourceTest(String authVersion) + { + authMapper = new AuthorizerMapper(null, authVersion) { @Override public Authorizer getAuthorizer(String name) @@ -112,6 +122,10 @@ public Authorizer getAuthorizer(String name) @Override public Access authorize(AuthenticationResult authenticationResult, Resource resource, Action action) { + if (authVersion.equals(AuthConfig.AUTH_VERSION_2)) { + throw new ISE("Unexpected call"); + } + actualResourceActions.add(new ResourceAction(resource, action)); if (resource.getName().equals("allow")) { return new Access(true); } else { @@ -119,9 +133,37 @@ public Access authorize(AuthenticationResult authenticationResult, Resource reso } } + @Override + public Access authorizeV2(AuthenticationResult authenticationResult, Resource resource, Action action) + { + if (authVersion.equals(AuthConfig.AUTH_VERSION_1)) { + throw new ISE("Unexpected call"); + } + actualResourceActions.add(new ResourceAction(resource, action)); + if (resource.getName().equals("allow")) { + return new Access(true); + } else { + return new Access(false); + } + } }; } }; + } + + @Before + public void setUp() + { + taskRunner = EasyMock.createMock(TaskRunner.class); + taskMaster = EasyMock.createStrictMock(TaskMaster.class); + taskStorageQueryAdapter = EasyMock.createStrictMock(TaskStorageQueryAdapter.class); + indexerMetadataStorageAdapter = EasyMock.createStrictMock(IndexerMetadataStorageAdapter.class); + req = EasyMock.createStrictMock(HttpServletRequest.class); + workerTaskRunnerQueryAdapter = EasyMock.createStrictMock(WorkerTaskRunnerQueryAdapter.class); + + EasyMock.expect(taskMaster.getTaskRunner()).andReturn( + Optional.of(taskRunner) + ).anyTimes(); overlordResource = new OverlordResource( taskMaster, @@ -922,6 +964,52 @@ public void testSecuredTaskPost() overlordResource.taskPost(task, req); } + @Test + public void testSecuredReindexingTaskPost() + { + expectedException.expect(ForbiddenException.class); + expectAuthorizationTokenCheck(); + + DataSchema dataSchema = EasyMock.createMock(DataSchema.class); + DruidInputSource druidInputSource = EasyMock.createMock(DruidInputSource.class); + + EasyMock.expect(dataSchema.getParserMap()).andReturn(null).anyTimes(); + EasyMock.expect(dataSchema.getDataSource()).andReturn("destination").anyTimes(); + EasyMock.expect(druidInputSource.needsFormat()).andReturn(false).anyTimes(); + EasyMock.expect(druidInputSource.getDataSource()).andReturn("source").anyTimes(); + + EasyMock.replay( + taskRunner, + taskMaster, + taskStorageQueryAdapter, + indexerMetadataStorageAdapter, + req, + workerTaskRunnerQueryAdapter, + dataSchema, + druidInputSource + ); + + Task task = new IndexTask( + "id", + new TaskResource("", 1), + new IndexTask.IndexIngestionSpec( + dataSchema, + new IndexTask.IndexIOConfig(null, druidInputSource, null, false), + null + ), + ImmutableMap.of() + ); + final List expectedResourceActions = Lists.newArrayList( + new ResourceAction(new Resource("destination", ResourceType.DATASOURCE), Action.WRITE), + new ResourceAction(new Resource("source", ResourceType.DATASOURCE), Action.READ) + ); + + overlordResource.taskPost(task, req); + Assert.assertEquals(expectedResourceActions, actualResourceActions); + + EasyMock.verify(dataSchema, druidInputSource); + } + @Test public void testKillPendingSegments() { diff --git a/indexing-service/src/test/java/org/apache/druid/indexing/overlord/sampler/SamplerResourceTest.java b/indexing-service/src/test/java/org/apache/druid/indexing/overlord/sampler/SamplerResourceTest.java new file mode 100644 index 000000000000..47c97dc26d41 --- /dev/null +++ b/indexing-service/src/test/java/org/apache/druid/indexing/overlord/sampler/SamplerResourceTest.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.druid.indexing.overlord.sampler; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Lists; +import org.apache.druid.indexing.input.DruidInputSource; +import org.apache.druid.java.util.common.ISE; +import org.apache.druid.server.security.Access; +import org.apache.druid.server.security.Action; +import org.apache.druid.server.security.AuthConfig; +import org.apache.druid.server.security.AuthenticationResult; +import org.apache.druid.server.security.Authorizer; +import org.apache.druid.server.security.AuthorizerMapper; +import org.apache.druid.server.security.Resource; +import org.apache.druid.server.security.ResourceAction; +import org.apache.druid.server.security.ResourceType; +import org.easymock.EasyMock; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import javax.servlet.http.HttpServletRequest; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class SamplerResourceTest +{ + private AuthorizerMapper authorizerMapper; + private List actualResourceActions; + private SamplerResource samplerResource; + private IndexTaskSamplerSpec samplerSpec; + private HttpServletRequest request; + + @Before + public void setUp() + { + actualResourceActions = new ArrayList<>(); + authorizerMapper = new AuthorizerMapper( + ImmutableMap.of( + "auth1", + new Authorizer() + { + @Override + public Access authorize(AuthenticationResult authenticationResult, Resource resource, Action action) + { + throw new ISE("Unexpected call"); + } + + @Override + public Access authorizeV2(AuthenticationResult authenticationResult, Resource resource, Action action) + { + actualResourceActions.add(new ResourceAction(resource, action)); + return new Access(true); + } + } + ), AuthConfig.AUTH_VERSION_2); + samplerResource = new SamplerResource(authorizerMapper); + + request = EasyMock.createMock(HttpServletRequest.class); + EasyMock.expect(request.getAttribute(AuthConfig.DRUID_ALLOW_UNSECURED_PATH)).andReturn(null).once(); + EasyMock.expect(request.getAttribute(AuthConfig.DRUID_AUTHORIZATION_CHECKED)).andReturn(null).once(); + EasyMock.expect(request.getAttribute(AuthConfig.DRUID_AUTHENTICATION_RESULT)) + .andReturn(new AuthenticationResult("", "auth1", "", null)).once(); + request.setAttribute(EasyMock.anyString(), EasyMock.anyObject()); + EasyMock.expectLastCall().once(); + EasyMock.replay(request); + } + + @After + public void tearDown() + { + EasyMock.verify(samplerSpec, request); + } + + @Test + public void testPost() + { + samplerSpec = EasyMock.createMock(IndexTaskSamplerSpec.class); + EasyMock.expect(samplerSpec.getInputSource()).andReturn(null).once(); + EasyMock.expect(samplerSpec.sample()).andReturn(new SamplerResponse(0, 0, null)).once(); + EasyMock.replay(samplerSpec); + List expectedResourceActions = Collections + .singletonList(new ResourceAction(Resource.SERVER_USER_RESOURCE, Action.WRITE)); + samplerResource.post(samplerSpec, request); + Assert.assertEquals(expectedResourceActions, actualResourceActions); + } + + @Test + public void testPostReindexSampler() + { + samplerSpec = EasyMock.createMock(IndexTaskSamplerSpec.class); + DruidInputSource inputSource = EasyMock.createMock(DruidInputSource.class); + EasyMock.expect(samplerSpec.getInputSource()).andReturn(inputSource).anyTimes(); + EasyMock.expect(inputSource.getDataSource()).andReturn("source").once(); + EasyMock.expect(samplerSpec.sample()).andReturn(new SamplerResponse(0, 0, null)).once(); + EasyMock.replay(samplerSpec, inputSource); + List expectedResourceActions = Lists.newArrayList( + new ResourceAction(Resource.SERVER_USER_RESOURCE, Action.WRITE), + new ResourceAction(new Resource("source", ResourceType.DATASOURCE), Action.READ) + ); + samplerResource.post(samplerSpec, request); + Assert.assertEquals(expectedResourceActions, actualResourceActions); + EasyMock.verify(inputSource); + } +} diff --git a/server/src/main/java/org/apache/druid/server/http/LookupCoordinatorResource.java b/server/src/main/java/org/apache/druid/server/http/LookupCoordinatorResource.java index b821397ae01d..23bc1df5ca41 100644 --- a/server/src/main/java/org/apache/druid/server/http/LookupCoordinatorResource.java +++ b/server/src/main/java/org/apache/druid/server/http/LookupCoordinatorResource.java @@ -111,10 +111,7 @@ public LookupCoordinatorResource( @Path("/config") @Produces({MediaType.APPLICATION_JSON, SmileMediaTypes.APPLICATION_JACKSON_SMILE}) @ResourceFilters({ ConfigResourceFilter.class, ServerUserResourceFilter.class }) - public Response getTiers( - @DefaultValue("false") @QueryParam("discover") boolean discover, - @Context HttpServletRequest request - ) + public Response getTiers(@DefaultValue("false") @QueryParam("discover") boolean discover) { try { final Map> knownLookups = @@ -148,12 +145,12 @@ public Response getAllLookupSpecs(@Context HttpServletRequest request) try { Map> knownLookups = lookupCoordinatorManager .getKnownLookups(); - if (authorizerMapper.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { - knownLookups = filterByLookupAccess(knownLookups, request, authorizerMapper, Action.READ); - } if (knownLookups == null) { return Response.status(Response.Status.NOT_FOUND).build(); } else { + if (authorizerMapper.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + knownLookups = filterByLookupAccess(knownLookups, request, authorizerMapper, Action.READ); + } return Response.ok().entity(knownLookups).build(); } } @@ -864,7 +861,7 @@ private Map> filterByLoo Collections.singleton(new ResourceAction(new Resource(loookupId, ResourceType.LOOKUP), action)), authorizerMapper ); - if (access.isAllowed()) { + if (!access.isAllowed()) { filteredMap.remove(loookupId); } }); diff --git a/server/src/test/java/org/apache/druid/server/http/LookupCoordinatorResourceTest.java b/server/src/test/java/org/apache/druid/server/http/LookupCoordinatorResourceTest.java index bcf1c7960707..560dd1a05e32 100644 --- a/server/src/test/java/org/apache/druid/server/http/LookupCoordinatorResourceTest.java +++ b/server/src/test/java/org/apache/druid/server/http/LookupCoordinatorResourceTest.java @@ -27,15 +27,25 @@ import com.google.common.net.HostAndPort; import org.apache.druid.audit.AuditInfo; import org.apache.druid.jackson.DefaultObjectMapper; +import org.apache.druid.java.util.common.ISE; import org.apache.druid.java.util.common.StringUtils; import org.apache.druid.query.lookup.LookupsState; import org.apache.druid.server.lookup.cache.LookupCoordinatorManager; import org.apache.druid.server.lookup.cache.LookupExtractorFactoryMapContainer; +import org.apache.druid.server.security.Access; +import org.apache.druid.server.security.Action; +import org.apache.druid.server.security.AuthConfig; +import org.apache.druid.server.security.AuthenticationResult; +import org.apache.druid.server.security.Authorizer; import org.apache.druid.server.security.AuthorizerMapper; +import org.apache.druid.server.security.Resource; import org.easymock.Capture; import org.easymock.EasyMock; +import org.junit.After; import org.junit.Assert; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.core.MediaType; @@ -44,10 +54,12 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Set; +@RunWith(Parameterized.class) public class LookupCoordinatorResourceTest { private static final ObjectMapper MAPPER = new DefaultObjectMapper(); @@ -91,6 +103,68 @@ public InputStream openStream() throws IOException private static final Map> NODES_LOOKUP_STATE = ImmutableMap.of(LOOKUP_NODE, LOOKUP_STATE); + @Parameterized.Parameters(name = "{index}: authVersion={0}") + public static Iterable data() + { + return Arrays.asList(AuthConfig.AUTH_VERSION_1, AuthConfig.AUTH_VERSION_2); + } + + private AuthorizerMapper authorizerMapper; + private HttpServletRequest request; + private boolean verifyRequestMock = false; + + public LookupCoordinatorResourceTest(String authVersion) + { + this.request = EasyMock.createMock(HttpServletRequest.class); + + this.authorizerMapper = new AuthorizerMapper( + ImmutableMap.of("auth1", + new Authorizer() + { + @Override + public Access authorize(AuthenticationResult authenticationResult, Resource resource, Action action) + { + if (authVersion.equals(AuthConfig.AUTH_VERSION_2)) { + throw new ISE("Unexpected call"); + } + return new Access(true); + } + + @Override + public Access authorizeV2(AuthenticationResult authenticationResult, Resource resource, Action action) + { + if (authVersion.equals(AuthConfig.AUTH_VERSION_1)) { + throw new ISE("Unexpected call"); + } + return new Access(true); + } + } + ), + authVersion + ); + } + + private void setupRequestExpectation() + { + if (authorizerMapper.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + EasyMock.expect(request.getAttribute(AuthConfig.DRUID_ALLOW_UNSECURED_PATH)).andReturn(null).anyTimes(); + EasyMock.expect(request.getAttribute(AuthConfig.DRUID_AUTHORIZATION_CHECKED)).andReturn(null).anyTimes(); + EasyMock.expect(request.getAttribute(AuthConfig.DRUID_AUTHENTICATION_RESULT)) + .andReturn(new AuthenticationResult("", "auth1", "", null)).anyTimes(); + request.setAttribute(EasyMock.anyString(), EasyMock.anyObject()); + EasyMock.expectLastCall().anyTimes(); + } + verifyRequestMock = true; + } + + @After + public void tearDown() + { + if (verifyRequestMock) { + EasyMock.verify(request); + } + } + @Test public void testSimpleGet() { @@ -103,9 +177,9 @@ public void testSimpleGet() lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); - final Response response = lookupCoordinatorResource.getTiers(false, null); + final Response response = lookupCoordinatorResource.getTiers(false); Assert.assertEquals(200, response.getStatus()); Assert.assertEquals(retVal.keySet(), response.getEntity()); EasyMock.verify(lookupCoordinatorManager); @@ -122,9 +196,9 @@ public void testMissingGet() lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); - final Response response = lookupCoordinatorResource.getTiers(false, null); + final Response response = lookupCoordinatorResource.getTiers(false); Assert.assertEquals(404, response.getStatus()); EasyMock.verify(lookupCoordinatorManager); } @@ -141,9 +215,9 @@ public void testExceptionalGet() lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); - final Response response = lookupCoordinatorResource.getTiers(false, null); + final Response response = lookupCoordinatorResource.getTiers(false); Assert.assertEquals(500, response.getStatus()); Assert.assertEquals(ImmutableMap.of("error", errMsg), response.getEntity()); EasyMock.verify(lookupCoordinatorManager); @@ -162,9 +236,9 @@ public void testDiscoveryGet() lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); - final Response response = lookupCoordinatorResource.getTiers(true, null); + final Response response = lookupCoordinatorResource.getTiers(true); Assert.assertEquals(200, response.getStatus()); Assert.assertEquals(ImmutableSet.of("lookupTier", "discoveredLookupTier"), response.getEntity()); @@ -185,9 +259,9 @@ public void testDiscoveryExceptionalGet() lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); - final Response response = lookupCoordinatorResource.getTiers(true, null); + final Response response = lookupCoordinatorResource.getTiers(true); Assert.assertEquals(500, response.getStatus()); Assert.assertEquals(ImmutableMap.of("error", errMsg), response.getEntity()); EasyMock.verify(lookupCoordinatorManager); @@ -210,9 +284,11 @@ public void testSimpleGetLookup() lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); - final Response response = lookupCoordinatorResource.getSpecificLookup(LOOKUP_TIER, LOOKUP_NAME, null); + setupRequestExpectation(); + EasyMock.replay(request); + final Response response = lookupCoordinatorResource.getSpecificLookup(LOOKUP_TIER, LOOKUP_NAME, request); Assert.assertEquals(200, response.getStatus()); Assert.assertEquals(container, response.getEntity()); EasyMock.verify(lookupCoordinatorManager); @@ -228,9 +304,11 @@ public void testDetailedGetLookup() lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); - final Response response = lookupCoordinatorResource.getSpecificTier(LOOKUP_TIER, true, null); + setupRequestExpectation(); + EasyMock.replay(request); + final Response response = lookupCoordinatorResource.getSpecificTier(LOOKUP_TIER, true, request); Assert.assertEquals(200, response.getStatus()); Assert.assertEquals(SINGLE_TIER_MAP.get(LOOKUP_TIER), response.getEntity()); EasyMock.verify(lookupCoordinatorManager); @@ -249,9 +327,11 @@ public void testMissingGetLookup() lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); - final Response response = lookupCoordinatorResource.getSpecificLookup(LOOKUP_TIER, LOOKUP_NAME, null); + setupRequestExpectation(); + EasyMock.replay(request); + final Response response = lookupCoordinatorResource.getSpecificLookup(LOOKUP_TIER, LOOKUP_NAME, request); Assert.assertEquals(404, response.getStatus()); EasyMock.verify(lookupCoordinatorManager); } @@ -266,12 +346,14 @@ public void testInvalidGetLookup() lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); - Assert.assertEquals(400, lookupCoordinatorResource.getSpecificLookup("foo", null, null).getStatus()); - Assert.assertEquals(400, lookupCoordinatorResource.getSpecificLookup("foo", "", null).getStatus()); - Assert.assertEquals(400, lookupCoordinatorResource.getSpecificLookup("", "foo", null).getStatus()); - Assert.assertEquals(400, lookupCoordinatorResource.getSpecificLookup(null, "foo", null).getStatus()); + setupRequestExpectation(); + EasyMock.replay(request); + Assert.assertEquals(400, lookupCoordinatorResource.getSpecificLookup("foo", null, request).getStatus()); + Assert.assertEquals(400, lookupCoordinatorResource.getSpecificLookup("foo", "", request).getStatus()); + Assert.assertEquals(400, lookupCoordinatorResource.getSpecificLookup("", "foo", request).getStatus()); + Assert.assertEquals(400, lookupCoordinatorResource.getSpecificLookup(null, "foo", request).getStatus()); EasyMock.verify(lookupCoordinatorManager); } @@ -289,9 +371,11 @@ public void testExceptionalGetLookup() lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); - final Response response = lookupCoordinatorResource.getSpecificLookup(LOOKUP_TIER, LOOKUP_NAME, null); + setupRequestExpectation(); + EasyMock.replay(request); + final Response response = lookupCoordinatorResource.getSpecificLookup(LOOKUP_TIER, LOOKUP_NAME, request); Assert.assertEquals(500, response.getStatus()); Assert.assertEquals(ImmutableMap.of("error", errMsg), response.getEntity()); EasyMock.verify(lookupCoordinatorManager); @@ -304,12 +388,16 @@ public void testSimpleDeleteTier() final String comment = "some comment"; final String ip = "127.0.0.1"; - final HttpServletRequest request = EasyMock.createStrictMock(HttpServletRequest.class); + setupRequestExpectation(); EasyMock.expect(request.getRemoteAddr()).andReturn(ip).once(); final Capture auditInfoCapture = Capture.newInstance(); final LookupCoordinatorManager lookupCoordinatorManager = EasyMock.createStrictMock( LookupCoordinatorManager.class); + if (authorizerMapper.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { + // in auth v2 write access to all lookups are checked before deleting the tier + EasyMock.expect(lookupCoordinatorManager.getTierLookups(LOOKUP_TIER)).andReturn(SINGLE_LOOKUP_MAP).once(); + } EasyMock.expect(lookupCoordinatorManager.deleteTier( EasyMock.eq(LOOKUP_TIER), EasyMock.capture(auditInfoCapture) @@ -321,7 +409,7 @@ public void testSimpleDeleteTier() lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); final Response response = lookupCoordinatorResource.deleteTier( LOOKUP_TIER, @@ -347,7 +435,7 @@ public void testSimpleDelete() final String comment = "some comment"; final String ip = "127.0.0.1"; - final HttpServletRequest request = EasyMock.createStrictMock(HttpServletRequest.class); + setupRequestExpectation(); EasyMock.expect(request.getRemoteAddr()).andReturn(ip).once(); final Capture auditInfoCapture = Capture.newInstance(); @@ -365,7 +453,7 @@ public void testSimpleDelete() lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); final Response response = lookupCoordinatorResource.deleteLookup( LOOKUP_TIER, @@ -393,7 +481,7 @@ public void testMissingDelete() final String comment = "some comment"; final String ip = "127.0.0.1"; - final HttpServletRequest request = EasyMock.createStrictMock(HttpServletRequest.class); + setupRequestExpectation(); EasyMock.expect(request.getRemoteAddr()).andReturn(ip).once(); final Capture auditInfoCapture = Capture.newInstance(); @@ -411,7 +499,7 @@ public void testMissingDelete() lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); final Response response = lookupCoordinatorResource.deleteLookup( LOOKUP_TIER, @@ -440,7 +528,7 @@ public void testExceptionalDelete() final String ip = "127.0.0.1"; final String errMsg = "some error"; - final HttpServletRequest request = EasyMock.createStrictMock(HttpServletRequest.class); + setupRequestExpectation(); EasyMock.expect(request.getRemoteAddr()).andReturn(ip).once(); final Capture auditInfoCapture = Capture.newInstance(); @@ -458,7 +546,7 @@ public void testExceptionalDelete() lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); final Response response = lookupCoordinatorResource.deleteLookup( LOOKUP_TIER, @@ -489,13 +577,15 @@ public void testInvalidDelete() lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); - Assert.assertEquals(400, lookupCoordinatorResource.deleteLookup("foo", null, null, null, null).getStatus()); - Assert.assertEquals(400, lookupCoordinatorResource.deleteLookup(null, null, null, null, null).getStatus()); - Assert.assertEquals(400, lookupCoordinatorResource.deleteLookup(null, "foo", null, null, null).getStatus()); - Assert.assertEquals(400, lookupCoordinatorResource.deleteLookup("foo", "", null, null, null).getStatus()); - Assert.assertEquals(400, lookupCoordinatorResource.deleteLookup("", "foo", null, null, null).getStatus()); + setupRequestExpectation(); + EasyMock.replay(request); + Assert.assertEquals(400, lookupCoordinatorResource.deleteLookup("foo", null, null, null, request).getStatus()); + Assert.assertEquals(400, lookupCoordinatorResource.deleteLookup(null, null, null, null, request).getStatus()); + Assert.assertEquals(400, lookupCoordinatorResource.deleteLookup(null, "foo", null, null, request).getStatus()); + Assert.assertEquals(400, lookupCoordinatorResource.deleteLookup("foo", "", null, null, request).getStatus()); + Assert.assertEquals(400, lookupCoordinatorResource.deleteLookup("", "foo", null, null, request).getStatus()); EasyMock.verify(lookupCoordinatorManager); } @@ -505,8 +595,7 @@ public void testSimpleNew() throws Exception final String author = "some author"; final String comment = "some comment"; final String ip = "127.0.0.1"; - - final HttpServletRequest request = EasyMock.createStrictMock(HttpServletRequest.class); + setupRequestExpectation(); EasyMock.expect(request.getContentType()).andReturn(MediaType.APPLICATION_JSON).once(); EasyMock.expect(request.getRemoteAddr()).andReturn(ip).once(); final Capture auditInfoCapture = Capture.newInstance(); @@ -522,7 +611,7 @@ public void testSimpleNew() throws Exception lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); final Response response = lookupCoordinatorResource.updateAllLookups( SINGLE_TIER_MAP_SOURCE.openStream(), @@ -549,7 +638,7 @@ public void testExceptionalNew() throws Exception final String ip = "127.0.0.1"; final String errMsg = "some error"; - final HttpServletRequest request = EasyMock.createStrictMock(HttpServletRequest.class); + setupRequestExpectation(); EasyMock.expect(request.getContentType()).andReturn(MediaType.APPLICATION_JSON).once(); EasyMock.expect(request.getRemoteAddr()).andReturn(ip).once(); @@ -567,7 +656,7 @@ public void testExceptionalNew() throws Exception lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); final Response response = lookupCoordinatorResource.updateAllLookups( SINGLE_TIER_MAP_SOURCE.openStream(), @@ -594,7 +683,7 @@ public void testFailedNew() throws Exception final String comment = "some comment"; final String ip = "127.0.0.1"; - final HttpServletRequest request = EasyMock.createStrictMock(HttpServletRequest.class); + setupRequestExpectation(); EasyMock.expect(request.getContentType()).andReturn(MediaType.APPLICATION_JSON).once(); EasyMock.expect(request.getRemoteAddr()).andReturn(ip).once(); @@ -612,7 +701,7 @@ public void testFailedNew() throws Exception lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); final Response response = lookupCoordinatorResource.updateAllLookups( SINGLE_TIER_MAP_SOURCE.openStream(), @@ -639,7 +728,7 @@ public void testSimpleNewLookup() throws Exception final String comment = "some comment"; final String ip = "127.0.0.1"; - final HttpServletRequest request = EasyMock.createStrictMock(HttpServletRequest.class); + setupRequestExpectation(); EasyMock.expect(request.getContentType()).andReturn(MediaType.APPLICATION_JSON).once(); EasyMock.expect(request.getRemoteAddr()).andReturn(ip).once(); @@ -659,7 +748,7 @@ public void testSimpleNewLookup() throws Exception lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); final Response response = lookupCoordinatorResource.createOrUpdateLookup( LOOKUP_TIER, @@ -688,7 +777,7 @@ public void testDBErrNewLookup() throws Exception final String comment = "some comment"; final String ip = "127.0.0.1"; - final HttpServletRequest request = EasyMock.createStrictMock(HttpServletRequest.class); + setupRequestExpectation(); EasyMock.expect(request.getContentType()).andReturn(MediaType.APPLICATION_JSON).once(); EasyMock.expect(request.getRemoteAddr()).andReturn(ip).once(); @@ -708,7 +797,7 @@ public void testDBErrNewLookup() throws Exception lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); final Response response = lookupCoordinatorResource.createOrUpdateLookup( LOOKUP_TIER, @@ -738,7 +827,7 @@ public void testExceptionalNewLookup() throws Exception final String comment = "some comment"; final String ip = "127.0.0.1"; - final HttpServletRequest request = EasyMock.createStrictMock(HttpServletRequest.class); + setupRequestExpectation(); EasyMock.expect(request.getContentType()).andReturn(MediaType.APPLICATION_JSON).once(); EasyMock.expect(request.getRemoteAddr()).andReturn(ip).once(); @@ -758,7 +847,7 @@ public void testExceptionalNewLookup() throws Exception lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); final Response response = lookupCoordinatorResource.createOrUpdateLookup( LOOKUP_TIER, @@ -797,7 +886,7 @@ public void testNullValsNewLookup() throws Exception lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); EasyMock.replay(lookupCoordinatorManager, request); @@ -849,9 +938,11 @@ public void testSimpleGetTier() lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); - final Response response = lookupCoordinatorResource.getSpecificTier(LOOKUP_TIER, false, null); + setupRequestExpectation(); + EasyMock.replay(request); + final Response response = lookupCoordinatorResource.getSpecificTier(LOOKUP_TIER, false, request); Assert.assertEquals(200, response.getStatus()); Assert.assertEquals(SINGLE_TIER_MAP.get(LOOKUP_TIER).keySet(), response.getEntity()); EasyMock.verify(lookupCoordinatorManager); @@ -870,9 +961,11 @@ public void testMissingGetTier() lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); - final Response response = lookupCoordinatorResource.getSpecificTier(tier, false, null); + setupRequestExpectation(); + EasyMock.replay(request); + final Response response = lookupCoordinatorResource.getSpecificTier(tier, false, request); Assert.assertEquals(404, response.getStatus()); EasyMock.verify(lookupCoordinatorManager); } @@ -887,9 +980,11 @@ public void testNullGetTier() lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); - final Response response = lookupCoordinatorResource.getSpecificTier(tier, false, null); + setupRequestExpectation(); + EasyMock.replay(request); + final Response response = lookupCoordinatorResource.getSpecificTier(tier, false, request); Assert.assertEquals(400, response.getStatus()); Assert.assertEquals(ImmutableMap.of("error", "`tier` required"), response.getEntity()); EasyMock.verify(lookupCoordinatorManager); @@ -906,9 +1001,11 @@ public void testNullLookupsGetTier() lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); - final Response response = lookupCoordinatorResource.getSpecificTier(tier, false, null); + setupRequestExpectation(); + EasyMock.replay(request); + final Response response = lookupCoordinatorResource.getSpecificTier(tier, false, request); Assert.assertEquals(404, response.getStatus()); Assert.assertEquals(ImmutableMap.of("error", "No lookups found"), response.getEntity()); EasyMock.verify(lookupCoordinatorManager); @@ -926,9 +1023,11 @@ public void testExceptionalGetTier() lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); - final Response response = lookupCoordinatorResource.getSpecificTier(tier, false, null); + setupRequestExpectation(); + EasyMock.replay(request); + final Response response = lookupCoordinatorResource.getSpecificTier(tier, false, request); Assert.assertEquals(500, response.getStatus()); Assert.assertEquals(ImmutableMap.of("error", errMsg), response.getEntity()); EasyMock.verify(lookupCoordinatorManager); @@ -950,10 +1049,11 @@ public void testGetAllLookupsStatus() lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); - - final Response response = lookupCoordinatorResource.getAllLookupsStatus(false, null); + setupRequestExpectation(); + EasyMock.replay(request); + final Response response = lookupCoordinatorResource.getAllLookupsStatus(false, request); Assert.assertEquals(200, response.getStatus()); Assert.assertEquals( ImmutableMap.of( @@ -984,10 +1084,11 @@ public void testGetLookupStatusForTier() lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); - - final Response response = lookupCoordinatorResource.getLookupStatusForTier(LOOKUP_TIER, false, null); + setupRequestExpectation(); + EasyMock.replay(request); + final Response response = lookupCoordinatorResource.getLookupStatusForTier(LOOKUP_TIER, false, request); Assert.assertEquals(200, response.getStatus()); Assert.assertEquals( ImmutableMap.of( @@ -1015,10 +1116,11 @@ public void testGetSpecificLookupStatus() lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); - - final Response response = lookupCoordinatorResource.getSpecificLookupStatus(LOOKUP_TIER, LOOKUP_NAME, false, null); + setupRequestExpectation(); + EasyMock.replay(request); + final Response response = lookupCoordinatorResource.getSpecificLookupStatus(LOOKUP_TIER, LOOKUP_NAME, false, request); Assert.assertEquals(200, response.getStatus()); Assert.assertEquals( new LookupCoordinatorResource.LookupStatus(true, null), response.getEntity() @@ -1034,7 +1136,7 @@ public void testGetLookupStatusDetailedTrue() EasyMock.createStrictMock(LookupCoordinatorManager.class), MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); HostAndPort newNode = HostAndPort.fromParts("localhost", 4352); @@ -1057,7 +1159,7 @@ public void testGetLookupStatusDetailedFalse() EasyMock.createStrictMock(LookupCoordinatorManager.class), MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); HostAndPort newNode = HostAndPort.fromParts("localhost", 4352); @@ -1089,7 +1191,7 @@ public void testGetAllNodesStatus() lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); final Response response = lookupCoordinatorResource.getAllNodesStatus(false, null); @@ -1124,7 +1226,7 @@ public void testGetAllNodesStatusDetailedFalse() lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); final Response response = lookupCoordinatorResource.getAllNodesStatus(false, false); @@ -1160,7 +1262,7 @@ public void testGetNodesStatusInTier() lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); final Response response = lookupCoordinatorResource.getNodesStatusInTier(LOOKUP_TIER); @@ -1189,7 +1291,7 @@ public void testGetSpecificNodeStatus() lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); final Response response = lookupCoordinatorResource.getSpecificNodeStatus(LOOKUP_TIER, LOOKUP_NODE); @@ -1236,9 +1338,11 @@ public void testGetAllLookupSpecs() lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); - final Response response = lookupCoordinatorResource.getAllLookupSpecs(null); + setupRequestExpectation(); + EasyMock.replay(request); + final Response response = lookupCoordinatorResource.getAllLookupSpecs(request); Assert.assertEquals(Status.OK.getStatusCode(), response.getStatus()); Assert.assertEquals(lookups, response.getEntity()); EasyMock.verify(lookupCoordinatorManager); @@ -1258,9 +1362,11 @@ public void testGetEmptyAllLookupSpecs() lookupCoordinatorManager, MAPPER, MAPPER, - new AuthorizerMapper(null, null) + authorizerMapper ); - final Response response = lookupCoordinatorResource.getAllLookupSpecs(null); + setupRequestExpectation(); + EasyMock.replay(request); + final Response response = lookupCoordinatorResource.getAllLookupSpecs(request); Assert.assertEquals(Status.NOT_FOUND.getStatusCode(), response.getStatus()); EasyMock.verify(lookupCoordinatorManager); } From 42e6f868c8f85100318cea8459eec6f5256989dc Mon Sep 17 00:00:00 2001 From: Parag Jain Date: Thu, 12 Nov 2020 15:37:23 +0530 Subject: [PATCH 14/15] fix spelling --- docs/design/auth-model.md | 10 +++++----- website/.spelling | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/design/auth-model.md b/docs/design/auth-model.md index 4c6653426e95..44b84fad1789 100644 --- a/docs/design/auth-model.md +++ b/docs/design/auth-model.md @@ -112,12 +112,12 @@ This model can be enabled by setting the flag `druid.auth.authVersion=v2`. The i personas like admin, viewer etc. in an easy manner. There are two action types in Druid: READ and WRITE. Depending on HTTP method used for endpoints action is decided, for -GET and HEAD method actoin is `READ`, for all other methods action is `WRITE`. +GET and HEAD method action is `READ`, for all other methods action is `WRITE`. There are 4 resource types in Druid: DATASOURCE, INTERNAL, LOOKUP and SERVER. 1. `DATASOURCE` resource type is concerned with all the actions that can be taken for querying, indexing, setting retention, -compaction rules for a dataset. Thus, if a user has read/write action on datasource they can control the lifecycle of that datasouce. +compaction rules for a dataset. Thus, if a user has read/write action on datasource they can control the lifecycle of that datasource. 1. `SERVER` resource type covers all the cluster administration endpoints. @@ -127,7 +127,7 @@ compaction rules for a dataset. Thus, if a user has read/write action on datasou Examples - A server admin role can have read/write on `DATASOURCE`, `SERVER` and `LOOKUP` resource types. A viewer can have just read on all or specific `DATASOURCE`. Many times users may want to create roles with custom permissions, all these -is supported using `Resource Name`, each `Resource type` can have mulitple of them. Authorizer uses `Action`, `Resource Type` +is supported using `Resource Name`, each `Resource type` can have multiple of them. Authorizer uses `Action`, `Resource Type` and `Resource Name` to authorize users action. For example, while querying a datasource named `ds` a `READ` action on resource type `DATASOURCE` with resource name `ds` is required. Below are the details - @@ -174,7 +174,7 @@ write on destination datasource. There are 3 possible resource name for the "SERVER" config resource type, `SERVER`, `STATUS` and `USER`. -`SERVER` resoruce name covers following the end points, these are relevant for managing the cluster and can be used by +`SERVER` resource name covers following the end points, these are relevant for managing the cluster and can be used by cluster managers. This resource name is used in SQL queries asking for server information see `SQL Permissions` section below. |Endpoint|Process Type| @@ -272,7 +272,7 @@ for druid nodes talking to each other and cluster admins. ### LOOKUP `LOOKUP` is similar to `DATASOURCE` resource type having lookup name as resource name and specifying a lookup permission -allows the administrator to grant users access to specific lookup name. Endpoints that return list of lookpus will return +allows the administrator to grant users access to specific lookup name. Endpoints that return list of lookup will return filtered list by the granted permissions. It covers the following endpoints. |Endpoint|Process Type| diff --git a/website/.spelling b/website/.spelling index 096b3009e459..8abd2d9d19d4 100644 --- a/website/.spelling +++ b/website/.spelling @@ -444,6 +444,8 @@ HttpClient allowAll authenticatorChain defaultUser +- ../docs/design/auth-model.md +INFORMATION_SCHEMA - ../docs/design/coordinator.md inputSegmentSizeBytes skipOffsetFromLatest @@ -648,7 +650,6 @@ metricColumns nominalEntries numberOfValues - ../docs/development/extensions-core/druid-basic-security.md -INFORMATION_SCHEMA MyBasicAuthenticator MyBasicAuthorizer authenticatorName From 016cd443a8975b602f39c5374fd6b86175172730 Mon Sep 17 00:00:00 2001 From: Parag Jain Date: Fri, 13 Nov 2020 15:30:30 +0530 Subject: [PATCH 15/15] fix filter order, fixes compaction IT test --- .../server/http/CoordinatorCompactionConfigsResource.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/server/src/main/java/org/apache/druid/server/http/CoordinatorCompactionConfigsResource.java b/server/src/main/java/org/apache/druid/server/http/CoordinatorCompactionConfigsResource.java index 02968d2fb758..eac1d32d67bb 100644 --- a/server/src/main/java/org/apache/druid/server/http/CoordinatorCompactionConfigsResource.java +++ b/server/src/main/java/org/apache/druid/server/http/CoordinatorCompactionConfigsResource.java @@ -60,7 +60,6 @@ import java.util.stream.Collectors; @Path("/druid/coordinator/v1/config/compaction") -@ResourceFilters(ConfigResourceFilter.class) public class CoordinatorCompactionConfigsResource { private final JacksonConfigManager manager; @@ -78,6 +77,7 @@ public CoordinatorCompactionConfigsResource( @GET @Produces(MediaType.APPLICATION_JSON) + @ResourceFilters(ConfigResourceFilter.class) public Response getCompactionConfig(@Context HttpServletRequest request) { if (authorizerMapper.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { @@ -92,7 +92,7 @@ public Response getCompactionConfig(@Context HttpServletRequest request) @POST @Path("/taskslots") @Consumes(MediaType.APPLICATION_JSON) - @ResourceFilters(ServerServerResourceFilter.class) + @ResourceFilters({ ConfigResourceFilter.class, ServerServerResourceFilter.class }) public Response setCompactionTaskLimit( @QueryParam("ratio") Double compactionTaskSlotRatio, @QueryParam("max") Integer maxCompactionTaskSlots, @@ -126,6 +126,7 @@ public Response setCompactionTaskLimit( @POST @Consumes(MediaType.APPLICATION_JSON) + @ResourceFilters(ConfigResourceFilter.class) public Response addOrUpdateCompactionConfig( final DataSourceCompactionConfig newConfig, @HeaderParam(AuditManager.X_DRUID_AUTHOR) @DefaultValue("") final String author, @@ -164,6 +165,7 @@ public Response addOrUpdateCompactionConfig( @GET @Path("/{dataSource}") @Produces(MediaType.APPLICATION_JSON) + @ResourceFilters(ConfigResourceFilter.class) public Response getCompactionConfig(@PathParam("dataSource") String dataSource, @Context HttpServletRequest req) { if (authorizerMapper.getAuthVersion().equals(AuthConfig.AUTH_VERSION_2)) { @@ -192,6 +194,7 @@ public Response getCompactionConfig(@PathParam("dataSource") String dataSource, @DELETE @Path("/{dataSource}") @Produces(MediaType.APPLICATION_JSON) + @ResourceFilters(ConfigResourceFilter.class) public Response deleteCompactionConfig( @PathParam("dataSource") String dataSource, @HeaderParam(AuditManager.X_DRUID_AUTHOR) @DefaultValue("") final String author,