From 5ad15a7b9920163679081aae0ad1390ce3d419f0 Mon Sep 17 00:00:00 2001 From: david-leifker <114954101+david-leifker@users.noreply.github.com> Date: Mon, 7 Oct 2024 15:36:13 -0500 Subject: [PATCH] privileges(refactor): consolidate individual sys op privileges (#11549) --- docs/authorization/policies.md | 32 +++++++------------ docs/how/updating-datahub.md | 2 ++ .../com/datahub/authorization/AuthUtil.java | 25 +++++++++++++++ .../elastic/OperationsController.java | 11 ++++--- .../resources/entity/AspectResource.java | 3 +- .../resources/entity/EntityResource.java | 4 +-- .../operations/OperationsResource.java | 5 +-- .../resources/platform/PlatformResource.java | 3 +- .../authorization/PoliciesConfig.java | 12 +++++-- 9 files changed, 63 insertions(+), 34 deletions(-) diff --git a/docs/authorization/policies.md b/docs/authorization/policies.md index 5c99241f75190..abe42e1a21051 100644 --- a/docs/authorization/policies.md +++ b/docs/authorization/policies.md @@ -191,16 +191,18 @@ These privileges are for DataHub operators to access & manage the administrative #### System Management -| Platform Privileges | Description | -|-----------------------------------------------|------------------------------------------------------------------------| -| Restore Indices API[^1] | Allow actor to use the Restore Indices API. | | -| Get Timeseries index sizes API[^1] | Allow actor to use the get Timeseries indices size API. | -| Truncate timeseries aspect index size API[^1] | Allow actor to use the API to truncate a timeseries index. | -| Get ES task status API[^1] | Allow actor to use the get task status API for an ElasticSearch task. | -| Enable/Disable Writeability API[^1] | Allow actor to enable or disable GMS writeability for data migrations. | -| Apply Retention API[^1] | Allow actor to apply retention using the API. | -| Analytics API access[^1] | Allow actor to use API read access to raw analytics data. | -| Manage System Operations | Allow actor to manage system operation controls. | +| Platform Privileges | Description | +|-----------------------------------------------|----------------------------------------------------------------------------------------------------------| +| Restore Indices API[^1] | Allow actor to use the Restore Indices API. | | +| Get Timeseries index sizes API[^1] | Allow actor to use the get Timeseries indices size API. | +| Truncate timeseries aspect index size API[^1] | Allow actor to use the API to truncate a timeseries index. | +| Get ES task status API[^1] | Allow actor to use the get task status API for an ElasticSearch task. | +| Enable/Disable Writeability API[^1] | Allow actor to enable or disable GMS writeability for data migrations. | +| Apply Retention API[^1] | Allow actor to apply retention using the API. | +| Analytics API access[^1] | Allow actor to use API read access to raw analytics data. | +| Explain ElasticSearch Query API[^1] | Allow actor to use the Operations API explain endpoint. | +| Produce Platform Event API[^1] | Allow actor to produce Platform Events using the API. | +| Manage System Operations | Allow actor to manage system operation controls. This setting includes all System Management privileges. | [^1]: Only active if REST_API_AUTHORIZATION_ENABLED is true [^2]: DataHub Cloud only @@ -262,16 +264,6 @@ These privileges are to view & modify any entity within DataHub. [^1]: Only active if REST_API_AUTHORIZATION_ENABLED is true [^2]: DataHub Cloud only -#### System Management - -| System Privileges | Description | -|-------------------------------------|--------------------------------------------------------------------------------------------| -| Explain ElasticSearch Query API[^1] | Allow actor to use the Operations API explain endpoint. | -| Produce Platform Event API[^1] | Allow actor to produce Platform Events using the API. | - -[^1]: Only active if REST_API_AUTHORIZATION_ENABLED is true -[^2]: DataHub Cloud only - ### Specific Entity-level Privileges These privileges are not generalizable. diff --git a/docs/how/updating-datahub.md b/docs/how/updating-datahub.md index 89ea8ce8c543a..00e020bd2a387 100644 --- a/docs/how/updating-datahub.md +++ b/docs/how/updating-datahub.md @@ -31,6 +31,8 @@ This file documents any backwards-incompatible changes in DataHub and assists pe ### Other Notable Changes +- #11549 - Manage Operations Privilege is extended from throttle control to all system management and operations APIs. + ## 0.14.1 ### Breaking Changes diff --git a/metadata-auth/auth-api/src/main/java/com/datahub/authorization/AuthUtil.java b/metadata-auth/auth-api/src/main/java/com/datahub/authorization/AuthUtil.java index 62d8206e42565..4f1f0686e7686 100644 --- a/metadata-auth/auth-api/src/main/java/com/datahub/authorization/AuthUtil.java +++ b/metadata-auth/auth-api/src/main/java/com/datahub/authorization/AuthUtil.java @@ -24,6 +24,7 @@ import static com.linkedin.metadata.authorization.Disjunctive.DENY_ACCESS; import static com.linkedin.metadata.authorization.PoliciesConfig.API_ENTITY_PRIVILEGE_MAP; import static com.linkedin.metadata.authorization.PoliciesConfig.API_PRIVILEGE_MAP; +import static com.linkedin.metadata.authorization.PoliciesConfig.MANAGE_SYSTEM_OPERATIONS_PRIVILEGE; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableSet; @@ -304,6 +305,30 @@ public static boolean isAPIAuthorized( return isAPIAuthorized(session, Disjunctive.disjoint(privilege), (EntitySpec) null); } + /** + * Allow specific privilege OR MANAGE_SYSTEM_OPERATIONS_PRIVILEGE + * + * @param session authorization session + * @param privilege specific privilege + * @return authorized status + */ + public static boolean isAPIOperationsAuthorized( + @Nonnull final AuthorizationSession session, + @Nonnull final PoliciesConfig.Privilege privilege) { + return isAPIAuthorized( + session, + Disjunctive.disjoint(privilege, MANAGE_SYSTEM_OPERATIONS_PRIVILEGE), + (EntitySpec) null); + } + + public static boolean isAPIOperationsAuthorized( + @Nonnull final AuthorizationSession session, + @Nonnull final PoliciesConfig.Privilege privilege, + @Nullable final EntitySpec resource) { + return isAPIAuthorized( + session, Disjunctive.disjoint(privilege, MANAGE_SYSTEM_OPERATIONS_PRIVILEGE), resource); + } + private static boolean isAPIAuthorized( @Nonnull final AuthorizationSession session, @Nonnull final Disjunctive> privileges, diff --git a/metadata-service/openapi-servlet/src/main/java/io/datahubproject/openapi/operations/elastic/OperationsController.java b/metadata-service/openapi-servlet/src/main/java/io/datahubproject/openapi/operations/elastic/OperationsController.java index 083e515d055f5..6151b866e5208 100644 --- a/metadata-service/openapi-servlet/src/main/java/io/datahubproject/openapi/operations/elastic/OperationsController.java +++ b/metadata-service/openapi-servlet/src/main/java/io/datahubproject/openapi/operations/elastic/OperationsController.java @@ -154,7 +154,8 @@ public ResponseEntity getIndexSizes(HttpServletRequest request) { authentication, true); - if (!AuthUtil.isAPIAuthorized(opContext, PoliciesConfig.GET_TIMESERIES_INDEX_SIZES_PRIVILEGE)) { + if (!AuthUtil.isAPIOperationsAuthorized( + opContext, PoliciesConfig.GET_TIMESERIES_INDEX_SIZES_PRIVILEGE)) { return ResponseEntity.status(HttpStatus.FORBIDDEN) .body(String.format(actorUrnStr + " is not authorized to get timeseries index sizes")); } @@ -255,7 +256,7 @@ public ResponseEntity explainSearchQuery( } }); - if (!AuthUtil.isAPIAuthorized(opContext, PoliciesConfig.ES_EXPLAIN_QUERY_PRIVILEGE)) { + if (!AuthUtil.isAPIOperationsAuthorized(opContext, PoliciesConfig.ES_EXPLAIN_QUERY_PRIVILEGE)) { log.error("{} is not authorized to get explain queries", actorUrnStr); return ResponseEntity.status(HttpStatus.FORBIDDEN).body(null); } @@ -363,7 +364,7 @@ public ResponseEntity explainSearchQueryDiff( } }); - if (!AuthUtil.isAPIAuthorized(opContext, PoliciesConfig.ES_EXPLAIN_QUERY_PRIVILEGE)) { + if (!AuthUtil.isAPIOperationsAuthorized(opContext, PoliciesConfig.ES_EXPLAIN_QUERY_PRIVILEGE)) { log.error("{} is not authorized to get explain queries", actorUrnStr); return ResponseEntity.status(HttpStatus.FORBIDDEN).body(null); } @@ -449,7 +450,7 @@ public ResponseEntity> restoreIndices( authentication, true); - if (!AuthUtil.isAPIAuthorized(opContext, PoliciesConfig.RESTORE_INDICES_PRIVILEGE)) { + if (!AuthUtil.isAPIOperationsAuthorized(opContext, PoliciesConfig.RESTORE_INDICES_PRIVILEGE)) { return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); } @@ -492,7 +493,7 @@ public ResponseEntity> restoreIndices( authentication, true); - if (!AuthUtil.isAPIAuthorized(opContext, PoliciesConfig.RESTORE_INDICES_PRIVILEGE)) { + if (!AuthUtil.isAPIOperationsAuthorized(opContext, PoliciesConfig.RESTORE_INDICES_PRIVILEGE)) { return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); } diff --git a/metadata-service/restli-servlet-impl/src/main/java/com/linkedin/metadata/resources/entity/AspectResource.java b/metadata-service/restli-servlet-impl/src/main/java/com/linkedin/metadata/resources/entity/AspectResource.java index 42265e902cc6f..12422044bc8ef 100644 --- a/metadata-service/restli-servlet-impl/src/main/java/com/linkedin/metadata/resources/entity/AspectResource.java +++ b/metadata-service/restli-servlet-impl/src/main/java/com/linkedin/metadata/resources/entity/AspectResource.java @@ -3,6 +3,7 @@ import static com.datahub.authorization.AuthUtil.isAPIAuthorized; import static com.datahub.authorization.AuthUtil.isAPIAuthorizedEntityUrns; import static com.datahub.authorization.AuthUtil.isAPIAuthorizedUrns; +import static com.datahub.authorization.AuthUtil.isAPIOperationsAuthorized; import static com.linkedin.metadata.authorization.ApiGroup.COUNTS; import static com.linkedin.metadata.authorization.ApiGroup.ENTITY; import static com.linkedin.metadata.authorization.ApiGroup.TIMESERIES; @@ -372,7 +373,7 @@ public Task restoreIndices( systemOperationContext, RequestContext.builder().buildRestli(authentication.getActor().toUrnStr(), getContext(), ACTION_RESTORE_INDICES), _authorizer, authentication, true); - if (!isAPIAuthorized( + if (!isAPIOperationsAuthorized( opContext, PoliciesConfig.RESTORE_INDICES_PRIVILEGE)) { throw new RestLiServiceException( diff --git a/metadata-service/restli-servlet-impl/src/main/java/com/linkedin/metadata/resources/entity/EntityResource.java b/metadata-service/restli-servlet-impl/src/main/java/com/linkedin/metadata/resources/entity/EntityResource.java index 74c15d1f35889..9755a76848adf 100644 --- a/metadata-service/restli-servlet-impl/src/main/java/com/linkedin/metadata/resources/entity/EntityResource.java +++ b/metadata-service/restli-servlet-impl/src/main/java/com/linkedin/metadata/resources/entity/EntityResource.java @@ -1058,7 +1058,7 @@ public Task setWriteable( systemOperationContext, RequestContext.builder().buildRestli(auth.getActor().toUrnStr(), getContext(), "setWriteable"), authorizer, auth, true); - if (!isAPIAuthorized( + if (!isAPIOperationsAuthorized( opContext, PoliciesConfig.SET_WRITEABLE_PRIVILEGE)) { throw new RestLiServiceException( @@ -1168,7 +1168,7 @@ public Task applyRetention( systemOperationContext, RequestContext.builder().buildRestli(auth.getActor().toUrnStr(), getContext(), ACTION_APPLY_RETENTION, resourceSpec.getType()), authorizer, auth, true); - if (!isAPIAuthorized( + if (!isAPIOperationsAuthorized( opContext, PoliciesConfig.APPLY_RETENTION_PRIVILEGE, resourceSpec)) { diff --git a/metadata-service/restli-servlet-impl/src/main/java/com/linkedin/metadata/resources/operations/OperationsResource.java b/metadata-service/restli-servlet-impl/src/main/java/com/linkedin/metadata/resources/operations/OperationsResource.java index c1c41f0996f9f..705089baed8f5 100644 --- a/metadata-service/restli-servlet-impl/src/main/java/com/linkedin/metadata/resources/operations/OperationsResource.java +++ b/metadata-service/restli-servlet-impl/src/main/java/com/linkedin/metadata/resources/operations/OperationsResource.java @@ -1,6 +1,7 @@ package com.linkedin.metadata.resources.operations; import static com.datahub.authorization.AuthUtil.isAPIAuthorized; +import static com.datahub.authorization.AuthUtil.isAPIOperationsAuthorized; import static com.linkedin.metadata.resources.restli.RestliConstants.*; import static com.linkedin.metadata.utils.CriterionUtils.buildCriterion; @@ -136,7 +137,7 @@ public Task getTaskStatus( systemOperationContext, RequestContext.builder().buildRestli(auth.getActor().toUrnStr(), getContext(), ACTION_GET_ES_TASK_STATUS), _authorizer, auth, true); - if (!isAPIAuthorized( + if (!isAPIOperationsAuthorized( opContext, PoliciesConfig.GET_ES_TASK_STATUS_PRIVILEGE)) { throw new RestLiServiceException( @@ -199,7 +200,7 @@ public Task getIndexSizes() { systemOperationContext, RequestContext.builder().buildRestli(auth.getActor().toUrnStr(), getContext(), ACTION_GET_INDEX_SIZES, List.of()), _authorizer, auth, true); - if (!isAPIAuthorized( + if (!isAPIOperationsAuthorized( opContext, PoliciesConfig.GET_TIMESERIES_INDEX_SIZES_PRIVILEGE)) { throw new RestLiServiceException( diff --git a/metadata-service/restli-servlet-impl/src/main/java/com/linkedin/metadata/resources/platform/PlatformResource.java b/metadata-service/restli-servlet-impl/src/main/java/com/linkedin/metadata/resources/platform/PlatformResource.java index 46fab05133651..4fea3b0a1aca6 100644 --- a/metadata-service/restli-servlet-impl/src/main/java/com/linkedin/metadata/resources/platform/PlatformResource.java +++ b/metadata-service/restli-servlet-impl/src/main/java/com/linkedin/metadata/resources/platform/PlatformResource.java @@ -1,6 +1,7 @@ package com.linkedin.metadata.resources.platform; import static com.datahub.authorization.AuthUtil.isAPIAuthorized; +import static com.datahub.authorization.AuthUtil.isAPIOperationsAuthorized; import com.datahub.authentication.Authentication; import com.datahub.authentication.AuthenticationContext; @@ -63,7 +64,7 @@ public Task producePlatformEvent( ACTION_PRODUCE_PLATFORM_EVENT), _authorizer, auth, true); - if (!isAPIAuthorized( + if (!isAPIOperationsAuthorized( opContext, PoliciesConfig.PRODUCE_PLATFORM_EVENT_PRIVILEGE)) { throw new RestLiServiceException( diff --git a/metadata-utils/src/main/java/com/linkedin/metadata/authorization/PoliciesConfig.java b/metadata-utils/src/main/java/com/linkedin/metadata/authorization/PoliciesConfig.java index 5964bab946528..bc676e94ecd4f 100644 --- a/metadata-utils/src/main/java/com/linkedin/metadata/authorization/PoliciesConfig.java +++ b/metadata-utils/src/main/java/com/linkedin/metadata/authorization/PoliciesConfig.java @@ -183,7 +183,7 @@ public class PoliciesConfig { Privilege.of( "MANAGE_SYSTEM_OPERATIONS", "Manage System Operations", - "Allow access to system operations APIs and controls."); + "Allow access to all system operations/management APIs and controls."); public static final List PLATFORM_PRIVILEGES = ImmutableList.of( @@ -877,13 +877,19 @@ public class PoliciesConfig { .put(ApiOperation.CREATE, DENY_ACCESS) .put( ApiOperation.READ, - Disjunctive.disjoint(VIEW_ANALYTICS_PRIVILEGE, GET_ANALYTICS_PRIVILEGE)) + Disjunctive.disjoint( + VIEW_ANALYTICS_PRIVILEGE, + GET_ANALYTICS_PRIVILEGE, + MANAGE_SYSTEM_OPERATIONS_PRIVILEGE)) .put(ApiOperation.UPDATE, DENY_ACCESS) .put(ApiOperation.DELETE, DENY_ACCESS) .put( ApiOperation.EXISTS, Disjunctive.disjoint( - VIEW_ANALYTICS_PRIVILEGE, GET_ANALYTICS_PRIVILEGE, SEARCH_PRIVILEGE)) + VIEW_ANALYTICS_PRIVILEGE, + GET_ANALYTICS_PRIVILEGE, + SEARCH_PRIVILEGE, + MANAGE_SYSTEM_OPERATIONS_PRIVILEGE)) .build()) .put( ApiGroup.TIMESERIES,