From 0a9d40c518118a737c3b68be8df4b517ba010752 Mon Sep 17 00:00:00 2001 From: Alexandre DUVAL Date: Thu, 27 Feb 2020 17:41:12 +0100 Subject: [PATCH 01/19] introduce operations --- .../common/policies/data/BrokerOperation.java | 35 ++++++++ .../policies/data/ClusterOperation.java | 52 +++++++++++ .../policies/data/NamespaceOperation.java | 87 +++++++++++++++++++ .../common/policies/data/TenantOperation.java | 29 +++++++ .../common/policies/data/TopicOperation.java | 60 +++++++++++++ 5 files changed, 263 insertions(+) create mode 100644 pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/BrokerOperation.java create mode 100644 pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/ClusterOperation.java create mode 100644 pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/NamespaceOperation.java create mode 100644 pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/TenantOperation.java create mode 100644 pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/TopicOperation.java diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/BrokerOperation.java b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/BrokerOperation.java new file mode 100644 index 0000000000000..362ce8c25aa86 --- /dev/null +++ b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/BrokerOperation.java @@ -0,0 +1,35 @@ +/** + * 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.pulsar.common.policies.data; + +/** + * Broker authorization operations. + */ +public enum BrokerOperation { + DELETE_DYNAMIC_CONFIG, + GET_ALL_DYNAMIC_CONFIG, + GET_INTERNAL_CONFIG, + GET_RUNTIME_CONFIG, + HEALTHCHECK, + LIST, + LIST_DYNAMIC_CONFIG, + NAMESPACES, + UPDATE_DYNAMIC_CONFIG, +} diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/ClusterOperation.java b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/ClusterOperation.java new file mode 100644 index 0000000000000..8fa88dd9939d7 --- /dev/null +++ b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/ClusterOperation.java @@ -0,0 +1,52 @@ +/** + * 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.pulsar.common.policies.data; + +/** + * Cluster authorization operations. + */ +public enum ClusterOperation { + CREATE, + CREATE_FAILURE_DOMAIN, + DELETE, + DELETE_FAILURE_DOMAIN, + GET, + GET_BOOKIE_AFFINITY_GROUP, + GET_FAILURE_DOMAIN, + GET_NAMESPACE_ISOLATION_POLICIES, + GET_PEER_CLUSTERS, + GET_PUBLISH_RATE, + LIST, + LIST_FAILURE_DOMAIN, + SET_BOOKIE_AFFINITY_GROUP, + SET_DELAYED_DELIVERY, + SET_FAILURE_DOMAIN, + SET_NAMESPACE_ISOLATION_POLICIES, + SET_PUBLISH_RATE, + SET_REPLICATOR_DISPATCH_RATE, + SET_SUBSCRIBE_RATE, + SET_SUBSCRIPTION_DISPATCH_RATE, + SET_TOPIC_DISPATCH_RATE, + SPLIT_NAMESPACE_BUNDLE, + UNLOAD_NAMESPACE, + UPDATE, + UPDATE_FAILURE_DOMAIN, + UPDATE_PEER_CLUSTERS, +} diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/NamespaceOperation.java b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/NamespaceOperation.java new file mode 100644 index 0000000000000..fa5c72d461a92 --- /dev/null +++ b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/NamespaceOperation.java @@ -0,0 +1,87 @@ +/** + * 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.pulsar.common.policies.data; + +/** + * Namespace authorization operations. + */ +public enum NamespaceOperation { + CLEAR_BACKLOG, + DELETE_BUNDLE, + GET_ANTI_AFFINITY_GROUP, + GET_BACKLOG_QUOTAS, + GET_BUNDLES, + GET_COMPACTION_THRESHOLD, + GET_DELAYED_DELIVERY, + GET_DISPATCH_RATE, + GET_MAX_CONSUMERS_PER_SUBSCRIPTION, + GET_MAX_CONSUMERS_PER_TOPIC, + GET_MAX_PRODUCERS_PER_TOPIC, + GET_MAX_UNACKED_MESSAGES_PER_CONSUMER, + GET_MAX_UNACKED_MESSAGES_PER_SUBSCRIPTION, + GET_MESSAGE_TTL, + GET_OFFLOAD_DELETION_LAG, + GET_OFFLOAD_POLICIES, + GET_OFFLOAD_THRESHOLD, + GET_PERSISTENCE, + GET_PUBLISH_RATE, + GET_REPLICATION_CLUSTERS, + GET_REPLICATOR_DISPATCH_RATE, + GET_RETENTION, + GET_SCHEMA_AUTOUPDATE_STRATEGY, + GET_SCHEMA_COMPATIBILITY_STRATEGY, + GET_SCHEMA_VALIDATION_ENFORCED, + GET_SUBSCRIBE_RATE, + GET_SUBSCRIPTION_DISPATCH_RATE, + GRANT_PERMISSIONS, + GRANT_SUBSCRIPTION_PERMISSION, + REVOKE_PERMISSIONS, + REVOKE_SUBSCRIPTION_PERMISSION, + PERMISSIONS, + POLICIES, + SCHEMA_AUTOUPDATE, + SET_BACKLOG_QUOTA, + SET_ANTI_AFFINITY_GROUP, + SET_COMPACTION_THRESHOLD, + SET_DEDUPLICATION, + SET_DELAYED_DELIVERY, + SET_ENCRYPTION_REQUIRED, + SET_MAX_CONSUMERS_PER_SUBSCRIPTION, + SET_MAX_CONSUMERS_PER_TOPIC, + SET_MAX_PRODUCERS_PER_TOPIC, + SET_MAX_UNACKED_MESSAGES_PER_CONSUMER, + SET_MAX_UNACKED_MESSAGES_PER_SUBSCRIPTION, + SET_MESSAGE_TTL, + SET_OFFLOAD_DELETION_LAG, + SET_OFFLOAD_POLICIES, + SET_OFFLOAD_THRESHOLD, + SET_PERSISTENCE, + SET_REPLICATION_CLUSTERS, + SET_REPLICATOR_DISPATCH_RATE, + SET_RETENTION, + SET_SCHEMA_AUTOUPDATE_STRATEGY, + SET_SCHEMA_COMPATIBILITY_STRATEGY, + SET_SCHEMA_VALIDATION_ENFORCED, + SET_SUBSCRIBE_RATE, + SET_SUBSCRIPTION_AUTH_MODE, + SET_SUBSCRIPTION_DISPATCH_RATE, + TOPICS, + UNSUBSCRIBE, +} diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/TenantOperation.java b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/TenantOperation.java new file mode 100644 index 0000000000000..b444433f6fa90 --- /dev/null +++ b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/TenantOperation.java @@ -0,0 +1,29 @@ +/** + * 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.pulsar.common.policies.data; + +/** + * Tenant authorization operations. + */ +public enum TenantOperation { + CREATE_NAMESPACE, + DELETE_NAMESPACE, + LIST_NAMESPACES, +} diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/TopicOperation.java b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/TopicOperation.java new file mode 100644 index 0000000000000..b3c8aa8abe551 --- /dev/null +++ b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/TopicOperation.java @@ -0,0 +1,60 @@ +/** + * 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.pulsar.common.policies.data; + +/** + * Topic authorization operations. + */ +public enum TopicOperation { + BUNDLE_RANGE, + COMPACT, + COMPACTION_STATUS, + CONSUME, + CREATE, + CREATE_PARTITIONED_TOPIC, + CREATE_SUBSCRIPTION, + DELETE, + DELETE_PARTITIONED_TOPIC, + EXPIRE_MESSAGES, + EXPIRE_MESSAGES_ALL_SUBSCRIPTIONS, + GET_PARTITIONED_TOPIC_METADATA, + GRANT_PERMISSION, + INFO_INTERNAL, + LAST_MESSAGE_ID, + LIST, + LOOKUP, + OFFLOAD, + OFFLOAD_STATUS, + PARTITIONED_STATS, + PEEK_MESSAGES, + PERMISSIONS, + PRODUCE, + RESET_CURSOR, + REVOKE_PERMISSION, + SKIP, + SKIP_ALL, + STATS, + STATS_INTERNAL, + SUBSCRIPTIONS, + TERMINATE, + UNLOAD, + UNSUBSCRIBE, + UPDATE_PARTITIONED_TOPIC, +} From 987d1ae82430ad1e4af3dbda7bc82114e5d845a5 Mon Sep 17 00:00:00 2001 From: Alexandre DUVAL Date: Thu, 27 Feb 2020 17:41:40 +0100 Subject: [PATCH 02/19] add new methods to authorizationprovider interface using operations and uses them in defaultauthz --- .../AuthenticationDataCommand.java | 32 +++- .../AuthenticationDataSource.java | 23 +++ .../authorization/AuthorizationProvider.java | 76 ++++++++++ .../authorization/AuthorizationService.java | 137 ++++++++++++++++++ .../PulsarAuthorizationProvider.java | 121 +++++++++++++++- 5 files changed, 386 insertions(+), 3 deletions(-) diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationDataCommand.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationDataCommand.java index 6539fb38fbe9b..7299eaee654ac 100644 --- a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationDataCommand.java +++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationDataCommand.java @@ -28,9 +28,22 @@ public class AuthenticationDataCommand implements AuthenticationDataSource { protected final String authData; protected final SocketAddress remoteAddress; protected final SSLSession sslSession; + protected String subscription; public AuthenticationDataCommand(String authData) { - this(authData, null, null); + this(authData, null, null, null); + } + + public AuthenticationDataCommand(String authData, String subscription) { + this(authData, null, null, subscription); + } + + public AuthenticationDataCommand(String authData, SocketAddress remoteAddress, SSLSession sslSession, + String subscription) { + this.authData = authData; + this.remoteAddress = remoteAddress; + this.sslSession = sslSession; + this.subscription = subscription; } public AuthenticationDataCommand(String authData, SocketAddress remoteAddress, SSLSession sslSession) { @@ -85,4 +98,21 @@ public Certificate[] getTlsCertificates() { } } + /* + * Subscription + */ + @Override + public boolean hasSubscription() { + return this.subscription != null; + } + + @Override + public void setSubscription(String subscription) { + this.subscription = subscription; + } + + @Override + public String getSubscription() { + return subscription; + } } diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationDataSource.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationDataSource.java index b72b99beaae9f..eb9ed2bc747e6 100644 --- a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationDataSource.java +++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authentication/AuthenticationDataSource.java @@ -127,4 +127,27 @@ default boolean hasDataFromPeer() { default SocketAddress getPeerAddress() { return null; } + + /** + * Check if subscription is defined available. + * + * @return true if this authentication data contain subscription + */ + default boolean hasSubscription() { + return false; + } + + /** + * Subscription name can be necessary for consumption + * + * @return a String containing the subscription name + */ + default String getSubscription() { return null; } + + /** + * Subscription name can be necessary for consumption + * + * @return a String containing the subscription name + */ + default void setSubscription(String subscription) { }; } diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationProvider.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationProvider.java index 572e4031d3271..e6570d26be64d 100644 --- a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationProvider.java +++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationProvider.java @@ -29,7 +29,12 @@ import org.apache.pulsar.common.naming.TopicName; import org.apache.pulsar.common.naming.NamespaceName; import org.apache.pulsar.common.policies.data.AuthAction; +import org.apache.pulsar.common.policies.data.ClusterOperation; import org.apache.pulsar.common.policies.data.TenantInfo; +import org.apache.pulsar.common.policies.data.NamespaceOperation; +import org.apache.pulsar.common.policies.data.TenantOperation; +import org.apache.pulsar.common.policies.data.TopicOperation; +import org.apache.pulsar.common.util.FutureUtil; /** * Provider of authorization mechanism @@ -186,4 +191,75 @@ CompletableFuture revokeSubscriptionPermissionAsync(NamespaceName namespac CompletableFuture grantPermissionAsync(TopicName topicName, Set actions, String role, String authDataJson); + /** + * Grant authorization-action permission on a cluster to the given client + * @param clusterName + * @param role + * @param operation + * @param authData + * @return CompletableFuture + * @completesWith
+ * IllegalArgumentException when topic not found
+ * IllegalStateException when failed to grant permission + */ + default CompletableFuture allowClusterOperation(String clusterName, String role, + ClusterOperation operation, + AuthenticationDataSource authData) { + return FutureUtil.failedFuture( + new IllegalStateException("ClusterOperation is not supported by the Authorization provider you are using.")); + } + + /** + * Grant authorization-action permission on a tenant to the given client + * @param tenantName + * @param originalRole + * @param role + * @param operation + * @param authData + * @return + */ + default CompletableFuture allowTenantOperation(String tenantName, String originalRole, String role, + TenantOperation operation, + AuthenticationDataSource authData) { + return FutureUtil.failedFuture(new IllegalStateException( + String.format("allowTenantOperation(%s) on tenant %s is not supported by the Authorization" + + " provider you are using.", + operation.toString(), tenantName))); + } + + /** + * Grant authorization-action permission on a namespace to the given client + * @param namespaceName + * @param originalRole + * @param role + * @param operation + * @param authData + * @return CompletableFuture + * @completesWith
+ * IllegalArgumentException when namespace not found
+ * IllegalStateException when failed to grant permission + */ + default CompletableFuture allowNamespaceOperation(NamespaceName namespaceName, String originalRole, + String role, NamespaceOperation operation, + AuthenticationDataSource authData) { + return FutureUtil.failedFuture( + new IllegalStateException("NamespaceOperation is not supported by the Authorization provider you are using.")); + } + + /** + * Grant authorization-action permission on a topic to the given client + * @param topic + * @param role + * @param operation + * @param authData + * @return CompletableFuture + * @completesWith
+ * IllegalArgumentException when topic not found
+ * IllegalStateException when failed to grant permission + */ + default CompletableFuture allowTopicOperation(TopicName topic, String role, TopicOperation operation, + AuthenticationDataSource authData) { + return FutureUtil.failedFuture( + new IllegalStateException("TopicOperation is not supported by the Authorization provider you are using.")); + } } diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationService.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationService.java index 3bf4458a7c1ba..bb5fbee332453 100644 --- a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationService.java +++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationService.java @@ -21,18 +21,31 @@ import org.apache.commons.lang3.StringUtils; import org.apache.pulsar.broker.PulsarServerException; import org.apache.pulsar.broker.ServiceConfiguration; +import org.apache.pulsar.broker.authentication.AuthenticationDataCommand; import org.apache.pulsar.broker.authentication.AuthenticationDataSource; import org.apache.pulsar.broker.cache.ConfigurationCacheService; import org.apache.pulsar.common.naming.NamespaceName; import org.apache.pulsar.common.naming.TopicName; import org.apache.pulsar.common.policies.data.AuthAction; +import org.apache.pulsar.common.policies.data.ClusterOperation; import org.apache.pulsar.common.policies.data.TenantInfo; +import org.apache.pulsar.common.policies.data.NamespaceOperation; +import org.apache.pulsar.common.policies.data.TenantOperation; +import org.apache.pulsar.common.policies.data.TopicOperation; import org.apache.pulsar.common.util.FutureUtil; +import org.apache.pulsar.common.util.RestException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.ws.rs.core.Response; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; +import java.util.stream.Stream; import static java.util.concurrent.TimeUnit.SECONDS; @@ -318,4 +331,128 @@ public CompletableFuture allowFunctionOpsAsync(NamespaceName namespaceN AuthenticationDataSource authenticationData) { return provider.allowFunctionOpsAsync(namespaceName, role, authenticationData); } + + /** + * Grant authorization-action permission on a cluster to the given client + * + * @param clusterName + * @param operation + * @param role + * @param authData + * additional authdata in json for targeted authorization provider + * @return IllegalArgumentException when tenant not found + * @throws IllegalStateException + * when failed to grant permission + */ + public CompletableFuture allowClusterOperation(String clusterName, ClusterOperation operation, + String role, AuthenticationDataSource authData) { + if (!this.conf.isAuthorizationEnabled()) { + return CompletableFuture.completedFuture(true); + } + + if (provider != null) { + return provider.allowClusterOperation(clusterName, role, operation, authData) + .thenApply(authorized -> { + if (!authorized) { + throw new RestException(Response.Status.FORBIDDEN, "Unauthorized"); + } + return authorized; + }); + } + return FutureUtil.failedFuture(new IllegalStateException("No authorization provider configured")); + } + + /** + * Grant authorization-action permission on a tenant to the given client + * + * @param tenantName + * @param operation + * @param originalRole + * @param role + * @param authData + * additional authdata in json for targeted authorization provider + * @return IllegalArgumentException when tenant not found + * @throws IllegalStateException + * when failed to grant permission + */ + public CompletableFuture allowTenantOperation(String tenantName, TenantOperation operation, + String originalRole, String role, + AuthenticationDataSource authData) { + if (!this.conf.isAuthorizationEnabled()) { + return CompletableFuture.completedFuture(true); + } + + if (provider != null) { + return provider.allowTenantOperation(tenantName, originalRole, role, operation, authData) + .thenApply(authorized -> { + if (!authorized) { + throw new RestException(Response.Status.FORBIDDEN, "Unauthorized"); + } + return authorized; + }); + } + return FutureUtil.failedFuture(new IllegalStateException("No authorization provider configured")); + } + + /** + * Grant authorization-action permission on a namespace to the given client + * + * @param namespaceName + * @param operation + * @param originalRole + * @param role + * @param authData + * additional authdata in json for targeted authorization provider + * @return IllegalArgumentException when namespace not found + * @throws IllegalStateException + * when failed to grant permission + */ + public CompletableFuture allowNamespaceOperation(NamespaceName namespaceName, NamespaceOperation operation, + String originalRole, String role, + AuthenticationDataSource authData) { + if (!this.conf.isAuthorizationEnabled()) { + return CompletableFuture.completedFuture(true); + } + + if (provider != null) { + return provider.allowNamespaceOperation(namespaceName, originalRole, role, operation, authData) + .thenApply(authorized -> { + if (!authorized) { + throw new RestException(Response.Status.FORBIDDEN, "Unauthorized"); + } + return authorized; + }); + } + return FutureUtil.failedFuture(new IllegalStateException("No authorization provider configured")); + } + + /** + * Grant authorization-action permission on a topic to the given client + * + * @param topicName + * @param operation + * @param role + * @param authData + * additional authdata in json for targeted authorization provider + * @return IllegalArgumentException when namespace not found + * @throws IllegalStateException + * when failed to grant permission + */ + public CompletableFuture allowTopicOperation(TopicName topicName, String role, TopicOperation operation, + AuthenticationDataSource authData) { + if (!this.conf.isAuthorizationEnabled()) { + return CompletableFuture.completedFuture(true); + } + + if (provider != null) { + return provider.allowTopicOperation(topicName, role, operation, authData) + .thenApply(authorized -> { + if (!authorized) { + throw new RestException(Response.Status.FORBIDDEN, "Unauthorized"); + } + return authorized; + }); + } + return FutureUtil.failedFuture(new IllegalStateException("No authorization provider configured")); + } } diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/PulsarAuthorizationProvider.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/PulsarAuthorizationProvider.java index b025f806f8636..489f7a5d95342 100644 --- a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/PulsarAuthorizationProvider.java +++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/PulsarAuthorizationProvider.java @@ -27,8 +27,8 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; +import com.google.common.base.Joiner; import org.apache.pulsar.broker.PulsarServerException; import org.apache.pulsar.broker.ServiceConfiguration; import org.apache.pulsar.broker.authentication.AuthenticationDataSource; @@ -36,8 +36,16 @@ import org.apache.pulsar.common.naming.TopicName; import org.apache.pulsar.common.naming.NamespaceName; import org.apache.pulsar.common.policies.data.AuthAction; +import org.apache.pulsar.common.policies.data.ClusterOperation; +import org.apache.pulsar.common.policies.data.NamespaceOperation; import org.apache.pulsar.common.policies.data.Policies; import static org.apache.pulsar.common.util.ObjectMapperFactory.getThreadLocal; + +import org.apache.pulsar.common.policies.data.TenantInfo; +import org.apache.pulsar.common.policies.data.TenantOperation; +import org.apache.pulsar.common.policies.data.TopicOperation; +import org.apache.pulsar.common.util.FutureUtil; +import org.apache.pulsar.common.util.RestException; import org.apache.pulsar.zookeeper.ZooKeeperCache; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.ZooKeeper; @@ -46,7 +54,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.collect.Sets; +import javax.ws.rs.core.Response; /** * Default authorization provider that stores authorization policies under local-zookeeper. @@ -501,4 +509,113 @@ private void validatePoliciesReadOnlyAccess() { } } + @Override + public CompletableFuture allowClusterOperation(String clusterName, String role, ClusterOperation operation, + AuthenticationDataSource authData) { + return isSuperUser(role, conf); + } + + @Override + public CompletableFuture allowTenantOperation(String tenantName, String originalRole, String role, + TenantOperation operation, + AuthenticationDataSource authData) { + return validateTenantAdminAccess(tenantName, originalRole, role, authData); + } + + @Override + public CompletableFuture allowNamespaceOperation(NamespaceName namespaceName, String originalRole, + String role, NamespaceOperation operation, + AuthenticationDataSource authData) { + return validateTenantAdminAccess(namespaceName.getTenant(), originalRole, role, authData); + } + + @Override + public CompletableFuture allowTopicOperation(TopicName topicName, String role, TopicOperation operation, + AuthenticationDataSource authData) { + CompletableFuture isAuthorizedFuture; + + switch (operation) { + case LOOKUP: isAuthorizedFuture = canLookupAsync(topicName, role, authData); + break; + case PRODUCE: isAuthorizedFuture= canProduceAsync(topicName, role, authData); + break; + case CONSUME: isAuthorizedFuture = canConsumeAsync(topicName, role, authData, authData.getSubscription()); + break; + default: isAuthorizedFuture = FutureUtil.failedFuture( + new IllegalStateException("TopicOperation is not supported.")); + } + + CompletableFuture isSuperUserFuture = isSuperUser(role, conf); + + return isSuperUserFuture + .thenCombine(isAuthorizedFuture, (isSuperUser, isAuthorized) -> isSuperUser || isAuthorized); + } + + private static String path(String... parts) { + StringBuilder sb = new StringBuilder(); + sb.append("/admin/"); + Joiner.on('/').appendTo(sb, parts); + return sb.toString(); + } + + private CompletableFuture validateTenantAdminAccess(String tenantName, String originalRole, String role, + AuthenticationDataSource authData) { + try { + TenantInfo tenantInfo = configCache.propertiesCache() + .get(path(POLICIES, tenantName)) + .orElseThrow(() -> new RestException(Response.Status.NOT_FOUND, "Tenant does not exist")); + + validateOriginalPrincipal(conf.getProxyRoles(), role, originalRole); + + if (role != null && conf.getProxyRoles().contains(role)) { + // role check + CompletableFuture isRoleSuperUserFuture = isSuperUser(role, conf); + CompletableFuture isRoleTenantAdminFuture = isTenantAdmin(tenantName, role, tenantInfo, authData); + CompletableFuture isRoleAuthorizedFuture = isRoleSuperUserFuture + .thenCombine(isRoleTenantAdminFuture, (isRoleSuperUser, isRoleTenantAdmin) -> + isRoleSuperUser || isRoleTenantAdmin); + + // originalRole check + CompletableFuture isOriginalRoleSuperUserFuture = isSuperUser(originalRole, conf); + CompletableFuture isOriginalRoleTenantAdminFuture = isTenantAdmin(tenantName, originalRole, + tenantInfo, authData); + CompletableFuture isOriginalRoleAuthorizedFuture = isOriginalRoleSuperUserFuture + .thenCombine(isOriginalRoleTenantAdminFuture, (isOriginalRoleSuperUser, isOriginalRoleTenantAdmin) -> + isOriginalRoleSuperUser || isOriginalRoleTenantAdmin); + + // merging + return isRoleAuthorizedFuture + .thenCombine(isOriginalRoleAuthorizedFuture, (isRoleAuthorized, isOriginalRoleAuthorized) -> + isRoleAuthorized && isOriginalRoleAuthorized); + } else { + // role check + CompletableFuture isRoleSuperUserFuture = isSuperUser(role, conf); + CompletableFuture isRoleTenantAdminFuture = isTenantAdmin(tenantName, role, tenantInfo, authData); + return isRoleSuperUserFuture + .thenCombine(isRoleTenantAdminFuture, (isRoleSuperUser, isRoleTenantAdmin) -> + isRoleSuperUser || isRoleTenantAdmin); + } + } catch (KeeperException.NoNodeException e) { + log.warn("Failed to get tenant info data for non existing tenant {}", tenantName); + throw new RestException(Response.Status.NOT_FOUND, "Tenant does not exist"); + } catch (Exception e) { + log.error("Failed to get tenant {}", tenantName, e); + throw new RestException(e); + } + } + + private static void validateOriginalPrincipal(Set proxyRoles, String authenticatedPrincipal, + String originalPrincipal) { + if (proxyRoles.contains(authenticatedPrincipal)) { + // Request has come from a proxy + if (StringUtils.isBlank(originalPrincipal)) { + log.warn("Original principal empty in request authenticated as {}", authenticatedPrincipal); + throw new RestException(Response.Status.UNAUTHORIZED, "Original principal cannot be empty if the request is via proxy."); + } + if (proxyRoles.contains(originalPrincipal)) { + log.warn("Original principal {} cannot be a proxy role ({})", originalPrincipal, proxyRoles); + throw new RestException(Response.Status.UNAUTHORIZED, "Original principal cannot be a proxy role"); + } + } + } } From 2aca6b58e39e43d763bc20fb805532aaf48529f9 Mon Sep 17 00:00:00 2001 From: Alexandre DUVAL Date: Thu, 26 Mar 2020 17:54:52 +0100 Subject: [PATCH 03/19] provide sync authz interface operations impl --- .../authorization/AuthorizationProvider.java | 63 ++++++++-- .../authorization/AuthorizationService.java | 110 ++++++++++-------- .../PulsarAuthorizationProvider.java | 15 ++- .../pulsar/broker/web/PulsarWebResource.java | 69 +++++++++++ 4 files changed, 194 insertions(+), 63 deletions(-) diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationProvider.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationProvider.java index e6570d26be64d..636237b9d5aca 100644 --- a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationProvider.java +++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationProvider.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.util.Set; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import org.apache.pulsar.broker.ServiceConfiguration; import org.apache.pulsar.broker.authentication.AuthenticationDataSource; @@ -35,6 +36,7 @@ import org.apache.pulsar.common.policies.data.TenantOperation; import org.apache.pulsar.common.policies.data.TopicOperation; import org.apache.pulsar.common.util.FutureUtil; +import org.apache.pulsar.common.util.RestException; /** * Provider of authorization mechanism @@ -194,6 +196,7 @@ CompletableFuture grantPermissionAsync(TopicName topicName, Set grantPermissionAsync(TopicName topicName, Set * IllegalStateException when failed to grant permission */ - default CompletableFuture allowClusterOperation(String clusterName, String role, + default CompletableFuture allowClusterOperationAsync(String clusterName, String originalRole, String role, ClusterOperation operation, AuthenticationDataSource authData) { return FutureUtil.failedFuture( new IllegalStateException("ClusterOperation is not supported by the Authorization provider you are using.")); } + default Boolean allowClusterOperation(String clusterName, String originalRole, String role, ClusterOperation operation, + AuthenticationDataSource authData) { + try { + return allowClusterOperationAsync(clusterName, originalRole, role, operation, authData).get(); + } catch (InterruptedException e) { + throw new RestException(e); + } catch (ExecutionException e) { + throw new RestException(e.getCause()); + } + } + /** * Grant authorization-action permission on a tenant to the given client * @param tenantName @@ -218,7 +232,7 @@ default CompletableFuture allowClusterOperation(String clusterName, Str * @param authData * @return */ - default CompletableFuture allowTenantOperation(String tenantName, String originalRole, String role, + default CompletableFuture allowTenantOperationAsync(String tenantName, String originalRole, String role, TenantOperation operation, AuthenticationDataSource authData) { return FutureUtil.failedFuture(new IllegalStateException( @@ -227,6 +241,17 @@ default CompletableFuture allowTenantOperation(String tenantName, Strin operation.toString(), tenantName))); } + default Boolean allowTenantOperation(String tenantName, String originalRole, String role, TenantOperation operation, + AuthenticationDataSource authData) { + try { + return allowTenantOperationAsync(tenantName, originalRole, role, operation, authData).get(); + } catch (InterruptedException e) { + throw new RestException(e); + } catch (ExecutionException e) { + throw new RestException(e.getCause()); + } + } + /** * Grant authorization-action permission on a namespace to the given client * @param namespaceName @@ -239,16 +264,28 @@ default CompletableFuture allowTenantOperation(String tenantName, Strin * IllegalArgumentException when namespace not found
* IllegalStateException when failed to grant permission */ - default CompletableFuture allowNamespaceOperation(NamespaceName namespaceName, String originalRole, - String role, NamespaceOperation operation, - AuthenticationDataSource authData) { + default CompletableFuture allowNamespaceOperationAsync(NamespaceName namespaceName, String originalRole, + String role, NamespaceOperation operation, + AuthenticationDataSource authData) { return FutureUtil.failedFuture( new IllegalStateException("NamespaceOperation is not supported by the Authorization provider you are using.")); } + default Boolean allowNamespaceOperation(NamespaceName namespaceName, String originalRole, String role, + NamespaceOperation operation, AuthenticationDataSource authData) { + try { + return allowNamespaceOperationAsync(namespaceName, originalRole, role, operation, authData).get(); + } catch (InterruptedException e) { + throw new RestException(e); + } catch (ExecutionException e) { + throw new RestException(e.getCause()); + } + } + /** * Grant authorization-action permission on a topic to the given client * @param topic + * @param originalRole * @param role * @param operation * @param authData @@ -257,9 +294,21 @@ default CompletableFuture allowNamespaceOperation(NamespaceName namespa * IllegalArgumentException when topic not found
* IllegalStateException when failed to grant permission */ - default CompletableFuture allowTopicOperation(TopicName topic, String role, TopicOperation operation, - AuthenticationDataSource authData) { + default CompletableFuture allowTopicOperationAsync(TopicName topic, String originalRole, String role, + TopicOperation operation, + AuthenticationDataSource authData) { return FutureUtil.failedFuture( new IllegalStateException("TopicOperation is not supported by the Authorization provider you are using.")); } + + default Boolean allowTopicOperation(TopicName topicName, String originalRole, String role, TopicOperation operation, + AuthenticationDataSource authData) { + try { + return allowTopicOperationAsync(topicName, originalRole, role, operation, authData).get(); + } catch (InterruptedException e) { + throw new RestException(e); + } catch (ExecutionException e) { + throw new RestException(e.getCause()); + } + } } diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationService.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationService.java index bb5fbee332453..57c2c188e7519 100644 --- a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationService.java +++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationService.java @@ -337,6 +337,7 @@ public CompletableFuture allowFunctionOpsAsync(NamespaceName namespaceN * * @param clusterName * @param operation + * @param originalRole * @param role * @param authData * additional authdata in json for targeted authorization provider @@ -344,22 +345,25 @@ public CompletableFuture allowFunctionOpsAsync(NamespaceName namespaceN * @throws IllegalStateException * when failed to grant permission */ - public CompletableFuture allowClusterOperation(String clusterName, ClusterOperation operation, - String role, AuthenticationDataSource authData) { - if (!this.conf.isAuthorizationEnabled()) { - return CompletableFuture.completedFuture(true); + public CompletableFuture allowClusterOperationAsync(String clusterName, ClusterOperation operation, + String originalRole, String role, + AuthenticationDataSource authData) { + if (provider != null) { + return provider.allowClusterOperationAsync(clusterName, originalRole, role, operation, authData); } + return FutureUtil.failedFuture(new IllegalStateException("No authorization provider configured for " + + "allowClusterOperationAsync")); + } + + public Boolean allowClusterOperation(String clusterName, ClusterOperation operation, + String orignalRole, String role, + AuthenticationDataSource authData) { if (provider != null) { - return provider.allowClusterOperation(clusterName, role, operation, authData) - .thenApply(authorized -> { - if (!authorized) { - throw new RestException(Response.Status.FORBIDDEN, "Unauthorized"); - } - return authorized; - }); + return provider.allowClusterOperation(clusterName, orignalRole, role, operation, authData); } - return FutureUtil.failedFuture(new IllegalStateException("No authorization provider configured")); + + throw new IllegalStateException("No authorization provider configured for allowClusterOperation"); } /** @@ -375,23 +379,24 @@ public CompletableFuture allowClusterOperation(String clusterName, Clus * @throws IllegalStateException * when failed to grant permission */ - public CompletableFuture allowTenantOperation(String tenantName, TenantOperation operation, - String originalRole, String role, - AuthenticationDataSource authData) { - if (!this.conf.isAuthorizationEnabled()) { - return CompletableFuture.completedFuture(true); + public CompletableFuture allowTenantOperationAsync(String tenantName, TenantOperation operation, + String originalRole, String role, + AuthenticationDataSource authData) { + if (provider != null) { + return provider.allowTenantOperationAsync(tenantName, originalRole, role, operation, authData); } + return FutureUtil.failedFuture(new IllegalStateException("No authorization provider configured for " + + "allowTenantOperationAsync")); + } + + public Boolean allowTenantOperation(String tenantName, TenantOperation operation, String orignalRole, String role, + AuthenticationDataSource authData) { if (provider != null) { - return provider.allowTenantOperation(tenantName, originalRole, role, operation, authData) - .thenApply(authorized -> { - if (!authorized) { - throw new RestException(Response.Status.FORBIDDEN, "Unauthorized"); - } - return authorized; - }); + return provider.allowTenantOperation(tenantName, orignalRole, role, operation, authData); } - return FutureUtil.failedFuture(new IllegalStateException("No authorization provider configured")); + + throw new IllegalStateException("No authorization provider configured for allowClusterOperation"); } /** @@ -407,23 +412,25 @@ public CompletableFuture allowTenantOperation(String tenantName, Tenant * @throws IllegalStateException * when failed to grant permission */ - public CompletableFuture allowNamespaceOperation(NamespaceName namespaceName, NamespaceOperation operation, - String originalRole, String role, - AuthenticationDataSource authData) { - if (!this.conf.isAuthorizationEnabled()) { - return CompletableFuture.completedFuture(true); + public CompletableFuture allowNamespaceOperationAsync(NamespaceName namespaceName, + NamespaceOperation operation, + String originalRole, String role, + AuthenticationDataSource authData) { + if (provider != null) { + return provider.allowNamespaceOperationAsync(namespaceName, originalRole, role, operation, authData); } + return FutureUtil.failedFuture(new IllegalStateException("No authorization provider configured for " + + "allowNamespaceOperationAsync")); + } + + public Boolean allowNamespaceOperation(NamespaceName namespaceName, NamespaceOperation operation, + String orignalRole, String role, AuthenticationDataSource authData) { if (provider != null) { - return provider.allowNamespaceOperation(namespaceName, originalRole, role, operation, authData) - .thenApply(authorized -> { - if (!authorized) { - throw new RestException(Response.Status.FORBIDDEN, "Unauthorized"); - } - return authorized; - }); + return provider.allowNamespaceOperation(namespaceName, orignalRole, role, operation, authData); } - return FutureUtil.failedFuture(new IllegalStateException("No authorization provider configured")); + + throw new IllegalStateException("No authorization provider configured for allowNamespaceOperation"); } /** @@ -438,21 +445,24 @@ public CompletableFuture allowNamespaceOperation(NamespaceName namespac * @throws IllegalStateException * when failed to grant permission */ - public CompletableFuture allowTopicOperation(TopicName topicName, String role, TopicOperation operation, - AuthenticationDataSource authData) { - if (!this.conf.isAuthorizationEnabled()) { - return CompletableFuture.completedFuture(true); + public CompletableFuture allowTopicOperationAsync(TopicName topicName, TopicOperation operation, + String originalRole, String role, + AuthenticationDataSource authData) { + if (provider != null) { + return provider.allowTopicOperationAsync(topicName, originalRole, role, operation, authData); } + return FutureUtil.failedFuture(new IllegalStateException("No authorization provider configured for " + + "allowTopicOperationAsync")); + } + + public Boolean allowTopicOperation(TopicName topicName, TopicOperation operation, + String orignalRole, String role, + AuthenticationDataSource authData) { if (provider != null) { - return provider.allowTopicOperation(topicName, role, operation, authData) - .thenApply(authorized -> { - if (!authorized) { - throw new RestException(Response.Status.FORBIDDEN, "Unauthorized"); - } - return authorized; - }); + return provider.allowTopicOperation(topicName, orignalRole, role, operation, authData); } - return FutureUtil.failedFuture(new IllegalStateException("No authorization provider configured")); + + throw new IllegalStateException("No authorization provider configured for allowTopicOperation"); } } diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/PulsarAuthorizationProvider.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/PulsarAuthorizationProvider.java index 489f7a5d95342..60c98fa27b4d9 100644 --- a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/PulsarAuthorizationProvider.java +++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/PulsarAuthorizationProvider.java @@ -29,6 +29,7 @@ import java.util.concurrent.CompletableFuture; import com.google.common.base.Joiner; +import org.apache.commons.lang3.StringUtils; import org.apache.pulsar.broker.PulsarServerException; import org.apache.pulsar.broker.ServiceConfiguration; import org.apache.pulsar.broker.authentication.AuthenticationDataSource; @@ -510,28 +511,30 @@ private void validatePoliciesReadOnlyAccess() { } @Override - public CompletableFuture allowClusterOperation(String clusterName, String role, ClusterOperation operation, - AuthenticationDataSource authData) { + public CompletableFuture allowClusterOperationAsync(String clusterName, String originalRole, String role, + ClusterOperation operation, + AuthenticationDataSource authData) { return isSuperUser(role, conf); } @Override - public CompletableFuture allowTenantOperation(String tenantName, String originalRole, String role, + public CompletableFuture allowTenantOperationAsync(String tenantName, String originalRole, String role, TenantOperation operation, AuthenticationDataSource authData) { return validateTenantAdminAccess(tenantName, originalRole, role, authData); } @Override - public CompletableFuture allowNamespaceOperation(NamespaceName namespaceName, String originalRole, + public CompletableFuture allowNamespaceOperationAsync(NamespaceName namespaceName, String originalRole, String role, NamespaceOperation operation, AuthenticationDataSource authData) { return validateTenantAdminAccess(namespaceName.getTenant(), originalRole, role, authData); } @Override - public CompletableFuture allowTopicOperation(TopicName topicName, String role, TopicOperation operation, - AuthenticationDataSource authData) { + public CompletableFuture allowTopicOperationAsync(TopicName topicName, String originalRole, String role, + TopicOperation operation, + AuthenticationDataSource authData) { CompletableFuture isAuthorizedFuture; switch (operation) { diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java index 2273d1d6751d0..430bff9464f66 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java @@ -61,8 +61,12 @@ import org.apache.pulsar.common.naming.TopicName; import org.apache.pulsar.common.policies.data.BundlesData; import org.apache.pulsar.common.policies.data.ClusterData; +import org.apache.pulsar.common.policies.data.ClusterOperation; +import org.apache.pulsar.common.policies.data.NamespaceOperation; import org.apache.pulsar.common.policies.data.Policies; import org.apache.pulsar.common.policies.data.TenantInfo; +import org.apache.pulsar.common.policies.data.TenantOperation; +import org.apache.pulsar.common.policies.data.TopicOperation; import org.apache.zookeeper.KeeperException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -771,4 +775,69 @@ protected static boolean isLeaderBroker(PulsarService pulsar) { // Non-Usual HTTP error codes protected static final int NOT_IMPLEMENTED = 501; + public void validateClusterOperation(String cluster, ClusterOperation operation) { + if (pulsar().getConfiguration().isAuthenticationEnabled() && pulsar().getBrokerService().isAuthorizationEnabled()) { + if (!isClientAuthenticated(clientAppId())) { + throw new RestException(Status.UNAUTHORIZED, "Need to authenticate to perform the request"); + } + + Boolean isAuthorized = pulsar().getBrokerService().getAuthorizationService() + .allowClusterOperation(cluster, operation, originalPrincipal(), clientAppId(), clientAuthData()); + + if (!isAuthorized) { + throw new RestException(Status.UNAUTHORIZED, String.format("Unauthorized to validateClusterOperation for" + + " operation [%s] on cluster [%s]", operation.toString(), cluster)); + } + } + } + + public void validateTenantOperation(String tenant, TenantOperation operation) { + if (pulsar().getConfiguration().isAuthenticationEnabled() && pulsar().getBrokerService().isAuthorizationEnabled()) { + if (!isClientAuthenticated(clientAppId())) { + throw new RestException(Status.UNAUTHORIZED, "Need to authenticate to perform the request"); + } + + Boolean isAuthorized = pulsar().getBrokerService().getAuthorizationService() + .allowTenantOperation( + tenant, operation, originalPrincipal(), clientAppId(), clientAuthData()); + + if (!isAuthorized) { + throw new RestException(Status.UNAUTHORIZED, String.format("Unauthorized to validateTenantOperation for" + + " originalPrincipal [%s] and clientAppId [%s] about operation [%s] on tenant [%s]", + originalPrincipal(), clientAppId(), operation.toString(), tenant)); + } + } + } + + public void validateNamespaceOperation(NamespaceName namespaceName, NamespaceOperation operation) { + if (pulsar().getConfiguration().isAuthenticationEnabled() && pulsar().getBrokerService().isAuthorizationEnabled()) { + if (!isClientAuthenticated(clientAppId())) { + throw new RestException(Status.FORBIDDEN, "Need to authenticate to perform the request"); + } + + Boolean isAuthorized = pulsar().getBrokerService().getAuthorizationService() + .allowNamespaceOperation(namespaceName, operation, originalPrincipal(), clientAppId(), clientAuthData()); + + if (!isAuthorized) { + throw new RestException(Status.FORBIDDEN, String.format("Unauthorized to validateNamespaceOperation for" + + " operation [%s] on namespace [%s]", operation.toString(), namespaceName)); + } + } + } + + public void validateTopicOperation(TopicName topicName, TopicOperation operation) { + if (pulsar().getConfiguration().isAuthenticationEnabled() && pulsar().getBrokerService().isAuthorizationEnabled()) { + if (!isClientAuthenticated(clientAppId())) { + throw new RestException(Status.UNAUTHORIZED, "Need to authenticate to perform the request"); + } + + Boolean isAuthorized = pulsar().getBrokerService().getAuthorizationService() + .allowTopicOperation(topicName, operation, originalPrincipal(), clientAppId(), clientAuthData()); + + if (!isAuthorized) { + throw new RestException(Status.UNAUTHORIZED, String.format("Unauthorized to validateTopicOperation for" + + " operation [%s] on topic [%s]", operation.toString(), topicName)); + } + } + } } From 476e38ac40c1f242c1dd9a82a7c4af8d9fc32516 Mon Sep 17 00:00:00 2001 From: Alexandre DUVAL Date: Fri, 28 Feb 2020 15:38:35 +0100 Subject: [PATCH 04/19] ServerCnx uses authz --- .../pulsar/broker/service/ServerCnx.java | 33 +++++++++++-------- .../pulsar/broker/service/ServerCnxTest.java | 16 ++++----- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/ServerCnx.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/ServerCnx.java index f10d32acbd54f..a74da35d352f6 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/ServerCnx.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/service/ServerCnx.java @@ -58,6 +58,7 @@ import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.pulsar.broker.PulsarServerException; import org.apache.pulsar.broker.PulsarService; +import org.apache.pulsar.broker.authentication.AuthenticationDataCommand; import org.apache.pulsar.broker.authentication.AuthenticationDataSource; import org.apache.pulsar.broker.authentication.AuthenticationProvider; import org.apache.pulsar.broker.authentication.AuthenticationState; @@ -76,6 +77,7 @@ import org.apache.pulsar.client.impl.MessageIdImpl; import org.apache.pulsar.common.api.AuthData; import org.apache.pulsar.common.api.proto.PulsarApi.CommandNewTxn; +import org.apache.pulsar.common.policies.data.TopicOperation; import org.apache.pulsar.common.protocol.CommandUtils; import org.apache.pulsar.common.protocol.Commands; import org.apache.pulsar.common.protocol.PulsarHandler; @@ -291,8 +293,8 @@ protected void handleLookup(CommandLookupTopic lookup) { } CompletableFuture isProxyAuthorizedFuture; if (service.isAuthorizationEnabled() && originalPrincipal != null) { - isProxyAuthorizedFuture = service.getAuthorizationService().canLookupAsync(topicName, authRole, - authenticationData); + isProxyAuthorizedFuture = service.getAuthorizationService().allowTopicOperationAsync(topicName, + TopicOperation.LOOKUP, originalPrincipal, authRole, authenticationData); } else { isProxyAuthorizedFuture = CompletableFuture.completedFuture(true); } @@ -363,8 +365,8 @@ protected void handlePartitionMetadataRequest(CommandPartitionedTopicMetadata pa } CompletableFuture isProxyAuthorizedFuture; if (service.isAuthorizationEnabled() && originalPrincipal != null) { - isProxyAuthorizedFuture = service.getAuthorizationService() - .canLookupAsync(topicName, authRole, authenticationData); + isProxyAuthorizedFuture = service.getAuthorizationService().allowTopicOperationAsync(topicName, + TopicOperation.LOOKUP, originalPrincipal, authRole, authenticationData); } else { isProxyAuthorizedFuture = CompletableFuture.completedFuture(true); } @@ -745,8 +747,9 @@ protected void handleSubscribe(final CommandSubscribe subscribe) { CompletableFuture isProxyAuthorizedFuture; if (service.isAuthorizationEnabled() && originalPrincipal != null) { - isProxyAuthorizedFuture = service.getAuthorizationService().canConsumeAsync(topicName, authRole, - authenticationData, subscribe.getSubscription()); + authenticationData.setSubscription(subscriptionName); + isProxyAuthorizedFuture = service.getAuthorizationService().allowTopicOperationAsync(topicName, + TopicOperation.CONSUME, originalPrincipal, authRole, authenticationData); } else { isProxyAuthorizedFuture = CompletableFuture.completedFuture(true); } @@ -754,9 +757,13 @@ protected void handleSubscribe(final CommandSubscribe subscribe) { if (isProxyAuthorized) { CompletableFuture authorizationFuture; if (service.isAuthorizationEnabled()) { - authorizationFuture = service.getAuthorizationService().canConsumeAsync(topicName, - originalPrincipal != null ? originalPrincipal : authRole, authenticationData, - subscriptionName); + if (authenticationData == null) { + authenticationData = new AuthenticationDataCommand("", subscriptionName); + } else { + authenticationData.setSubscription(subscriptionName); + } + authorizationFuture = service.getAuthorizationService().allowTopicOperationAsync(topicName, + TopicOperation.CONSUME, originalPrincipal, authRole, authenticationData); } else { authorizationFuture = CompletableFuture.completedFuture(true); } @@ -964,8 +971,8 @@ protected void handleProducer(final CommandProducer cmdProducer) { CompletableFuture isProxyAuthorizedFuture; if (service.isAuthorizationEnabled() && originalPrincipal != null) { - isProxyAuthorizedFuture = service.getAuthorizationService().canProduceAsync(topicName, - authRole, authenticationData); + isProxyAuthorizedFuture = service.getAuthorizationService().allowTopicOperationAsync(topicName, + TopicOperation.PRODUCE, originalPrincipal, authRole, authenticationData); } else { isProxyAuthorizedFuture = CompletableFuture.completedFuture(true); } @@ -973,8 +980,8 @@ protected void handleProducer(final CommandProducer cmdProducer) { if (isProxyAuthorized) { CompletableFuture authorizationFuture; if (service.isAuthorizationEnabled()) { - authorizationFuture = service.getAuthorizationService().canProduceAsync(topicName, - originalPrincipal != null ? originalPrincipal : authRole, authenticationData); + authorizationFuture = service.getAuthorizationService().allowTopicOperationAsync(topicName, + TopicOperation.PRODUCE, originalPrincipal, authRole, authenticationData); } else { authorizationFuture = CompletableFuture.completedFuture(true); } diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/ServerCnxTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/ServerCnxTest.java index cb913fb9348b3..aa718010ad583 100644 --- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/ServerCnxTest.java +++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/service/ServerCnxTest.java @@ -478,8 +478,8 @@ public void testProducerOnNotOwnedTopic() throws Exception { @Test(timeOut = 30000) public void testProducerCommandWithAuthorizationPositive() throws Exception { AuthorizationService authorizationService = mock(AuthorizationService.class); - doReturn(CompletableFuture.completedFuture(true)).when(authorizationService).canProduceAsync(Mockito.any(), - Mockito.any(), Mockito.any()); + doReturn(CompletableFuture.completedFuture(true)).when(authorizationService).allowTopicOperationAsync(Mockito.any(), + Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); doReturn(authorizationService).when(brokerService).getAuthorizationService(); doReturn(true).when(brokerService).isAuthenticationEnabled(); resetChannel(); @@ -605,8 +605,8 @@ public void testNonExistentTopicSuperUserAccess() throws Exception { public void testProducerCommandWithAuthorizationNegative() throws Exception { AuthorizationService authorizationService = mock(AuthorizationService.class); - doReturn(CompletableFuture.completedFuture(false)).when(authorizationService).canProduceAsync(Mockito.any(), - Mockito.any(), Mockito.any()); + doReturn(CompletableFuture.completedFuture(false)).when(authorizationService).allowTopicOperationAsync(Mockito.any(), + Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); doReturn(authorizationService).when(brokerService).getAuthorizationService(); doReturn(true).when(brokerService).isAuthenticationEnabled(); doReturn(true).when(brokerService).isAuthorizationEnabled(); @@ -1195,8 +1195,8 @@ public void testUnsupportedBatchMsgSubscribeCommand() throws Exception { @Test(timeOut = 30000) public void testSubscribeCommandWithAuthorizationPositive() throws Exception { AuthorizationService authorizationService = mock(AuthorizationService.class); - doReturn(CompletableFuture.completedFuture(true)).when(authorizationService).canConsumeAsync(Mockito.any(), - Mockito.any(), Mockito.any(), Mockito.any()); + doReturn(CompletableFuture.completedFuture(true)).when(authorizationService).allowTopicOperationAsync(Mockito.any(), + Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); doReturn(authorizationService).when(brokerService).getAuthorizationService(); doReturn(true).when(brokerService).isAuthenticationEnabled(); doReturn(true).when(brokerService).isAuthorizationEnabled(); @@ -1217,8 +1217,8 @@ public void testSubscribeCommandWithAuthorizationPositive() throws Exception { @Test(timeOut = 30000) public void testSubscribeCommandWithAuthorizationNegative() throws Exception { AuthorizationService authorizationService = mock(AuthorizationService.class); - doReturn(CompletableFuture.completedFuture(false)).when(authorizationService).canConsumeAsync(Mockito.any(), - Mockito.any(), Mockito.any(), Mockito.any()); + doReturn(CompletableFuture.completedFuture(false)).when(authorizationService).allowTopicOperationAsync(Mockito.any(), + Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); doReturn(authorizationService).when(brokerService).getAuthorizationService(); doReturn(true).when(brokerService).isAuthenticationEnabled(); doReturn(true).when(brokerService).isAuthorizationEnabled(); From 1eb7d7c7afd98cbbbc3c09f7571a74731bad1813 Mon Sep 17 00:00:00 2001 From: Alexandre DUVAL Date: Thu, 26 Mar 2020 20:35:59 +0100 Subject: [PATCH 05/19] Namespaces stuffs use new authz --- .../broker/admin/impl/NamespacesBase.java | 153 +++++++++--------- .../pulsar/broker/admin/v1/Namespaces.java | 23 +-- .../pulsar/broker/admin/v2/Namespaces.java | 24 +-- .../pulsar/broker/admin/NamespacesTest.java | 18 ++- 4 files changed, 114 insertions(+), 104 deletions(-) diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java index 5de3915be9960..89c9a2b2643ce 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java @@ -77,9 +77,11 @@ import org.apache.pulsar.common.policies.data.BookieAffinityGroupData; import org.apache.pulsar.common.policies.data.BundlesData; import org.apache.pulsar.common.policies.data.ClusterData; +import org.apache.pulsar.common.policies.data.ClusterOperation; import org.apache.pulsar.common.policies.data.DispatchRate; import org.apache.pulsar.common.policies.data.DelayedDeliveryPolicies; import org.apache.pulsar.common.policies.data.LocalPolicies; +import org.apache.pulsar.common.policies.data.NamespaceOperation; import org.apache.pulsar.common.policies.data.OffloadPolicies; import org.apache.pulsar.common.policies.data.PersistencePolicies; import org.apache.pulsar.common.policies.data.Policies; @@ -89,6 +91,7 @@ import org.apache.pulsar.common.policies.data.SchemaCompatibilityStrategy; import org.apache.pulsar.common.policies.data.SubscribeRate; import org.apache.pulsar.common.policies.data.SubscriptionAuthMode; +import org.apache.pulsar.common.policies.data.TenantOperation; import org.apache.pulsar.common.util.FutureUtil; import org.apache.pulsar.common.util.ObjectMapperFactory; import org.apache.zookeeper.KeeperException; @@ -103,7 +106,7 @@ public abstract class NamespacesBase extends AdminResource { private static final long MAX_BUNDLES = ((long) 1) << 32; protected List internalGetTenantNamespaces(String tenant) { - validateAdminAccessForTenant(tenant); + validateTenantOperation(tenant, TenantOperation.LIST_NAMESPACES); try { return getListOfNamespaces(tenant); @@ -117,9 +120,8 @@ protected List internalGetTenantNamespaces(String tenant) { } protected void internalCreateNamespace(Policies policies) { + validateTenantOperation(namespaceName.getTenant(), TenantOperation.CREATE_NAMESPACE); validatePoliciesReadOnlyAccess(); - validateAdminAccessForTenant(namespaceName.getTenant()); - validatePolicies(namespaceName, policies); try { @@ -138,7 +140,7 @@ protected void internalCreateNamespace(Policies policies) { @SuppressWarnings("deprecation") protected void internalDeleteNamespace(AsyncResponse asyncResponse, boolean authoritative) { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateTenantOperation(namespaceName.getTenant(), TenantOperation.DELETE_NAMESPACE); validatePoliciesReadOnlyAccess(); // ensure that non-global namespace is directed to the correct cluster @@ -282,7 +284,7 @@ && getPartitionedTopicList(TopicDomain.persistent).isEmpty() @SuppressWarnings("deprecation") protected void internalDeleteNamespaceBundle(String bundleRange, boolean authoritative) { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.DELETE_BUNDLE); validatePoliciesReadOnlyAccess(); // ensure that non-global namespace is directed to the correct cluster @@ -353,7 +355,7 @@ protected void internalDeleteNamespaceBundle(String bundleRange, boolean authori } protected void internalGrantPermissionOnNamespace(String role, Set actions) { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.GRANT_PERMISSIONS); try { AuthorizationService authService = pulsar().getBrokerService().getAuthorizationService(); @@ -384,7 +386,7 @@ protected void internalGrantPermissionOnNamespace(String role, Set a protected void internalGrantPermissionOnSubscription(String subscription, Set roles) { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.GRANT_SUBSCRIPTION_PERMISSION); try { AuthorizationService authService = pulsar().getBrokerService().getAuthorizationService(); @@ -414,7 +416,7 @@ protected void internalGrantPermissionOnSubscription(String subscription, Set internalGetNamespaceReplicationClusters() { + validateNamespaceOperation(namespaceName, NamespaceOperation.GET_REPLICATION_CLUSTERS); + if (!namespaceName.isGlobal()) { throw new RestException(Status.PRECONDITION_FAILED, "Cannot get the replication clusters for a non-global namespace"); @@ -467,7 +471,7 @@ protected Set internalGetNamespaceReplicationClusters() { } protected void internalSetNamespaceReplicationClusters(List clusterIds) { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.SET_REPLICATION_CLUSTERS); validatePoliciesReadOnlyAccess(); Set replicationClusterSet = Sets.newHashSet(clusterIds); @@ -525,7 +529,7 @@ protected void internalSetNamespaceReplicationClusters(List clusterIds) } protected void internalSetNamespaceMessageTTL(int messageTTL) { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.SET_MESSAGE_TTL); validatePoliciesReadOnlyAccess(); if (messageTTL < 0) { @@ -781,7 +785,7 @@ protected void internalRemoveAutoSubscriptionCreation(AsyncResponse asyncRespons } protected void internalModifyDeduplication(boolean enableDeduplication) { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.SET_DEDUPLICATION); validatePoliciesReadOnlyAccess(); Entry policiesNode = null; @@ -817,6 +821,7 @@ protected void internalModifyDeduplication(boolean enableDeduplication) { @SuppressWarnings("deprecation") protected void internalUnloadNamespace(AsyncResponse asyncResponse) { + validateClusterOperation(namespaceName.getCluster(), ClusterOperation.UNLOAD_NAMESPACE); log.info("[{}] Unloading namespace {}", clientAppId(), namespaceName); validateSuperUserAccess(); @@ -864,11 +869,10 @@ protected void internalUnloadNamespace(AsyncResponse asyncResponse) { protected void internalSetBookieAffinityGroup(BookieAffinityGroupData bookieAffinityGroup) { + validateClusterOperation(namespaceName.getCluster(), ClusterOperation.SET_BOOKIE_AFFINITY_GROUP); log.info("[{}] Setting bookie-affinity-group {} for namespace {}", clientAppId(), bookieAffinityGroup, this.namespaceName); - validateSuperUserAccess(); - if (namespaceName.isGlobal()) { // check cluster ownership for a given global namespace: redirect if peer-cluster owns it validateGlobalNamespaceOwnership(namespaceName); @@ -921,7 +925,7 @@ protected void internalDeleteBookieAffinityGroup() { } protected BookieAffinityGroupData internalGetBookieAffinityGroup() { - validateSuperUserAccess(); + validateClusterOperation(namespaceName.getCluster(), ClusterOperation.GET_BOOKIE_AFFINITY_GROUP); if (namespaceName.isGlobal()) { // check cluster ownership for a given global namespace: redirect if peer-cluster owns it @@ -955,9 +959,9 @@ protected BookieAffinityGroupData internalGetBookieAffinityGroup() { @SuppressWarnings("deprecation") public void internalUnloadNamespaceBundle(String bundleRange, boolean authoritative) { + validateClusterOperation(namespaceName.getCluster(), ClusterOperation.UNLOAD_NAMESPACE); log.info("[{}] Unloading namespace bundle {}/{}", clientAppId(), namespaceName, bundleRange); - validateSuperUserAccess(); Policies policies = getNamespacePolicies(namespaceName); NamespaceBundle bundle = pulsar().getNamespaceService().getNamespaceBundleFactory().getBundle(namespaceName.toString(), bundleRange); @@ -1003,9 +1007,9 @@ public void internalUnloadNamespaceBundle(String bundleRange, boolean authoritat @SuppressWarnings("deprecation") protected void internalSplitNamespaceBundle(String bundleRange, boolean authoritative, boolean unload, String splitAlgorithmName) { + validateClusterOperation(namespaceName.getCluster(), ClusterOperation.SPLIT_NAMESPACE_BUNDLE); log.info("[{}] Split namespace bundle {}/{}", clientAppId(), namespaceName, bundleRange); - validateSuperUserAccess(); Policies policies = getNamespacePolicies(namespaceName); if (namespaceName.isGlobal()) { @@ -1056,8 +1060,8 @@ private NamespaceBundleSplitAlgorithm getNamespaceBundleSplitAlgorithmByName(Str } protected void internalSetPublishRate(PublishRate maxPublishMessageRate) { + validateClusterOperation(namespaceName.getCluster(), ClusterOperation.SET_PUBLISH_RATE); log.info("[{}] Set namespace publish-rate {}/{}", clientAppId(), namespaceName, maxPublishMessageRate); - validateSuperUserAccess(); Entry policiesNode = null; @@ -1093,7 +1097,7 @@ protected void internalSetPublishRate(PublishRate maxPublishMessageRate) { } protected PublishRate internalGetPublishRate() { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.GET_PUBLISH_RATE); Policies policies = getNamespacePolicies(namespaceName); PublishRate publishRate = policies.publishMaxMessageRate.get(pulsar().getConfiguration().getClusterName()); @@ -1107,8 +1111,8 @@ protected PublishRate internalGetPublishRate() { @SuppressWarnings("deprecation") protected void internalSetTopicDispatchRate(DispatchRate dispatchRate) { + validateClusterOperation(namespaceName.getCluster(), ClusterOperation.SET_TOPIC_DISPATCH_RATE); log.info("[{}] Set namespace dispatch-rate {}/{}", clientAppId(), namespaceName, dispatchRate); - validateSuperUserAccess(); Entry policiesNode = null; @@ -1146,7 +1150,7 @@ protected void internalSetTopicDispatchRate(DispatchRate dispatchRate) { @SuppressWarnings("deprecation") protected DispatchRate internalGetTopicDispatchRate() { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.GET_DISPATCH_RATE); Policies policies = getNamespacePolicies(namespaceName); DispatchRate dispatchRate = policies.topicDispatchRate.get(pulsar().getConfiguration().getClusterName()); @@ -1162,8 +1166,8 @@ protected DispatchRate internalGetTopicDispatchRate() { } protected void internalSetSubscriptionDispatchRate(DispatchRate dispatchRate) { + validateClusterOperation(namespaceName.getCluster(), ClusterOperation.SET_SUBSCRIPTION_DISPATCH_RATE); log.info("[{}] Set namespace subscription dispatch-rate {}/{}", clientAppId(), namespaceName, dispatchRate); - validateSuperUserAccess(); Entry policiesNode = null; @@ -1199,7 +1203,7 @@ protected void internalSetSubscriptionDispatchRate(DispatchRate dispatchRate) { } protected DispatchRate internalGetSubscriptionDispatchRate() { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.GET_DISPATCH_RATE); Policies policies = getNamespacePolicies(namespaceName); DispatchRate dispatchRate = policies.subscriptionDispatchRate.get(pulsar().getConfiguration().getClusterName()); @@ -1212,8 +1216,9 @@ protected DispatchRate internalGetSubscriptionDispatchRate() { } protected void internalSetSubscribeRate(SubscribeRate subscribeRate) { + validateClusterOperation(namespaceName.getCluster(), ClusterOperation.SET_SUBSCRIBE_RATE); + log.info("[{}] Set namespace subscribe-rate {}/{}", clientAppId(), namespaceName, subscribeRate); - validateSuperUserAccess(); Entry policiesNode = null; @@ -1249,7 +1254,7 @@ protected void internalSetSubscribeRate(SubscribeRate subscribeRate) { } protected SubscribeRate internalGetSubscribeRate() { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.GET_SUBSCRIBE_RATE); Policies policies = getNamespacePolicies(namespaceName); SubscribeRate subscribeRate = policies.clusterSubscribeRate.get(pulsar().getConfiguration().getClusterName()); if (subscribeRate != null) { @@ -1261,8 +1266,8 @@ protected SubscribeRate internalGetSubscribeRate() { } protected void internalSetReplicatorDispatchRate(DispatchRate dispatchRate) { + validateClusterOperation(namespaceName.getCluster(), ClusterOperation.SET_REPLICATOR_DISPATCH_RATE); log.info("[{}] Set namespace replicator dispatch-rate {}/{}", clientAppId(), namespaceName, dispatchRate); - validateSuperUserAccess(); Entry policiesNode = null; @@ -1298,7 +1303,7 @@ protected void internalSetReplicatorDispatchRate(DispatchRate dispatchRate) { } protected DispatchRate internalGetReplicatorDispatchRate() { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.GET_REPLICATOR_DISPATCH_RATE); Policies policies = getNamespacePolicies(namespaceName); DispatchRate dispatchRate = policies.replicatorDispatchRate.get(pulsar().getConfiguration().getClusterName()); @@ -1311,7 +1316,7 @@ protected DispatchRate internalGetReplicatorDispatchRate() { } protected void internalSetBacklogQuota(BacklogQuotaType backlogQuotaType, BacklogQuota backlogQuota) { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.SET_BACKLOG_QUOTA); validatePoliciesReadOnlyAccess(); if (backlogQuotaType == null) { @@ -1358,7 +1363,7 @@ protected void internalSetBacklogQuota(BacklogQuotaType backlogQuotaType, Backlo } protected void internalRemoveBacklogQuota(BacklogQuotaType backlogQuotaType) { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.SET_BACKLOG_QUOTA); validatePoliciesReadOnlyAccess(); if (backlogQuotaType == null) { @@ -1391,7 +1396,7 @@ protected void internalRemoveBacklogQuota(BacklogQuotaType backlogQuotaType) { } protected void internalSetRetention(RetentionPolicies retention) { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.SET_RETENTION); validatePoliciesReadOnlyAccess(); try { @@ -1429,7 +1434,7 @@ protected void internalSetRetention(RetentionPolicies retention) { } protected void internalSetPersistence(PersistencePolicies persistence) { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.SET_PERSISTENCE); validatePoliciesReadOnlyAccess(); validatePersistencePolicies(persistence); @@ -1460,7 +1465,7 @@ protected void internalSetPersistence(PersistencePolicies persistence) { } protected PersistencePolicies internalGetPersistence() { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.GET_PERSISTENCE); Policies policies = getNamespacePolicies(namespaceName); if (policies.persistence == null) { @@ -1472,7 +1477,7 @@ protected PersistencePolicies internalGetPersistence() { } protected void internalClearNamespaceBacklog(AsyncResponse asyncResponse, boolean authoritative) { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.CLEAR_BACKLOG); final List> futures = Lists.newArrayList(); try { @@ -1514,7 +1519,7 @@ protected void internalClearNamespaceBacklog(AsyncResponse asyncResponse, boolea @SuppressWarnings("deprecation") protected void internalClearNamespaceBundleBacklog(String bundleRange, boolean authoritative) { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.CLEAR_BACKLOG); Policies policies = getNamespacePolicies(namespaceName); @@ -1535,7 +1540,7 @@ protected void internalClearNamespaceBundleBacklog(String bundleRange, boolean a protected void internalClearNamespaceBacklogForSubscription(AsyncResponse asyncResponse, String subscription, boolean authoritative) { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.CLEAR_BACKLOG); final List> futures = Lists.newArrayList(); try { @@ -1578,7 +1583,7 @@ protected void internalClearNamespaceBacklogForSubscription(AsyncResponse asyncR @SuppressWarnings("deprecation") protected void internalClearNamespaceBundleBacklogForSubscription(String subscription, String bundleRange, boolean authoritative) { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.CLEAR_BACKLOG); Policies policies = getNamespacePolicies(namespaceName); @@ -1599,7 +1604,7 @@ protected void internalClearNamespaceBundleBacklogForSubscription(String subscri protected void internalUnsubscribeNamespace(AsyncResponse asyncResponse, String subscription, boolean authoritative) { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.UNSUBSCRIBE); final List> futures = Lists.newArrayList(); try { @@ -1641,7 +1646,7 @@ protected void internalUnsubscribeNamespace(AsyncResponse asyncResponse, String @SuppressWarnings("deprecation") protected void internalUnsubscribeNamespaceBundle(String subscription, String bundleRange, boolean authoritative) { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.UNSUBSCRIBE); Policies policies = getNamespacePolicies(namespaceName); @@ -1661,7 +1666,7 @@ protected void internalUnsubscribeNamespaceBundle(String subscription, String bu } protected void internalSetSubscriptionAuthMode(SubscriptionAuthMode subscriptionAuthMode) { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.SET_SUBSCRIPTION_AUTH_MODE); validatePoliciesReadOnlyAccess(); if (subscriptionAuthMode == null) { @@ -1697,7 +1702,7 @@ protected void internalSetSubscriptionAuthMode(SubscriptionAuthMode subscription } protected void internalModifyEncryptionRequired(boolean encryptionRequired) { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.SET_ENCRYPTION_REQUIRED); validatePoliciesReadOnlyAccess(); Entry policiesNode = null; @@ -1733,7 +1738,7 @@ protected void internalModifyEncryptionRequired(boolean encryptionRequired) { } protected DelayedDeliveryPolicies internalGetDelayedDelivery() { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.GET_DELAYED_DELIVERY); Policies policies = getNamespacePolicies(namespaceName); if (policies.delayed_delivery_policies == null) { @@ -1745,7 +1750,7 @@ protected DelayedDeliveryPolicies internalGetDelayedDelivery() { } protected void internalSetDelayedDelivery(DelayedDeliveryPolicies delayedDeliveryPolicies) { - validateSuperUserAccess(); + validateClusterOperation(namespaceName.getCluster(), ClusterOperation.SET_DELAYED_DELIVERY); validateAdminAccessForTenant(namespaceName.getTenant()); validatePoliciesReadOnlyAccess(); @@ -1779,7 +1784,7 @@ protected void internalSetDelayedDelivery(DelayedDeliveryPolicies delayedDeliver } protected void internalSetNamespaceAntiAffinityGroup(String antiAffinityGroup) { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.SET_ANTI_AFFINITY_GROUP); validatePoliciesReadOnlyAccess(); log.info("[{}] Setting anti-affinity group {} for {}", clientAppId(), antiAffinityGroup, namespaceName); @@ -1820,12 +1825,12 @@ protected void internalSetNamespaceAntiAffinityGroup(String antiAffinityGroup) { } protected String internalGetNamespaceAntiAffinityGroup() { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.GET_ANTI_AFFINITY_GROUP); return getNamespacePolicies(namespaceName).antiAffinityGroup; } protected void internalRemoveNamespaceAntiAffinityGroup() { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.SET_ANTI_AFFINITY_GROUP); validatePoliciesReadOnlyAccess(); log.info("[{}] Deleting anti-affinity group for {}", clientAppId(), namespaceName); @@ -1856,7 +1861,7 @@ protected void internalRemoveNamespaceAntiAffinityGroup() { protected List internalGetAntiAffinityNamespaces(String cluster, String antiAffinityGroup, String tenant) { - validateAdminAccessForTenant(tenant); + validateNamespaceOperation(namespaceName, NamespaceOperation.GET_ANTI_AFFINITY_GROUP); log.info("[{}]-{} Finding namespaces for {} in {}", clientAppId(), tenant, antiAffinityGroup, cluster); @@ -1908,7 +1913,7 @@ private void validatePersistencePolicies(PersistencePolicies persistence) { } protected RetentionPolicies internalGetRetention() { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.GET_RETENTION); Policies policies = getNamespacePolicies(namespaceName); if (policies.retention_policies == null) { @@ -2101,12 +2106,12 @@ private void validatePolicies(NamespaceName ns, Policies policies) { protected int internalGetMaxProducersPerTopic() { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.GET_MAX_PRODUCERS_PER_TOPIC); return getNamespacePolicies(namespaceName).max_producers_per_topic; } protected void internalSetMaxProducersPerTopic(int maxProducersPerTopic) { - validateSuperUserAccess(); + validateNamespaceOperation(namespaceName, NamespaceOperation.SET_MAX_PRODUCERS_PER_TOPIC); validatePoliciesReadOnlyAccess(); try { @@ -2142,12 +2147,12 @@ protected void internalSetMaxProducersPerTopic(int maxProducersPerTopic) { } protected int internalGetMaxConsumersPerTopic() { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.GET_MAX_CONSUMERS_PER_TOPIC); return getNamespacePolicies(namespaceName).max_consumers_per_topic; } protected void internalSetMaxConsumersPerTopic(int maxConsumersPerTopic) { - validateSuperUserAccess(); + validateNamespaceOperation(namespaceName, NamespaceOperation.SET_MAX_CONSUMERS_PER_TOPIC); validatePoliciesReadOnlyAccess(); try { @@ -2183,12 +2188,12 @@ protected void internalSetMaxConsumersPerTopic(int maxConsumersPerTopic) { } protected int internalGetMaxConsumersPerSubscription() { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.GET_MAX_CONSUMERS_PER_SUBSCRIPTION); return getNamespacePolicies(namespaceName).max_consumers_per_subscription; } protected void internalSetMaxConsumersPerSubscription(int maxConsumersPerSubscription) { - validateSuperUserAccess(); + validateNamespaceOperation(namespaceName, NamespaceOperation.SET_MAX_CONSUMERS_PER_SUBSCRIPTION); validatePoliciesReadOnlyAccess(); try { @@ -2224,12 +2229,12 @@ protected void internalSetMaxConsumersPerSubscription(int maxConsumersPerSubscri } protected int internalGetMaxUnackedMessagesPerConsumer() { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.GET_MAX_UNACKED_MESSAGES_PER_CONSUMER); return getNamespacePolicies(namespaceName).max_unacked_messages_per_consumer; } protected void internalSetMaxUnackedMessagesPerConsumer(int maxUnackedMessagesPerConsumer) { - validateSuperUserAccess(); + validateNamespaceOperation(namespaceName, NamespaceOperation.SET_MAX_UNACKED_MESSAGES_PER_CONSUMER); validatePoliciesReadOnlyAccess(); try { @@ -2265,12 +2270,12 @@ protected void internalSetMaxUnackedMessagesPerConsumer(int maxUnackedMessagesPe } protected int internalGetMaxUnackedMessagesPerSubscription() { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.GET_MAX_UNACKED_MESSAGES_PER_SUBSCRIPTION); return getNamespacePolicies(namespaceName).max_unacked_messages_per_subscription; } protected void internalSetMaxUnackedMessagesPerSubscription(int maxUnackedMessagesPerSubscription) { - validateSuperUserAccess(); + validateNamespaceOperation(namespaceName, NamespaceOperation.SET_MAX_UNACKED_MESSAGES_PER_SUBSCRIPTION); validatePoliciesReadOnlyAccess(); try { @@ -2306,12 +2311,12 @@ protected void internalSetMaxUnackedMessagesPerSubscription(int maxUnackedMessag } protected long internalGetCompactionThreshold() { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.GET_COMPACTION_THRESHOLD); return getNamespacePolicies(namespaceName).compaction_threshold; } protected void internalSetCompactionThreshold(long newThreshold) { - validateSuperUserAccess(); + validateNamespaceOperation(namespaceName, NamespaceOperation.SET_COMPACTION_THRESHOLD); validatePoliciesReadOnlyAccess(); try { @@ -2347,7 +2352,7 @@ protected void internalSetCompactionThreshold(long newThreshold) { } protected long internalGetOffloadThreshold() { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.GET_OFFLOAD_THRESHOLD); Policies policies = getNamespacePolicies(namespaceName); if (policies.offload_policies == null) { return policies.offload_threshold; @@ -2357,7 +2362,7 @@ protected long internalGetOffloadThreshold() { } protected void internalSetOffloadThreshold(long newThreshold) { - validateSuperUserAccess(); + validateNamespaceOperation(namespaceName, NamespaceOperation.SET_OFFLOAD_THRESHOLD); validatePoliciesReadOnlyAccess(); try { @@ -2394,7 +2399,7 @@ protected void internalSetOffloadThreshold(long newThreshold) { } protected Long internalGetOffloadDeletionLag() { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.GET_OFFLOAD_DELETION_LAG); Policies policies = getNamespacePolicies(namespaceName); if (policies.offload_policies == null) { return policies.offload_deletion_lag_ms; @@ -2404,7 +2409,7 @@ protected Long internalGetOffloadDeletionLag() { } protected void internalSetOffloadDeletionLag(Long newDeletionLagMs) { - validateSuperUserAccess(); + validateNamespaceOperation(namespaceName, NamespaceOperation.SET_OFFLOAD_DELETION_LAG); validatePoliciesReadOnlyAccess(); try { @@ -2442,12 +2447,12 @@ protected void internalSetOffloadDeletionLag(Long newDeletionLagMs) { @Deprecated protected SchemaAutoUpdateCompatibilityStrategy internalGetSchemaAutoUpdateCompatibilityStrategy() { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.GET_SCHEMA_AUTOUPDATE_STRATEGY); return getNamespacePolicies(namespaceName).schema_auto_update_compatibility_strategy; } protected SchemaCompatibilityStrategy internalGetSchemaCompatibilityStrategy() { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.GET_SCHEMA_COMPATIBILITY_STRATEGY); Policies policies = getNamespacePolicies(namespaceName); SchemaCompatibilityStrategy schemaCompatibilityStrategy = policies.schema_compatibility_strategy; if (schemaCompatibilityStrategy == SchemaCompatibilityStrategy.UNDEFINED){ @@ -2459,7 +2464,7 @@ protected SchemaCompatibilityStrategy internalGetSchemaCompatibilityStrategy() { @Deprecated protected void internalSetSchemaAutoUpdateCompatibilityStrategy(SchemaAutoUpdateCompatibilityStrategy strategy) { - validateSuperUserAccess(); + validateNamespaceOperation(namespaceName, NamespaceOperation.SET_SCHEMA_AUTOUPDATE_STRATEGY); validatePoliciesReadOnlyAccess(); mutatePolicy((policies) -> { @@ -2470,7 +2475,7 @@ protected void internalSetSchemaAutoUpdateCompatibilityStrategy(SchemaAutoUpdate } protected void internalSetSchemaCompatibilityStrategy(SchemaCompatibilityStrategy strategy) { - validateSuperUserAccess(); + validateNamespaceOperation(namespaceName, NamespaceOperation.SET_SCHEMA_COMPATIBILITY_STRATEGY); validatePoliciesReadOnlyAccess(); mutatePolicy((policies) -> { @@ -2481,13 +2486,12 @@ protected void internalSetSchemaCompatibilityStrategy(SchemaCompatibilityStrateg } protected boolean internalGetSchemaValidationEnforced() { - validateSuperUserAccess(); - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.GET_SCHEMA_VALIDATION_ENFORCED); return getNamespacePolicies(namespaceName).schema_validation_enforced; } protected void internalSetSchemaValidationEnforced(boolean schemaValidationEnforced) { - validateSuperUserAccess(); + validateNamespaceOperation(namespaceName, NamespaceOperation.SET_SCHEMA_VALIDATION_ENFORCED); validatePoliciesReadOnlyAccess(); mutatePolicy((policies) -> { @@ -2498,13 +2502,12 @@ protected void internalSetSchemaValidationEnforced(boolean schemaValidationEnfor } protected boolean internalGetIsAllowAutoUpdateSchema() { - validateSuperUserAccess(); - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.GET_SCHEMA_AUTOUPDATE_STRATEGY); return getNamespacePolicies(namespaceName).is_allow_auto_update_schema; } protected void internalSetIsAllowAutoUpdateSchema(boolean isAllowAutoUpdateSchema) { - validateSuperUserAccess(); + validateNamespaceOperation(namespaceName, NamespaceOperation.SET_SCHEMA_AUTOUPDATE_STRATEGY); validatePoliciesReadOnlyAccess(); mutatePolicy((policies) -> { @@ -2547,7 +2550,7 @@ private void mutatePolicy(Function policyTransformation, } protected void internalSetOffloadPolicies(AsyncResponse asyncResponse, OffloadPolicies offloadPolicies) { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.SET_OFFLOAD_POLICIES); validatePoliciesReadOnlyAccess(); validateOffloadPolicies(offloadPolicies); @@ -2624,7 +2627,7 @@ private void validateOffloadPolicies(OffloadPolicies offloadPolicies) { } protected OffloadPolicies internalGetOffloadPolicies() { - validateAdminAccessForTenant(namespaceName.getTenant()); + validateNamespaceOperation(namespaceName, NamespaceOperation.GET_OFFLOAD_POLICIES); Policies policies = getNamespacePolicies(namespaceName); return policies.offload_policies; diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v1/Namespaces.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v1/Namespaces.java index 957a23a95c0ef..71f4b45828b2d 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v1/Namespaces.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v1/Namespaces.java @@ -27,6 +27,7 @@ import org.apache.pulsar.broker.web.RestException; import org.apache.pulsar.common.api.proto.PulsarApi.CommandGetTopicsOfNamespace.Mode; import org.apache.pulsar.common.naming.NamespaceBundleSplitAlgorithm; +import org.apache.pulsar.common.naming.NamespaceName; import org.apache.pulsar.common.policies.data.AuthAction; import org.apache.pulsar.common.policies.data.AutoSubscriptionCreationOverride; import org.apache.pulsar.common.policies.data.AutoTopicCreationOverride; @@ -35,12 +36,14 @@ import org.apache.pulsar.common.policies.data.BacklogQuota.BacklogQuotaType; import org.apache.pulsar.common.policies.data.BundlesData; import org.apache.pulsar.common.policies.data.DispatchRate; +import org.apache.pulsar.common.policies.data.NamespaceOperation; import org.apache.pulsar.common.policies.data.PersistencePolicies; import org.apache.pulsar.common.policies.data.Policies; import org.apache.pulsar.common.policies.data.PublishRate; import org.apache.pulsar.common.policies.data.RetentionPolicies; import org.apache.pulsar.common.policies.data.SubscriptionAuthMode; import org.apache.pulsar.common.policies.data.SchemaAutoUpdateCompatibilityStrategy; +import org.apache.pulsar.common.policies.data.TenantOperation; import org.apache.zookeeper.KeeperException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -89,7 +92,7 @@ public List getTenantNamespaces(@PathParam("property") String property) @ApiResponse(code = 404, message = "Property or cluster doesn't exist") }) public List getNamespacesForCluster(@PathParam("property") String property, @PathParam("cluster") String cluster) { - validateAdminAccessForTenant(property); + validateTenantOperation(property, TenantOperation.LIST_NAMESPACES); List namespaces = Lists.newArrayList(); if (!clusters().contains(cluster)) { log.warn("[{}] Failed to get namespace list for property: {}/{} - Cluster does not exist", clientAppId(), @@ -121,16 +124,14 @@ public void getTopics(@PathParam("property") String property, @PathParam("cluster") String cluster, @PathParam("namespace") String namespace, @QueryParam("mode") @DefaultValue("PERSISTENT") Mode mode, @Suspended AsyncResponse asyncResponse) { - validateAdminAccessForTenant(property); validateNamespaceName(property, cluster, namespace); + validateNamespaceOperation(NamespaceName.get(property, namespace), NamespaceOperation.TOPICS); // Validate that namespace exists, throws 404 if it doesn't exist getNamespacePolicies(namespaceName); pulsar().getNamespaceService().getListOfTopics(namespaceName, mode) - .thenAccept(topics -> { - asyncResponse.resume(topics); - }) + .thenAccept(asyncResponse::resume) .exceptionally(ex -> { log.error("Failed to get topics list for namespace {}", namespaceName, ex); asyncResponse.resume(ex); @@ -145,8 +146,8 @@ public void getTopics(@PathParam("property") String property, @ApiResponse(code = 404, message = "Property or cluster or namespace doesn't exist") }) public Policies getPolicies(@PathParam("property") String property, @PathParam("cluster") String cluster, @PathParam("namespace") String namespace) { - validateAdminAccessForTenant(property); validateNamespaceName(property, cluster, namespace); + validateNamespaceOperation(NamespaceName.get(property, namespace), NamespaceOperation.POLICIES); return getNamespacePolicies(namespaceName); } @@ -228,8 +229,8 @@ public void deleteNamespaceBundle(@PathParam("property") String property, @ApiResponse(code = 409, message = "Namespace is not empty") }) public Map> getPermissions(@PathParam("property") String property, @PathParam("cluster") String cluster, @PathParam("namespace") String namespace) { - validateAdminAccessForTenant(property); validateNamespaceName(property, cluster, namespace); + validateNamespaceOperation(NamespaceName.get(property, namespace), NamespaceOperation.PERMISSIONS); Policies policies = getNamespacePolicies(namespaceName); return policies.auth_policies.namespace_auth; @@ -294,8 +295,8 @@ public void revokePermissionOnSubscription(@PathParam("property") String propert @ApiResponse(code = 412, message = "Namespace is not global") }) public Set getNamespaceReplicationClusters(@PathParam("property") String property, @PathParam("cluster") String cluster, @PathParam("namespace") String namespace) { - validateAdminAccessForTenant(property); validateNamespaceName(property, cluster, namespace); + validateNamespaceOperation(NamespaceName.get(property, namespace), NamespaceOperation.GET_REPLICATION_CLUSTERS); return internalGetNamespaceReplicationClusters(); } @@ -320,8 +321,8 @@ public void setNamespaceReplicationClusters(@PathParam("property") String proper @ApiResponse(code = 404, message = "Property or cluster or namespace doesn't exist") }) public int getNamespaceMessageTTL(@PathParam("property") String property, @PathParam("cluster") String cluster, @PathParam("namespace") String namespace) { - validateAdminAccessForTenant(property); validateNamespaceName(property, cluster, namespace); + validateNamespaceOperation(NamespaceName.get(property, namespace), NamespaceOperation.GET_MESSAGE_TTL); Policies policies = getNamespacePolicies(namespaceName); return policies.message_ttl_in_seconds; @@ -478,9 +479,9 @@ public void removeAutoSubscriptionCreation(@Suspended final AsyncResponse asyncR @ApiResponse(code = 412, message = "Namespace is not setup to split in bundles") }) public BundlesData getBundlesData(@PathParam("property") String property, @PathParam("cluster") String cluster, @PathParam("namespace") String namespace) { - validateAdminAccessForTenant(property); validatePoliciesReadOnlyAccess(); validateNamespaceName(property, cluster, namespace); + validateNamespaceOperation(NamespaceName.get(property, namespace), NamespaceOperation.GET_BUNDLES); Policies policies = getNamespacePolicies(namespaceName); @@ -611,8 +612,8 @@ public DispatchRate getSubscriptionDispatchRate(@PathParam("property") String pr @ApiResponse(code = 404, message = "Namespace does not exist") }) public Map getBacklogQuotaMap(@PathParam("property") String property, @PathParam("cluster") String cluster, @PathParam("namespace") String namespace) { - validateAdminAccessForTenant(property); validateNamespaceName(property, cluster, namespace); + validateNamespaceOperation(NamespaceName.get(property, namespace), NamespaceOperation.GET_BACKLOG_QUOTAS); Policies policies = getNamespacePolicies(namespaceName); return policies.backlog_quota_map; diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/Namespaces.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/Namespaces.java index dd2c57e535492..467e5900ff094 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/Namespaces.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/Namespaces.java @@ -46,6 +46,7 @@ import org.apache.pulsar.broker.web.RestException; import org.apache.pulsar.common.api.proto.PulsarApi.CommandGetTopicsOfNamespace.Mode; import org.apache.pulsar.common.policies.data.AutoSubscriptionCreationOverride; +import org.apache.pulsar.common.naming.NamespaceName; import org.apache.pulsar.common.policies.data.AutoTopicCreationOverride; import org.apache.pulsar.common.policies.data.AuthAction; import org.apache.pulsar.common.policies.data.BacklogQuota; @@ -53,6 +54,7 @@ import org.apache.pulsar.common.policies.data.BookieAffinityGroupData; import org.apache.pulsar.common.policies.data.BundlesData; import org.apache.pulsar.common.policies.data.DispatchRate; +import org.apache.pulsar.common.policies.data.NamespaceOperation; import org.apache.pulsar.common.policies.data.OffloadPolicies; import org.apache.pulsar.common.policies.data.PersistencePolicies; import org.apache.pulsar.common.policies.data.Policies; @@ -64,6 +66,7 @@ import org.apache.pulsar.common.policies.data.SubscriptionAuthMode; import org.apache.pulsar.common.policies.data.DelayedDeliveryPolicies; +import org.apache.pulsar.common.policies.data.TenantOperation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -91,16 +94,14 @@ public void getTopics(@PathParam("tenant") String tenant, @PathParam("namespace") String namespace, @QueryParam("mode") @DefaultValue("PERSISTENT") Mode mode, @Suspended AsyncResponse asyncResponse) { - validateAdminAccessForTenant(tenant); validateNamespaceName(tenant, namespace); + validateNamespaceOperation(NamespaceName.get(tenant, namespace), NamespaceOperation.TOPICS); // Validate that namespace exists, throws 404 if it doesn't exist getNamespacePolicies(namespaceName); pulsar().getNamespaceService().getListOfTopics(namespaceName, mode) - .thenAccept(topics -> { - asyncResponse.resume(topics); - }) + .thenAccept(asyncResponse::resume) .exceptionally(ex -> { log.error("Failed to get topics list for namespace {}", namespaceName, ex); asyncResponse.resume(ex); @@ -114,8 +115,8 @@ public void getTopics(@PathParam("tenant") String tenant, @ApiResponses(value = { @ApiResponse(code = 403, message = "Don't have admin permission"), @ApiResponse(code = 404, message = "Tenant or cluster or namespace doesn't exist") }) public Policies getPolicies(@PathParam("tenant") String tenant, @PathParam("namespace") String namespace) { - validateAdminAccessForTenant(tenant); validateNamespaceName(tenant, namespace); + validateNamespaceOperation(NamespaceName.get(tenant, namespace), NamespaceOperation.POLICIES); return getNamespacePolicies(namespaceName); } @@ -129,7 +130,7 @@ public Policies getPolicies(@PathParam("tenant") String tenant, @PathParam("name public void createNamespace(@PathParam("tenant") String tenant, @PathParam("namespace") String namespace, Policies policies) { validateNamespaceName(tenant, namespace); - + validateTenantOperation(tenant, TenantOperation.CREATE_NAMESPACE); policies = getDefaultPolicesIfNull(policies); internalCreateNamespace(policies); } @@ -178,8 +179,8 @@ public void deleteNamespaceBundle(@PathParam("tenant") String tenant, @PathParam @ApiResponse(code = 409, message = "Namespace is not empty") }) public Map> getPermissions(@PathParam("tenant") String tenant, @PathParam("namespace") String namespace) { - validateAdminAccessForTenant(tenant); validateNamespaceName(tenant, namespace); + validateNamespaceOperation(NamespaceName.get(tenant, namespace), NamespaceOperation.PERMISSIONS); Policies policies = getNamespacePolicies(namespaceName); return policies.auth_policies.namespace_auth; @@ -244,8 +245,8 @@ public void revokePermissionOnSubscription(@PathParam("property") String propert @ApiResponse(code = 412, message = "Namespace is not global") }) public Set getNamespaceReplicationClusters(@PathParam("tenant") String tenant, @PathParam("namespace") String namespace) { - validateAdminAccessForTenant(tenant); validateNamespaceName(tenant, namespace); + validateNamespaceOperation(NamespaceName.get(tenant, namespace), NamespaceOperation.GET_REPLICATION_CLUSTERS); return internalGetNamespaceReplicationClusters(); } @@ -270,9 +271,8 @@ public void setNamespaceReplicationClusters(@PathParam("tenant") String tenant, @ApiResponse(code = 404, message = "Tenant or cluster or namespace doesn't exist") }) public int getNamespaceMessageTTL(@PathParam("tenant") String tenant, @PathParam("namespace") String namespace) { - - validateAdminAccessForTenant(tenant); validateNamespaceName(tenant, namespace); + validateNamespaceOperation(NamespaceName.get(tenant, namespace), NamespaceOperation.GET_MESSAGE_TTL); Policies policies = getNamespacePolicies(namespaceName); return policies.message_ttl_in_seconds; @@ -384,9 +384,9 @@ public void removeAutoSubscriptionCreation(@Suspended final AsyncResponse asyncR @ApiResponse(code = 412, message = "Namespace is not setup to split in bundles") }) public BundlesData getBundlesData(@PathParam("tenant") String tenant, @PathParam("namespace") String namespace) { - validateAdminAccessForTenant(tenant); validatePoliciesReadOnlyAccess(); validateNamespaceName(tenant, namespace); + validateNamespaceOperation(NamespaceName.get(tenant, namespace), NamespaceOperation.GET_BUNDLES); Policies policies = getNamespacePolicies(namespaceName); @@ -558,8 +558,8 @@ public DispatchRate getReplicatorDispatchRate(@PathParam("tenant") String tenant @ApiResponse(code = 404, message = "Namespace does not exist") }) public Map getBacklogQuotaMap(@PathParam("tenant") String tenant, @PathParam("namespace") String namespace) { - validateAdminAccessForTenant(tenant); validateNamespaceName(tenant, namespace); + validateNamespaceOperation(NamespaceName.get(tenant, namespace), NamespaceOperation.GET_BACKLOG_QUOTAS); Policies policies = getNamespacePolicies(namespaceName); return policies.backlog_quota_map; diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/NamespacesTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/NamespacesTest.java index f98758cc60222..2561ea74ddf4a 100644 --- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/NamespacesTest.java +++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/NamespacesTest.java @@ -81,6 +81,7 @@ import org.apache.pulsar.common.policies.data.AuthAction; import org.apache.pulsar.common.policies.data.BundlesData; import org.apache.pulsar.common.policies.data.ClusterData; +import org.apache.pulsar.common.policies.data.NamespaceOperation; import org.apache.pulsar.common.policies.data.OffloadPolicies; import org.apache.pulsar.common.policies.data.PersistencePolicies; import org.apache.pulsar.common.policies.data.Policies; @@ -154,9 +155,6 @@ public void setup() throws Exception { doReturn(null).when(namespaces).originalPrincipal(); doReturn(null).when(namespaces).clientAuthData(); doReturn(Sets.newTreeSet(Lists.newArrayList("use", "usw", "usc", "global"))).when(namespaces).clusters(); - doNothing().when(namespaces).validateAdminAccessForTenant(this.testTenant); - doNothing().when(namespaces).validateAdminAccessForTenant("non-existing-tenant"); - doNothing().when(namespaces).validateAdminAccessForTenant("new-property"); admin.clusters().createCluster("use", new ClusterData("http://broker-use.com:8080")); admin.clusters().createCluster("usw", new ClusterData("http://broker-usw.com:8080")); @@ -171,7 +169,15 @@ public void setup() throws Exception { new BundlesData()); doThrow(new RestException(Status.UNAUTHORIZED, "unauthorized")).when(namespaces) - .validateAdminAccessForTenant(this.testOtherTenant); + .validateTenantOperation(this.testOtherTenant, null); + + doThrow(new RestException(Status.UNAUTHORIZED, "unauthorized")).when(namespaces) + .validateNamespaceOperation( + NamespaceName.get("other-tenant/use/test-namespace-1"), NamespaceOperation.SET_PERSISTENCE); + + doThrow(new RestException(Status.UNAUTHORIZED, "unauthorized")).when(namespaces) + .validateNamespaceOperation( + NamespaceName.get("other-tenant/use/test-namespace-1"), NamespaceOperation.SET_RETENTION); nsSvc = pulsar.getNamespaceService(); } @@ -878,7 +884,6 @@ private void createTestNamespaces(List nsnames, BundlesData bundl @Test public void testValidateAdminAccessOnTenant() throws Exception { - try { final String property = "prop"; pulsar.getConfiguration().setAuthenticationEnabled(true); @@ -888,7 +893,8 @@ public void testValidateAdminAccessOnTenant() throws Exception { new TenantInfo(Sets.newHashSet(namespaces.clientAppId()), Sets.newHashSet("use"))); ZkUtils.createFullPathOptimistic(pulsar.getConfigurationCache().getZooKeeper(), path, data.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); - namespaces.validateAdminAccessForTenant(property); + + namespaces.validateTenantOperation(property, null); } finally { pulsar.getConfiguration().setAuthenticationEnabled(false); pulsar.getConfiguration().setAuthorizationEnabled(false); From c476187027c77df964f040aef52ffce8d8c89db3 Mon Sep 17 00:00:00 2001 From: Alexandre DUVAL Date: Fri, 27 Mar 2020 16:16:32 +0100 Subject: [PATCH 06/19] authorize when Authorization disabled --- .../authorization/AuthorizationService.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationService.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationService.java index 57c2c188e7519..dc3d14069b9c5 100644 --- a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationService.java +++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationService.java @@ -348,6 +348,10 @@ public CompletableFuture allowFunctionOpsAsync(NamespaceName namespaceN public CompletableFuture allowClusterOperationAsync(String clusterName, ClusterOperation operation, String originalRole, String role, AuthenticationDataSource authData) { + if (!this.conf.isAuthorizationEnabled()) { + return CompletableFuture.completedFuture(true); + } + if (provider != null) { return provider.allowClusterOperationAsync(clusterName, originalRole, role, operation, authData); } @@ -359,6 +363,10 @@ public CompletableFuture allowClusterOperationAsync(String clusterName, public Boolean allowClusterOperation(String clusterName, ClusterOperation operation, String orignalRole, String role, AuthenticationDataSource authData) { + if (!this.conf.isAuthorizationEnabled()) { + return true; + } + if (provider != null) { return provider.allowClusterOperation(clusterName, orignalRole, role, operation, authData); } @@ -382,6 +390,10 @@ public Boolean allowClusterOperation(String clusterName, ClusterOperation operat public CompletableFuture allowTenantOperationAsync(String tenantName, TenantOperation operation, String originalRole, String role, AuthenticationDataSource authData) { + if (!this.conf.isAuthorizationEnabled()) { + return CompletableFuture.completedFuture(true); + } + if (provider != null) { return provider.allowTenantOperationAsync(tenantName, originalRole, role, operation, authData); } @@ -392,6 +404,10 @@ public CompletableFuture allowTenantOperationAsync(String tenantName, T public Boolean allowTenantOperation(String tenantName, TenantOperation operation, String orignalRole, String role, AuthenticationDataSource authData) { + if (!this.conf.isAuthorizationEnabled()) { + return true; + } + if (provider != null) { return provider.allowTenantOperation(tenantName, orignalRole, role, operation, authData); } @@ -416,6 +432,10 @@ public CompletableFuture allowNamespaceOperationAsync(NamespaceName nam NamespaceOperation operation, String originalRole, String role, AuthenticationDataSource authData) { + if (!this.conf.isAuthorizationEnabled()) { + return CompletableFuture.completedFuture(true); + } + if (provider != null) { return provider.allowNamespaceOperationAsync(namespaceName, originalRole, role, operation, authData); } @@ -426,6 +446,10 @@ public CompletableFuture allowNamespaceOperationAsync(NamespaceName nam public Boolean allowNamespaceOperation(NamespaceName namespaceName, NamespaceOperation operation, String orignalRole, String role, AuthenticationDataSource authData) { + if (!this.conf.isAuthorizationEnabled()) { + return true; + } + if (provider != null) { return provider.allowNamespaceOperation(namespaceName, orignalRole, role, operation, authData); } @@ -448,6 +472,10 @@ public Boolean allowNamespaceOperation(NamespaceName namespaceName, NamespaceOpe public CompletableFuture allowTopicOperationAsync(TopicName topicName, TopicOperation operation, String originalRole, String role, AuthenticationDataSource authData) { + if (!this.conf.isAuthorizationEnabled()) { + return CompletableFuture.completedFuture(true); + } + if (provider != null) { return provider.allowTopicOperationAsync(topicName, originalRole, role, operation, authData); } @@ -459,6 +487,10 @@ public CompletableFuture allowTopicOperationAsync(TopicName topicName, public Boolean allowTopicOperation(TopicName topicName, TopicOperation operation, String orignalRole, String role, AuthenticationDataSource authData) { + if (!this.conf.isAuthorizationEnabled()) { + return true; + } + if (provider != null) { return provider.allowTopicOperation(topicName, orignalRole, role, operation, authData); } From 3ee9a585f0957122af2b62f62c4108182cef8fe8 Mon Sep 17 00:00:00 2001 From: Alexandre DUVAL Date: Mon, 6 Apr 2020 13:50:46 +0200 Subject: [PATCH 07/19] drop unused imports --- .../pulsar/broker/authorization/AuthorizationService.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationService.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationService.java index dc3d14069b9c5..eb97583629527 100644 --- a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationService.java +++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationService.java @@ -33,19 +33,11 @@ import org.apache.pulsar.common.policies.data.TenantOperation; import org.apache.pulsar.common.policies.data.TopicOperation; import org.apache.pulsar.common.util.FutureUtil; -import org.apache.pulsar.common.util.RestException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.ws.rs.core.Response; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; -import java.util.stream.Collectors; -import java.util.stream.Stream; import static java.util.concurrent.TimeUnit.SECONDS; From 1d4d639f3c40cb72b53e42c911dce7053d2720d1 Mon Sep 17 00:00:00 2001 From: Alexandre DUVAL Date: Tue, 7 Apr 2020 13:08:40 +0200 Subject: [PATCH 08/19] remove cluster operation --- .../authorization/AuthorizationProvider.java | 31 ----------- .../authorization/AuthorizationService.java | 45 +--------------- .../PulsarAuthorizationProvider.java | 8 --- .../pulsar/broker/web/PulsarWebResource.java | 17 ------ .../policies/data/ClusterOperation.java | 52 ------------------- 5 files changed, 1 insertion(+), 152 deletions(-) delete mode 100644 pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/ClusterOperation.java diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationProvider.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationProvider.java index 636237b9d5aca..3f4a4bf498afb 100644 --- a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationProvider.java +++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationProvider.java @@ -30,7 +30,6 @@ import org.apache.pulsar.common.naming.TopicName; import org.apache.pulsar.common.naming.NamespaceName; import org.apache.pulsar.common.policies.data.AuthAction; -import org.apache.pulsar.common.policies.data.ClusterOperation; import org.apache.pulsar.common.policies.data.TenantInfo; import org.apache.pulsar.common.policies.data.NamespaceOperation; import org.apache.pulsar.common.policies.data.TenantOperation; @@ -193,36 +192,6 @@ CompletableFuture revokeSubscriptionPermissionAsync(NamespaceName namespac CompletableFuture grantPermissionAsync(TopicName topicName, Set actions, String role, String authDataJson); - /** - * Grant authorization-action permission on a cluster to the given client - * @param clusterName - * @param originalRole - * @param role - * @param operation - * @param authData - * @return CompletableFuture - * @completesWith
- * IllegalArgumentException when topic not found
- * IllegalStateException when failed to grant permission - */ - default CompletableFuture allowClusterOperationAsync(String clusterName, String originalRole, String role, - ClusterOperation operation, - AuthenticationDataSource authData) { - return FutureUtil.failedFuture( - new IllegalStateException("ClusterOperation is not supported by the Authorization provider you are using.")); - } - - default Boolean allowClusterOperation(String clusterName, String originalRole, String role, ClusterOperation operation, - AuthenticationDataSource authData) { - try { - return allowClusterOperationAsync(clusterName, originalRole, role, operation, authData).get(); - } catch (InterruptedException e) { - throw new RestException(e); - } catch (ExecutionException e) { - throw new RestException(e.getCause()); - } - } - /** * Grant authorization-action permission on a tenant to the given client * @param tenantName diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationService.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationService.java index eb97583629527..27df5f0150305 100644 --- a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationService.java +++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationService.java @@ -27,7 +27,6 @@ import org.apache.pulsar.common.naming.NamespaceName; import org.apache.pulsar.common.naming.TopicName; import org.apache.pulsar.common.policies.data.AuthAction; -import org.apache.pulsar.common.policies.data.ClusterOperation; import org.apache.pulsar.common.policies.data.TenantInfo; import org.apache.pulsar.common.policies.data.NamespaceOperation; import org.apache.pulsar.common.policies.data.TenantOperation; @@ -324,48 +323,6 @@ public CompletableFuture allowFunctionOpsAsync(NamespaceName namespaceN return provider.allowFunctionOpsAsync(namespaceName, role, authenticationData); } - /** - * Grant authorization-action permission on a cluster to the given client - * - * @param clusterName - * @param operation - * @param originalRole - * @param role - * @param authData - * additional authdata in json for targeted authorization provider - * @return IllegalArgumentException when tenant not found - * @throws IllegalStateException - * when failed to grant permission - */ - public CompletableFuture allowClusterOperationAsync(String clusterName, ClusterOperation operation, - String originalRole, String role, - AuthenticationDataSource authData) { - if (!this.conf.isAuthorizationEnabled()) { - return CompletableFuture.completedFuture(true); - } - - if (provider != null) { - return provider.allowClusterOperationAsync(clusterName, originalRole, role, operation, authData); - } - - return FutureUtil.failedFuture(new IllegalStateException("No authorization provider configured for " + - "allowClusterOperationAsync")); - } - - public Boolean allowClusterOperation(String clusterName, ClusterOperation operation, - String orignalRole, String role, - AuthenticationDataSource authData) { - if (!this.conf.isAuthorizationEnabled()) { - return true; - } - - if (provider != null) { - return provider.allowClusterOperation(clusterName, orignalRole, role, operation, authData); - } - - throw new IllegalStateException("No authorization provider configured for allowClusterOperation"); - } - /** * Grant authorization-action permission on a tenant to the given client * @@ -404,7 +361,7 @@ public Boolean allowTenantOperation(String tenantName, TenantOperation operation return provider.allowTenantOperation(tenantName, orignalRole, role, operation, authData); } - throw new IllegalStateException("No authorization provider configured for allowClusterOperation"); + throw new IllegalStateException("No authorization provider configured for allowTenantOperation"); } /** diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/PulsarAuthorizationProvider.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/PulsarAuthorizationProvider.java index 60c98fa27b4d9..4352cc439d95f 100644 --- a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/PulsarAuthorizationProvider.java +++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/PulsarAuthorizationProvider.java @@ -37,7 +37,6 @@ import org.apache.pulsar.common.naming.TopicName; import org.apache.pulsar.common.naming.NamespaceName; import org.apache.pulsar.common.policies.data.AuthAction; -import org.apache.pulsar.common.policies.data.ClusterOperation; import org.apache.pulsar.common.policies.data.NamespaceOperation; import org.apache.pulsar.common.policies.data.Policies; import static org.apache.pulsar.common.util.ObjectMapperFactory.getThreadLocal; @@ -510,13 +509,6 @@ private void validatePoliciesReadOnlyAccess() { } } - @Override - public CompletableFuture allowClusterOperationAsync(String clusterName, String originalRole, String role, - ClusterOperation operation, - AuthenticationDataSource authData) { - return isSuperUser(role, conf); - } - @Override public CompletableFuture allowTenantOperationAsync(String tenantName, String originalRole, String role, TenantOperation operation, diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java index 430bff9464f66..fb1085b6fc0fc 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java @@ -61,7 +61,6 @@ import org.apache.pulsar.common.naming.TopicName; import org.apache.pulsar.common.policies.data.BundlesData; import org.apache.pulsar.common.policies.data.ClusterData; -import org.apache.pulsar.common.policies.data.ClusterOperation; import org.apache.pulsar.common.policies.data.NamespaceOperation; import org.apache.pulsar.common.policies.data.Policies; import org.apache.pulsar.common.policies.data.TenantInfo; @@ -775,22 +774,6 @@ protected static boolean isLeaderBroker(PulsarService pulsar) { // Non-Usual HTTP error codes protected static final int NOT_IMPLEMENTED = 501; - public void validateClusterOperation(String cluster, ClusterOperation operation) { - if (pulsar().getConfiguration().isAuthenticationEnabled() && pulsar().getBrokerService().isAuthorizationEnabled()) { - if (!isClientAuthenticated(clientAppId())) { - throw new RestException(Status.UNAUTHORIZED, "Need to authenticate to perform the request"); - } - - Boolean isAuthorized = pulsar().getBrokerService().getAuthorizationService() - .allowClusterOperation(cluster, operation, originalPrincipal(), clientAppId(), clientAuthData()); - - if (!isAuthorized) { - throw new RestException(Status.UNAUTHORIZED, String.format("Unauthorized to validateClusterOperation for" + - " operation [%s] on cluster [%s]", operation.toString(), cluster)); - } - } - } - public void validateTenantOperation(String tenant, TenantOperation operation) { if (pulsar().getConfiguration().isAuthenticationEnabled() && pulsar().getBrokerService().isAuthorizationEnabled()) { if (!isClientAuthenticated(clientAppId())) { diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/ClusterOperation.java b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/ClusterOperation.java deleted file mode 100644 index 8fa88dd9939d7..0000000000000 --- a/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/ClusterOperation.java +++ /dev/null @@ -1,52 +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.pulsar.common.policies.data; - -/** - * Cluster authorization operations. - */ -public enum ClusterOperation { - CREATE, - CREATE_FAILURE_DOMAIN, - DELETE, - DELETE_FAILURE_DOMAIN, - GET, - GET_BOOKIE_AFFINITY_GROUP, - GET_FAILURE_DOMAIN, - GET_NAMESPACE_ISOLATION_POLICIES, - GET_PEER_CLUSTERS, - GET_PUBLISH_RATE, - LIST, - LIST_FAILURE_DOMAIN, - SET_BOOKIE_AFFINITY_GROUP, - SET_DELAYED_DELIVERY, - SET_FAILURE_DOMAIN, - SET_NAMESPACE_ISOLATION_POLICIES, - SET_PUBLISH_RATE, - SET_REPLICATOR_DISPATCH_RATE, - SET_SUBSCRIBE_RATE, - SET_SUBSCRIPTION_DISPATCH_RATE, - SET_TOPIC_DISPATCH_RATE, - SPLIT_NAMESPACE_BUNDLE, - UNLOAD_NAMESPACE, - UPDATE, - UPDATE_FAILURE_DOMAIN, - UPDATE_PEER_CLUSTERS, -} From eaa70ff862d7f00142da1ace35638701d822fda3 Mon Sep 17 00:00:00 2001 From: Alexandre DUVAL Date: Tue, 7 Apr 2020 13:08:49 +0200 Subject: [PATCH 09/19] use superUser when required --- .../broker/admin/impl/NamespacesBase.java | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java index 89c9a2b2643ce..5a0397c018f6d 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java @@ -77,7 +77,6 @@ import org.apache.pulsar.common.policies.data.BookieAffinityGroupData; import org.apache.pulsar.common.policies.data.BundlesData; import org.apache.pulsar.common.policies.data.ClusterData; -import org.apache.pulsar.common.policies.data.ClusterOperation; import org.apache.pulsar.common.policies.data.DispatchRate; import org.apache.pulsar.common.policies.data.DelayedDeliveryPolicies; import org.apache.pulsar.common.policies.data.LocalPolicies; @@ -821,10 +820,8 @@ protected void internalModifyDeduplication(boolean enableDeduplication) { @SuppressWarnings("deprecation") protected void internalUnloadNamespace(AsyncResponse asyncResponse) { - validateClusterOperation(namespaceName.getCluster(), ClusterOperation.UNLOAD_NAMESPACE); - log.info("[{}] Unloading namespace {}", clientAppId(), namespaceName); - validateSuperUserAccess(); + log.info("[{}] Unloading namespace {}", clientAppId(), namespaceName); if (namespaceName.isGlobal()) { // check cluster ownership for a given global namespace: redirect if peer-cluster owns it @@ -869,7 +866,7 @@ protected void internalUnloadNamespace(AsyncResponse asyncResponse) { protected void internalSetBookieAffinityGroup(BookieAffinityGroupData bookieAffinityGroup) { - validateClusterOperation(namespaceName.getCluster(), ClusterOperation.SET_BOOKIE_AFFINITY_GROUP); + validateSuperUserAccess(); log.info("[{}] Setting bookie-affinity-group {} for namespace {}", clientAppId(), bookieAffinityGroup, this.namespaceName); @@ -925,7 +922,7 @@ protected void internalDeleteBookieAffinityGroup() { } protected BookieAffinityGroupData internalGetBookieAffinityGroup() { - validateClusterOperation(namespaceName.getCluster(), ClusterOperation.GET_BOOKIE_AFFINITY_GROUP); + validateSuperUserAccess(); if (namespaceName.isGlobal()) { // check cluster ownership for a given global namespace: redirect if peer-cluster owns it @@ -959,7 +956,7 @@ protected BookieAffinityGroupData internalGetBookieAffinityGroup() { @SuppressWarnings("deprecation") public void internalUnloadNamespaceBundle(String bundleRange, boolean authoritative) { - validateClusterOperation(namespaceName.getCluster(), ClusterOperation.UNLOAD_NAMESPACE); + validateSuperUserAccess(); log.info("[{}] Unloading namespace bundle {}/{}", clientAppId(), namespaceName, bundleRange); Policies policies = getNamespacePolicies(namespaceName); @@ -1007,7 +1004,7 @@ public void internalUnloadNamespaceBundle(String bundleRange, boolean authoritat @SuppressWarnings("deprecation") protected void internalSplitNamespaceBundle(String bundleRange, boolean authoritative, boolean unload, String splitAlgorithmName) { - validateClusterOperation(namespaceName.getCluster(), ClusterOperation.SPLIT_NAMESPACE_BUNDLE); + validateSuperUserAccess(); log.info("[{}] Split namespace bundle {}/{}", clientAppId(), namespaceName, bundleRange); Policies policies = getNamespacePolicies(namespaceName); @@ -1060,7 +1057,7 @@ private NamespaceBundleSplitAlgorithm getNamespaceBundleSplitAlgorithmByName(Str } protected void internalSetPublishRate(PublishRate maxPublishMessageRate) { - validateClusterOperation(namespaceName.getCluster(), ClusterOperation.SET_PUBLISH_RATE); + validateSuperUserAccess(); log.info("[{}] Set namespace publish-rate {}/{}", clientAppId(), namespaceName, maxPublishMessageRate); Entry policiesNode = null; @@ -1111,7 +1108,7 @@ protected PublishRate internalGetPublishRate() { @SuppressWarnings("deprecation") protected void internalSetTopicDispatchRate(DispatchRate dispatchRate) { - validateClusterOperation(namespaceName.getCluster(), ClusterOperation.SET_TOPIC_DISPATCH_RATE); + validateSuperUserAccess(); log.info("[{}] Set namespace dispatch-rate {}/{}", clientAppId(), namespaceName, dispatchRate); Entry policiesNode = null; @@ -1166,7 +1163,7 @@ protected DispatchRate internalGetTopicDispatchRate() { } protected void internalSetSubscriptionDispatchRate(DispatchRate dispatchRate) { - validateClusterOperation(namespaceName.getCluster(), ClusterOperation.SET_SUBSCRIPTION_DISPATCH_RATE); + validateSuperUserAccess(); log.info("[{}] Set namespace subscription dispatch-rate {}/{}", clientAppId(), namespaceName, dispatchRate); Entry policiesNode = null; @@ -1216,7 +1213,7 @@ protected DispatchRate internalGetSubscriptionDispatchRate() { } protected void internalSetSubscribeRate(SubscribeRate subscribeRate) { - validateClusterOperation(namespaceName.getCluster(), ClusterOperation.SET_SUBSCRIBE_RATE); + validateSuperUserAccess(); log.info("[{}] Set namespace subscribe-rate {}/{}", clientAppId(), namespaceName, subscribeRate); @@ -1266,7 +1263,7 @@ protected SubscribeRate internalGetSubscribeRate() { } protected void internalSetReplicatorDispatchRate(DispatchRate dispatchRate) { - validateClusterOperation(namespaceName.getCluster(), ClusterOperation.SET_REPLICATOR_DISPATCH_RATE); + validateSuperUserAccess(); log.info("[{}] Set namespace replicator dispatch-rate {}/{}", clientAppId(), namespaceName, dispatchRate); Entry policiesNode = null; @@ -1750,8 +1747,7 @@ protected DelayedDeliveryPolicies internalGetDelayedDelivery() { } protected void internalSetDelayedDelivery(DelayedDeliveryPolicies delayedDeliveryPolicies) { - validateClusterOperation(namespaceName.getCluster(), ClusterOperation.SET_DELAYED_DELIVERY); - validateAdminAccessForTenant(namespaceName.getTenant()); + validateSuperUserAccess(); validatePoliciesReadOnlyAccess(); try { From 74f8ff30ce4b8c54549de8b2679aee8694ef08b8 Mon Sep 17 00:00:00 2001 From: Alexandre DUVAL Date: Tue, 7 Apr 2020 14:58:29 +0200 Subject: [PATCH 10/19] fix test --- .../AuthorizationProducerConsumerTest.java | 65 +++++++++++++++++-- .../service/web/DiscoveryServiceWebTest.java | 8 ++- 2 files changed, 63 insertions(+), 10 deletions(-) diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/client/api/AuthorizationProducerConsumerTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/client/api/AuthorizationProducerConsumerTest.java index 7b324c0ba2b79..ddeed965eb523 100644 --- a/pulsar-broker/src/test/java/org/apache/pulsar/client/api/AuthorizationProducerConsumerTest.java +++ b/pulsar-broker/src/test/java/org/apache/pulsar/client/api/AuthorizationProducerConsumerTest.java @@ -29,6 +29,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import javax.naming.AuthenticationException; @@ -49,7 +50,11 @@ import org.apache.pulsar.common.naming.TopicName; import org.apache.pulsar.common.policies.data.AuthAction; import org.apache.pulsar.common.policies.data.ClusterData; +import org.apache.pulsar.common.policies.data.NamespaceOperation; import org.apache.pulsar.common.policies.data.TenantInfo; +import org.apache.pulsar.common.policies.data.TenantOperation; +import org.apache.pulsar.common.policies.data.TopicOperation; +import org.apache.pulsar.common.util.RestException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.Assert; @@ -487,6 +492,41 @@ public CompletableFuture revokeSubscriptionPermissionAsync(NamespaceName n String subscriptionName, String role, String authDataJson) { return CompletableFuture.completedFuture(null); } + + @Override + public CompletableFuture isTenantAdmin(String tenant, String role, TenantInfo tenantInfo, AuthenticationDataSource authenticationData) { + return CompletableFuture.completedFuture(true); + } + + @Override + public CompletableFuture allowTenantOperationAsync(String tenantName, String originalRole, String role, TenantOperation operation, AuthenticationDataSource authData) { + return CompletableFuture.completedFuture(true); + } + + @Override + public Boolean allowTenantOperation(String tenantName, String originalRole, String role, TenantOperation operation, AuthenticationDataSource authData) { + return true; + } + + @Override + public CompletableFuture allowNamespaceOperationAsync(NamespaceName namespaceName, String originalRole, String role, NamespaceOperation operation, AuthenticationDataSource authData) { + return CompletableFuture.completedFuture(true); + } + + @Override + public Boolean allowNamespaceOperation(NamespaceName namespaceName, String originalRole, String role, NamespaceOperation operation, AuthenticationDataSource authData) { + return null; + } + + @Override + public CompletableFuture allowTopicOperationAsync(TopicName topic, String originalRole, String role, TopicOperation operation, AuthenticationDataSource authData) { + return CompletableFuture.completedFuture(true); + } + + @Override + public Boolean allowTopicOperation(TopicName topicName, String originalRole, String role, TopicOperation operation, AuthenticationDataSource authData) { + return true; + } } /** @@ -515,21 +555,32 @@ public CompletableFuture canLookupAsync(TopicName topicName, String rol } public static class TestAuthorizationProviderWithSubscriptionPrefix extends TestAuthorizationProvider { + @Override + public Boolean allowTopicOperation(TopicName topicName, String originalRole, String role, TopicOperation operation, AuthenticationDataSource authData) { + try { + return allowTopicOperationAsync(topicName, originalRole, role, operation, authData).get(); + } catch (InterruptedException e) { + throw new RestException(e); + } catch (ExecutionException e) { + throw new RestException(e.getCause()); + } + } @Override - public CompletableFuture canConsumeAsync(TopicName topicName, String role, - AuthenticationDataSource authenticationData, String subscription) { + public CompletableFuture allowTopicOperationAsync(TopicName topic, String originalRole, String role, TopicOperation operation, AuthenticationDataSource authData) { CompletableFuture future = new CompletableFuture<>(); - if (isNotBlank(subscription)) { - if (!subscription.startsWith(role)) { - future.completeExceptionally(new PulsarServerException( - "The subscription name needs to be prefixed by the authentication role")); + if (authData.hasSubscription()) { + String subscription = authData.getSubscription(); + if (isNotBlank(subscription)) { + if (!subscription.startsWith(role)) { + future.completeExceptionally(new PulsarServerException( + "The subscription name needs to be prefixed by the authentication role")); + } } } future.complete(clientRole.equals(role)); return future; } - } public static class TestAuthorizationProviderWithGrantPermission extends TestAuthorizationProvider { diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/discovery/service/web/DiscoveryServiceWebTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/discovery/service/web/DiscoveryServiceWebTest.java index cc3cb3ba3ed66..ec9d4e4f0bd94 100644 --- a/pulsar-broker/src/test/java/org/apache/pulsar/discovery/service/web/DiscoveryServiceWebTest.java +++ b/pulsar-broker/src/test/java/org/apache/pulsar/discovery/service/web/DiscoveryServiceWebTest.java @@ -61,6 +61,8 @@ public class DiscoveryServiceWebTest extends ProducerConsumerBase { protected void setup() throws Exception { super.internalSetup(); super.producerBaseSetup(); + super.conf.setAuthorizationEnabled(true); + super.conf.setAuthenticationEnabled(true); } @AfterMethod @@ -99,9 +101,9 @@ public void testRedirectUrlWithServerStarted() throws Exception { **/ assertEquals(hitBrokerService(HttpMethod.POST, postRequestUrl, Lists.newArrayList("use")), - "Tenant does not exist"); - assertEquals(hitBrokerService(HttpMethod.PUT, putRequestUrl, new BundlesData(1)), "Tenant does not exist"); - assertEquals(hitBrokerService(HttpMethod.GET, getRequestUrl, null), "Tenant does not exist"); + "Need to authenticate to perform the request"); + assertEquals(hitBrokerService(HttpMethod.PUT, putRequestUrl, new BundlesData(1)), "Need to authenticate to perform the request"); + assertEquals(hitBrokerService(HttpMethod.GET, getRequestUrl, null), "Need to authenticate to perform the request"); server.stop(); From 4771997b39353d381b3128dad71b99ec0edb4f6f Mon Sep 17 00:00:00 2001 From: Alexandre DUVAL Date: Thu, 9 Apr 2020 10:26:10 +0200 Subject: [PATCH 11/19] drop brokeroperation --- .../common/policies/data/BrokerOperation.java | 35 ------------------- 1 file changed, 35 deletions(-) delete mode 100644 pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/BrokerOperation.java diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/BrokerOperation.java b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/BrokerOperation.java deleted file mode 100644 index 362ce8c25aa86..0000000000000 --- a/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/BrokerOperation.java +++ /dev/null @@ -1,35 +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.pulsar.common.policies.data; - -/** - * Broker authorization operations. - */ -public enum BrokerOperation { - DELETE_DYNAMIC_CONFIG, - GET_ALL_DYNAMIC_CONFIG, - GET_INTERNAL_CONFIG, - GET_RUNTIME_CONFIG, - HEALTHCHECK, - LIST, - LIST_DYNAMIC_CONFIG, - NAMESPACES, - UPDATE_DYNAMIC_CONFIG, -} From d7cdf0f254aee81707ef34440f56e4cddbd59706 Mon Sep 17 00:00:00 2001 From: Alexandre DUVAL Date: Thu, 9 Apr 2020 10:27:47 +0200 Subject: [PATCH 12/19] document new methods to use to override authzprovider --- .../authorization/AuthorizationProvider.java | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationProvider.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationProvider.java index 3f4a4bf498afb..0702f81134be2 100644 --- a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationProvider.java +++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationProvider.java @@ -195,11 +195,11 @@ CompletableFuture grantPermissionAsync(TopicName topicName, Set */ default CompletableFuture allowTenantOperationAsync(String tenantName, String originalRole, String role, TenantOperation operation, @@ -224,14 +224,11 @@ default Boolean allowTenantOperation(String tenantName, String originalRole, Str /** * Grant authorization-action permission on a namespace to the given client * @param namespaceName - * @param originalRole - * @param role + * @param originalRole role not overriden by proxy role if request do pass through proxy + * @param role originalRole | proxyRole if the request didn't pass through proxy * @param operation * @param authData - * @return CompletableFuture - * @completesWith
- * IllegalArgumentException when namespace not found
- * IllegalStateException when failed to grant permission + * @return CompletableFuture */ default CompletableFuture allowNamespaceOperationAsync(NamespaceName namespaceName, String originalRole, String role, NamespaceOperation operation, @@ -254,14 +251,11 @@ default Boolean allowNamespaceOperation(NamespaceName namespaceName, String orig /** * Grant authorization-action permission on a topic to the given client * @param topic - * @param originalRole - * @param role + * @param originalRole role not overriden by proxy role if request do pass through proxy + * @param role originalRole | proxyRole if the request didn't pass through proxy * @param operation * @param authData - * @return CompletableFuture - * @completesWith
- * IllegalArgumentException when topic not found
- * IllegalStateException when failed to grant permission + * @return CompletableFuture */ default CompletableFuture allowTopicOperationAsync(TopicName topic, String originalRole, String role, TopicOperation operation, From 0f448c6b2825479df5a38690efb2ad1edf02f74d Mon Sep 17 00:00:00 2001 From: Alexandre DUVAL Date: Thu, 9 Apr 2020 10:51:21 +0200 Subject: [PATCH 13/19] clarify operations names --- .../java/org/apache/pulsar/broker/admin/v1/Namespaces.java | 6 +++--- .../java/org/apache/pulsar/broker/admin/v2/Namespaces.java | 6 +++--- .../pulsar/common/policies/data/NamespaceOperation.java | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v1/Namespaces.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v1/Namespaces.java index 71f4b45828b2d..162d27870fa05 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v1/Namespaces.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v1/Namespaces.java @@ -125,7 +125,7 @@ public void getTopics(@PathParam("property") String property, @QueryParam("mode") @DefaultValue("PERSISTENT") Mode mode, @Suspended AsyncResponse asyncResponse) { validateNamespaceName(property, cluster, namespace); - validateNamespaceOperation(NamespaceName.get(property, namespace), NamespaceOperation.TOPICS); + validateNamespaceOperation(NamespaceName.get(property, namespace), NamespaceOperation.LIST_TOPICS); // Validate that namespace exists, throws 404 if it doesn't exist getNamespacePolicies(namespaceName); @@ -147,7 +147,7 @@ public void getTopics(@PathParam("property") String property, public Policies getPolicies(@PathParam("property") String property, @PathParam("cluster") String cluster, @PathParam("namespace") String namespace) { validateNamespaceName(property, cluster, namespace); - validateNamespaceOperation(NamespaceName.get(property, namespace), NamespaceOperation.POLICIES); + validateNamespaceOperation(NamespaceName.get(property, namespace), NamespaceOperation.GET_POLICIES); return getNamespacePolicies(namespaceName); } @@ -230,7 +230,7 @@ public void deleteNamespaceBundle(@PathParam("property") String property, public Map> getPermissions(@PathParam("property") String property, @PathParam("cluster") String cluster, @PathParam("namespace") String namespace) { validateNamespaceName(property, cluster, namespace); - validateNamespaceOperation(NamespaceName.get(property, namespace), NamespaceOperation.PERMISSIONS); + validateNamespaceOperation(NamespaceName.get(property, namespace), NamespaceOperation.GET_PERMISSIONS); Policies policies = getNamespacePolicies(namespaceName); return policies.auth_policies.namespace_auth; diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/Namespaces.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/Namespaces.java index 467e5900ff094..6854d5974eb2f 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/Namespaces.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/Namespaces.java @@ -95,7 +95,7 @@ public void getTopics(@PathParam("tenant") String tenant, @QueryParam("mode") @DefaultValue("PERSISTENT") Mode mode, @Suspended AsyncResponse asyncResponse) { validateNamespaceName(tenant, namespace); - validateNamespaceOperation(NamespaceName.get(tenant, namespace), NamespaceOperation.TOPICS); + validateNamespaceOperation(NamespaceName.get(tenant, namespace), NamespaceOperation.LIST_TOPICS); // Validate that namespace exists, throws 404 if it doesn't exist getNamespacePolicies(namespaceName); @@ -116,7 +116,7 @@ public void getTopics(@PathParam("tenant") String tenant, @ApiResponse(code = 404, message = "Tenant or cluster or namespace doesn't exist") }) public Policies getPolicies(@PathParam("tenant") String tenant, @PathParam("namespace") String namespace) { validateNamespaceName(tenant, namespace); - validateNamespaceOperation(NamespaceName.get(tenant, namespace), NamespaceOperation.POLICIES); + validateNamespaceOperation(NamespaceName.get(tenant, namespace), NamespaceOperation.GET_POLICIES); return getNamespacePolicies(namespaceName); } @@ -180,7 +180,7 @@ public void deleteNamespaceBundle(@PathParam("tenant") String tenant, @PathParam public Map> getPermissions(@PathParam("tenant") String tenant, @PathParam("namespace") String namespace) { validateNamespaceName(tenant, namespace); - validateNamespaceOperation(NamespaceName.get(tenant, namespace), NamespaceOperation.PERMISSIONS); + validateNamespaceOperation(NamespaceName.get(tenant, namespace), NamespaceOperation.GET_PERMISSIONS); Policies policies = getNamespacePolicies(namespaceName); return policies.auth_policies.namespace_auth; diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/NamespaceOperation.java b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/NamespaceOperation.java index fa5c72d461a92..10106281cae24 100644 --- a/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/NamespaceOperation.java +++ b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/NamespaceOperation.java @@ -40,7 +40,9 @@ public enum NamespaceOperation { GET_OFFLOAD_DELETION_LAG, GET_OFFLOAD_POLICIES, GET_OFFLOAD_THRESHOLD, + GET_PERMISSIONS, GET_PERSISTENCE, + GET_POLICIES, GET_PUBLISH_RATE, GET_REPLICATION_CLUSTERS, GET_REPLICATOR_DISPATCH_RATE, @@ -52,10 +54,9 @@ public enum NamespaceOperation { GET_SUBSCRIPTION_DISPATCH_RATE, GRANT_PERMISSIONS, GRANT_SUBSCRIPTION_PERMISSION, + LIST_TOPICS, REVOKE_PERMISSIONS, REVOKE_SUBSCRIPTION_PERMISSION, - PERMISSIONS, - POLICIES, SCHEMA_AUTOUPDATE, SET_BACKLOG_QUOTA, SET_ANTI_AFFINITY_GROUP, @@ -82,6 +83,5 @@ public enum NamespaceOperation { SET_SUBSCRIBE_RATE, SET_SUBSCRIPTION_AUTH_MODE, SET_SUBSCRIPTION_DISPATCH_RATE, - TOPICS, UNSUBSCRIBE, } From 8c97a74a6261e7a276aadf919c488c524840f18f Mon Sep 17 00:00:00 2001 From: Alexandre DUVAL Date: Thu, 9 Apr 2020 19:58:22 +0200 Subject: [PATCH 14/19] split into policies operations --- .../policies/data/NamespaceOperation.java | 63 ++----------------- .../common/policies/data/PolicyName.java | 44 +++++++++++++ .../common/policies/data/PolicyOperation.java | 28 +++++++++ 3 files changed, 76 insertions(+), 59 deletions(-) create mode 100644 pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/PolicyName.java create mode 100644 pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/PolicyOperation.java diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/NamespaceOperation.java b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/NamespaceOperation.java index 10106281cae24..6030c106f5d47 100644 --- a/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/NamespaceOperation.java +++ b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/NamespaceOperation.java @@ -23,65 +23,10 @@ * Namespace authorization operations. */ public enum NamespaceOperation { - CLEAR_BACKLOG, - DELETE_BUNDLE, - GET_ANTI_AFFINITY_GROUP, - GET_BACKLOG_QUOTAS, - GET_BUNDLES, - GET_COMPACTION_THRESHOLD, - GET_DELAYED_DELIVERY, - GET_DISPATCH_RATE, - GET_MAX_CONSUMERS_PER_SUBSCRIPTION, - GET_MAX_CONSUMERS_PER_TOPIC, - GET_MAX_PRODUCERS_PER_TOPIC, - GET_MAX_UNACKED_MESSAGES_PER_CONSUMER, - GET_MAX_UNACKED_MESSAGES_PER_SUBSCRIPTION, - GET_MESSAGE_TTL, - GET_OFFLOAD_DELETION_LAG, - GET_OFFLOAD_POLICIES, - GET_OFFLOAD_THRESHOLD, - GET_PERMISSIONS, - GET_PERSISTENCE, - GET_POLICIES, - GET_PUBLISH_RATE, - GET_REPLICATION_CLUSTERS, - GET_REPLICATOR_DISPATCH_RATE, - GET_RETENTION, - GET_SCHEMA_AUTOUPDATE_STRATEGY, - GET_SCHEMA_COMPATIBILITY_STRATEGY, - GET_SCHEMA_VALIDATION_ENFORCED, - GET_SUBSCRIBE_RATE, - GET_SUBSCRIPTION_DISPATCH_RATE, - GRANT_PERMISSIONS, - GRANT_SUBSCRIPTION_PERMISSION, + AUTH_MODE, + BACKLOG, + BUNDLE, LIST_TOPICS, - REVOKE_PERMISSIONS, - REVOKE_SUBSCRIPTION_PERMISSION, - SCHEMA_AUTOUPDATE, - SET_BACKLOG_QUOTA, - SET_ANTI_AFFINITY_GROUP, - SET_COMPACTION_THRESHOLD, - SET_DEDUPLICATION, - SET_DELAYED_DELIVERY, - SET_ENCRYPTION_REQUIRED, - SET_MAX_CONSUMERS_PER_SUBSCRIPTION, - SET_MAX_CONSUMERS_PER_TOPIC, - SET_MAX_PRODUCERS_PER_TOPIC, - SET_MAX_UNACKED_MESSAGES_PER_CONSUMER, - SET_MAX_UNACKED_MESSAGES_PER_SUBSCRIPTION, - SET_MESSAGE_TTL, - SET_OFFLOAD_DELETION_LAG, - SET_OFFLOAD_POLICIES, - SET_OFFLOAD_THRESHOLD, - SET_PERSISTENCE, - SET_REPLICATION_CLUSTERS, - SET_REPLICATOR_DISPATCH_RATE, - SET_RETENTION, - SET_SCHEMA_AUTOUPDATE_STRATEGY, - SET_SCHEMA_COMPATIBILITY_STRATEGY, - SET_SCHEMA_VALIDATION_ENFORCED, - SET_SUBSCRIBE_RATE, - SET_SUBSCRIPTION_AUTH_MODE, - SET_SUBSCRIPTION_DISPATCH_RATE, + PERMISSION, UNSUBSCRIBE, } diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/PolicyName.java b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/PolicyName.java new file mode 100644 index 0000000000000..43c7bd22e9dde --- /dev/null +++ b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/PolicyName.java @@ -0,0 +1,44 @@ +/** + * 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.pulsar.common.policies.data; + +/** + * PolicyName authorization operations. + */ +public enum PolicyName { + ALL, + ANTI_AFFINITY, + BACKLOG, + COMPACTION, + DELAYED, + DEDUPLICATION, + MAX_CONSUMERS, + MAX_PRODUCERS, + MAX_UNACKED, + OFFLOAD, + PERSISTENCE, + RATE, + RETENTION, + REPLICATION, + REPLICATION_RATE, + SCHEMA, + ENCRYPTION, + TTL, +} diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/PolicyOperation.java b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/PolicyOperation.java new file mode 100644 index 0000000000000..ce70341a71604 --- /dev/null +++ b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/PolicyOperation.java @@ -0,0 +1,28 @@ +/** + * 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.pulsar.common.policies.data; + +/** + * PolicyOperation authorization operations. + */ +public enum PolicyOperation { + READ, + WRITE, +} From c69bd0bdbbd5b2b5f62c6229eabb2ce867db3558 Mon Sep 17 00:00:00 2001 From: Alexandre DUVAL Date: Thu, 9 Apr 2020 19:58:49 +0200 Subject: [PATCH 15/19] support in authz --- .../authorization/AuthorizationProvider.java | 30 ++++++++++++ .../authorization/AuthorizationService.java | 49 ++++++++++++++++++- .../PulsarAuthorizationProvider.java | 9 ++++ 3 files changed, 86 insertions(+), 2 deletions(-) diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationProvider.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationProvider.java index 0702f81134be2..c4fcc5ed860e8 100644 --- a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationProvider.java +++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationProvider.java @@ -30,6 +30,8 @@ import org.apache.pulsar.common.naming.TopicName; import org.apache.pulsar.common.naming.NamespaceName; import org.apache.pulsar.common.policies.data.AuthAction; +import org.apache.pulsar.common.policies.data.PolicyName; +import org.apache.pulsar.common.policies.data.PolicyOperation; import org.apache.pulsar.common.policies.data.TenantInfo; import org.apache.pulsar.common.policies.data.NamespaceOperation; import org.apache.pulsar.common.policies.data.TenantOperation; @@ -248,6 +250,34 @@ default Boolean allowNamespaceOperation(NamespaceName namespaceName, String orig } } + /** + * Grant authorization-action permission on a namespace to the given client + * @param namespaceName + * @param originalRole role not overriden by proxy role if request do pass through proxy + * @param role originalRole | proxyRole if the request didn't pass through proxy + * @param operation + * @param authData + * @return CompletableFuture + */ + default CompletableFuture allowNamespacePolicyOperationAsync(NamespaceName namespaceName, PolicyName policy, + PolicyOperation operation, String originalRole, + String role, AuthenticationDataSource authData) { + return FutureUtil.failedFuture( + new IllegalStateException("NamespacePolicyOperation is not supported by the Authorization provider you are using.")); + } + + default Boolean allowNamespacePolicyOperation(NamespaceName namespaceName, PolicyName policy, PolicyOperation operation, + String originalRole, String role, AuthenticationDataSource authData) { + try { + return allowNamespacePolicyOperationAsync(namespaceName, policy, operation, originalRole, role, authData).get(); + } catch (InterruptedException e) { + throw new RestException(e); + } catch (ExecutionException e) { + throw new RestException(e.getCause()); + } + } + + /** * Grant authorization-action permission on a topic to the given client * @param topic diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationService.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationService.java index 27df5f0150305..e92ab8400addb 100644 --- a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationService.java +++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/AuthorizationService.java @@ -22,11 +22,14 @@ import org.apache.pulsar.broker.PulsarServerException; import org.apache.pulsar.broker.ServiceConfiguration; import org.apache.pulsar.broker.authentication.AuthenticationDataCommand; +import org.apache.pulsar.broker.authentication.AuthenticationDataHttps; import org.apache.pulsar.broker.authentication.AuthenticationDataSource; import org.apache.pulsar.broker.cache.ConfigurationCacheService; import org.apache.pulsar.common.naming.NamespaceName; import org.apache.pulsar.common.naming.TopicName; import org.apache.pulsar.common.policies.data.AuthAction; +import org.apache.pulsar.common.policies.data.PolicyName; +import org.apache.pulsar.common.policies.data.PolicyOperation; import org.apache.pulsar.common.policies.data.TenantInfo; import org.apache.pulsar.common.policies.data.NamespaceOperation; import org.apache.pulsar.common.policies.data.TenantOperation; @@ -394,18 +397,60 @@ public CompletableFuture allowNamespaceOperationAsync(NamespaceName nam } public Boolean allowNamespaceOperation(NamespaceName namespaceName, NamespaceOperation operation, - String orignalRole, String role, AuthenticationDataSource authData) { + String originalPrincipal, String role, AuthenticationDataSource authData) { if (!this.conf.isAuthorizationEnabled()) { return true; } if (provider != null) { - return provider.allowNamespaceOperation(namespaceName, orignalRole, role, operation, authData); + return provider.allowNamespaceOperation(namespaceName, originalPrincipal, role, operation, authData); } throw new IllegalStateException("No authorization provider configured for allowNamespaceOperation"); } + /** + * Grant authorization-action permission on a namespace to the given client + * + * @param namespaceName + * @param operation + * @param originalRole + * @param role + * @param authData + * additional authdata in json for targeted authorization provider + * @return IllegalArgumentException when namespace not found + * @throws IllegalStateException + * when failed to grant permission + */ + public CompletableFuture allowNamespacePolicyOperationAsync(NamespaceName namespaceName, PolicyName policy, + PolicyOperation operation, String originalRole, + String role, AuthenticationDataSource authData) { + if (!this.conf.isAuthorizationEnabled()) { + return CompletableFuture.completedFuture(true); + } + + if (provider != null) { + return provider.allowNamespacePolicyOperationAsync(namespaceName, policy, operation, originalRole, role, authData); + } + + return FutureUtil.failedFuture(new IllegalStateException("No authorization provider configured for " + + "allowNamespacePolicyOperationAsync")); + } + + public Boolean allowNamespacePolicyOperation(NamespaceName namespaceName, PolicyName policy, + PolicyOperation operation, String originalPrincipal, String role, + AuthenticationDataHttps authData) { + if (!this.conf.isAuthorizationEnabled()) { + return true; + } + + if (provider != null) { + return provider.allowNamespacePolicyOperation(namespaceName, policy, operation, originalPrincipal, role, authData); + } + + throw new IllegalStateException("No authorization provider configured for allowNamespacePolicyOperation"); + } + /** * Grant authorization-action permission on a topic to the given client * diff --git a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/PulsarAuthorizationProvider.java b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/PulsarAuthorizationProvider.java index 4352cc439d95f..40b20217d4200 100644 --- a/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/PulsarAuthorizationProvider.java +++ b/pulsar-broker-common/src/main/java/org/apache/pulsar/broker/authorization/PulsarAuthorizationProvider.java @@ -41,6 +41,8 @@ import org.apache.pulsar.common.policies.data.Policies; import static org.apache.pulsar.common.util.ObjectMapperFactory.getThreadLocal; +import org.apache.pulsar.common.policies.data.PolicyName; +import org.apache.pulsar.common.policies.data.PolicyOperation; import org.apache.pulsar.common.policies.data.TenantInfo; import org.apache.pulsar.common.policies.data.TenantOperation; import org.apache.pulsar.common.policies.data.TopicOperation; @@ -523,6 +525,13 @@ public CompletableFuture allowNamespaceOperationAsync(NamespaceName nam return validateTenantAdminAccess(namespaceName.getTenant(), originalRole, role, authData); } + @Override + public CompletableFuture allowNamespacePolicyOperationAsync(NamespaceName namespaceName, PolicyName policy, + PolicyOperation operation, String originalRole, + String role, AuthenticationDataSource authData) { + return validateTenantAdminAccess(namespaceName.getTenant(), originalRole, role, authData); + } + @Override public CompletableFuture allowTopicOperationAsync(TopicName topicName, String originalRole, String role, TopicOperation operation, From ac3b7dd419699eb06ca5245ca0bf9e00e3f5f41c Mon Sep 17 00:00:00 2001 From: Alexandre DUVAL Date: Thu, 9 Apr 2020 19:59:27 +0200 Subject: [PATCH 16/19] support into web resource --- .../pulsar/broker/web/PulsarWebResource.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java index fb1085b6fc0fc..96569e37900f1 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java @@ -63,6 +63,8 @@ import org.apache.pulsar.common.policies.data.ClusterData; import org.apache.pulsar.common.policies.data.NamespaceOperation; import org.apache.pulsar.common.policies.data.Policies; +import org.apache.pulsar.common.policies.data.PolicyName; +import org.apache.pulsar.common.policies.data.PolicyOperation; import org.apache.pulsar.common.policies.data.TenantInfo; import org.apache.pulsar.common.policies.data.TenantOperation; import org.apache.pulsar.common.policies.data.TopicOperation; @@ -808,6 +810,22 @@ public void validateNamespaceOperation(NamespaceName namespaceName, NamespaceOpe } } + public void validateNamespacePolicyOperation(NamespaceName namespaceName, PolicyName policy, PolicyOperation operation) { + if (pulsar().getConfiguration().isAuthenticationEnabled() && pulsar().getBrokerService().isAuthorizationEnabled()) { + if (!isClientAuthenticated(clientAppId())) { + throw new RestException(Status.FORBIDDEN, "Need to authenticate to perform the request"); + } + + Boolean isAuthorized = pulsar().getBrokerService().getAuthorizationService() + .allowNamespacePolicyOperation(namespaceName, policy, operation, originalPrincipal(), clientAppId(), clientAuthData()); + + if (!isAuthorized) { + throw new RestException(Status.FORBIDDEN, String.format("Unauthorized to validateNamespacePolicyOperation for" + + " operation [%s] on namespace [%s] on policy [%s]", operation.toString(), namespaceName, policy.toString())); + } + } + } + public void validateTopicOperation(TopicName topicName, TopicOperation operation) { if (pulsar().getConfiguration().isAuthenticationEnabled() && pulsar().getBrokerService().isAuthorizationEnabled()) { if (!isClientAuthenticated(clientAppId())) { From ee6fb7c44b09d0d0edc189765ab759da29e6e603 Mon Sep 17 00:00:00 2001 From: Alexandre DUVAL Date: Thu, 9 Apr 2020 19:59:20 +0200 Subject: [PATCH 17/19] support in ns --- .../broker/admin/impl/NamespacesBase.java | 116 +++++++++--------- .../pulsar/broker/admin/v1/Namespaces.java | 14 ++- .../pulsar/broker/admin/v2/Namespaces.java | 16 +-- .../pulsar/broker/admin/NamespacesTest.java | 10 +- 4 files changed, 81 insertions(+), 75 deletions(-) diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java index 5a0397c018f6d..698a5fd4d0202 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java @@ -84,6 +84,8 @@ import org.apache.pulsar.common.policies.data.OffloadPolicies; import org.apache.pulsar.common.policies.data.PersistencePolicies; import org.apache.pulsar.common.policies.data.Policies; +import org.apache.pulsar.common.policies.data.PolicyName; +import org.apache.pulsar.common.policies.data.PolicyOperation; import org.apache.pulsar.common.policies.data.PublishRate; import org.apache.pulsar.common.policies.data.RetentionPolicies; import org.apache.pulsar.common.policies.data.SchemaAutoUpdateCompatibilityStrategy; @@ -283,7 +285,7 @@ && getPartitionedTopicList(TopicDomain.persistent).isEmpty() @SuppressWarnings("deprecation") protected void internalDeleteNamespaceBundle(String bundleRange, boolean authoritative) { - validateNamespaceOperation(namespaceName, NamespaceOperation.DELETE_BUNDLE); + validateNamespaceOperation(namespaceName, NamespaceOperation.BUNDLE); validatePoliciesReadOnlyAccess(); // ensure that non-global namespace is directed to the correct cluster @@ -354,7 +356,7 @@ protected void internalDeleteNamespaceBundle(String bundleRange, boolean authori } protected void internalGrantPermissionOnNamespace(String role, Set actions) { - validateNamespaceOperation(namespaceName, NamespaceOperation.GRANT_PERMISSIONS); + validateNamespaceOperation(namespaceName, NamespaceOperation.PERMISSION); try { AuthorizationService authService = pulsar().getBrokerService().getAuthorizationService(); @@ -385,7 +387,7 @@ protected void internalGrantPermissionOnNamespace(String role, Set a protected void internalGrantPermissionOnSubscription(String subscription, Set roles) { - validateNamespaceOperation(namespaceName, NamespaceOperation.GRANT_SUBSCRIPTION_PERMISSION); + validateNamespaceOperation(namespaceName, NamespaceOperation.PERMISSION); try { AuthorizationService authService = pulsar().getBrokerService().getAuthorizationService(); @@ -415,7 +417,7 @@ protected void internalGrantPermissionOnSubscription(String subscription, Set internalGetNamespaceReplicationClusters() { - validateNamespaceOperation(namespaceName, NamespaceOperation.GET_REPLICATION_CLUSTERS); + validateNamespacePolicyOperation(namespaceName, PolicyName.REPLICATION, PolicyOperation.READ); if (!namespaceName.isGlobal()) { throw new RestException(Status.PRECONDITION_FAILED, @@ -470,7 +472,7 @@ protected Set internalGetNamespaceReplicationClusters() { } protected void internalSetNamespaceReplicationClusters(List clusterIds) { - validateNamespaceOperation(namespaceName, NamespaceOperation.SET_REPLICATION_CLUSTERS); + validateNamespacePolicyOperation(namespaceName, PolicyName.REPLICATION, PolicyOperation.WRITE); validatePoliciesReadOnlyAccess(); Set replicationClusterSet = Sets.newHashSet(clusterIds); @@ -528,7 +530,7 @@ protected void internalSetNamespaceReplicationClusters(List clusterIds) } protected void internalSetNamespaceMessageTTL(int messageTTL) { - validateNamespaceOperation(namespaceName, NamespaceOperation.SET_MESSAGE_TTL); + validateNamespacePolicyOperation(namespaceName, PolicyName.TTL, PolicyOperation.WRITE); validatePoliciesReadOnlyAccess(); if (messageTTL < 0) { @@ -784,7 +786,7 @@ protected void internalRemoveAutoSubscriptionCreation(AsyncResponse asyncRespons } protected void internalModifyDeduplication(boolean enableDeduplication) { - validateNamespaceOperation(namespaceName, NamespaceOperation.SET_DEDUPLICATION); + validateNamespacePolicyOperation(namespaceName, PolicyName.DEDUPLICATION, PolicyOperation.WRITE); validatePoliciesReadOnlyAccess(); Entry policiesNode = null; @@ -1094,7 +1096,7 @@ protected void internalSetPublishRate(PublishRate maxPublishMessageRate) { } protected PublishRate internalGetPublishRate() { - validateNamespaceOperation(namespaceName, NamespaceOperation.GET_PUBLISH_RATE); + validateNamespacePolicyOperation(namespaceName, PolicyName.RATE, PolicyOperation.READ); Policies policies = getNamespacePolicies(namespaceName); PublishRate publishRate = policies.publishMaxMessageRate.get(pulsar().getConfiguration().getClusterName()); @@ -1147,7 +1149,7 @@ protected void internalSetTopicDispatchRate(DispatchRate dispatchRate) { @SuppressWarnings("deprecation") protected DispatchRate internalGetTopicDispatchRate() { - validateNamespaceOperation(namespaceName, NamespaceOperation.GET_DISPATCH_RATE); + validateNamespacePolicyOperation(namespaceName, PolicyName.RATE, PolicyOperation.READ); Policies policies = getNamespacePolicies(namespaceName); DispatchRate dispatchRate = policies.topicDispatchRate.get(pulsar().getConfiguration().getClusterName()); @@ -1200,7 +1202,7 @@ protected void internalSetSubscriptionDispatchRate(DispatchRate dispatchRate) { } protected DispatchRate internalGetSubscriptionDispatchRate() { - validateNamespaceOperation(namespaceName, NamespaceOperation.GET_DISPATCH_RATE); + validateNamespacePolicyOperation(namespaceName, PolicyName.RATE, PolicyOperation.READ); Policies policies = getNamespacePolicies(namespaceName); DispatchRate dispatchRate = policies.subscriptionDispatchRate.get(pulsar().getConfiguration().getClusterName()); @@ -1251,7 +1253,7 @@ protected void internalSetSubscribeRate(SubscribeRate subscribeRate) { } protected SubscribeRate internalGetSubscribeRate() { - validateNamespaceOperation(namespaceName, NamespaceOperation.GET_SUBSCRIBE_RATE); + validateNamespacePolicyOperation(namespaceName, PolicyName.RATE, PolicyOperation.READ); Policies policies = getNamespacePolicies(namespaceName); SubscribeRate subscribeRate = policies.clusterSubscribeRate.get(pulsar().getConfiguration().getClusterName()); if (subscribeRate != null) { @@ -1300,7 +1302,7 @@ protected void internalSetReplicatorDispatchRate(DispatchRate dispatchRate) { } protected DispatchRate internalGetReplicatorDispatchRate() { - validateNamespaceOperation(namespaceName, NamespaceOperation.GET_REPLICATOR_DISPATCH_RATE); + validateNamespacePolicyOperation(namespaceName, PolicyName.REPLICATION_RATE, PolicyOperation.READ); Policies policies = getNamespacePolicies(namespaceName); DispatchRate dispatchRate = policies.replicatorDispatchRate.get(pulsar().getConfiguration().getClusterName()); @@ -1313,7 +1315,7 @@ protected DispatchRate internalGetReplicatorDispatchRate() { } protected void internalSetBacklogQuota(BacklogQuotaType backlogQuotaType, BacklogQuota backlogQuota) { - validateNamespaceOperation(namespaceName, NamespaceOperation.SET_BACKLOG_QUOTA); + validateNamespacePolicyOperation(namespaceName, PolicyName.BACKLOG, PolicyOperation.WRITE); validatePoliciesReadOnlyAccess(); if (backlogQuotaType == null) { @@ -1360,7 +1362,7 @@ protected void internalSetBacklogQuota(BacklogQuotaType backlogQuotaType, Backlo } protected void internalRemoveBacklogQuota(BacklogQuotaType backlogQuotaType) { - validateNamespaceOperation(namespaceName, NamespaceOperation.SET_BACKLOG_QUOTA); + validateNamespacePolicyOperation(namespaceName, PolicyName.BACKLOG, PolicyOperation.WRITE); validatePoliciesReadOnlyAccess(); if (backlogQuotaType == null) { @@ -1393,7 +1395,7 @@ protected void internalRemoveBacklogQuota(BacklogQuotaType backlogQuotaType) { } protected void internalSetRetention(RetentionPolicies retention) { - validateNamespaceOperation(namespaceName, NamespaceOperation.SET_RETENTION); + validateNamespacePolicyOperation(namespaceName, PolicyName.REPLICATION, PolicyOperation.WRITE); validatePoliciesReadOnlyAccess(); try { @@ -1431,7 +1433,7 @@ protected void internalSetRetention(RetentionPolicies retention) { } protected void internalSetPersistence(PersistencePolicies persistence) { - validateNamespaceOperation(namespaceName, NamespaceOperation.SET_PERSISTENCE); + validateNamespacePolicyOperation(namespaceName, PolicyName.PERSISTENCE, PolicyOperation.WRITE); validatePoliciesReadOnlyAccess(); validatePersistencePolicies(persistence); @@ -1462,7 +1464,7 @@ protected void internalSetPersistence(PersistencePolicies persistence) { } protected PersistencePolicies internalGetPersistence() { - validateNamespaceOperation(namespaceName, NamespaceOperation.GET_PERSISTENCE); + validateNamespacePolicyOperation(namespaceName, PolicyName.PERSISTENCE, PolicyOperation.READ); Policies policies = getNamespacePolicies(namespaceName); if (policies.persistence == null) { @@ -1474,7 +1476,7 @@ protected PersistencePolicies internalGetPersistence() { } protected void internalClearNamespaceBacklog(AsyncResponse asyncResponse, boolean authoritative) { - validateNamespaceOperation(namespaceName, NamespaceOperation.CLEAR_BACKLOG); + validateNamespaceOperation(namespaceName, NamespaceOperation.BACKLOG); final List> futures = Lists.newArrayList(); try { @@ -1516,7 +1518,7 @@ protected void internalClearNamespaceBacklog(AsyncResponse asyncResponse, boolea @SuppressWarnings("deprecation") protected void internalClearNamespaceBundleBacklog(String bundleRange, boolean authoritative) { - validateNamespaceOperation(namespaceName, NamespaceOperation.CLEAR_BACKLOG); + validateNamespaceOperation(namespaceName, NamespaceOperation.BACKLOG); Policies policies = getNamespacePolicies(namespaceName); @@ -1537,7 +1539,7 @@ protected void internalClearNamespaceBundleBacklog(String bundleRange, boolean a protected void internalClearNamespaceBacklogForSubscription(AsyncResponse asyncResponse, String subscription, boolean authoritative) { - validateNamespaceOperation(namespaceName, NamespaceOperation.CLEAR_BACKLOG); + validateNamespaceOperation(namespaceName, NamespaceOperation.BACKLOG); final List> futures = Lists.newArrayList(); try { @@ -1580,7 +1582,7 @@ protected void internalClearNamespaceBacklogForSubscription(AsyncResponse asyncR @SuppressWarnings("deprecation") protected void internalClearNamespaceBundleBacklogForSubscription(String subscription, String bundleRange, boolean authoritative) { - validateNamespaceOperation(namespaceName, NamespaceOperation.CLEAR_BACKLOG); + validateNamespaceOperation(namespaceName, NamespaceOperation.BACKLOG); Policies policies = getNamespacePolicies(namespaceName); @@ -1663,7 +1665,7 @@ protected void internalUnsubscribeNamespaceBundle(String subscription, String bu } protected void internalSetSubscriptionAuthMode(SubscriptionAuthMode subscriptionAuthMode) { - validateNamespaceOperation(namespaceName, NamespaceOperation.SET_SUBSCRIPTION_AUTH_MODE); + validateNamespaceOperation(namespaceName, NamespaceOperation.AUTH_MODE); validatePoliciesReadOnlyAccess(); if (subscriptionAuthMode == null) { @@ -1699,7 +1701,7 @@ protected void internalSetSubscriptionAuthMode(SubscriptionAuthMode subscription } protected void internalModifyEncryptionRequired(boolean encryptionRequired) { - validateNamespaceOperation(namespaceName, NamespaceOperation.SET_ENCRYPTION_REQUIRED); + validateNamespacePolicyOperation(namespaceName, PolicyName.ENCRYPTION, PolicyOperation.WRITE); validatePoliciesReadOnlyAccess(); Entry policiesNode = null; @@ -1735,7 +1737,7 @@ protected void internalModifyEncryptionRequired(boolean encryptionRequired) { } protected DelayedDeliveryPolicies internalGetDelayedDelivery() { - validateNamespaceOperation(namespaceName, NamespaceOperation.GET_DELAYED_DELIVERY); + validateNamespacePolicyOperation(namespaceName, PolicyName.DELAYED, PolicyOperation.READ); Policies policies = getNamespacePolicies(namespaceName); if (policies.delayed_delivery_policies == null) { @@ -1780,7 +1782,7 @@ protected void internalSetDelayedDelivery(DelayedDeliveryPolicies delayedDeliver } protected void internalSetNamespaceAntiAffinityGroup(String antiAffinityGroup) { - validateNamespaceOperation(namespaceName, NamespaceOperation.SET_ANTI_AFFINITY_GROUP); + validateNamespacePolicyOperation(namespaceName, PolicyName.ANTI_AFFINITY, PolicyOperation.WRITE); validatePoliciesReadOnlyAccess(); log.info("[{}] Setting anti-affinity group {} for {}", clientAppId(), antiAffinityGroup, namespaceName); @@ -1821,12 +1823,12 @@ protected void internalSetNamespaceAntiAffinityGroup(String antiAffinityGroup) { } protected String internalGetNamespaceAntiAffinityGroup() { - validateNamespaceOperation(namespaceName, NamespaceOperation.GET_ANTI_AFFINITY_GROUP); + validateNamespacePolicyOperation(namespaceName, PolicyName.ANTI_AFFINITY, PolicyOperation.READ); return getNamespacePolicies(namespaceName).antiAffinityGroup; } protected void internalRemoveNamespaceAntiAffinityGroup() { - validateNamespaceOperation(namespaceName, NamespaceOperation.SET_ANTI_AFFINITY_GROUP); + validateNamespacePolicyOperation(namespaceName, PolicyName.ANTI_AFFINITY, PolicyOperation.WRITE); validatePoliciesReadOnlyAccess(); log.info("[{}] Deleting anti-affinity group for {}", clientAppId(), namespaceName); @@ -1857,7 +1859,7 @@ protected void internalRemoveNamespaceAntiAffinityGroup() { protected List internalGetAntiAffinityNamespaces(String cluster, String antiAffinityGroup, String tenant) { - validateNamespaceOperation(namespaceName, NamespaceOperation.GET_ANTI_AFFINITY_GROUP); + validateNamespacePolicyOperation(namespaceName, PolicyName.ANTI_AFFINITY, PolicyOperation.READ); log.info("[{}]-{} Finding namespaces for {} in {}", clientAppId(), tenant, antiAffinityGroup, cluster); @@ -1909,7 +1911,7 @@ private void validatePersistencePolicies(PersistencePolicies persistence) { } protected RetentionPolicies internalGetRetention() { - validateNamespaceOperation(namespaceName, NamespaceOperation.GET_RETENTION); + validateNamespacePolicyOperation(namespaceName, PolicyName.RETENTION, PolicyOperation.READ); Policies policies = getNamespacePolicies(namespaceName); if (policies.retention_policies == null) { @@ -2102,12 +2104,12 @@ private void validatePolicies(NamespaceName ns, Policies policies) { protected int internalGetMaxProducersPerTopic() { - validateNamespaceOperation(namespaceName, NamespaceOperation.GET_MAX_PRODUCERS_PER_TOPIC); + validateNamespacePolicyOperation(namespaceName, PolicyName.MAX_PRODUCERS, PolicyOperation.READ); return getNamespacePolicies(namespaceName).max_producers_per_topic; } protected void internalSetMaxProducersPerTopic(int maxProducersPerTopic) { - validateNamespaceOperation(namespaceName, NamespaceOperation.SET_MAX_PRODUCERS_PER_TOPIC); + validateNamespacePolicyOperation(namespaceName, PolicyName.MAX_PRODUCERS, PolicyOperation.WRITE); validatePoliciesReadOnlyAccess(); try { @@ -2143,12 +2145,12 @@ protected void internalSetMaxProducersPerTopic(int maxProducersPerTopic) { } protected int internalGetMaxConsumersPerTopic() { - validateNamespaceOperation(namespaceName, NamespaceOperation.GET_MAX_CONSUMERS_PER_TOPIC); + validateNamespacePolicyOperation(namespaceName, PolicyName.MAX_CONSUMERS, PolicyOperation.READ); return getNamespacePolicies(namespaceName).max_consumers_per_topic; } protected void internalSetMaxConsumersPerTopic(int maxConsumersPerTopic) { - validateNamespaceOperation(namespaceName, NamespaceOperation.SET_MAX_CONSUMERS_PER_TOPIC); + validateNamespacePolicyOperation(namespaceName, PolicyName.MAX_CONSUMERS, PolicyOperation.WRITE); validatePoliciesReadOnlyAccess(); try { @@ -2184,12 +2186,12 @@ protected void internalSetMaxConsumersPerTopic(int maxConsumersPerTopic) { } protected int internalGetMaxConsumersPerSubscription() { - validateNamespaceOperation(namespaceName, NamespaceOperation.GET_MAX_CONSUMERS_PER_SUBSCRIPTION); + validateNamespacePolicyOperation(namespaceName, PolicyName.MAX_CONSUMERS, PolicyOperation.READ); return getNamespacePolicies(namespaceName).max_consumers_per_subscription; } protected void internalSetMaxConsumersPerSubscription(int maxConsumersPerSubscription) { - validateNamespaceOperation(namespaceName, NamespaceOperation.SET_MAX_CONSUMERS_PER_SUBSCRIPTION); + validateNamespacePolicyOperation(namespaceName, PolicyName.MAX_CONSUMERS, PolicyOperation.WRITE); validatePoliciesReadOnlyAccess(); try { @@ -2225,12 +2227,12 @@ protected void internalSetMaxConsumersPerSubscription(int maxConsumersPerSubscri } protected int internalGetMaxUnackedMessagesPerConsumer() { - validateNamespaceOperation(namespaceName, NamespaceOperation.GET_MAX_UNACKED_MESSAGES_PER_CONSUMER); + validateNamespacePolicyOperation(namespaceName, PolicyName.MAX_UNACKED, PolicyOperation.READ); return getNamespacePolicies(namespaceName).max_unacked_messages_per_consumer; } protected void internalSetMaxUnackedMessagesPerConsumer(int maxUnackedMessagesPerConsumer) { - validateNamespaceOperation(namespaceName, NamespaceOperation.SET_MAX_UNACKED_MESSAGES_PER_CONSUMER); + validateNamespacePolicyOperation(namespaceName, PolicyName.MAX_UNACKED, PolicyOperation.WRITE); validatePoliciesReadOnlyAccess(); try { @@ -2266,12 +2268,12 @@ protected void internalSetMaxUnackedMessagesPerConsumer(int maxUnackedMessagesPe } protected int internalGetMaxUnackedMessagesPerSubscription() { - validateNamespaceOperation(namespaceName, NamespaceOperation.GET_MAX_UNACKED_MESSAGES_PER_SUBSCRIPTION); + validateNamespacePolicyOperation(namespaceName, PolicyName.MAX_UNACKED, PolicyOperation.READ); return getNamespacePolicies(namespaceName).max_unacked_messages_per_subscription; } protected void internalSetMaxUnackedMessagesPerSubscription(int maxUnackedMessagesPerSubscription) { - validateNamespaceOperation(namespaceName, NamespaceOperation.SET_MAX_UNACKED_MESSAGES_PER_SUBSCRIPTION); + validateNamespacePolicyOperation(namespaceName, PolicyName.MAX_UNACKED, PolicyOperation.WRITE); validatePoliciesReadOnlyAccess(); try { @@ -2307,12 +2309,12 @@ protected void internalSetMaxUnackedMessagesPerSubscription(int maxUnackedMessag } protected long internalGetCompactionThreshold() { - validateNamespaceOperation(namespaceName, NamespaceOperation.GET_COMPACTION_THRESHOLD); + validateNamespacePolicyOperation(namespaceName, PolicyName.COMPACTION, PolicyOperation.READ); return getNamespacePolicies(namespaceName).compaction_threshold; } protected void internalSetCompactionThreshold(long newThreshold) { - validateNamespaceOperation(namespaceName, NamespaceOperation.SET_COMPACTION_THRESHOLD); + validateNamespacePolicyOperation(namespaceName, PolicyName.COMPACTION, PolicyOperation.WRITE); validatePoliciesReadOnlyAccess(); try { @@ -2348,7 +2350,7 @@ protected void internalSetCompactionThreshold(long newThreshold) { } protected long internalGetOffloadThreshold() { - validateNamespaceOperation(namespaceName, NamespaceOperation.GET_OFFLOAD_THRESHOLD); + validateNamespacePolicyOperation(namespaceName, PolicyName.OFFLOAD, PolicyOperation.READ); Policies policies = getNamespacePolicies(namespaceName); if (policies.offload_policies == null) { return policies.offload_threshold; @@ -2358,7 +2360,7 @@ protected long internalGetOffloadThreshold() { } protected void internalSetOffloadThreshold(long newThreshold) { - validateNamespaceOperation(namespaceName, NamespaceOperation.SET_OFFLOAD_THRESHOLD); + validateNamespacePolicyOperation(namespaceName, PolicyName.OFFLOAD, PolicyOperation.WRITE); validatePoliciesReadOnlyAccess(); try { @@ -2395,7 +2397,7 @@ protected void internalSetOffloadThreshold(long newThreshold) { } protected Long internalGetOffloadDeletionLag() { - validateNamespaceOperation(namespaceName, NamespaceOperation.GET_OFFLOAD_DELETION_LAG); + validateNamespacePolicyOperation(namespaceName, PolicyName.OFFLOAD, PolicyOperation.READ); Policies policies = getNamespacePolicies(namespaceName); if (policies.offload_policies == null) { return policies.offload_deletion_lag_ms; @@ -2405,7 +2407,7 @@ protected Long internalGetOffloadDeletionLag() { } protected void internalSetOffloadDeletionLag(Long newDeletionLagMs) { - validateNamespaceOperation(namespaceName, NamespaceOperation.SET_OFFLOAD_DELETION_LAG); + validateNamespacePolicyOperation(namespaceName, PolicyName.OFFLOAD, PolicyOperation.WRITE); validatePoliciesReadOnlyAccess(); try { @@ -2443,12 +2445,12 @@ protected void internalSetOffloadDeletionLag(Long newDeletionLagMs) { @Deprecated protected SchemaAutoUpdateCompatibilityStrategy internalGetSchemaAutoUpdateCompatibilityStrategy() { - validateNamespaceOperation(namespaceName, NamespaceOperation.GET_SCHEMA_AUTOUPDATE_STRATEGY); + validateNamespacePolicyOperation(namespaceName, PolicyName.SCHEMA, PolicyOperation.READ); return getNamespacePolicies(namespaceName).schema_auto_update_compatibility_strategy; } protected SchemaCompatibilityStrategy internalGetSchemaCompatibilityStrategy() { - validateNamespaceOperation(namespaceName, NamespaceOperation.GET_SCHEMA_COMPATIBILITY_STRATEGY); + validateNamespacePolicyOperation(namespaceName, PolicyName.SCHEMA, PolicyOperation.READ); Policies policies = getNamespacePolicies(namespaceName); SchemaCompatibilityStrategy schemaCompatibilityStrategy = policies.schema_compatibility_strategy; if (schemaCompatibilityStrategy == SchemaCompatibilityStrategy.UNDEFINED){ @@ -2460,7 +2462,7 @@ protected SchemaCompatibilityStrategy internalGetSchemaCompatibilityStrategy() { @Deprecated protected void internalSetSchemaAutoUpdateCompatibilityStrategy(SchemaAutoUpdateCompatibilityStrategy strategy) { - validateNamespaceOperation(namespaceName, NamespaceOperation.SET_SCHEMA_AUTOUPDATE_STRATEGY); + validateNamespacePolicyOperation(namespaceName, PolicyName.SCHEMA, PolicyOperation.WRITE); validatePoliciesReadOnlyAccess(); mutatePolicy((policies) -> { @@ -2471,7 +2473,7 @@ protected void internalSetSchemaAutoUpdateCompatibilityStrategy(SchemaAutoUpdate } protected void internalSetSchemaCompatibilityStrategy(SchemaCompatibilityStrategy strategy) { - validateNamespaceOperation(namespaceName, NamespaceOperation.SET_SCHEMA_COMPATIBILITY_STRATEGY); + validateNamespacePolicyOperation(namespaceName, PolicyName.SCHEMA, PolicyOperation.WRITE); validatePoliciesReadOnlyAccess(); mutatePolicy((policies) -> { @@ -2482,12 +2484,12 @@ protected void internalSetSchemaCompatibilityStrategy(SchemaCompatibilityStrateg } protected boolean internalGetSchemaValidationEnforced() { - validateNamespaceOperation(namespaceName, NamespaceOperation.GET_SCHEMA_VALIDATION_ENFORCED); + validateNamespacePolicyOperation(namespaceName, PolicyName.SCHEMA, PolicyOperation.READ); return getNamespacePolicies(namespaceName).schema_validation_enforced; } protected void internalSetSchemaValidationEnforced(boolean schemaValidationEnforced) { - validateNamespaceOperation(namespaceName, NamespaceOperation.SET_SCHEMA_VALIDATION_ENFORCED); + validateNamespacePolicyOperation(namespaceName, PolicyName.SCHEMA, PolicyOperation.WRITE); validatePoliciesReadOnlyAccess(); mutatePolicy((policies) -> { @@ -2498,12 +2500,12 @@ protected void internalSetSchemaValidationEnforced(boolean schemaValidationEnfor } protected boolean internalGetIsAllowAutoUpdateSchema() { - validateNamespaceOperation(namespaceName, NamespaceOperation.GET_SCHEMA_AUTOUPDATE_STRATEGY); + validateNamespacePolicyOperation(namespaceName, PolicyName.SCHEMA, PolicyOperation.READ); return getNamespacePolicies(namespaceName).is_allow_auto_update_schema; } protected void internalSetIsAllowAutoUpdateSchema(boolean isAllowAutoUpdateSchema) { - validateNamespaceOperation(namespaceName, NamespaceOperation.SET_SCHEMA_AUTOUPDATE_STRATEGY); + validateNamespacePolicyOperation(namespaceName, PolicyName.SCHEMA, PolicyOperation.WRITE); validatePoliciesReadOnlyAccess(); mutatePolicy((policies) -> { @@ -2546,7 +2548,7 @@ private void mutatePolicy(Function policyTransformation, } protected void internalSetOffloadPolicies(AsyncResponse asyncResponse, OffloadPolicies offloadPolicies) { - validateNamespaceOperation(namespaceName, NamespaceOperation.SET_OFFLOAD_POLICIES); + validateNamespacePolicyOperation(namespaceName, PolicyName.OFFLOAD, PolicyOperation.WRITE); validatePoliciesReadOnlyAccess(); validateOffloadPolicies(offloadPolicies); @@ -2623,7 +2625,7 @@ private void validateOffloadPolicies(OffloadPolicies offloadPolicies) { } protected OffloadPolicies internalGetOffloadPolicies() { - validateNamespaceOperation(namespaceName, NamespaceOperation.GET_OFFLOAD_POLICIES); + validateNamespacePolicyOperation(namespaceName, PolicyName.OFFLOAD, PolicyOperation.READ); Policies policies = getNamespacePolicies(namespaceName); return policies.offload_policies; diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v1/Namespaces.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v1/Namespaces.java index 162d27870fa05..2ef56f79e15d3 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v1/Namespaces.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v1/Namespaces.java @@ -39,6 +39,8 @@ import org.apache.pulsar.common.policies.data.NamespaceOperation; import org.apache.pulsar.common.policies.data.PersistencePolicies; import org.apache.pulsar.common.policies.data.Policies; +import org.apache.pulsar.common.policies.data.PolicyName; +import org.apache.pulsar.common.policies.data.PolicyOperation; import org.apache.pulsar.common.policies.data.PublishRate; import org.apache.pulsar.common.policies.data.RetentionPolicies; import org.apache.pulsar.common.policies.data.SubscriptionAuthMode; @@ -147,7 +149,7 @@ public void getTopics(@PathParam("property") String property, public Policies getPolicies(@PathParam("property") String property, @PathParam("cluster") String cluster, @PathParam("namespace") String namespace) { validateNamespaceName(property, cluster, namespace); - validateNamespaceOperation(NamespaceName.get(property, namespace), NamespaceOperation.GET_POLICIES); + validateNamespacePolicyOperation(NamespaceName.get(property, namespace), PolicyName.ALL, PolicyOperation.READ); return getNamespacePolicies(namespaceName); } @@ -230,7 +232,7 @@ public void deleteNamespaceBundle(@PathParam("property") String property, public Map> getPermissions(@PathParam("property") String property, @PathParam("cluster") String cluster, @PathParam("namespace") String namespace) { validateNamespaceName(property, cluster, namespace); - validateNamespaceOperation(NamespaceName.get(property, namespace), NamespaceOperation.GET_PERMISSIONS); + validateNamespaceOperation(NamespaceName.get(property, namespace), NamespaceOperation.PERMISSION); Policies policies = getNamespacePolicies(namespaceName); return policies.auth_policies.namespace_auth; @@ -296,7 +298,7 @@ public void revokePermissionOnSubscription(@PathParam("property") String propert public Set getNamespaceReplicationClusters(@PathParam("property") String property, @PathParam("cluster") String cluster, @PathParam("namespace") String namespace) { validateNamespaceName(property, cluster, namespace); - validateNamespaceOperation(NamespaceName.get(property, namespace), NamespaceOperation.GET_REPLICATION_CLUSTERS); + validateNamespacePolicyOperation(NamespaceName.get(property, namespace), PolicyName.REPLICATION, PolicyOperation.READ); return internalGetNamespaceReplicationClusters(); } @@ -322,7 +324,7 @@ public void setNamespaceReplicationClusters(@PathParam("property") String proper public int getNamespaceMessageTTL(@PathParam("property") String property, @PathParam("cluster") String cluster, @PathParam("namespace") String namespace) { validateNamespaceName(property, cluster, namespace); - validateNamespaceOperation(NamespaceName.get(property, namespace), NamespaceOperation.GET_MESSAGE_TTL); + validateNamespacePolicyOperation(NamespaceName.get(property, namespace), PolicyName.TTL, PolicyOperation.READ); Policies policies = getNamespacePolicies(namespaceName); return policies.message_ttl_in_seconds; @@ -481,7 +483,7 @@ public BundlesData getBundlesData(@PathParam("property") String property, @PathP @PathParam("namespace") String namespace) { validatePoliciesReadOnlyAccess(); validateNamespaceName(property, cluster, namespace); - validateNamespaceOperation(NamespaceName.get(property, namespace), NamespaceOperation.GET_BUNDLES); + validateNamespaceOperation(NamespaceName.get(property, namespace), NamespaceOperation.BUNDLE); Policies policies = getNamespacePolicies(namespaceName); @@ -613,7 +615,7 @@ public DispatchRate getSubscriptionDispatchRate(@PathParam("property") String pr public Map getBacklogQuotaMap(@PathParam("property") String property, @PathParam("cluster") String cluster, @PathParam("namespace") String namespace) { validateNamespaceName(property, cluster, namespace); - validateNamespaceOperation(NamespaceName.get(property, namespace), NamespaceOperation.GET_BACKLOG_QUOTAS); + validateNamespacePolicyOperation(NamespaceName.get(property, namespace), PolicyName.BACKLOG, PolicyOperation.READ); Policies policies = getNamespacePolicies(namespaceName); return policies.backlog_quota_map; diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/Namespaces.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/Namespaces.java index 6854d5974eb2f..10c7928b72131 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/Namespaces.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/Namespaces.java @@ -58,6 +58,8 @@ import org.apache.pulsar.common.policies.data.OffloadPolicies; import org.apache.pulsar.common.policies.data.PersistencePolicies; import org.apache.pulsar.common.policies.data.Policies; +import org.apache.pulsar.common.policies.data.PolicyName; +import org.apache.pulsar.common.policies.data.PolicyOperation; import org.apache.pulsar.common.policies.data.PublishRate; import org.apache.pulsar.common.policies.data.RetentionPolicies; import org.apache.pulsar.common.policies.data.SchemaCompatibilityStrategy; @@ -116,7 +118,7 @@ public void getTopics(@PathParam("tenant") String tenant, @ApiResponse(code = 404, message = "Tenant or cluster or namespace doesn't exist") }) public Policies getPolicies(@PathParam("tenant") String tenant, @PathParam("namespace") String namespace) { validateNamespaceName(tenant, namespace); - validateNamespaceOperation(NamespaceName.get(tenant, namespace), NamespaceOperation.GET_POLICIES); + validateNamespacePolicyOperation(NamespaceName.get(tenant, namespace), PolicyName.ALL, PolicyOperation.READ); return getNamespacePolicies(namespaceName); } @@ -180,7 +182,7 @@ public void deleteNamespaceBundle(@PathParam("tenant") String tenant, @PathParam public Map> getPermissions(@PathParam("tenant") String tenant, @PathParam("namespace") String namespace) { validateNamespaceName(tenant, namespace); - validateNamespaceOperation(NamespaceName.get(tenant, namespace), NamespaceOperation.GET_PERMISSIONS); + validateNamespaceOperation(NamespaceName.get(tenant, namespace), NamespaceOperation.PERMISSION); Policies policies = getNamespacePolicies(namespaceName); return policies.auth_policies.namespace_auth; @@ -246,8 +248,7 @@ public void revokePermissionOnSubscription(@PathParam("property") String propert public Set getNamespaceReplicationClusters(@PathParam("tenant") String tenant, @PathParam("namespace") String namespace) { validateNamespaceName(tenant, namespace); - validateNamespaceOperation(NamespaceName.get(tenant, namespace), NamespaceOperation.GET_REPLICATION_CLUSTERS); - + validateNamespacePolicyOperation(NamespaceName.get(tenant, namespace), PolicyName.REPLICATION, PolicyOperation.READ); return internalGetNamespaceReplicationClusters(); } @@ -272,7 +273,7 @@ public void setNamespaceReplicationClusters(@PathParam("tenant") String tenant, public int getNamespaceMessageTTL(@PathParam("tenant") String tenant, @PathParam("namespace") String namespace) { validateNamespaceName(tenant, namespace); - validateNamespaceOperation(NamespaceName.get(tenant, namespace), NamespaceOperation.GET_MESSAGE_TTL); + validateNamespacePolicyOperation(NamespaceName.get(tenant, namespace), PolicyName.TTL, PolicyOperation.READ); Policies policies = getNamespacePolicies(namespaceName); return policies.message_ttl_in_seconds; @@ -386,7 +387,7 @@ public BundlesData getBundlesData(@PathParam("tenant") String tenant, @PathParam("namespace") String namespace) { validatePoliciesReadOnlyAccess(); validateNamespaceName(tenant, namespace); - validateNamespaceOperation(NamespaceName.get(tenant, namespace), NamespaceOperation.GET_BUNDLES); + validateNamespaceOperation(NamespaceName.get(tenant, namespace), NamespaceOperation.BUNDLE); Policies policies = getNamespacePolicies(namespaceName); @@ -559,8 +560,7 @@ public DispatchRate getReplicatorDispatchRate(@PathParam("tenant") String tenant public Map getBacklogQuotaMap(@PathParam("tenant") String tenant, @PathParam("namespace") String namespace) { validateNamespaceName(tenant, namespace); - validateNamespaceOperation(NamespaceName.get(tenant, namespace), NamespaceOperation.GET_BACKLOG_QUOTAS); - + validateNamespacePolicyOperation(NamespaceName.get(tenant, namespace), PolicyName.BACKLOG, PolicyOperation.READ); Policies policies = getNamespacePolicies(namespaceName); return policies.backlog_quota_map; } diff --git a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/NamespacesTest.java b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/NamespacesTest.java index 2561ea74ddf4a..2df24bb2e4358 100644 --- a/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/NamespacesTest.java +++ b/pulsar-broker/src/test/java/org/apache/pulsar/broker/admin/NamespacesTest.java @@ -85,6 +85,8 @@ import org.apache.pulsar.common.policies.data.OffloadPolicies; import org.apache.pulsar.common.policies.data.PersistencePolicies; import org.apache.pulsar.common.policies.data.Policies; +import org.apache.pulsar.common.policies.data.PolicyName; +import org.apache.pulsar.common.policies.data.PolicyOperation; import org.apache.pulsar.common.policies.data.RetentionPolicies; import org.apache.pulsar.common.policies.data.SubscribeRate; import org.apache.pulsar.common.policies.data.TenantInfo; @@ -172,12 +174,12 @@ public void setup() throws Exception { .validateTenantOperation(this.testOtherTenant, null); doThrow(new RestException(Status.UNAUTHORIZED, "unauthorized")).when(namespaces) - .validateNamespaceOperation( - NamespaceName.get("other-tenant/use/test-namespace-1"), NamespaceOperation.SET_PERSISTENCE); + .validateNamespacePolicyOperation(NamespaceName.get("other-tenant/use/test-namespace-1"), + PolicyName.PERSISTENCE, PolicyOperation.WRITE); doThrow(new RestException(Status.UNAUTHORIZED, "unauthorized")).when(namespaces) - .validateNamespaceOperation( - NamespaceName.get("other-tenant/use/test-namespace-1"), NamespaceOperation.SET_RETENTION); + .validateNamespacePolicyOperation(NamespaceName.get("other-tenant/use/test-namespace-1"), + PolicyName.REPLICATION, PolicyOperation.WRITE); nsSvc = pulsar.getNamespaceService(); } From 5ce27d9bafce96259a1a44c8967c6c4b86c385b8 Mon Sep 17 00:00:00 2001 From: Alexandre DUVAL Date: Fri, 10 Apr 2020 11:30:34 +0200 Subject: [PATCH 18/19] clarify operations --- .../broker/admin/impl/NamespacesBase.java | 38 +++++++++--------- .../pulsar/broker/admin/v1/Namespaces.java | 6 +-- .../pulsar/broker/admin/v2/Namespaces.java | 6 +-- .../policies/data/NamespaceOperation.java | 20 +++++++--- .../common/policies/data/PolicyName.java | 5 ++- .../common/policies/data/TopicOperation.java | 40 +++++++------------ 6 files changed, 58 insertions(+), 57 deletions(-) diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java index 698a5fd4d0202..b605907a0bb70 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java @@ -285,7 +285,7 @@ && getPartitionedTopicList(TopicDomain.persistent).isEmpty() @SuppressWarnings("deprecation") protected void internalDeleteNamespaceBundle(String bundleRange, boolean authoritative) { - validateNamespaceOperation(namespaceName, NamespaceOperation.BUNDLE); + validateNamespaceOperation(namespaceName, NamespaceOperation.DELETE_BUNDLE); validatePoliciesReadOnlyAccess(); // ensure that non-global namespace is directed to the correct cluster @@ -356,7 +356,7 @@ protected void internalDeleteNamespaceBundle(String bundleRange, boolean authori } protected void internalGrantPermissionOnNamespace(String role, Set actions) { - validateNamespaceOperation(namespaceName, NamespaceOperation.PERMISSION); + validateNamespaceOperation(namespaceName, NamespaceOperation.GRANT_PERMISSION); try { AuthorizationService authService = pulsar().getBrokerService().getAuthorizationService(); @@ -387,7 +387,7 @@ protected void internalGrantPermissionOnNamespace(String role, Set a protected void internalGrantPermissionOnSubscription(String subscription, Set roles) { - validateNamespaceOperation(namespaceName, NamespaceOperation.PERMISSION); + validateNamespaceOperation(namespaceName, NamespaceOperation.GRANT_PERMISSION); try { AuthorizationService authService = pulsar().getBrokerService().getAuthorizationService(); @@ -417,7 +417,7 @@ protected void internalGrantPermissionOnSubscription(String subscription, Set> futures = Lists.newArrayList(); try { @@ -1518,7 +1518,7 @@ protected void internalClearNamespaceBacklog(AsyncResponse asyncResponse, boolea @SuppressWarnings("deprecation") protected void internalClearNamespaceBundleBacklog(String bundleRange, boolean authoritative) { - validateNamespaceOperation(namespaceName, NamespaceOperation.BACKLOG); + validateNamespaceOperation(namespaceName, NamespaceOperation.CLEAR_BACKLOG); Policies policies = getNamespacePolicies(namespaceName); @@ -1539,7 +1539,7 @@ protected void internalClearNamespaceBundleBacklog(String bundleRange, boolean a protected void internalClearNamespaceBacklogForSubscription(AsyncResponse asyncResponse, String subscription, boolean authoritative) { - validateNamespaceOperation(namespaceName, NamespaceOperation.BACKLOG); + validateNamespaceOperation(namespaceName, NamespaceOperation.CLEAR_BACKLOG); final List> futures = Lists.newArrayList(); try { @@ -1582,7 +1582,7 @@ protected void internalClearNamespaceBacklogForSubscription(AsyncResponse asyncR @SuppressWarnings("deprecation") protected void internalClearNamespaceBundleBacklogForSubscription(String subscription, String bundleRange, boolean authoritative) { - validateNamespaceOperation(namespaceName, NamespaceOperation.BACKLOG); + validateNamespaceOperation(namespaceName, NamespaceOperation.CLEAR_BACKLOG); Policies policies = getNamespacePolicies(namespaceName); @@ -1665,7 +1665,7 @@ protected void internalUnsubscribeNamespaceBundle(String subscription, String bu } protected void internalSetSubscriptionAuthMode(SubscriptionAuthMode subscriptionAuthMode) { - validateNamespaceOperation(namespaceName, NamespaceOperation.AUTH_MODE); + validateNamespacePolicyOperation(namespaceName, PolicyName.SUBSCRIPTION_AUTH_MODE, PolicyOperation.WRITE); validatePoliciesReadOnlyAccess(); if (subscriptionAuthMode == null) { @@ -1737,7 +1737,7 @@ protected void internalModifyEncryptionRequired(boolean encryptionRequired) { } protected DelayedDeliveryPolicies internalGetDelayedDelivery() { - validateNamespacePolicyOperation(namespaceName, PolicyName.DELAYED, PolicyOperation.READ); + validateNamespacePolicyOperation(namespaceName, PolicyName.DELAYED_DELIVERY, PolicyOperation.READ); Policies policies = getNamespacePolicies(namespaceName); if (policies.delayed_delivery_policies == null) { @@ -2445,12 +2445,12 @@ protected void internalSetOffloadDeletionLag(Long newDeletionLagMs) { @Deprecated protected SchemaAutoUpdateCompatibilityStrategy internalGetSchemaAutoUpdateCompatibilityStrategy() { - validateNamespacePolicyOperation(namespaceName, PolicyName.SCHEMA, PolicyOperation.READ); + validateNamespacePolicyOperation(namespaceName, PolicyName.SCHEMA_COMPATIBILITY_STRATEGY, PolicyOperation.READ); return getNamespacePolicies(namespaceName).schema_auto_update_compatibility_strategy; } protected SchemaCompatibilityStrategy internalGetSchemaCompatibilityStrategy() { - validateNamespacePolicyOperation(namespaceName, PolicyName.SCHEMA, PolicyOperation.READ); + validateNamespacePolicyOperation(namespaceName, PolicyName.SCHEMA_COMPATIBILITY_STRATEGY, PolicyOperation.READ); Policies policies = getNamespacePolicies(namespaceName); SchemaCompatibilityStrategy schemaCompatibilityStrategy = policies.schema_compatibility_strategy; if (schemaCompatibilityStrategy == SchemaCompatibilityStrategy.UNDEFINED){ @@ -2462,7 +2462,7 @@ protected SchemaCompatibilityStrategy internalGetSchemaCompatibilityStrategy() { @Deprecated protected void internalSetSchemaAutoUpdateCompatibilityStrategy(SchemaAutoUpdateCompatibilityStrategy strategy) { - validateNamespacePolicyOperation(namespaceName, PolicyName.SCHEMA, PolicyOperation.WRITE); + validateNamespacePolicyOperation(namespaceName, PolicyName.SCHEMA_COMPATIBILITY_STRATEGY, PolicyOperation.WRITE); validatePoliciesReadOnlyAccess(); mutatePolicy((policies) -> { @@ -2473,7 +2473,7 @@ protected void internalSetSchemaAutoUpdateCompatibilityStrategy(SchemaAutoUpdate } protected void internalSetSchemaCompatibilityStrategy(SchemaCompatibilityStrategy strategy) { - validateNamespacePolicyOperation(namespaceName, PolicyName.SCHEMA, PolicyOperation.WRITE); + validateNamespacePolicyOperation(namespaceName, PolicyName.SCHEMA_COMPATIBILITY_STRATEGY, PolicyOperation.WRITE); validatePoliciesReadOnlyAccess(); mutatePolicy((policies) -> { @@ -2484,12 +2484,12 @@ protected void internalSetSchemaCompatibilityStrategy(SchemaCompatibilityStrateg } protected boolean internalGetSchemaValidationEnforced() { - validateNamespacePolicyOperation(namespaceName, PolicyName.SCHEMA, PolicyOperation.READ); + validateNamespacePolicyOperation(namespaceName, PolicyName.SCHEMA_COMPATIBILITY_STRATEGY, PolicyOperation.READ); return getNamespacePolicies(namespaceName).schema_validation_enforced; } protected void internalSetSchemaValidationEnforced(boolean schemaValidationEnforced) { - validateNamespacePolicyOperation(namespaceName, PolicyName.SCHEMA, PolicyOperation.WRITE); + validateNamespacePolicyOperation(namespaceName, PolicyName.SCHEMA_COMPATIBILITY_STRATEGY, PolicyOperation.WRITE); validatePoliciesReadOnlyAccess(); mutatePolicy((policies) -> { @@ -2500,12 +2500,12 @@ protected void internalSetSchemaValidationEnforced(boolean schemaValidationEnfor } protected boolean internalGetIsAllowAutoUpdateSchema() { - validateNamespacePolicyOperation(namespaceName, PolicyName.SCHEMA, PolicyOperation.READ); + validateNamespacePolicyOperation(namespaceName, PolicyName.SCHEMA_COMPATIBILITY_STRATEGY, PolicyOperation.READ); return getNamespacePolicies(namespaceName).is_allow_auto_update_schema; } protected void internalSetIsAllowAutoUpdateSchema(boolean isAllowAutoUpdateSchema) { - validateNamespacePolicyOperation(namespaceName, PolicyName.SCHEMA, PolicyOperation.WRITE); + validateNamespacePolicyOperation(namespaceName, PolicyName.SCHEMA_COMPATIBILITY_STRATEGY, PolicyOperation.WRITE); validatePoliciesReadOnlyAccess(); mutatePolicy((policies) -> { diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v1/Namespaces.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v1/Namespaces.java index 2ef56f79e15d3..9472a036c6d7c 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v1/Namespaces.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v1/Namespaces.java @@ -127,7 +127,7 @@ public void getTopics(@PathParam("property") String property, @QueryParam("mode") @DefaultValue("PERSISTENT") Mode mode, @Suspended AsyncResponse asyncResponse) { validateNamespaceName(property, cluster, namespace); - validateNamespaceOperation(NamespaceName.get(property, namespace), NamespaceOperation.LIST_TOPICS); + validateNamespaceOperation(NamespaceName.get(property, namespace), NamespaceOperation.GET_TOPICS); // Validate that namespace exists, throws 404 if it doesn't exist getNamespacePolicies(namespaceName); @@ -232,7 +232,7 @@ public void deleteNamespaceBundle(@PathParam("property") String property, public Map> getPermissions(@PathParam("property") String property, @PathParam("cluster") String cluster, @PathParam("namespace") String namespace) { validateNamespaceName(property, cluster, namespace); - validateNamespaceOperation(NamespaceName.get(property, namespace), NamespaceOperation.PERMISSION); + validateNamespaceOperation(NamespaceName.get(property, namespace), NamespaceOperation.GET_PERMISSION); Policies policies = getNamespacePolicies(namespaceName); return policies.auth_policies.namespace_auth; @@ -483,7 +483,7 @@ public BundlesData getBundlesData(@PathParam("property") String property, @PathP @PathParam("namespace") String namespace) { validatePoliciesReadOnlyAccess(); validateNamespaceName(property, cluster, namespace); - validateNamespaceOperation(NamespaceName.get(property, namespace), NamespaceOperation.BUNDLE); + validateNamespaceOperation(NamespaceName.get(property, namespace), NamespaceOperation.GET_BUNDLE); Policies policies = getNamespacePolicies(namespaceName); diff --git a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/Namespaces.java b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/Namespaces.java index 10c7928b72131..23e095d93d92d 100644 --- a/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/Namespaces.java +++ b/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/Namespaces.java @@ -97,7 +97,7 @@ public void getTopics(@PathParam("tenant") String tenant, @QueryParam("mode") @DefaultValue("PERSISTENT") Mode mode, @Suspended AsyncResponse asyncResponse) { validateNamespaceName(tenant, namespace); - validateNamespaceOperation(NamespaceName.get(tenant, namespace), NamespaceOperation.LIST_TOPICS); + validateNamespaceOperation(NamespaceName.get(tenant, namespace), NamespaceOperation.GET_TOPICS); // Validate that namespace exists, throws 404 if it doesn't exist getNamespacePolicies(namespaceName); @@ -182,7 +182,7 @@ public void deleteNamespaceBundle(@PathParam("tenant") String tenant, @PathParam public Map> getPermissions(@PathParam("tenant") String tenant, @PathParam("namespace") String namespace) { validateNamespaceName(tenant, namespace); - validateNamespaceOperation(NamespaceName.get(tenant, namespace), NamespaceOperation.PERMISSION); + validateNamespaceOperation(NamespaceName.get(tenant, namespace), NamespaceOperation.GET_PERMISSION); Policies policies = getNamespacePolicies(namespaceName); return policies.auth_policies.namespace_auth; @@ -387,7 +387,7 @@ public BundlesData getBundlesData(@PathParam("tenant") String tenant, @PathParam("namespace") String namespace) { validatePoliciesReadOnlyAccess(); validateNamespaceName(tenant, namespace); - validateNamespaceOperation(NamespaceName.get(tenant, namespace), NamespaceOperation.BUNDLE); + validateNamespaceOperation(NamespaceName.get(tenant, namespace), NamespaceOperation.GET_BUNDLE); Policies policies = getNamespacePolicies(namespaceName); diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/NamespaceOperation.java b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/NamespaceOperation.java index 6030c106f5d47..67bde53fffaf2 100644 --- a/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/NamespaceOperation.java +++ b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/NamespaceOperation.java @@ -23,10 +23,20 @@ * Namespace authorization operations. */ public enum NamespaceOperation { - AUTH_MODE, - BACKLOG, - BUNDLE, - LIST_TOPICS, - PERMISSION, + CREATE_TOPIC, + GET_TOPIC, + DELETE_TOPIC, + + CLEAR_BACKLOG, + ADD_BUNDLE, + DELETE_BUNDLE, + + GET_BUNDLE, + GET_TOPICS, + GET_PERMISSION, + + GRANT_PERMISSION, + REVOKE_PERMISSION, + UNSUBSCRIBE, } diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/PolicyName.java b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/PolicyName.java index 43c7bd22e9dde..439ed7b82c9ec 100644 --- a/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/PolicyName.java +++ b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/PolicyName.java @@ -27,7 +27,7 @@ public enum PolicyName { ANTI_AFFINITY, BACKLOG, COMPACTION, - DELAYED, + DELAYED_DELIVERY, DEDUPLICATION, MAX_CONSUMERS, MAX_PRODUCERS, @@ -38,7 +38,8 @@ public enum PolicyName { RETENTION, REPLICATION, REPLICATION_RATE, - SCHEMA, + SCHEMA_COMPATIBILITY_STRATEGY, + SUBSCRIPTION_AUTH_MODE, ENCRYPTION, TTL, } diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/TopicOperation.java b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/TopicOperation.java index b3c8aa8abe551..7e54cca1515d3 100644 --- a/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/TopicOperation.java +++ b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/TopicOperation.java @@ -23,38 +23,28 @@ * Topic authorization operations. */ public enum TopicOperation { - BUNDLE_RANGE, - COMPACT, - COMPACTION_STATUS, + LOOKUP, + PRODUCE, CONSUME, - CREATE, - CREATE_PARTITIONED_TOPIC, - CREATE_SUBSCRIPTION, - DELETE, - DELETE_PARTITIONED_TOPIC, + + COMPACT, EXPIRE_MESSAGES, - EXPIRE_MESSAGES_ALL_SUBSCRIPTIONS, - GET_PARTITIONED_TOPIC_METADATA, - GRANT_PERMISSION, - INFO_INTERNAL, - LAST_MESSAGE_ID, - LIST, - LOOKUP, OFFLOAD, - OFFLOAD_STATUS, - PARTITIONED_STATS, PEEK_MESSAGES, - PERMISSIONS, - PRODUCE, RESET_CURSOR, - REVOKE_PERMISSION, SKIP, - SKIP_ALL, - STATS, - STATS_INTERNAL, - SUBSCRIPTIONS, TERMINATE, UNLOAD, + + GRANT_PERMISSION, + GET_PERMISSION, + REVOKE_PERMISSION, + + ADD_BUNDLE_RANGE, + GET_BUNDLE_RANGE, + DELETE_BUNDLE_RANGE, + + SUBSCRIBE, + GET_SUBSCRIPTIONS, UNSUBSCRIBE, - UPDATE_PARTITIONED_TOPIC, } From 462b4b52dbb025c1d8d9c3826d067737c426c5c5 Mon Sep 17 00:00:00 2001 From: "Alexandre DUVAL - @kannarfr" Date: Mon, 20 Apr 2020 18:33:56 +0200 Subject: [PATCH 19/19] order in a more meaningful way --- .../pulsar/common/policies/data/NamespaceOperation.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/NamespaceOperation.java b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/NamespaceOperation.java index 67bde53fffaf2..bda93c4d9a03d 100644 --- a/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/NamespaceOperation.java +++ b/pulsar-common/src/main/java/org/apache/pulsar/common/policies/data/NamespaceOperation.java @@ -25,18 +25,17 @@ public enum NamespaceOperation { CREATE_TOPIC, GET_TOPIC, + GET_TOPICS, DELETE_TOPIC, - CLEAR_BACKLOG, ADD_BUNDLE, DELETE_BUNDLE, - GET_BUNDLE, - GET_TOPICS, - GET_PERMISSION, + GET_PERMISSION, GRANT_PERMISSION, REVOKE_PERMISSION, + CLEAR_BACKLOG, UNSUBSCRIBE, }