com.adobe.acs.commons.users.impl.EnsureGroup Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of acs-aem-commons-bundle Show documentation
Show all versions of acs-aem-commons-bundle Show documentation
Main ACS AEM Commons OSGi Bundle. Includes commons utilities.
/*
* ACS AEM Commons
*
* Copyright (C) 2013 - 2023 Adobe
*
* Licensed 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 com.adobe.acs.commons.users.impl;
import java.security.Principal;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.jcr.RepositoryException;
import org.apache.commons.lang.StringUtils;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.ConfigurationPolicy;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.PropertyOption;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.jackrabbit.api.security.user.UserManager;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.commons.osgi.PropertiesUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Component(label = "ACS AEM Commons - Ensure Group",
configurationFactory = true,
metatype = true,
immediate = true,
policy = ConfigurationPolicy.REQUIRE)
@Properties({
@Property(name = "webconsole.configurationFactory.nameHint",
value = "Ensure Group: {operation} {principalName}")
})
@Service(value = { EnsureGroup.class, EnsureAuthorizable.class })
public final class EnsureGroup implements EnsureAuthorizable {
@Property(label = "Ensure immediately", boolValue = true,
description = "Ensure on activation. When set to false, this must be ensured via the JMX MBean.")
public static final String PROP_ENSURE_IMMEDIATELY = "ensure-immediately";
public static final String DEFAULT_OPERATION = "add";
@Property(
label = "Operation",
description = "Defines if the group (principal name) should be adjusted to align with this config or removed completely",
options = { @PropertyOption(name = "add", value = "Ensure existence (add)"),
@PropertyOption(name = "remove", value = "Ensure extinction (remove)") })
public static final String PROP_OPERATION = "operation";
@Property(label = "Principal Name", description = "The grouo's principal name")
public static final String PROP_PRINCIPAL_NAME = "principalName";
@Property(label = "ACEs",
description = "This field is ignored if the Operation is set to 'Ensure extinction' (remove)",
cardinality = Integer.MAX_VALUE)
public static final String PROP_ACES = "aces";
@Property(
label = "Member Of",
description = "Defines groups this group must be a member of. Group will be removed from any groups not listed.",
cardinality = Integer.MAX_VALUE)
public static final String PROP_MEMBER_OF = "member-of";
private static final Logger log = LoggerFactory.getLogger(EnsureGroup.class);
private static final String SERVICE_NAME = "ensure-service-user";
private static final Map AUTH_INFO;
private static final boolean DEFAULT_ENSURE_IMMEDIATELY = true;
static {
AUTH_INFO = Collections.singletonMap(ResourceResolverFactory.SUBSERVICE, (Object) SERVICE_NAME);
}
private Group group = null;
private Operation operation = null;
@Reference
private ResourceResolverFactory resourceResolverFactory;
@Reference
private EnsureAce ensureAce;
/**
* @return the Operation this OSGi Config represents
*/
@Override
public Operation getOperation() {
return operation;
}
/**
* @return the Service User this OSGi Config represents
*/
@Override
public Group getAuthorizable() {
return group;
}
/**
* Entry point for Ensuring a Group.
*
* @param operation
* the ensure operation to execute (ADD or REMOVE)
* @param group
* the group configuration to ensure
* @throws EnsureAuthorizableException
*/
@Override
public void ensure(Operation operation, AbstractAuthorizable group) throws EnsureAuthorizableException {
final long start = System.currentTimeMillis();
try (ResourceResolver resourceResolver = resourceResolverFactory.getServiceResourceResolver(AUTH_INFO)){
if (Operation.ADD.equals(operation)) {
ensureExistance(resourceResolver, (Group) group);
} else if (Operation.REMOVE.equals(operation)) {
ensureRemoval(resourceResolver, (Group) group);
} else {
throw new EnsureAuthorizableException(
"Unable to determine Ensure Group operation Could not create or locate value group (it is null).");
}
if (resourceResolver.hasChanges()) {
resourceResolver.commit();
log.debug("Persisted change to Group [ {} ]", group.getPrincipalName());
} else {
log.debug("No changes required for Group [ {} ]. Skipping...", group.getPrincipalName());
}
log.info("Successfully ensured [ {} ] of Group [ {} ] in [ {} ms ]", operation,
getAuthorizable().getPrincipalName(), String.valueOf(System.currentTimeMillis() - start));
} catch (Exception e) {
throw new EnsureAuthorizableException(String.format("Failed to ensure [ %s ] of Group [ %s ]",
operation.toString(), group.getPrincipalName()), e);
}
}
/**
* Ensures that the provided Group and configured ACEs exist. Any extra ACEs will be removed, and any missing ACEs
* added.
*
* @param resourceResolver
* the resource resolver to perform the group and ACE management
* @param group
* the group to ensure
* @throws RepositoryException
* @throws EnsureAuthorizableException
*/
@SuppressWarnings("squid:S2583")
protected void ensureExistance(ResourceResolver resourceResolver, Group group) throws RepositoryException,
EnsureAuthorizableException {
final org.apache.jackrabbit.api.security.user.Group jcrGroup = ensureGroup(resourceResolver, group);
if (jcrGroup != null) {
ensureAce.ensureAces(resourceResolver, jcrGroup, group);
ensureMembership(resourceResolver, jcrGroup, group);
} else {
log.error("Could not create or locate Group with principal name [ {} ]", group.getPrincipalName());
}
}
/**
* Ensures that the provided Group and any of its ACEs are removed.
*
* @param resourceResolver
* the resource resolver to perform the group and ACE management
* @param group
* the group to ensure
* @throws RepositoryException
* @throws EnsureAuthorizableException
*/
@SuppressWarnings("squid:S2589")
private void ensureRemoval(ResourceResolver resourceResolver, Group group) throws RepositoryException,
EnsureAuthorizableException {
org.apache.jackrabbit.api.security.user.Group jcrGroup = findGroup(resourceResolver, group.getPrincipalName());
ensureAce.removeAces(resourceResolver, jcrGroup, group);
if (jcrGroup != null) {
ensureRemoveMembership(jcrGroup);
jcrGroup.remove();
}
}
/**
* Ensures a Group exists with the principal name provided by the Group configuration.
*
* @param resourceResolver
* the resource resolver to perform the group management
* @param group
* the group to ensure
* @return the Group; this should never return null
* @throws RepositoryException
* @throws EnsureAuthorizableException
*/
private org.apache.jackrabbit.api.security.user.Group ensureGroup(ResourceResolver resourceResolver, Group group)
throws RepositoryException, EnsureAuthorizableException {
org.apache.jackrabbit.api.security.user.Group jcrGroup = findGroup(resourceResolver, group.getPrincipalName());
if (jcrGroup == null) {
final UserManager userManager = resourceResolver.adaptTo(UserManager.class);
// No principal found with this name; create the group
log.debug("Requesting creation of group [ {} ] at [ {} ]", group.getPrincipalName(),
group.getIntermediatePath());
jcrGroup = userManager.createGroup(new SimplePrincipal(group.getPrincipalName()), group.getIntermediatePath());
log.debug("Created group at [ {} ]", jcrGroup.getPath());
}
return jcrGroup;
}
/**
* Locates a Group by principal name, or null.
*
* @param resourceResolver
* the resource resolver to perform the group management
* @param principalName
* the principal name
* @return the Group or null
* @throws RepositoryException
* @throws EnsureAuthorizableException
*/
private org.apache.jackrabbit.api.security.user.Group findGroup(ResourceResolver resourceResolver,
String principalName) throws RepositoryException, EnsureAuthorizableException {
UserManager userManager = resourceResolver.adaptTo(UserManager.class);
org.apache.jackrabbit.api.security.user.Group jcrGroup = null;
Authorizable authorizable = userManager.getAuthorizable(principalName);
if (authorizable != null) {
// Am authorizable was found with this name; check if this is a group
if (authorizable instanceof org.apache.jackrabbit.api.security.user.Group) {
jcrGroup = (org.apache.jackrabbit.api.security.user.Group) authorizable;
} else {
throw new EnsureAuthorizableException(String.format("Authorizable [ %s ] at [ %s ] is not a group",
principalName, authorizable.getPath()));
}
}
return jcrGroup;
}
/**
* Ensure the group is direct member of all groups listed in the Ensure Group config. Any extra memberships are
* removed.
*
* @param resourceResolver
* the resource resolver to perform the group management
* @param jcrGroup
* the Jackrabbit security group object
* @param group
* the Group configuration object
* @throws EnsureAuthorizableException
* if any of the groups in the config don't exist, or exist but are not groups
* @throws RepositoryException
* if an error occurs while performing repository operations
*/
private void ensureMembership(ResourceResolver resourceResolver,
org.apache.jackrabbit.api.security.user.Group jcrGroup, Group group) throws EnsureAuthorizableException,
RepositoryException {
UserManager userManager = resourceResolver.adaptTo(UserManager.class);
List memberOf = group.getMemberOf();
Iterator groupIterator = jcrGroup.declaredMemberOf();
// for each group this group is a member of, check if it should be per config, if not remove it. if yes,
// mark it as already added
while (groupIterator.hasNext()) {
org.apache.jackrabbit.api.security.user.Group next = groupIterator.next();
String groupName = next.getPrincipal().getName();
if (!memberOf.contains(groupName)) {
// remove
next.removeMember(jcrGroup);
} else {
// mark as satisfied
group.addMembership(groupName);
}
}
for (String groupName : group.getMissingMemberOf()) {
Authorizable authorizable = userManager.getAuthorizable(groupName);
if (authorizable != null) {
if (authorizable instanceof org.apache.jackrabbit.api.security.user.Group) {
org.apache.jackrabbit.api.security.user.Group groupToAdd =
(org.apache.jackrabbit.api.security.user.Group) authorizable;
groupToAdd.addMember(jcrGroup);
} else {
throw new EnsureAuthorizableException(String.format(
"Authorizable [ %s ] at [ %s ] is not a group", groupName, authorizable.getPath()));
}
}
}
}
/**
* Remove the group from all groups it belongs to.
*
* @param jcrGroup
* the Jackrabbit security group object
* @throws RepositoryException
* if an error occurs while performing repository operations
*/
private void ensureRemoveMembership(org.apache.jackrabbit.api.security.user.Group jcrGroup)
throws RepositoryException {
Iterator groupIterator = jcrGroup.declaredMemberOf();
while (groupIterator.hasNext()) {
org.apache.jackrabbit.api.security.user.Group next = groupIterator.next();
// remove
next.removeMember(jcrGroup);
}
}
@Activate
protected void activate(final Map config) {
boolean ensureImmediately =
PropertiesUtil.toBoolean(config.get(PROP_ENSURE_IMMEDIATELY), DEFAULT_ENSURE_IMMEDIATELY);
String operationStr =
StringUtils.upperCase(PropertiesUtil.toString(config.get(PROP_OPERATION), DEFAULT_OPERATION));
try {
this.operation = Operation.valueOf(operationStr);
// Parse OSGi Configuration into Group object
this.group = new Group(config);
if (ensureImmediately) {
// Ensure
ensure(operation, getAuthorizable());
} else {
log.info("This Group is configured to NOT ensure immediately. Please ensure this Group via the JMX MBean.");
}
} catch (EnsureAuthorizableException e) {
log.error("Unable to ensure Group [ {} ]",
PropertiesUtil.toString(config.get(PROP_PRINCIPAL_NAME), "Undefined Group Principal Name"), e);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Unknown Ensure Group operation [ " + operationStr + " ]", e);
}
}
// taken from https://www.albinsblog.com/2015/04/how-to-craetemanage-groups-and-users-java-adobecq5.html
private static class SimplePrincipal implements Principal {
protected final String name;
public SimplePrincipal(String name) {
if (StringUtils.isBlank(name)) {
throw new IllegalArgumentException("Principal name cannot be blank.");
}
this.name = name;
}
public String getName() {
return name;
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Principal) {
return name.equals(((Principal) obj).getName());
}
return false;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy