org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils Maven / Gradle / Ivy
/*
* 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.jackrabbit.commons.jackrabbit.authorization;
import java.security.Principal;
import java.util.HashSet;
import java.util.Set;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.security.AccessControlEntry;
import javax.jcr.security.AccessControlManager;
import javax.jcr.security.AccessControlPolicy;
import javax.jcr.security.AccessControlPolicyIterator;
import javax.jcr.security.Privilege;
import org.apache.jackrabbit.api.JackrabbitSession;
import org.apache.jackrabbit.api.security.JackrabbitAccessControlList;
import org.apache.jackrabbit.api.security.principal.PrincipalManager;
/**
* This class provides common access control related utilities.
*/
public class AccessControlUtils {
/**
* Retrieves the {@link Privilege}s from the specified privilege names.
*
* @param session The editing session.
* @param privilegeNames The privilege names.
* @return An array of privileges.
* @throws RepositoryException If an error occurs or if {@code privilegeNames}
* contains an unknown/invalid privilege name.
*/
public static Privilege[] privilegesFromNames(Session session, String... privilegeNames) throws RepositoryException {
return privilegesFromNames(session.getAccessControlManager(), privilegeNames);
}
/**
* Retrieves the {@link Privilege}s from the specified privilege names.
*
* @param accessControlManager The access control manager.
* @param privilegeNames The privilege names.
* @return An array of privileges.
* @throws RepositoryException If an error occurs or if {@code privilegeNames}
* contains an unknown/invalid privilege name.
*/
public static Privilege[] privilegesFromNames(AccessControlManager accessControlManager, String... privilegeNames) throws RepositoryException {
Set privileges = new HashSet(privilegeNames.length);
for (String privName : privilegeNames) {
privileges.add(accessControlManager.privilegeFromName(privName));
}
return privileges.toArray(new Privilege[privileges.size()]);
}
/**
* Retrieves the names of the specified privileges.
*
* @param privileges One or more privileges.
* @return The names of the specified privileges.
*/
public static String[] namesFromPrivileges(Privilege... privileges) {
if (privileges == null || privileges.length == 0) {
return new String[0];
} else {
String[] names = new String[privileges.length];
for (int i = 0; i < privileges.length; i++) {
names[i] = privileges[i].getName();
}
return names;
}
}
/**
* Utility that combines {@link AccessControlManager#getApplicablePolicies(String)}
* and {@link AccessControlManager#getPolicies(String)} to retrieve
* a modifiable {@code JackrabbitAccessControlList} for the given path.
*
* Note that the policy must be {@link AccessControlManager#setPolicy(String,
* javax.jcr.security.AccessControlPolicy) reapplied}
* and the changes must be saved in order to make the AC modifications take
* effect.
*
* @param session The editing session.
* @param absPath The absolute path of the target node.
* @return A modifiable access control list or null if there is none.
* @throws RepositoryException If an error occurs.
*/
public static JackrabbitAccessControlList getAccessControlList(Session session, String absPath) throws RepositoryException {
AccessControlManager acMgr = session.getAccessControlManager();
return getAccessControlList(acMgr, absPath);
}
/**
* Utility that combines {@link AccessControlManager#getApplicablePolicies(String)}
* and {@link AccessControlManager#getPolicies(String)} to retrieve
* a modifiable {@code JackrabbitAccessControlList} for the given path.
*
* Note that the policy must be {@link AccessControlManager#setPolicy(String,
* javax.jcr.security.AccessControlPolicy) reapplied}
* and the changes must be saved in order to make the AC modifications take
* effect.
*
* @param accessControlManager The {@code AccessControlManager} .
* @param absPath The absolute path of the target node.
* @return A modifiable access control list or null if there is none.
* @throws RepositoryException If an error occurs.
*/
public static JackrabbitAccessControlList getAccessControlList(AccessControlManager accessControlManager, String absPath) throws RepositoryException {
// try applicable (new) ACLs
AccessControlPolicyIterator itr = accessControlManager.getApplicablePolicies(absPath);
while (itr.hasNext()) {
AccessControlPolicy policy = itr.nextAccessControlPolicy();
if (policy instanceof JackrabbitAccessControlList) {
return (JackrabbitAccessControlList) policy;
}
}
// try if there is an acl that has been set before
AccessControlPolicy[] pcls = accessControlManager.getPolicies(absPath);
for (AccessControlPolicy policy : pcls) {
if (policy instanceof JackrabbitAccessControlList) {
return (JackrabbitAccessControlList) policy;
}
}
// no policy found
return null;
}
/**
* A utility method to add a new access control entry.
* Please note, that calling {@link javax.jcr.Session#save()} is required
* in order to persist the changes.
*
* @param session The editing session.
* @param absPath The absolute path of the target node.
* @param principal The principal to grant/deny privileges to.
* @param privilegeNames The names of the privileges to grant or deny.
* @param isAllow {@code true} to grant; {@code false} otherwise.
* @return {@code true} if the node's ACL was modified and the session has
* pending changes.
* @throws RepositoryException If an error occurs.
*/
public static boolean addAccessControlEntry(Session session, String absPath,
Principal principal, String[] privilegeNames,
boolean isAllow) throws RepositoryException {
return addAccessControlEntry(session, absPath, principal, privilegesFromNames(session, privilegeNames), isAllow);
}
/**
* A utility method to add a new access control entry. Please note, that
* a call to {@link javax.jcr.Session#save()} is required in order
* to persist the changes.
*
* @param session The editing session
* @param absPath The absolute path of the target node.
* @param principal The principal to grant/deny privileges to.
* @param privileges The privileges to grant or deny
* @param isAllow {@code true} to grant; {@code false} otherwise;
* @return {@code true} if the node's ACL was modified and the session has
* pending changes.
* @throws RepositoryException If an error occurs.
*/
public static boolean addAccessControlEntry(Session session, String absPath,
Principal principal, Privilege[] privileges,
boolean isAllow) throws RepositoryException {
JackrabbitAccessControlList acl = getAccessControlList(session, absPath);
if (acl != null) {
if (acl.addEntry(principal, privileges, isAllow)) {
session.getAccessControlManager().setPolicy(absPath, acl);
return true;
} // else: not modified
} // else: no acl found.
return false;
}
/**
* Utility to grant jcr:all privilege to the everyone group principal.
* Please note, that {@link javax.jcr.Session#save()} is required in order
* to persist the changes.
*
* @param session The editing session.
* @param absPath The absolute path of the target node
* @return {@code true} if the node's access control list was modified;
* {@code false} otherwise;
* @throws RepositoryException If an error occurs.
*/
public static boolean grantAllToEveryone(Session session, String absPath) throws RepositoryException {
Principal everyone = getEveryonePrincipal(session);
Privilege[] privileges = privilegesFromNames(session, Privilege.JCR_ALL);
return addAccessControlEntry(session, absPath, everyone, privileges, true);
}
/**
* Utility to deny jcr:all privilege to the everyone group principal.
* Please note, that {@link javax.jcr.Session#save()} is required in order
* to persist the changes.
*
* @param session The editing session.
* @param absPath The absolute path of the target node
* @return {@code true} if the node's access control list was modified;
* {@code false} otherwise;
* @throws RepositoryException If an error occurs.
*/
public static boolean denyAllToEveryone(Session session, String absPath) throws RepositoryException {
Principal everyone = getEveryonePrincipal(session);
Privilege[] privileges = privilegesFromNames(session, Privilege.JCR_ALL);
return addAccessControlEntry(session, absPath, everyone, privileges, false);
}
/**
* Allow certain privileges on a given node for a given principal.
*
* To activate the ACL change, session.save() must be called.
*
* @param node node to set the resource-based ACL entry on; underlying session is used to write the ACL
* @param principalName Name of the principal for which the ACL entry should apply
* @param privileges list of privileges to set by name (see {@link javax.jcr.security.Privilege})
* @return {@code true} if the node's ACL was modified and the session has pending changes.
* @throws RepositoryException If an unexpected repository error occurs
*/
public static boolean allow(Node node, String principalName, String... privileges) throws RepositoryException {
return addAccessControlEntry(
node.getSession(),
node.getPath(),
getPrincipal(node.getSession(), principalName),
privileges,
true // allow
);
}
/**
* Deny certain privileges on a node for a given principal.
*
* To activate the ACL change, session.save() must be called.
*
* @param node node to set the resource-based ACL entry on; underlying session is used to write the ACL
* @param principalName Name of the principal for which the ACL entry should apply
* @param privileges list of privileges to set by name (see {@link javax.jcr.security.Privilege})
* @return {@code true} if the node's ACL was modified and the session has pending changes.
* @throws RepositoryException If an unexpected repository error occurs
*/
public static boolean deny(Node node, String principalName, String... privileges) throws RepositoryException {
return addAccessControlEntry(
node.getSession(),
node.getPath(),
getPrincipal(node.getSession(), principalName),
privileges,
false // deny
);
}
/**
* Removes all ACL entries for a principal at a given absolute path. If the specified
* {@code principalName} is {@code null} the policy will be removed altogether.
* Modifications only take effect upon {@code Session.save()}.
*
* @param session The editing session.
* @param absPath Absolute path of an existing node from which to remove ACL entries (or the policy)
* @param principalName Name of the principal whose entries should be removed;
* use {@code null} to clear the policy.
* @return {@code true} if the policy has been modified; {@code false} otherwise.
* @throws RepositoryException If an unexpected repository error occurs
*/
public static boolean clear(Session session, String absPath, String principalName) throws RepositoryException {
AccessControlManager acm = session.getAccessControlManager();
JackrabbitAccessControlList acl = null;
// only clear if there is an existing acl (no need to retrieve applicable policies)
AccessControlPolicy[] pcls = acm.getPolicies(absPath);
for (AccessControlPolicy policy : pcls) {
if (policy instanceof JackrabbitAccessControlList) {
acl = (JackrabbitAccessControlList) policy;
}
}
if (acl != null) {
if (principalName == null) {
acm.removePolicy(absPath, acl);
return true;
} else {
Principal principal = getPrincipal(session, principalName);
if (principal == null) {
return false;
}
boolean removedEntries = false;
// remove all existing entries for principal
for (AccessControlEntry ace : acl.getAccessControlEntries()) {
if (ace.getPrincipal().equals(principal)) {
acl.removeAccessControlEntry(ace);
removedEntries = true;
}
}
if (removedEntries) {
acm.setPolicy(absPath, acl);
return true;
}
}
}
return false;
}
/**
* Removes all ACL entries for a principal on a given node.
*
* Modification to the policy only take effect upon {@code Session.save()} must be called.
*
* @param node node from which to remove ACL entries; underlying session is used to write the changes
* @param principalName Name of the principal whose entries should be removed; use {@code null} to clear the policy altogether.
* @return {@code true} if the node's ACL was modified, {@code false} otherwise.
* @throws RepositoryException If an unexpected repository error occurs
*/
public static boolean clear(Node node, String principalName) throws RepositoryException {
return clear(node.getSession(), node.getPath(), principalName);
}
/**
* Removes the access control list at a given node.
* To persist the modifications, {@code Session.save()} must be called.
*
* @param node node from which to remove the ACL; underlying session is used to write the changes
* @return {@code true} if the node's ACL was removed, {@code false} otherwise.
* @throws RepositoryException If an unexpected repository error occurs
*/
public static boolean clear(Node node) throws RepositoryException {
return clear(node, null);
}
/**
* Removes the access control list at the specified absolute path.
* To persist the modification, session.save() must be called.
*
* @param session The editing session.
* @param absPath An absolute path of a valid node accessible to the editing session from which to remove the ACL.
* @return {@code true} if the node's ACL got removed, {@code false} otherwise.
* @throws RepositoryException If an unexpected repository error occurs
*/
public static boolean clear(Session session, String absPath) throws RepositoryException {
return clear(session, absPath, null);
}
/**
* Retrieves the principal with the specified {@code principalName}. Shortcut
* for calling {@link PrincipalManager#getPrincipal(String)}.
*
* @param session The editing session which must be a {@code JackrabbitSession}.
* @param principalName The name of the principal.
* @return The principal with the specified name or {@code null} if no such principal exists.
* @throws RepositoryException If an error occurs or if the session is not a {@code JackrabbitSession}.
*/
public static Principal getPrincipal(Session session, String principalName) throws RepositoryException {
if (session instanceof JackrabbitSession) {
return ((JackrabbitSession) session).getPrincipalManager().getPrincipal(principalName);
} else {
throw new UnsupportedOperationException("Failed to retrieve principal: JackrabbitSession expected.");
}
}
/**
* Shortcut for calling {@link PrincipalManager#getEveryone()}.
*
* @param session The editing session which must be a {@code JackrabbitSession}.
* @return The group principal presenting everyone.
* @throws RepositoryException If an error occurs or if the session is not a {@code JackrabbitSession}.
*/
public static Principal getEveryonePrincipal(Session session) throws RepositoryException {
if (session instanceof JackrabbitSession) {
return ((JackrabbitSession) session).getPrincipalManager().getEveryone();
} else {
throw new UnsupportedOperationException("Failed to retrieve everyone principal: JackrabbitSession expected.");
}
}
}