Service Provider Interface for CDAP's security and authorization framework enabling pluggable authorization mechanisms.
—
The authorization management components provide the core interfaces and implementations for controlling access to CDAP resources through role-based access control and privilege management.
The main interface that authorization extensions must implement to provide custom authorization logic.
@Beta
interface Authorizer extends PrivilegesFetcher, PrivilegesManager, AuthorizationEnforcer {
/**
* Initialize the Authorizer with the provided context.
*/
void initialize(AuthorizationContext context) throws Exception;
/**
* Create a new role.
*/
void createRole(Role role) throws Exception;
/**
* Drop a role.
*/
void dropRole(Role role) throws Exception;
/**
* Add a role to the specified Principal.
*/
void addRoleToPrincipal(Role role, Principal principal) throws Exception;
/**
* Remove a role from the specified Principal.
*/
void removeRoleFromPrincipal(Role role, Principal principal) throws Exception;
/**
* List all roles for the specified Principal.
*/
Set<Role> listRoles(Principal principal) throws Exception;
/**
* List all available roles in the system.
*/
Set<Role> listAllRoles() throws Exception;
/**
* Cleanup method called when the authorizer is destroyed.
*/
void destroy() throws Exception;
}Interface for enforcing access control decisions.
@Beta
interface AuthorizationEnforcer {
/**
* Enforce authorization for a single action.
*/
void enforce(EntityId entity, Principal principal, Action action) throws Exception;
/**
* Enforce authorization for multiple actions.
*/
void enforce(EntityId entity, Principal principal, Set<Action> actions) throws Exception;
/**
* Check which entities are visible to the principal.
*/
Set<? extends EntityId> isVisible(Set<? extends EntityId> entityIds, Principal principal) throws Exception;
}Interface for managing privileges on entities.
interface PrivilegesManager {
/**
* Grant privileges to a principal on an authorizable entity.
*/
void grant(Authorizable authorizable, Principal principal, Set<Action> actions) throws Exception;
/**
* Revoke specific privileges from a principal on an authorizable entity.
*/
void revoke(Authorizable authorizable, Principal principal, Set<Action> actions) throws Exception;
/**
* Revoke all privileges on an authorizable entity.
*/
void revoke(Authorizable authorizable) throws Exception;
/**
* List all privileges for a principal.
*/
Set<Privilege> listPrivileges(Principal principal) throws Exception;
}Interface for retrieving privilege information.
interface PrivilegesFetcher {
/**
* List all privileges for the specified principal.
*/
Set<Privilege> listPrivileges(Principal principal) throws Exception;
}Abstract base class providing default implementations for lifecycle methods.
abstract class AbstractAuthorizer implements Authorizer {
protected static final Predicate<EntityId> ALLOW_ALL;
/**
* Default no-op initialization.
*/
void initialize(AuthorizationContext context) throws Exception;
/**
* Default no-op cleanup.
*/
void destroy() throws Exception;
/**
* Single-action enforcement that delegates to multi-action method.
*/
void enforce(EntityId entity, Principal principal, Action action) throws Exception;
}No-operation implementation used when authorization is disabled.
class NoOpAuthorizer extends AbstractAuthorizer {
void enforce(EntityId entity, Principal principal, Set<Action> actions) throws Exception;
Set<? extends EntityId> isVisible(Set<? extends EntityId> entityIds, Principal principal) throws Exception;
void grant(Authorizable authorizable, Principal principal, Set<Action> actions) throws Exception;
void revoke(Authorizable authorizable, Principal principal, Set<Action> actions) throws Exception;
void revoke(Authorizable authorizable) throws Exception;
void createRole(Role role);
void dropRole(Role role);
void addRoleToPrincipal(Role role, Principal principal);
void removeRoleFromPrincipal(Role role, Principal principal);
Set<Role> listRoles(Principal principal);
Set<Role> listAllRoles();
Set<Privilege> listPrivileges(Principal principal);
}Context interface providing access to CDAP services for authorization extensions.
interface AuthorizationContext extends DatasetContext, Admin, Transactional,
AuthenticationContext, SecureStore {
/**
* Get extension properties from cdap-site.xml with prefix
* security.authorization.extension.config.
*/
Properties getExtensionProperties();
// Messaging operations (all throw UnsupportedOperationException)
void createTopic(String topic) throws TopicAlreadyExistsException, IOException;
void createTopic(String topic, Map<String, String> properties) throws TopicAlreadyExistsException, IOException;
Map<String, String> getTopicProperties(String topic) throws TopicNotFoundException, IOException;
void updateTopic(String topic, Map<String, String> properties) throws TopicNotFoundException, IOException;
void deleteTopic(String topic) throws TopicNotFoundException, IOException;
}public class LdapAuthorizer extends AbstractAuthorizer {
private LdapConnection ldapConnection;
private String baseDn;
@Override
public void initialize(AuthorizationContext context) throws Exception {
Properties props = context.getExtensionProperties();
String ldapUrl = props.getProperty("ldap.url");
String bindDn = props.getProperty("ldap.bind.dn");
String bindPassword = props.getProperty("ldap.bind.password");
baseDn = props.getProperty("ldap.base.dn");
ldapConnection = new LdapConnection(ldapUrl, bindDn, bindPassword);
}
@Override
public void enforce(EntityId entity, Principal principal, Set<Action> actions)
throws Exception {
// Query LDAP to check if user has required permissions
String userDn = "uid=" + principal.getName() + "," + baseDn;
for (Action action : actions) {
if (!hasLdapPermission(userDn, entity, action)) {
throw new UnauthorizedException(principal, action, entity);
}
}
}
@Override
public void grant(Authorizable authorizable, Principal principal, Set<Action> actions)
throws Exception {
// Add LDAP group membership or attributes
String userDn = "uid=" + principal.getName() + "," + baseDn;
for (Action action : actions) {
String groupDn = buildPermissionGroupDn(authorizable, action);
ldapConnection.addUserToGroup(userDn, groupDn);
}
}
@Override
public Set<Role> listRoles(Principal principal) throws Exception {
// Query LDAP groups for user
String userDn = "uid=" + principal.getName() + "," + baseDn;
return ldapConnection.getUserGroups(userDn)
.stream()
.map(groupName -> new Role(groupName))
.collect(Collectors.toSet());
}
@Override
public void destroy() throws Exception {
if (ldapConnection != null) {
ldapConnection.close();
}
}
private boolean hasLdapPermission(String userDn, EntityId entity, Action action) {
// Implementation specific to your LDAP schema
return false;
}
private String buildPermissionGroupDn(Authorizable authorizable, Action action) {
// Build group DN based on your LDAP schema
return "cn=" + authorizable.toString() + "-" + action.name() + "," + baseDn;
}
}public class SimpleRoleAuthorizer extends AbstractAuthorizer {
private Map<String, Set<Role>> userRoles = new HashMap<>();
private Map<Role, Set<Privilege>> rolePrivileges = new HashMap<>();
@Override
public void createRole(Role role) throws Exception {
if (rolePrivileges.containsKey(role)) {
throw new AlreadyExistsException(role);
}
rolePrivileges.put(role, new HashSet<>());
}
@Override
public void addRoleToPrincipal(Role role, Principal principal) throws Exception {
if (!rolePrivileges.containsKey(role)) {
throw new NotFoundException(role);
}
userRoles.computeIfAbsent(principal.getName(), k -> new HashSet<>()).add(role);
}
@Override
public void grant(Authorizable authorizable, Principal principal, Set<Action> actions)
throws Exception {
// If granting to a role, update role privileges
if (principal.getType() == PrincipalType.ROLE) {
Role role = new Role(principal.getName());
Set<Privilege> privileges = rolePrivileges.get(role);
if (privileges != null) {
for (Action action : actions) {
privileges.add(new Privilege(authorizable, action));
}
}
}
}
@Override
public void enforce(EntityId entity, Principal principal, Set<Action> actions)
throws Exception {
Set<Privilege> userPrivileges = getUserPrivileges(principal);
for (Action action : actions) {
boolean hasPermission = userPrivileges.stream()
.anyMatch(p -> p.getAction().equals(action) &&
entityMatches(p.getAuthorizable(), entity));
if (!hasPermission) {
throw new UnauthorizedException(principal, action, entity);
}
}
}
private Set<Privilege> getUserPrivileges(Principal principal) {
Set<Privilege> privileges = new HashSet<>();
Set<Role> roles = userRoles.get(principal.getName());
if (roles != null) {
for (Role role : roles) {
Set<Privilege> rolePrivs = rolePrivileges.get(role);
if (rolePrivs != null) {
privileges.addAll(rolePrivs);
}
}
}
return privileges;
}
private boolean entityMatches(Authorizable authorizable, EntityId entity) {
// Implementation-specific logic to match entities
return authorizable.toString().equals(entity.toString());
}
}Install with Tessl CLI
npx tessl i tessl/maven-co-cask-cdap--cdap-security-spi