From 9f1ef8f681a780ea6f81a1f7f32b3383431f9b33 Mon Sep 17 00:00:00 2001 From: adilburaksen Date: Mon, 1 Jun 2026 23:15:33 +0300 Subject: [PATCH] security: add authorization checks to UsageHandler The usage endpoints under /v3/namespaces/{namespace-id} returned the datasets used by an application or program, and the programs that use a dataset, for the application, program or dataset identified by the request path without enforcing any access control on that resource. A caller could therefore read dataset/program usage relationships for applications, programs and datasets it is not authorized to access. Enforce StandardPermission.GET on the resource each endpoint reports usage for via the injected ContextAccessEnforcer: the ApplicationId for application dataset usage, the ProgramId for program dataset usage and the DatasetId for dataset program usage, mirroring the resource-level enforcement already performed by the lineage handler. --- .../java/io/cdap/cdap/gateway/handlers/UsageHandler.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/cdap-app-fabric/src/main/java/io/cdap/cdap/gateway/handlers/UsageHandler.java b/cdap-app-fabric/src/main/java/io/cdap/cdap/gateway/handlers/UsageHandler.java index cc79a234a782..6e9767e57444 100644 --- a/cdap-app-fabric/src/main/java/io/cdap/cdap/gateway/handlers/UsageHandler.java +++ b/cdap-app-fabric/src/main/java/io/cdap/cdap/gateway/handlers/UsageHandler.java @@ -24,6 +24,8 @@ import io.cdap.cdap.proto.id.ApplicationId; import io.cdap.cdap.proto.id.DatasetId; import io.cdap.cdap.proto.id.ProgramId; +import io.cdap.cdap.proto.security.StandardPermission; +import io.cdap.cdap.security.spi.authorization.ContextAccessEnforcer; import io.cdap.http.AbstractHttpHandler; import io.cdap.http.HttpResponder; import io.netty.handler.codec.http.HttpRequest; @@ -41,10 +43,12 @@ public class UsageHandler extends AbstractHttpHandler { private static final Gson GSON = new Gson(); private final UsageRegistry registry; + private final ContextAccessEnforcer contextAccessEnforcer; @Inject - public UsageHandler(UsageRegistry registry) { + public UsageHandler(UsageRegistry registry, ContextAccessEnforcer contextAccessEnforcer) { this.registry = registry; + this.contextAccessEnforcer = contextAccessEnforcer; } @GET @@ -53,6 +57,7 @@ public void getAppDatasetUsage(HttpRequest request, HttpResponder responder, @PathParam("namespace-id") String namespaceId, @PathParam("app-id") String appId) { final ApplicationId id = new ApplicationId(namespaceId, appId); + contextAccessEnforcer.enforce(id, StandardPermission.GET); Set ids = registry.getDatasets(id); responder.sendJson(HttpResponseStatus.OK, GSON.toJson(ids)); } @@ -66,6 +71,7 @@ public void getProgramDatasetUsage(HttpRequest request, HttpResponder responder, @PathParam("program-id") String programId) { ProgramType type = ProgramType.valueOfCategoryName(programType); final ProgramId id = new ProgramId(namespaceId, appId, type, programId); + contextAccessEnforcer.enforce(id, StandardPermission.GET); Set ids = registry.getDatasets(id); responder.sendJson(HttpResponseStatus.OK, GSON.toJson(ids)); } @@ -77,6 +83,7 @@ public void getDatasetAppUsage(HttpRequest request, HttpResponder responder, @PathParam("namespace-id") String namespaceId, @PathParam("dataset-id") String datasetId) { final DatasetId id = new DatasetId(namespaceId, datasetId); + contextAccessEnforcer.enforce(id, StandardPermission.GET); Set ids = registry.getPrograms(id); responder.sendJson(HttpResponseStatus.OK, GSON.toJson(ids)); }