blob: 7192274fd512275ff64b20babf025d1b91d57949 [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.jackrabbit.oak.spi.security.user.action;
import java.security.Principal;
import java.util.Collections;
import java.util.Set;
import javax.jcr.RepositoryException;
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.security.JackrabbitAccessControlList;
import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.jackrabbit.api.security.user.Group;
import org.apache.jackrabbit.api.security.user.User;
import org.apache.jackrabbit.oak.api.Root;
import org.apache.jackrabbit.oak.namepath.NamePathMapper;
import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
import org.apache.jackrabbit.oak.spi.security.SecurityProvider;
import org.apache.jackrabbit.oak.spi.security.authorization.AuthorizationConfiguration;
import org.apache.jackrabbit.oak.spi.security.authorization.permission.PermissionConstants;
import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration;
import org.apache.jackrabbit.oak.spi.security.user.util.UserUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The {@code AccessControlAction} allows to setup permissions upon creation
* of a new authorizable; namely the privileges the new authorizable should be
* granted on it's own 'home directory' being represented by the new node
* associated with that new authorizable.
*
* <p>The following to configuration parameters are available with this implementation:</p>
* <ul>
* <li><strong>groupPrivilegeNames</strong>: the value is expected to be a
* comma separated list of privileges that will be granted to the new group on
* the group node</li>
* <li><strong>userPrivilegeNames</strong>: the value is expected to be a
* comma separated list of privileges that will be granted to the new user on
* the user node.</li>
* </ul>
* <p>Example configuration:
* <pre>
* groupPrivilegeNames : ["jcr:read"]
* userPrivilegeNames : ["jcr:read,rep:write"]
* </pre>
* <p>This configuration could for example lead to the following content
* structure upon user or group creation. Note however that the resulting
* structure depends on the actual access control management being in place:</p>
*
* <pre>
* UserManager umgr = ((JackrabbitSession) session).getUserManager();
* User user = umgr.createUser("testUser", "t");
*
* + t rep:AuthorizableFolder
* + te rep:AuthorizableFolder
* + testUser rep:User, mix:AccessControllable
* + rep:policy rep:ACL
* + allow rep:GrantACE
* - rep:principalName = "testUser"
* - rep:privileges = ["jcr:read","rep:write"]
* - rep:password
* - rep:principalName = "testUser"
* </pre>
*
* <pre>
* UserManager umgr = ((JackrabbitSession) session).getUserManager();
* Group group = umgr.createGroup("testGroup");
*
* + t rep:AuthorizableFolder
* + te rep:AuthorizableFolder
* + testGroup rep:Group, mix:AccessControllable
* + rep:policy rep:ACL
* + allow rep:GrantACE
* - rep:principalName = "testGroup"
* - rep:privileges = ["jcr:read"]
* - rep:principalName = "testGroup"
* </pre>
*/
public class AccessControlAction extends AbstractAuthorizableAction {
private static final Logger log = LoggerFactory.getLogger(AccessControlAction.class);
public static final String USER_PRIVILEGE_NAMES = "userPrivilegeNames";
public static final String GROUP_PRIVILEGE_NAMES = "groupPrivilegeNames";
private SecurityProvider securityProvider;
private String[] groupPrivilegeNames = new String[0];
private String[] userPrivilegeNames = new String[0];
private Set<String> administrativePrincipals = Collections.emptySet();
//-------------------------------------------------< AuthorizableAction >---
@Override
public void init(SecurityProvider securityProvider, ConfigurationParameters config) {
this.securityProvider = securityProvider;
userPrivilegeNames = privilegeNames(config, USER_PRIVILEGE_NAMES);
groupPrivilegeNames = privilegeNames(config, GROUP_PRIVILEGE_NAMES);
administrativePrincipals = securityProvider.getConfiguration(AuthorizationConfiguration.class).getParameters().getConfigValue(PermissionConstants.PARAM_ADMINISTRATIVE_PRINCIPALS, Collections.EMPTY_SET);
}
@Override
public void onCreate(@NotNull Group group, @NotNull Root root, @NotNull NamePathMapper namePathMapper) throws RepositoryException {
setAC(group, root, namePathMapper);
}
@Override
public void onCreate(@NotNull User user, @Nullable String password, @NotNull Root root, @NotNull NamePathMapper namePathMapper) throws RepositoryException {
setAC(user, root, namePathMapper);
}
//------------------------------------------------------------< private >---
/**
* Gets the privilege names that be granted on the group's home directory.
*
* @param config the configuration parameters.
* @param paramName the name of the configuration option
* @return a valid string array containing the privilege names.
*/
private static String[] privilegeNames(ConfigurationParameters config, String paramName) {
String[] privilegeNames = config.getConfigValue(paramName, null, String[].class);
if (privilegeNames != null && privilegeNames.length > 0) {
return privilegeNames;
} else {
return new String[0];
}
}
private void setAC(@NotNull Authorizable authorizable, @NotNull Root root,
@NotNull NamePathMapper namePathMapper) throws RepositoryException {
if (securityProvider == null) {
throw new IllegalStateException("Not initialized");
}
if (authorizable.isGroup()) {
if (groupPrivilegeNames.length == 0) {
log.debug("No privileges configured for groups; omit ac setup.");
return;
}
} else {
if (userPrivilegeNames.length == 0) {
log.debug("No privileges configured for users; omit ac setup.");
return;
}
if (isBuiltInUser(authorizable)) {
log.debug("System user: " + authorizable.getID() + "; omit ac setup.");
return;
}
}
Principal principal = authorizable.getPrincipal();
if (administrativePrincipals.contains(principal.getName())) {
log.debug("Administrative principal: " + principal.getName() + "; omit ac setup.");
return;
}
String path = authorizable.getPath();
AuthorizationConfiguration acConfig = securityProvider.getConfiguration(AuthorizationConfiguration.class);
AccessControlManager acMgr = acConfig.getAccessControlManager(root, namePathMapper);
JackrabbitAccessControlList acl = null;
for (AccessControlPolicyIterator it = acMgr.getApplicablePolicies(path); it.hasNext();) {
AccessControlPolicy plc = it.nextAccessControlPolicy();
if (plc instanceof JackrabbitAccessControlList) {
acl = (JackrabbitAccessControlList) plc;
break;
}
}
if (acl == null) {
log.warn("Cannot process AccessControlAction: no applicable ACL at " + path);
} else {
// setup acl according to configuration.
boolean modified = false;
String[] privNames = (authorizable.isGroup()) ? groupPrivilegeNames : userPrivilegeNames;
modified = acl.addAccessControlEntry(principal, getPrivileges(privNames, acMgr));
if (modified) {
acMgr.setPolicy(path, acl);
}
}
}
private boolean isBuiltInUser(@NotNull Authorizable authorizable) throws RepositoryException {
ConfigurationParameters userConfig = securityProvider.getConfiguration(UserConfiguration.class).getParameters();
String userId = authorizable.getID();
return UserUtil.getAdminId(userConfig).equals(userId) || UserUtil.getAnonymousId(userConfig).equals(userId);
}
/**
* Retrieve privileges for the specified privilege names.
*
* @param privNames The privilege names.
* @param acMgr The access control manager.
* @return Array of {@code Privilege}
* @throws javax.jcr.RepositoryException If a privilege name cannot be
* resolved to a valid privilege.
*/
private static Privilege[] getPrivileges(@Nullable String[] privNames,
@NotNull AccessControlManager acMgr) throws RepositoryException {
if (privNames == null || privNames.length == 0) {
return new Privilege[0];
}
Privilege[] privileges = new Privilege[privNames.length];
for (int i = 0; i < privNames.length; i++) {
privileges[i] = acMgr.privilegeFromName(privNames[i]);
}
return privileges;
}
}