| # PIP-383: Support granting/revoking permissions for multiple topics |
| |
| ## Background |
| |
| In AuthorizationProvider, the authorization interface `grantPermissionAsync(TopicName topicName, Set<AuthAction> actions, String role, String authDataJson)` currently only supports granting permissions to a single topic at a time. |
| When multiple topics need to be authorized under a namespace, the client makes the calls to the authorization interface concurrently. |
| Since the permissions information is stored in the namespace-level policies, and multiple topics may be on different brokers, concurrent authorization modification will cause concurrent modification exceptions. |
| Therefore, supporting granting permissions for multiple topics is very beneficial. |
| |
| |
| ## Motivation |
| |
| Supporting granting/revoking permissions for multiple topics, |
| add `grantPermissionAsync(List<GrantTopicPermissionOptions> options)` and `revokePermissionAsync(List<RevokeTopicPermissionOptions> options)` in AuthorizationProvider. |
| |
| ## Goals |
| |
| ### In Scope |
| |
| - Add `grantPermissionAsync(List<GrantTopicPermissionOptions> options)` in AuthorizationProvider. |
| - Add `revokePermissionAsync(List<GrantTopicPermissionOptions> options)` in AuthorizationProvider. |
| |
| ## High-Level Design |
| |
| ### Design & Implementation Details |
| |
| Add default method implementation in AuthorizationProvider |
| ```java |
| |
| public interface AuthorizationProvider extends Closeable { |
| |
| default CompletableFuture<Void> grantPermissionAsync(List<GrantTopicPermissionOptions> options) { |
| return FutureUtil.failedFuture(new IllegalStateException( |
| String.format("grantPermissionAsync is not supported by the Authorization"))); |
| } |
| |
| default CompletableFuture<Void> revokePermissionAsync(List<RevokeTopicPermissionOptions> options) { |
| return FutureUtil.failedFuture(new IllegalStateException( |
| String.format("revokePermissionAsync is not supported by the Authorization"))); |
| } |
| } |
| ``` |
| |
| ``` |
| @Data |
| @Builder |
| public class GrantTopicPermissionOptions { |
| |
| private final String topic; |
| |
| private final String role; |
| |
| private final Set<AuthAction> actions; |
| } |
| |
| @Data |
| @Builder |
| public class RevokeTopicPermissionOptions { |
| |
| private final String topic; |
| |
| private final String role; |
| } |
| ``` |
| |
| Add namespace admin API. |
| |
| ```java |
| public interface Namespaces { |
| |
| CompletableFuture<Void> grantPermissionOnTopicsAsync(List<GrantTopicPermissionOptions> options); |
| |
| void grantPermissionOnTopics(List<GrantTopicPermissionOptions> options) throws PulsarAdminException; |
| |
| CompletableFuture<Void> revokePermissionOnTopicsAsync(List<RevokeTopicPermissionOptions> options); |
| |
| void revokePermissionOnTopics(List<RevokeTopicPermissionOptions> options) throws PulsarAdminException; |
| } |
| ``` |
| |
| Add namespace rest implementation in broker side. |
| ```java |
| @POST |
| @Path("/grantPermissions") |
| public void grantPermissionOnTopics(@Suspended final AsyncResponse asyncResponse, |
| List<GrantTopicPermissionOptions> options) { |
| internalGrantPermissionsAsync(options) |
| .thenAccept(__ -> asyncResponse.resume(Response.noContent().build())) |
| .exceptionally(ex -> { |
| log.error("[{}] Failed to grant permissions {}", |
| clientAppId(), options, ex); |
| resumeAsyncResponseExceptionally(asyncResponse, ex); |
| return null; |
| }); |
| } |
| |
| @POST |
| @Path("/revokePermissions") |
| public void revokePermissionOnTopics(@Suspended final AsyncResponse asyncResponse, |
| List<RevokeTopicPermissionOptions> options) { |
| internalRevokePermissionsAsync(options) |
| .thenAccept(__ -> asyncResponse.resume(Response.noContent().build())) |
| .exceptionally(ex -> { |
| log.error("[{}] Failed to revoke permissions {}", |
| clientAppId(), options, ex); |
| resumeAsyncResponseExceptionally(asyncResponse, ex); |
| return null; |
| }); |
| } |
| ``` |
| |
| so user can grant/revoke permissions to multi-topics like : |
| ```java |
| public class TestAuthorization { |
| |
| @Test |
| public void testGrantPermission() { |
| // grant permission for multi-topics |
| List<GrantPermissionOptions> grantPermissions = new ArrayList<>(); |
| grantPermissions.add(GrantPermissionOptions.builder().topic("topic1").role("role1").actions(Set.of(AuthAction.produce)).build()); |
| grantPermissions.add(GrantPermissionOptions.builder().topic("topic2").role("role2").actions(Set.of(AuthAction.consume)).build()); |
| admin.namespaces().grantPermissionOnTopics(grantPermissions); |
| // revoke permission topics |
| List<RevokePermissionOptions> revokePermissions = new ArrayList<>(); |
| revokePermissions.add(RevokePermissionOptions.builder().topic("topic1").role("role1").build()); |
| revokePermissions.add(RevokePermissionOptions.builder().topic("topic2").role("role2").build()); |
| admin.namespaces().revokePermissionOnTopics(revokePermissions); |
| } |
| } |
| |
| ``` |
| |
| ## Backward & Forward Compatibility |
| |
| |
| |
| ## Alternatives |
| |
| ## General Notes |
| |
| ## Links |
| |
| * Mailing List discussion thread: https://lists.apache.org/thread/6n2jdl9bsf1f6xz2orygz3kvxmy11ykh |
| * Mailing List voting thread: https://lists.apache.org/thread/qbyvs75r0d64h6jk8w1swr782l85b77h |