blob: bc0d4b7f20be2ee74d17af8c2a502e1ce8655995 [file] [log] [blame]
/**
* 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.broker.authorization;
import java.io.Closeable;
import java.io.IOException;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import org.apache.commons.lang3.StringUtils;
import org.apache.pulsar.broker.ServiceConfiguration;
import org.apache.pulsar.broker.authentication.AuthenticationDataSource;
import org.apache.pulsar.broker.cache.ConfigurationCacheService;
import org.apache.pulsar.broker.resources.PulsarResources;
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.NamespaceOperation;
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;
import org.apache.pulsar.common.util.FutureUtil;
import org.apache.pulsar.common.util.RestException;
/**
* Provider of authorization mechanism.
*/
public interface AuthorizationProvider extends Closeable {
/**
* Check if specified role is a super user.
* @param role the role to check
* @param authenticationData authentication data related to the role
* @return a CompletableFuture containing a boolean in which true means the role is a super user
* and false if it is not
*/
default CompletableFuture<Boolean> isSuperUser(String role,
AuthenticationDataSource authenticationData,
ServiceConfiguration serviceConfiguration) {
Set<String> superUserRoles = serviceConfiguration.getSuperUserRoles();
return CompletableFuture.completedFuture(role != null && superUserRoles.contains(role));
}
/**
* @deprecated Use method {@link #isSuperUser(String, AuthenticationDataSource, ServiceConfiguration)}
* Check if specified role is a super user
* @param role the role to check
* @return a CompletableFuture containing a boolean in which true means the role is a super user
* and false if it is not
*/
default CompletableFuture<Boolean> isSuperUser(String role, ServiceConfiguration serviceConfiguration) {
Set<String> superUserRoles = serviceConfiguration.getSuperUserRoles();
return CompletableFuture.completedFuture(role != null && superUserRoles.contains(role));
}
/**
* Check if specified role is an admin of the tenant.
* @param tenant the tenant to check
* @param role the role to check
* @return a CompletableFuture containing a boolean in which true means the role is an admin user
* and false if it is not
*/
default CompletableFuture<Boolean> isTenantAdmin(String tenant, String role, TenantInfo tenantInfo,
AuthenticationDataSource authenticationData) {
return CompletableFuture.completedFuture(role != null && tenantInfo.getAdminRoles() != null
&& tenantInfo.getAdminRoles().contains(role));
}
/**
* Perform initialization for the authorization provider.
*
* @param conf
* broker config object
* @param configCache
* pulsar zk configuration cache service
* @throws IOException
* if the initialization fails
*
* @deprecated ConfigurationCacheService is not supported anymore as a way to get access to metadata.
* @see #initialize(ServiceConfiguration, PulsarResources)
*/
@Deprecated
default void initialize(ServiceConfiguration conf, ConfigurationCacheService configCache) throws IOException {
}
/**
* Perform initialization for the authorization provider.
*
* @param conf
* broker config object
* @param pulsarResources
* Resources component for access to metadata
* @throws IOException
* if the initialization fails
*/
default void initialize(ServiceConfiguration conf, PulsarResources pulsarResources) throws IOException {
// For compatibility, call the old deprecated initialize
initialize(conf, (ConfigurationCacheService) null);
}
/**
* Check if the specified role has permission to send messages to the specified fully qualified topic name.
*
* @param topicName
* the fully qualified topic name associated with the topic.
* @param role
* the app id used to send messages to the topic.
*/
CompletableFuture<Boolean> canProduceAsync(TopicName topicName, String role,
AuthenticationDataSource authenticationData);
/**
* Check if the specified role has permission to receive messages from the specified fully qualified topic name.
*
* @param topicName
* the fully qualified topic name associated with the topic.
* @param role
* the app id used to receive messages from the topic.
* @param subscription
* the subscription name defined by the client
*/
CompletableFuture<Boolean> canConsumeAsync(TopicName topicName, String role,
AuthenticationDataSource authenticationData, String subscription);
/**
* Check whether the specified role can perform a lookup for the specified topic.
*
* For that the caller needs to have producer or consumer permission.
*
* @param topicName
* @param role
* @return
* @throws Exception
*/
CompletableFuture<Boolean> canLookupAsync(TopicName topicName, String role,
AuthenticationDataSource authenticationData);
/**
* Allow all function operations with in this namespace.
* @param namespaceName The namespace that the function operations can be executed in
* @param role The role to check
* @param authenticationData authentication data related to the role
* @return a boolean to determine whether authorized or not
*/
CompletableFuture<Boolean> allowFunctionOpsAsync(NamespaceName namespaceName, String role,
AuthenticationDataSource authenticationData);
/**
* Allow all source operations with in this namespace.
* @param namespaceName The namespace that the sources operations can be executed in
* @param role The role to check
* @param authenticationData authentication data related to the role
* @return a boolean to determine whether authorized or not
*/
CompletableFuture<Boolean> allowSourceOpsAsync(NamespaceName namespaceName, String role,
AuthenticationDataSource authenticationData);
/**
* Allow all sink operations with in this namespace.
* @param namespaceName The namespace that the sink operations can be executed in
* @param role The role to check
* @param authenticationData authentication data related to the role
* @return a boolean to determine whether authorized or not
*/
CompletableFuture<Boolean> allowSinkOpsAsync(NamespaceName namespaceName, String role,
AuthenticationDataSource authenticationData);
/**
*
* Grant authorization-action permission on a namespace to the given client.
*
* @param namespace
* @param actions
* @param role
* @param authDataJson
* additional authdata in json format
* @return CompletableFuture
* @completesWith <br/>
* IllegalArgumentException when namespace not found<br/>
* IllegalStateException when failed to grant permission
*/
CompletableFuture<Void> grantPermissionAsync(NamespaceName namespace, Set<AuthAction> actions, String role,
String authDataJson);
/**
* Grant permission to roles that can access subscription-admin api.
*
* @param namespace
* @param subscriptionName
* @param roles
* @param authDataJson
* additional authdata in json format
* @return
*/
CompletableFuture<Void> grantSubscriptionPermissionAsync(NamespaceName namespace, String subscriptionName,
Set<String> roles, String authDataJson);
/**
* Revoke subscription admin-api access for a role.
* @param namespace
* @param subscriptionName
* @param role
* @return
*/
CompletableFuture<Void> revokeSubscriptionPermissionAsync(NamespaceName namespace, String subscriptionName,
String role, String authDataJson);
/**
* Grant authorization-action permission on a topic to the given client.
*
* @param topicName
* @param role
* @param authDataJson
* additional authdata in json format
* @return CompletableFuture
* @completesWith <br/>
* IllegalArgumentException when namespace not found<br/>
* IllegalStateException when failed to grant permission
*/
CompletableFuture<Void> grantPermissionAsync(TopicName topicName, Set<AuthAction> actions, String role,
String authDataJson);
/**
* Grant authorization-action permission on a tenant to the given client.
* @param tenantName
* @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<Boolean>
*/
@Deprecated
default CompletableFuture<Boolean> allowTenantOperationAsync(String tenantName, String originalRole, String role,
TenantOperation operation,
AuthenticationDataSource authData) {
return allowTenantOperationAsync(
tenantName,
StringUtils.isBlank(originalRole) ? role : originalRole,
operation,
authData
);
}
@Deprecated
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());
}
}
/**
* Check if a given <tt>role</tt> is allowed to execute a given <tt>operation</tt> on the tenant.
*
* @param tenantName tenant name
* @param role role name
* @param operation tenant operation
* @param authData authenticated data of the role
* @return a completable future represents check result
*/
default CompletableFuture<Boolean> allowTenantOperationAsync(String tenantName, 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)));
}
default Boolean allowTenantOperation(String tenantName, String role, TenantOperation operation,
AuthenticationDataSource authData) {
try {
return allowTenantOperationAsync(tenantName, role, operation, authData).get();
} catch (InterruptedException e) {
throw new RestException(e);
} catch (ExecutionException e) {
throw new RestException(e.getCause());
}
}
/**
* Check if a given <tt>role</tt> is allowed to execute a given <tt>operation</tt> on the namespace.
*
* @param namespaceName namespace name
* @param role role name
* @param operation namespace operation
* @param authData authenticated data
* @return a completable future represents check result
*/
default CompletableFuture<Boolean> allowNamespaceOperationAsync(NamespaceName namespaceName,
String role,
NamespaceOperation operation,
AuthenticationDataSource authData) {
return FutureUtil.failedFuture(
new IllegalStateException("NamespaceOperation [" + operation.name() + "] is not supported by "
+ "the Authorization provider you are using."));
}
default Boolean allowNamespaceOperation(NamespaceName namespaceName,
String role,
NamespaceOperation operation,
AuthenticationDataSource authData) {
try {
return allowNamespaceOperationAsync(namespaceName, 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
* @param role
* @param operation
* @param authData
* @return CompletableFuture<Boolean>
*/
@Deprecated
default CompletableFuture<Boolean> allowNamespaceOperationAsync(NamespaceName namespaceName,
String originalRole,
String role,
NamespaceOperation operation,
AuthenticationDataSource authData) {
return allowNamespaceOperationAsync(
namespaceName,
StringUtils.isBlank(originalRole) ? role : originalRole,
operation,
authData
);
}
@Deprecated
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());
}
}
/**
* Check if a given <tt>role</tt> is allowed to execute a given policy <tt>operation</tt> on the namespace.
*
* @param namespaceName namespace name
* @param policy policy name
* @param operation policy operation
* @param role role name
* @param authData authenticated data
* @return a completable future represents check result
*/
default CompletableFuture<Boolean> allowNamespacePolicyOperationAsync(NamespaceName namespaceName,
PolicyName policy,
PolicyOperation operation,
String role,
AuthenticationDataSource authData) {
return FutureUtil.failedFuture(
new IllegalStateException("NamespacePolicyOperation [" + policy.name() + "/" + operation.name() + "] "
+ "is not supported by is not supported by the Authorization provider you are using."));
}
default Boolean allowNamespacePolicyOperation(NamespaceName namespaceName,
PolicyName policy,
PolicyOperation operation,
String role,
AuthenticationDataSource authData) {
try {
return allowNamespacePolicyOperationAsync(namespaceName, policy, operation, role, 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
* @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<Boolean>
*/
@Deprecated
default CompletableFuture<Boolean> allowNamespacePolicyOperationAsync(NamespaceName namespaceName,
PolicyName policy,
PolicyOperation operation,
String originalRole,
String role,
AuthenticationDataSource authData) {
return allowNamespacePolicyOperationAsync(
namespaceName,
policy,
operation,
StringUtils.isBlank(originalRole) ? role : originalRole,
authData
);
}
@Deprecated
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());
}
}
/**
* Check if a given <tt>role</tt> is allowed to execute a given topic <tt>operation</tt> on the topic.
*
* @param topic topic name
* @param role role name
* @param operation topic operation
* @param authData authenticated data
* @return CompletableFuture<Boolean>
*/
default CompletableFuture<Boolean> allowTopicOperationAsync(TopicName topic,
String role,
TopicOperation operation,
AuthenticationDataSource authData) {
return FutureUtil.failedFuture(
new IllegalStateException("TopicOperation [" + operation.name() + "] is not supported by the Authorization"
+ "provider you are using."));
}
default Boolean allowTopicOperation(TopicName topicName,
String role,
TopicOperation operation,
AuthenticationDataSource authData) {
try {
return allowTopicOperationAsync(topicName, 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 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<Boolean>
*/
@Deprecated
default CompletableFuture<Boolean> allowTopicOperationAsync(TopicName topic,
String originalRole,
String role,
TopicOperation operation,
AuthenticationDataSource authData) {
return allowTopicOperationAsync(
topic,
StringUtils.isBlank(originalRole) ? role : originalRole,
operation,
authData
);
}
@Deprecated
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());
}
}
/**
* Check if a given <tt>role</tt> is allowed to execute a given topic <tt>operation</tt> on topic's <tt>policy</tt>.
*
* @param topic topic name
* @param role role name
* @param operation topic operation
* @param authData authenticated data
* @return CompletableFuture<Boolean>
*/
default CompletableFuture<Boolean> allowTopicPolicyOperationAsync(TopicName topic,
String role,
PolicyName policy,
PolicyOperation operation,
AuthenticationDataSource authData) {
return FutureUtil.failedFuture(
new IllegalStateException("TopicPolicyOperation [" + policy.name() + "/" + operation.name() + "] "
+ "is not supported by the Authorization provider you are using."));
}
default Boolean allowTopicPolicyOperation(TopicName topicName,
String role,
PolicyName policy,
PolicyOperation operation,
AuthenticationDataSource authData) {
try {
return allowTopicPolicyOperationAsync(topicName, role, policy, operation, authData).get();
} catch (InterruptedException e) {
throw new RestException(e);
} catch (ExecutionException e) {
throw new RestException(e.getCause());
}
}
}