-
Notifications
You must be signed in to change notification settings - Fork 248
Authentication and Authorization in Pulsar Manager
- Current Status: [ “Drafted” ]
- Author: Guangning E
- Reviewers: Sijie Guo, Jia Zhai
- Pull request: -
- Mailing List Discussion: https://lists.apache.org/thread.html/e7889faf0686875c7a0471afb4acd37342348b9f1393806776d92339@%3Cdev.pulsar.apache.org%3E
Apache Pulsar is a multi-tenant distributed event streaming platform. It already provides comprehensive security-related features such as authentication and authorization. Pulsar Manager, as the management console for Apache Pulsar, should also support authentication and authorization in order to manage Apache Pulsar clusters with security features turned on. This proposal introduces a user management system for authentication and role-based access control (RBAC) system for regulating access to Pulsar resources in Pulsar Manager.
The proposed authentication and authorization system in Pulsar Manager is illustrated below.
In Pulsar Manager, Users are the ones who logged into Pulsar Manager through an identity and authentication provider, such as Google Cloud Identity, LDAP, Github, OpenID, and etc. A simple registration and login system can be implemented for users to get started easily. Pulsar Manager adopters can also customize their own implementation. Once a user logs in Pulsar Manager, he/she is authentication to interact with Pulsar Manager. Pulsar Manager will authorize he/she permissions on accessing resources based on his/her associated role bindings.
Pulsar Manager uses an RBAC system for regulating access to Pulsar resources based on the roles of individual users it manages. In the RBAC system, there are 2 top-level types.
- Role
- RoleBinding
In the RBAC system, a role contains rules that represent a set of permissions. Permissions are purely additive (there are no “deny” rules).
Permission is comprised of resources
and verbs
. resources
indicates the resource that this permission is applied on; while verbs
indicates what accesses that this permission is granted for. We use <resource-type>#<resource-name>: [ verb1, verb2 ]
for describing the permission in the remaining of this proposal.
For example, cluster#my-cluster: [admin]
indicates that the role has admin permission on my-cluster
cluster; while namespace#my-tenant/my-namespace: [admin, produce]
indicates that the role has permissions to administer my-tenant/my-namespace
and produce messages to it.
Currently, the available resources and the corresponding supported verbs are listed in the following table.
Resource Type | Resource Name | Verbs |
---|---|---|
Clusters | <cluster-name> |
Admin |
Brokers | <cluster-name> |
Admin |
Ns Isolation Policy | <cluster-name> |
Admin |
Failure Domain | <cluster-name> |
Admin |
Tenants | <tenant-name> |
Admin |
Namespaces | <namespace-name> |
Admin, Produce, Consume, Function |
Topics | <topic-name> |
Produce, Consume |
Roles | <role name> |
Admin |
RoleBinding | <rolebinding name> |
Admin |
A role binding grants the permissions defined in a role to a user or a set of users. It holds a list of subjects (users, or groups if we add in the future), and a reference to the role being granted.
Back-end services We continue to maintain the existing architecture, we only need to add a few more tables.
public interface TenantsRepository {
// Create a tenant
void save(TenantEntity tenantsEntity);
// Get tenant information by tenant id
Optional<TenantEntity> findById(long tenantId);
// Get tenant information by tenant name
Optional<TenantEntity> findByName(String tenant);
// Get tenant information, support page
Page<TenantEntity> getTenantsList(Integer pageNum, Integer pageSize);
// Delete a tenant
void remove(Long tenantId);
// Update tenant
void updateByTenant(TenantEntity tenantsEntity);
}
public interface NamespacesRepository {
// Create a namespace
void save(NamespaceEntity namespacesEntity);
// Get a namespace by namespace id
Optional<NamespaceEntity> findById(long namespaceId);
// Get a namespace by tenant and namespace
Optional<NamespaceEntity> findByTenantNamespace(String tenant, String namespace);
// Get namespace list by name of tenant or namespace
Page<NamespaceEntity> findByTenantOrNamespace(Integer pageNum, Integer pageSize, String tenantOrNamespace);
// Get namespace list by name of namespace
Page<NamespaceEntity> findByNamespace(Integer pageNum, Integer pageSize, String namespace);
// Get namespace list
Page<NamespaceEntity> getNamespacesList(Integer pageNum, Integer pageSize);
// Get namespace list by a tenant
Page<NamespaceEntity> findByTenant(Integer pageNum, Integer pageSize, String tenant);
// Delete a namespace by name of tenant and namespace
void remove(String tenant, String namespace);
// Update namespace
void update(NamespaceEntity namespacesEntity);
}
public interface UsersRepository {
// Create a user
void save(UserInfoEntity userInfoEntity);
// Obtaining user information according to id
Optional<UserInfoEntity> findById(long userId);
// Obtaining user information according to name
Optional<UserInfoEntity> findByUserName(String name);
// Get user list
Page<UserInfoEntity> findUsersList(Integer pageNum, Integer pageSize);
// Delete a user by user name
void delete(String name);
// Update user info
void update(UserInfoEntity userInfoEntity);
}
public interface RolesRepository {
// Create a role
void save(RoleEntity roleEntity);
// Get a role by role id
Optional<RoleEntity> findById(long roleId);
// Get a role by name of role
Optional<RoleEntity> findByName(String name);
// Get role list, support page
Page<RoleEntity> getRolesList(Integer pageNum, Integer pageSize);
// Delete a role by role
void remove(Long roleId);
// Update a role
void updateByRole(RoleEntity roleEntity);
}
public interface RoleBindingRepository {
// Create a role binding
void save(RoleBindingEntitty roleBindingEntitty);
// Get a role binding by binding id
Optional<RoleBindingEntitty> findById(long roleBindingId);
// Get a role binding by binding name
Optional<RoleBindingEntitty> findByName(String name);
// Get a role binding list, support page
Page<RoleBindingEntitty> getRoleBindingList(Integer pageNum, Integer pageSize);
// Delete a role binding by binding id
void remove(Long roleBindingId);
// Update role binding
void updateByRoleBinding(RoleBindingEntitty roleBindingEntitty);
}
TenantEntity, NamespaceEntity, UserEntity, RoleEntity, RoleBindingEntitty, these five objects are defined according to the following table mechanism information.
CREATE TABLE IF NOT EXISTS tenants (
tenant_id INTEGER NOT NULL PRIMARY KEY,
tenant varchar(255) NOT NULL,
allowed_clusters TEXT,
admin_roles TEXt,
UNIQUE (tenant)
);
CREATE TABLE IF NOT EXISTS namespaces (
namespace_id INTEGER NOT NULL PRIMARY KEY,
tenant varchar(255) NOT NULL,
namespace varchar(255) NOT NULL,
UNIQUE (tenant, namespace)
);
These two tables are used to store tenants and namespaces. Paging query and filtering query cannot be implemented in pulsar at present. Therefore, after users create tenants and namespaces, they need to cache one here. After users log in, they can only see their own tenants and namespaces.
CREATE TABLE IF NOT EXISTS users (
user_id BIGINT PRIMARY KEY AUTO_INCREMENT,
access_token varchar(256) NOT NULL,
name varchar(256) NOT NULL,
description varchar(128),
email varchar(256),
phone_number varchar(48),
location varchar(256),
company varchar(256),
expire LONG NOT NULL,
UNIQUE (name)
);
The user name should be unique, whether the platform logs in or the third party logs in.
CREATE TABLE IF NOT EXISTS roles (
role_id BIGINT PRIMARY KEY AUTO_INCREMENT,
role_name varchar(256) NOT NULL,
description varchar(128),
resource_type varchar(48),
resouce_name varchar(48),
resource_verbs varchar(1024),
UNIQUE (role_name)
);
Table roles are used to save the role, which is the same as the role in pulsar. The role table includes resource types, resource names, and resource verbs, as described above.
CREATE TABLE IF NOT EXISTS role_binding(
role_binding_id BIGINT PRIMARY KEY AUTO_INCREMENT,
name varchar(256) NOT NULL,
description varchar(256),
role_id BIGINT NOT NULL,
user_id BIGINT NOT NULL,
UNIQUE (name)
);
The front-end content needs to be displayed according to the role of the backend user. For example, if a user only has the permissions of namespace A, the user should not see and operate other resources. The current frontend is for super administrators, so the contents of the frontend should be redesigned and implemented.
Currently, Pulsar doesn’t provide fine granularity on access controls. Hence when implementing RBAC in Pulsar Manager, we have to map the permissions to corresponding permission in Pulsar.
For example, the clusters
, brokers
, ns isolation policy
, failure domain
can only be allocated to a super-user
role that is configured in Pulsar Cluster configurations.
In order to support managing super-user
management, we can improve the existing cluster creation workflow in Pulsar Manager. We can allow the administrator to add super-user
role for this cluster.
We have the following test plans: Add unit tests for all operation Rejected Alternatives