This is an automated email from the ASF dual-hosted git repository.

jackie pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pinot.git


The following commit(s) were added to refs/heads/master by this push:
     new 0f2dcb770d refactor premission and access in controller rest api. 
(#13696)
0f2dcb770d is described below

commit 0f2dcb770dca414b02dc4d495d4b4458fa2e91ec
Author: Abhishek Sharma <abhishek.sha...@spothero.com>
AuthorDate: Mon Aug 5 20:48:52 2024 -0400

    refactor premission and access in controller rest api. (#13696)
---
 .../api/resources/PinotSchemaRestletResource.java  | 40 ++++------------
 .../api/resources/PinotTableRestletResource.java   | 54 +++++++++++-----------
 .../controller/api/resources/ResourceUtils.java    | 36 +++++++++++++++
 3 files changed, 70 insertions(+), 60 deletions(-)

diff --git 
a/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotSchemaRestletResource.java
 
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotSchemaRestletResource.java
index b372b1a633..876dd04be9 100644
--- 
a/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotSchemaRestletResource.java
+++ 
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotSchemaRestletResource.java
@@ -57,9 +57,7 @@ import 
org.apache.pinot.common.exception.TableNotFoundException;
 import org.apache.pinot.common.metrics.ControllerMeter;
 import org.apache.pinot.common.metrics.ControllerMetrics;
 import org.apache.pinot.common.utils.DatabaseUtils;
-import org.apache.pinot.controller.api.access.AccessControl;
 import org.apache.pinot.controller.api.access.AccessControlFactory;
-import org.apache.pinot.controller.api.access.AccessControlUtils;
 import org.apache.pinot.controller.api.access.AccessType;
 import org.apache.pinot.controller.api.access.Authenticate;
 import org.apache.pinot.controller.api.events.MetadataEventNotifierFactory;
@@ -240,7 +238,8 @@ public class PinotSchemaRestletResource {
     validateSchemaName(schema);
     String schemaName = 
DatabaseUtils.translateTableName(schema.getSchemaName(), httpHeaders);
     schema.setSchemaName(schemaName);
-    checkPermissionAndAccess(schemaName, request, httpHeaders, 
AccessType.CREATE, Actions.Table.CREATE_SCHEMA);
+    ResourceUtils.checkPermissionAndAccess(schemaName, request, httpHeaders,
+        AccessType.CREATE, Actions.Table.CREATE_SCHEMA, _accessControlFactory, 
LOGGER);
     SuccessResponse successResponse = addSchema(schema, override, force);
     return new ConfigSuccessResponse(successResponse.getStatus(), 
schemaAndUnrecognizedProps.getRight());
   }
@@ -271,7 +270,8 @@ public class PinotSchemaRestletResource {
     validateSchemaName(schema);
     String schemaName = 
DatabaseUtils.translateTableName(schema.getSchemaName(), httpHeaders);
     schema.setSchemaName(schemaName);
-    checkPermissionAndAccess(schemaName, request, httpHeaders, 
AccessType.CREATE, Actions.Table.CREATE_SCHEMA);
+    ResourceUtils.checkPermissionAndAccess(schemaName, request, httpHeaders,
+        AccessType.CREATE, Actions.Table.CREATE_SCHEMA, _accessControlFactory, 
LOGGER);
     SuccessResponse successResponse = addSchema(schema, override, force);
     return new ConfigSuccessResponse(successResponse.getStatus(), 
schemaAndUnrecognizedProperties.getRight());
   }
@@ -296,7 +296,8 @@ public class PinotSchemaRestletResource {
     String schemaName = 
DatabaseUtils.translateTableName(schema.getSchemaName(), httpHeaders);
     schema.setSchemaName(schemaName);
     validateSchemaInternal(schema);
-    checkPermissionAndAccess(schemaName, request, httpHeaders, 
AccessType.READ, Actions.Table.VALIDATE_SCHEMA);
+    ResourceUtils.checkPermissionAndAccess(schemaName, request, httpHeaders,
+        AccessType.READ, Actions.Table.VALIDATE_SCHEMA, _accessControlFactory, 
LOGGER);
     ObjectNode response = schema.toJsonObject();
     response.set("unrecognizedProperties", 
JsonUtils.objectToJsonNode(schemaAndUnrecognizedProps.getRight()));
     try {
@@ -326,7 +327,8 @@ public class PinotSchemaRestletResource {
     String schemaName = 
DatabaseUtils.translateTableName(schema.getSchemaName(), httpHeaders);
     schema.setSchemaName(schemaName);
     validateSchemaInternal(schema);
-    checkPermissionAndAccess(schemaName, request, httpHeaders, 
AccessType.READ, Actions.Table.VALIDATE_SCHEMA);
+    ResourceUtils.checkPermissionAndAccess(schemaName, request, httpHeaders,
+        AccessType.READ, Actions.Table.VALIDATE_SCHEMA, _accessControlFactory, 
LOGGER);
     ObjectNode response = schema.toJsonObject();
     response.set("unrecognizedProperties", 
JsonUtils.objectToJsonNode(schemaAndUnrecognizedProps.getRight()));
     try {
@@ -528,30 +530,4 @@ public class PinotSchemaRestletResource {
           Response.Status.INTERNAL_SERVER_ERROR);
     }
   }
-
-  /**
-   * Validates the permission and access for a given schema based on the 
request and HTTP headers.
-   * This method checks if the current user has the necessary permissions to 
perform an action on the specified schema.
-   * It utilizes the {@link AccessControl} mechanism to determine access rights
-   * and throws a {@link ControllerApplicationException} with a {@link 
Response.Status#FORBIDDEN} status
-   * if the access is denied.
-   *
-   * @param schemaName The name of the schema for which the permission and 
access are being checked.
-   * @param request The {@link Request} object containing information about 
the current request,
-   *                used to extract the endpoint URL.
-   * @param httpHeaders The {@link HttpHeaders} associated with the request,
-   *                    used for authorization and other header-based access 
control checks.
-   * @param accessType The type of access being requested (e.g., CREATE, READ, 
UPDATE, DELETE).
-   * @param action The specific action being checked against the access 
control policies.
-   * @throws ControllerApplicationException if the user does not have the 
required permissions or access.
-   */
-  private void checkPermissionAndAccess(String schemaName, Request request, 
HttpHeaders httpHeaders,
-      AccessType accessType, String action) {
-    String endpointUrl = request.getRequestURL().toString();
-    AccessControl accessControl = _accessControlFactory.create();
-    AccessControlUtils.validatePermission(schemaName, accessType, httpHeaders, 
endpointUrl, accessControl);
-    if (!accessControl.hasAccess(httpHeaders, TargetType.TABLE, schemaName, 
action)) {
-      throw new ControllerApplicationException(LOGGER, "Permission denied", 
Response.Status.FORBIDDEN);
-    }
-  }
 }
diff --git 
a/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotTableRestletResource.java
 
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotTableRestletResource.java
index dab2bc4681..7a5fb1936b 100644
--- 
a/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotTableRestletResource.java
+++ 
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotTableRestletResource.java
@@ -85,9 +85,7 @@ import 
org.apache.pinot.common.restlet.resources.ValidDocIdsType;
 import org.apache.pinot.common.utils.DatabaseUtils;
 import org.apache.pinot.common.utils.helix.HelixHelper;
 import org.apache.pinot.controller.ControllerConf;
-import org.apache.pinot.controller.api.access.AccessControl;
 import org.apache.pinot.controller.api.access.AccessControlFactory;
-import org.apache.pinot.controller.api.access.AccessControlUtils;
 import org.apache.pinot.controller.api.access.AccessType;
 import org.apache.pinot.controller.api.access.Authenticate;
 import 
org.apache.pinot.controller.api.exception.ControllerApplicationException;
@@ -212,19 +210,11 @@ public class PinotTableRestletResource {
       tableNameWithType = 
DatabaseUtils.translateTableName(tableConfig.getTableName(), httpHeaders);
       tableConfig.setTableName(tableNameWithType);
       // Handle legacy config
-      SegmentsValidationAndRetentionConfig validationConfig = 
tableConfig.getValidationConfig();
-      if (validationConfig.getSchemaName() != null) {
-        
validationConfig.setSchemaName(DatabaseUtils.translateTableName(validationConfig.getSchemaName(),
 httpHeaders));
-      }
+      handleLegacySchemaConfig(tableConfig, httpHeaders);
 
       // validate permission
-      String endpointUrl = request.getRequestURL().toString();
-      AccessControl accessControl = _accessControlFactory.create();
-      AccessControlUtils.validatePermission(tableNameWithType, 
AccessType.CREATE, httpHeaders, endpointUrl,
-          accessControl);
-      if (!accessControl.hasAccess(httpHeaders, TargetType.TABLE, 
tableNameWithType, Actions.Table.CREATE_TABLE)) {
-        throw new ControllerApplicationException(LOGGER, "Permission denied", 
Response.Status.FORBIDDEN);
-      }
+      ResourceUtils.checkPermissionAndAccess(tableNameWithType, request, 
httpHeaders,
+          AccessType.CREATE, Actions.Table.CREATE_TABLE, 
_accessControlFactory, LOGGER);
 
       Schema schema = 
_pinotHelixResourceManager.getSchemaForTableConfig(tableConfig);
 
@@ -487,10 +477,7 @@ public class PinotTableRestletResource {
       tableNameWithType = 
DatabaseUtils.translateTableName(tableConfig.getTableName(), headers);
       tableConfig.setTableName(tableNameWithType);
       // Handle legacy config
-      SegmentsValidationAndRetentionConfig validationConfig = 
tableConfig.getValidationConfig();
-      if (validationConfig.getSchemaName() != null) {
-        
validationConfig.setSchemaName(DatabaseUtils.translateTableName(validationConfig.getSchemaName(),
 headers));
-      }
+      handleLegacySchemaConfig(tableConfig, headers);
       String tableNameFromPath = DatabaseUtils.translateTableName(
           
TableNameBuilder.forType(tableConfig.getTableType()).tableNameWithType(tableName),
 headers);
       if (!tableNameFromPath.equals(tableNameWithType)) {
@@ -554,20 +541,13 @@ public class PinotTableRestletResource {
     TableConfig tableConfig = tableConfigAndUnrecognizedProperties.getLeft();
     String tableNameWithType = 
DatabaseUtils.translateTableName(tableConfig.getTableName(), httpHeaders);
     tableConfig.setTableName(tableNameWithType);
+
     // Handle legacy config
-    SegmentsValidationAndRetentionConfig validationConfig = 
tableConfig.getValidationConfig();
-    if (validationConfig.getSchemaName() != null) {
-      
validationConfig.setSchemaName(DatabaseUtils.translateTableName(validationConfig.getSchemaName(),
 httpHeaders));
-    }
+    handleLegacySchemaConfig(tableConfig, httpHeaders);
 
     // validate permission
-    String endpointUrl = request.getRequestURL().toString();
-    AccessControl accessControl = _accessControlFactory.create();
-    AccessControlUtils.validatePermission(tableNameWithType, AccessType.READ, 
httpHeaders, endpointUrl, accessControl);
-    if (!accessControl.hasAccess(httpHeaders, TargetType.TABLE, 
tableNameWithType,
-        Actions.Table.VALIDATE_TABLE_CONFIGS)) {
-      throw new ControllerApplicationException(LOGGER, "Permission denied", 
Response.Status.FORBIDDEN);
-    }
+    ResourceUtils.checkPermissionAndAccess(tableNameWithType, request, 
httpHeaders,
+        AccessType.READ, Actions.Table.VALIDATE_TABLE_CONFIGS, 
_accessControlFactory, LOGGER);
 
     ObjectNode validationResponse =
         validateConfig(tableConfig, 
_pinotHelixResourceManager.getSchemaForTableConfig(tableConfig), typesToSkip);
@@ -1270,4 +1250,22 @@ public class PinotTableRestletResource {
 
     return timeBoundaryMs;
   }
+
+  /**
+   * Handles the legacy schema configuration for a given table configuration.
+   * This method updates the schema name in the validation configuration of 
the table config
+   * to ensure it is correctly translated based on the provided HTTP headers.
+   * This is necessary to maintain compatibility with older configurations 
that may not
+   * have the schema name properly set or formatted.
+   *
+   * @param tableConfig The {@link TableConfig} object containing the table 
configuration.
+   * @param httpHeaders The {@link HttpHeaders} object containing the HTTP 
headers, used to
+   *                    translate the schema name if necessary.
+   */
+  private void handleLegacySchemaConfig(TableConfig tableConfig, HttpHeaders 
httpHeaders) {
+    SegmentsValidationAndRetentionConfig validationConfig = 
tableConfig.getValidationConfig();
+    if (validationConfig.getSchemaName() != null) {
+      
validationConfig.setSchemaName(DatabaseUtils.translateTableName(validationConfig.getSchemaName(),
 httpHeaders));
+    }
+  }
 }
diff --git 
a/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/ResourceUtils.java
 
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/ResourceUtils.java
index a1bb234ef2..ab091c8138 100644
--- 
a/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/ResourceUtils.java
+++ 
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/ResourceUtils.java
@@ -20,11 +20,18 @@ package org.apache.pinot.controller.api.resources;
 
 import java.util.List;
 import javax.annotation.Nullable;
+import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.Response;
 import org.apache.pinot.common.exception.TableNotFoundException;
+import org.apache.pinot.controller.api.access.AccessControl;
+import org.apache.pinot.controller.api.access.AccessControlFactory;
+import org.apache.pinot.controller.api.access.AccessControlUtils;
+import org.apache.pinot.controller.api.access.AccessType;
 import 
org.apache.pinot.controller.api.exception.ControllerApplicationException;
 import org.apache.pinot.controller.helix.core.PinotHelixResourceManager;
+import org.apache.pinot.core.auth.TargetType;
 import org.apache.pinot.spi.config.table.TableType;
+import org.glassfish.grizzly.http.server.Request;
 import org.slf4j.Logger;
 
 
@@ -42,4 +49,33 @@ public class ResourceUtils {
       throw new ControllerApplicationException(logger, e.getMessage(), 
Response.Status.FORBIDDEN);
     }
   }
+
+  /**
+   * Validates the permission and access for a specified type based on the 
incoming request and HTTP headers.
+   * This method ensures that the current user has the necessary permissions 
to perform the specified action
+   * on the given type. It leverages the {@link AccessControl} mechanism to 
assess access rights and
+   * throws a {@link ControllerApplicationException} with a {@link 
Response.Status#FORBIDDEN} status code
+   * if access is denied. This is crucial for enforcing security and access 
control within the application.
+   *
+   * @param typeName The name of the type for which permission and access are 
being verified.
+   * @param request The {@link Request} object containing details about the 
current request, utilized
+   *                to extract the endpoint URL for access validation.
+   * @param httpHeaders The {@link HttpHeaders} associated with the request, 
used for authorization
+   *                    and other header-based access control checks.
+   * @param accessType The type of access being requested (e.g., CREATE, READ, 
UPDATE, DELETE).
+   * @param action The specific action being checked against the access 
control policies.
+   * @param accessControlFactory The {@link AccessControlFactory} used to 
create an {@link AccessControl} instance
+   *                             for validating permissions.
+   * @param logger The {@link Logger} used for logging any access control 
related messages.
+   * @throws ControllerApplicationException if the user lacks the required 
permissions or access.
+   */
+  public static void checkPermissionAndAccess(String typeName, Request 
request, HttpHeaders httpHeaders,
+      AccessType accessType, String action, AccessControlFactory 
accessControlFactory, Logger logger) {
+    String endpointUrl = request.getRequestURL().toString();
+    AccessControl accessControl = accessControlFactory.create();
+    AccessControlUtils.validatePermission(typeName, accessType, httpHeaders, 
endpointUrl, accessControl);
+    if (!accessControl.hasAccess(httpHeaders, TargetType.TABLE, typeName, 
action)) {
+      throw new ControllerApplicationException(logger, "Permission denied", 
Response.Status.FORBIDDEN);
+    }
+  }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@pinot.apache.org
For additional commands, e-mail: commits-h...@pinot.apache.org

Reply via email to