diff --git a/accesscontroltool-bundle/pom.xml b/accesscontroltool-bundle/pom.xml index ffed059f..5fd81a84 100644 --- a/accesscontroltool-bundle/pom.xml +++ b/accesscontroltool-bundle/pom.xml @@ -11,7 +11,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 1.8.4 + 1.8.5 diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceservice/AceService.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceservice/AceService.java index e9b1a4af..777db89e 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceservice/AceService.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceservice/AceService.java @@ -64,12 +64,11 @@ public interface AceService { * * @param session * @param history - * @param newestConfigurations + * @param configurationFileContentsByFilename * @param authorizableInstallationHistorySet * @throws Exception */ - public void installNewConfigurations(Session session, - AcInstallationHistoryPojo history, - Map newestConfigurations, Set authorizableInstallationHistorySet) - throws Exception; + public void installConfigurationFiles(Session session, AcInstallationHistoryPojo history, + Map configurationFileContentsByFilename, + Set authorizableInstallationHistorySet) throws Exception; } diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceservice/impl/AceServiceImpl.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceservice/impl/AceServiceImpl.java index b35ab0bb..e968c54d 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceservice/impl/AceServiceImpl.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceservice/impl/AceServiceImpl.java @@ -15,7 +15,6 @@ import java.util.Collection; import java.util.HashSet; import java.util.LinkedHashSet; -import java.util.List; import java.util.Map; import java.util.Set; @@ -48,6 +47,7 @@ import biz.netcentric.cq.tools.actool.authorizableutils.AuthorizableCreatorException; import biz.netcentric.cq.tools.actool.authorizableutils.AuthorizableCreatorService; import biz.netcentric.cq.tools.actool.authorizableutils.AuthorizableInstallationHistory; +import biz.netcentric.cq.tools.actool.configmodel.AcConfiguration; import biz.netcentric.cq.tools.actool.configreader.ConfigFilesRetriever; import biz.netcentric.cq.tools.actool.configreader.ConfigReader; import biz.netcentric.cq.tools.actool.configreader.ConfigurationMerger; @@ -97,30 +97,26 @@ public class AceServiceImpl implements AceService { private String configurationPath; @Activate - public void activate(@SuppressWarnings("rawtypes") final Map properties) + public void activate(final Map properties) throws Exception { LOG.debug("Activated AceService!"); modified(properties); } @Modified - public void modified(@SuppressWarnings("rawtypes") final Map properties) { + public void modified(final Map properties) { LOG.debug("Modified AceService!"); configurationPath = PropertiesUtil.toString(properties.get(PROPERTY_CONFIGURATION_PATH), ""); } - // FIXME: Why is this called installConfigurationFromYamlList if it doesn't use YAML? - private void installConfigurationFromYamlList( - final List mergedConfigurations, AcInstallationHistoryPojo history, + private void installAcConfiguration( + AcConfiguration acConfiguration, AcInstallationHistoryPojo history, final Session session, Set authorizableHistorySet, Map> repositoryDumpAceMap) throws Exception { - // FIXME: putting different types of configuration objects in a heterogeneous list is pretty bad - Map> authorizablesMapfromConfig = (Map>) mergedConfigurations - .get(0); - Map> aceMapFromConfig = (Map>) mergedConfigurations - .get(1); + Map> authorizablesMapfromConfig = acConfiguration.getAuthorizablesConfig(); + Map> aceMapFromConfig = acConfiguration.getAceConfig(); if (aceMapFromConfig == null) { String message = "ace config not found in YAML file! installation aborted!"; @@ -135,7 +131,7 @@ private void installConfigurationFromYamlList( installAces(history, session, aceMapFromConfig); } - private Set getAuthorizablesToRemoveAcesFor(Map> authorizablesMapfromConfig) { + private Set getAuthorizablesToRemoveAcesFor(Map> authorizablesMapfromConfig) { Set authorizablesToRemoveAcesFor = new HashSet(authorizablesMapfromConfig.keySet()); Set authorizablesToBeMigrated = collectAuthorizablesToBeMigrated(authorizablesMapfromConfig); Collection invalidAuthorizablesInConfig = CollectionUtils.intersection(authorizablesToRemoveAcesFor, authorizablesToBeMigrated); @@ -149,10 +145,10 @@ private Set getAuthorizablesToRemoveAcesFor(Map collectAuthorizablesToBeMigrated(Map> authorizablesMapfromConfig) { + private Set collectAuthorizablesToBeMigrated(Map> authorizablesMapfromConfig) { Set authorizablesToBeMigrated = new HashSet(); for (String principalStr : authorizablesMapfromConfig.keySet()) { - LinkedHashSet authorizableConfigBeans = authorizablesMapfromConfig.get(principalStr); + Set authorizableConfigBeans = authorizablesMapfromConfig.get(principalStr); for (AuthorizableConfigBean authorizableConfigBean : authorizableConfigBeans) { String migrateFrom = authorizableConfigBean.getMigrateFrom(); if (StringUtils.isNotBlank(migrateFrom)) { @@ -203,7 +199,7 @@ private void installAces( private void installAuthorizables( AcInstallationHistoryPojo history, Set authorizableHistorySet, - Map> authorizablesMapfromConfig) + Map> authorizablesMapfromConfig) throws RepositoryException, Exception { // --- installation of Authorizables from configuration --- @@ -265,7 +261,7 @@ public AcInstallationHistoryPojo execute() { String rootPath = getConfigurationRootPath(); Node rootNode = session.getNode(rootPath); Map newestConfigurations = configFilesRetriever.getConfigFileContentFromNode(rootNode); - installNewConfigurations(session, history, newestConfigurations, authorizableInstallationHistorySet); + installConfigurationFiles(session, history, newestConfigurations, authorizableInstallationHistorySet); } catch (AuthorizableCreatorException e) { history.addError(e.toString()); // here no rollback of authorizables necessary since session wasn't @@ -299,7 +295,7 @@ public AcInstallationHistoryPojo execute() { /** Common entry point for JMX and install hook. */ @Override - public void installNewConfigurations(Session session, AcInstallationHistoryPojo history, Map currentConfiguration, + public void installConfigurationFiles(Session session, AcInstallationHistoryPojo history, Map configurationFileContentsByFilename, Set authorizableInstallationHistorySet) throws Exception { @@ -313,13 +309,14 @@ public void installNewConfigurations(Session session, AcInstallationHistoryPojo LOG.info(message); history.addMessage(message); - if (currentConfiguration != null) { + if (configurationFileContentsByFilename != null) { - history.setConfigFileContentsByName(currentConfiguration); + AcConfiguration acConfiguration = configurationMerger.getMergedConfigurations(configurationFileContentsByFilename, history, + configReader); + history.setAcConfiguration(acConfiguration); + history.setConfigFileContentsByName(configurationFileContentsByFilename); - List mergedConfigurations = configurationMerger.getMergedConfigurations(currentConfiguration, history, configReader); - - installMergedConfigurations(history, session, authorizableInstallationHistorySet, mergedConfigurations); + installMergedConfigurations(history, session, authorizableInstallationHistorySet, acConfiguration); // if everything went fine (no exceptions), save the session // thus persisting the changed ACLs @@ -352,7 +349,7 @@ private void installMergedConfigurations( AcInstallationHistoryPojo history, Session session, Set authorizableInstallationHistorySet, - List mergedConfigurations) throws ValueFormatException, + AcConfiguration acConfiguration) throws ValueFormatException, RepositoryException, Exception { String message = "Starting installation of merged configurations..."; @@ -366,9 +363,7 @@ private void installMergedConfigurations( AcHelper.ACE_ORDER_NONE, dumpservice.getQueryExcludePaths()).getAceDump(); - installConfigurationFromYamlList(mergedConfigurations, history, - session, authorizableInstallationHistorySet, - repositoryDumpAceMap); + installAcConfiguration(acConfiguration, history, session, authorizableInstallationHistorySet, repositoryDumpAceMap); } @@ -603,9 +598,9 @@ public Set getAllAuthorizablesFromConfig(Session session) AcInstallationHistoryPojo history = new AcInstallationHistoryPojo(); Node rootNode = session.getNode(configurationPath); Map newestConfigurations = configFilesRetriever.getConfigFileContentFromNode(rootNode); - List mergedConfigurations = configurationMerger.getMergedConfigurations( - newestConfigurations, history, configReader); - return ((Map>) mergedConfigurations.get(0)).keySet(); + AcConfiguration acConfiguration = configurationMerger.getMergedConfigurations(newestConfigurations, history, configReader); + Set allAuthorizablesFromConfig = acConfiguration.getAceConfig().keySet(); + return allAuthorizablesFromConfig; } } diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/authorizableutils/AuthorizableCreatorService.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/authorizableutils/AuthorizableCreatorService.java index 90126134..fe7f0fa8 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/authorizableutils/AuthorizableCreatorService.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/authorizableutils/AuthorizableCreatorService.java @@ -8,8 +8,8 @@ */ package biz.netcentric.cq.tools.actool.authorizableutils; -import java.util.LinkedHashSet; import java.util.Map; +import java.util.Set; import javax.jcr.AccessDeniedException; import javax.jcr.RepositoryException; @@ -23,7 +23,7 @@ public interface AuthorizableCreatorService { public void createNewAuthorizables( - Map> principalMapFromConfig, + Map> principalMapFromConfig, final Session session, AcInstallationHistoryPojo status, AuthorizableInstallationHistory authorizableInstallationHistory) throws AccessDeniedException, diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/authorizableutils/impl/AuthorizableCreatorServiceImpl.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/authorizableutils/impl/AuthorizableCreatorServiceImpl.java index 0213ae68..d4901029 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/authorizableutils/impl/AuthorizableCreatorServiceImpl.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/authorizableutils/impl/AuthorizableCreatorServiceImpl.java @@ -10,11 +10,13 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collection; import java.util.HashSet; import java.util.Iterator; -import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; +import java.util.regex.Pattern; import javax.jcr.AccessDeniedException; import javax.jcr.Node; @@ -28,6 +30,7 @@ import javax.jcr.nodetype.ConstraintViolationException; import javax.jcr.version.VersionException; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Service; @@ -51,22 +54,24 @@ import biz.netcentric.cq.tools.actool.installationhistory.AcInstallationHistoryPojo; @Service -@Component(metatype = true, label = "AuthorizableCreatorService Service", description = "Service that installs groups according to textual configuration files") +@Component(metatype = true, label = "AC AuthorizableCreatorService", description = "Service that installs groups according to textual configuration files") public class AuthorizableCreatorServiceImpl implements AuthorizableCreatorService { + private static final Logger LOG = LoggerFactory.getLogger(AuthorizableCreatorServiceImpl.class); + private static final String PATH_HOME_GROUPS = "/home/groups"; private static final String PATH_HOME_USERS = "/home/users"; - private static final Logger LOG = LoggerFactory.getLogger(AuthorizableCreatorServiceImpl.class); + private static final String PRINCIPAL_EVERYONE = "everyone"; AcInstallationHistoryPojo status; - Map> principalMapFromConfig; + Map> principalMapFromConfig; AuthorizableInstallationHistory authorizableInstallationHistory; @Override public void createNewAuthorizables( - Map> principalMapFromConfig, + Map> principalMapFromConfig, final Session session, AcInstallationHistoryPojo status, AuthorizableInstallationHistory authorizableInstallationHistory) throws AccessDeniedException, @@ -81,7 +86,7 @@ public void createNewAuthorizables( for (String principalId : groupsFromConfigurations) { - LinkedHashSet currentPrincipalData = principalMapFromConfig + Set currentPrincipalData = principalMapFromConfig .get(principalId); Iterator it = currentPrincipalData .iterator(); @@ -345,9 +350,7 @@ private void mergeGroup(AcInstallationHistoryPojo status, currentGroupFromRepository.getPath(), membershipGroupsFromRepository); - mergeMemberOfGroups(principalId, status, userManager, - currentGroupFromRepository, membershipGroupsFromConfig, - membershipGroupsFromRepository); + mergeMemberOfGroups(principalId, status, userManager, membershipGroupsFromConfig, membershipGroupsFromRepository); } private String getAuthorizableName(Authorizable currentGroupFromRepository) throws RepositoryException, ValueFormatException { @@ -389,19 +392,15 @@ private Authorizable createNewAuthorizable( return newAuthorizable; } - private Set getMembershipGroupsFromRepository( - Authorizable currentGroupFromRepository) throws RepositoryException { + private Set getMembershipGroupsFromRepository(Authorizable currentGroupFromRepository) throws RepositoryException { Set membershipGroupsFromRepository = new HashSet(); - Iterator memberOfGroupsIterator = currentGroupFromRepository - .declaredMemberOf(); + Iterator memberOfGroupsIterator = currentGroupFromRepository.declaredMemberOf(); // build Set which contains the all Groups of which the existingGroup is // a member of - while (memberOfGroupsIterator.hasNext()) { Authorizable memberOfGroup = memberOfGroupsIterator.next(); membershipGroupsFromRepository.add(memberOfGroup.getID()); - } return membershipGroupsFromRepository; } @@ -418,178 +417,80 @@ private Set getMembershipGroupsFromConfig(String[] memberOf) { return membershipGroupsFromConfig; } - private void mergeMemberOfGroups(String principalId, + @SuppressWarnings("unchecked") + void mergeMemberOfGroups(String principalId, AcInstallationHistoryPojo status, UserManager userManager, - Authorizable currentGroupFromRepository, Set membershipGroupsFromConfig, Set membershipGroupsFromRepository) throws RepositoryException, AuthorizableExistsException, AuthorizableCreatorException { - LOG.debug("...checking differences"); + LOG.debug("mergeMemberOfGroups() for {}", principalId); - // group in repo doesn't have any members and group in config doesn't - // have any members - // do nothing - if (!isMemberOfOtherGroup(currentGroupFromRepository) - && membershipGroupsFromConfig.isEmpty()) { - LOG.debug( - "{}: authorizable in repo is not member of any other group and group in config is not member of any other group. No change necessary here!", - principalId); - } + // membership to everyone cannot be removed or added => take it out from both lists + membershipGroupsFromConfig.remove(PRINCIPAL_EVERYONE); + membershipGroupsFromRepository.remove(PRINCIPAL_EVERYONE); - // group in repo is not member of any other group but group in config is - // member of at least one other group - // transfer members to group in repo + logAndVerboseHistoryMessage(status, "Principal " + principalId + " isMemberOf(repo)=" + membershipGroupsFromRepository); + logAndVerboseHistoryMessage(status, "Principal " + principalId + " isMemberOf(conifg)=" + membershipGroupsFromConfig); - else if (!isMemberOfOtherGroup(currentGroupFromRepository) - && !membershipGroupsFromConfig.isEmpty()) { - mergeMemberOfGroupsFromConfig(principalId, status, userManager, - membershipGroupsFromConfig); + Set validatedMembershipGroupsFromConfig = validateAssignedGroups(userManager, principalId, membershipGroupsFromConfig); - // group in repo is member of at least one other group and group in - // config is not member of any other group + Collection unChangedMembers = CollectionUtils.intersection(membershipGroupsFromRepository, + validatedMembershipGroupsFromConfig); + logAndVerboseHistoryMessage(status, "Principal " + principalId + " remains member of groups " + unChangedMembers); - } else if (isMemberOfOtherGroup(currentGroupFromRepository) - && membershipGroupsFromConfig.isEmpty()) { + Collection toBeAddedMembers = CollectionUtils.subtract(validatedMembershipGroupsFromConfig, membershipGroupsFromRepository); + logAndVerboseHistoryMessage(status, "Principal " + principalId + " will be added as member of " + toBeAddedMembers); - mergeMemberOfGroupsFromRepo(principalId, userManager, - membershipGroupsFromRepository); - } - - // group in repo does have members and group in config does have members - - else if (isMemberOfOtherGroup(currentGroupFromRepository) - && !membershipGroupsFromConfig.isEmpty()) { - mergeMultipleMembersOfBothGroups(principalId, status, userManager, - membershipGroupsFromConfig, membershipGroupsFromRepository); - } - } + Collection toBeRemovedMembers = CollectionUtils.subtract(membershipGroupsFromRepository, + validatedMembershipGroupsFromConfig); + Set toBeSkippedFromRemovalMembers = new HashSet(); - private void mergeMemberOfGroupsFromRepo(String principalId, - UserManager userManager, Set membershipGroupsFromRepository) - throws RepositoryException { - LOG.debug( - "{}: authorizable in repo is member of at least one other group and authorizable in config is not member of any other group", - principalId); - // delete memberOf groups of that group in repo - for (String group : membershipGroupsFromRepository) { - LOG.debug( - "{}: delete authorizable from members of group {} in repository", - principalId, group); - ((Group) userManager.getAuthorizable(group)) - .removeMember(userManager.getAuthorizable(principalId)); - } - } + Pattern ignoredMembershipsPattern = status.getAcConfiguration().getGlobalConfiguration().getAllowExternalGroupNamesRegEx(); - private void mergeMemberOfGroupsFromConfig(String principalId, - AcInstallationHistoryPojo status, UserManager userManager, - Set membershipGroupsFromConfig) throws RepositoryException, - AuthorizableExistsException, AuthorizableCreatorException { - LOG.debug( - "{}: authorizable in repo is not member of any other group but authorizable in config is member of at least one other group", - principalId); - - Set validatedGroups = validateAssignedGroups(userManager, - principalId, membershipGroupsFromConfig.toArray(new String[membershipGroupsFromConfig.size()])); - - for (Group membershipGroup : validatedGroups) { - LOG.debug( - "{}: add authorizable to members of group {} in repository", - principalId, membershipGroup.getID()); - - if (StringUtils.equals(membershipGroup.getID(), principalId)) { - String warning = "Attempt to add a group as member of itself (" - + membershipGroup.getID() + ")."; - LOG.warn(warning); - status.addWarning(warning); - } else { - ((Group) membershipGroup).addMember(userManager - .getAuthorizable(principalId)); + Iterator toBeRemovedMembersIt = toBeRemovedMembers.iterator(); + while (toBeRemovedMembersIt.hasNext()) { + String groupId = toBeRemovedMembersIt.next(); + if (ignoredMembershipsPattern != null && ignoredMembershipsPattern.matcher(groupId).find()) { + toBeSkippedFromRemovalMembers.add(groupId); + toBeRemovedMembersIt.remove(); } } - } - - private void mergeMultipleMembersOfBothGroups(String principalId, - AcInstallationHistoryPojo status, UserManager userManager, - Set membershipGroupsFromConfig, - Set membershipGroupsFromRepository) - throws RepositoryException, AuthorizableExistsException, - AuthorizableCreatorException { - - // are both groups members of exactly the same groups? - if (membershipGroupsFromRepository.equals(membershipGroupsFromConfig)) { - // do nothing! - LOG.debug( - "{}: authorizable in repo and authorizable in config are members of the same group(s). No change necessary here!", - principalId); - } else { - LOG.debug( - "{}: authorizable in repo is member of at least one other group and authorizable in config is member of at least one other group", - principalId); - - // loop through memberOf-groups of group from repo - - for (String authorizable : membershipGroupsFromRepository) { - // is current a also contained in memberOf-groups property of - // existing group? - if (membershipGroupsFromConfig.contains(authorizable)) { - continue; - - } else { - // if not delete that group of membersOf-property of - // existing group - - LOG.debug( - "delete {} from members of group {} in repository", - principalId, authorizable); - ((Group) userManager.getAuthorizable(authorizable)) - .removeMember(userManager - .getAuthorizable(principalId)); - } - } + logAndVerboseHistoryMessage(status, "Principal " + principalId + " will be removed from members of " + toBeRemovedMembers); - Set validatedGroups = validateAssignedGroups( - userManager, principalId, - membershipGroupsFromConfig.toArray(new String[0])); - for (Group validatedGroup : validatedGroups) { + if (!toBeSkippedFromRemovalMembers.isEmpty()) { + logAndVerboseHistoryMessage(status, "Principal " + principalId + " remains member of groups " + + toBeSkippedFromRemovalMembers + " (due to configured ignoredMembershipsPattern=" + ignoredMembershipsPattern + ")"); - // is current group also contained in memberOf-groups property - // of repo group? + } - if (membershipGroupsFromRepository.contains(validatedGroup - .getID())) { - continue; - } else { + // perform changes - // if not add that group to membersOf-property of existing - // group + Authorizable currentAuthorizable = userManager.getAuthorizable(principalId); - LOG.debug("add {} to members of group {} in repository", - principalId, validatedGroup); - if (StringUtils.equals(validatedGroup.getID(), principalId)) { - String warning = "Attempt to add a group as member of itself (" - + validatedGroup + ")."; - LOG.warn(warning); - status.addWarning(warning); - } else { - ((Group) validatedGroup).addMember(userManager - .getAuthorizable(principalId)); + for (String groupId : toBeAddedMembers) { + LOG.debug("Membership Change: Adding {} to members of group {} in repository", principalId, groupId); + Authorizable targetAuthorizable = userManager.getAuthorizable(groupId); + ((Group) targetAuthorizable).addMember(currentAuthorizable); + } - } - } - } + for (String groupId : toBeRemovedMembers) { + LOG.debug("Membership Change: Removing {} from members of group {} in repository", principalId, groupId); + Authorizable targetAuthorizable = userManager.getAuthorizable(groupId); + ((Group) targetAuthorizable).removeMember(currentAuthorizable); + } + if (!toBeAddedMembers.isEmpty() && !toBeAddedMembers.isEmpty()) { + logAndVerboseHistoryMessage(status, + "Membership Change: Principal " + principalId + " was added to " + toBeAddedMembers.size() + + " and removed from " + toBeRemovedMembers.size() + " groups"); } - } - private boolean isMemberOfOtherGroup(Authorizable group) - throws RepositoryException { - Iterator it = group.declaredMemberOf(); + } - if (!it.hasNext()) { - return false; - } - return true; + private void logAndVerboseHistoryMessage(AcInstallationHistoryPojo status, String msg) { + LOG.debug(msg); + status.addVerboseMessage(msg); } private Authorizable createNewGroup( @@ -598,7 +499,7 @@ private Authorizable createNewGroup( AcInstallationHistoryPojo status, AuthorizableInstallationHistory authorizableInstallationHistory, ValueFactory vf, - Map> principalMapFromConfig, Session session) + Map> principalMapFromConfig, Session session) throws AuthorizableExistsException, RepositoryException, AuthorizableCreatorException { @@ -653,9 +554,6 @@ private void setAuthorizableProperties(Authorizable authorizable, ValueFactory v authorizable.setProperty("profile/aboutMe", vf.createValue(description)); } } - - - private Authorizable createNewUser( final UserManager userManager, @@ -663,7 +561,7 @@ private Authorizable createNewUser( AcInstallationHistoryPojo status, AuthorizableInstallationHistory authorizableInstallationHistory, ValueFactory vf, - Map> principalMapFromConfig, Session session) + Map> principalMapFromConfig, Session session) throws AuthorizableExistsException, RepositoryException, AuthorizableCreatorException { String principalId = principalConfigBean.getPrincipalID(); @@ -690,10 +588,12 @@ private void addMembersToReferencingAuthorizables(Authorizable authorizable, Aut String[] memberOf = principalConfigBean.getMemberOf(); if ((authorizable != null) && (memberOf != null) && (memberOf.length > 0)) { // add group to groups according to configuration - Set referencingAuthorizablesToBeChanged = validateAssignedGroups(userManager, principalId, memberOf); + Set referencingAuthorizablesToBeChanged = validateAssignedGroups(userManager, principalId, + new HashSet(Arrays.asList(memberOf))); if (!referencingAuthorizablesToBeChanged.isEmpty()) { LOG.debug("start adding {} to assignedGroups", principalId); - for (Group referencingAuthorizableToBeChanged : referencingAuthorizablesToBeChanged) { + for (String referencingAuthorizableToBeChangedId : referencingAuthorizablesToBeChanged) { + Group referencingAuthorizableToBeChanged = (Group) userManager.getAuthorizable(referencingAuthorizableToBeChangedId); referencingAuthorizableToBeChanged.addMember(authorizable); LOG.debug("added to {} ", referencingAuthorizableToBeChanged); } @@ -733,17 +633,21 @@ private User userManagerCreateSystemUserViaReflection(UserManager userManager, S * * @param userManager * @param authorizablelID the ID of authorizable to validate - * @param memberOf String array that contains the groups which the authorizable should be a member of + * @param isMemberOf String array that contains the groups which the authorizable should be a member of * @return Set of authorizables which the current authorizable is a member of * @throws RepositoryException * @throws AuthorizableCreatorException if one of the authorizables contained in membersOf array is a user */ - private Set validateAssignedGroups( + Set validateAssignedGroups( final UserManager userManager, final String authorizablelID, - final String[] memberOf) throws RepositoryException, + final Set isMemberOf) throws RepositoryException, AuthorizableCreatorException { - Set authorizableSet = new HashSet(); - for (String memberOfPrincipal : memberOf) { + Set authorizableSet = new HashSet(); + for (String memberOfPrincipal : isMemberOf) { + + if (StringUtils.equals(authorizablelID, memberOfPrincipal)) { + throw new AuthorizableCreatorException("Cannot add authorizable " + authorizablelID + " as member of itself."); + } Authorizable authorizable = userManager.getAuthorizable(memberOfPrincipal); @@ -754,17 +658,12 @@ private Set validateAssignedGroups( // check if authorizable is a group if (authorizable.isGroup()) { - authorizableSet.add((Group) authorizable); + authorizableSet.add(authorizable.getID()); } else { String message = "Failed to add authorizable " - + authorizablelID + "to autorizable " + memberOfPrincipal + + authorizablelID + " to autorizable " + memberOfPrincipal + "! Authorizable is not a group"; - LOG.warn(message); - - throw new AuthorizableCreatorException( - "Failed to add authorizable " + authorizablelID - + "to autorizable " + memberOfPrincipal - + "! Authorizable is not a group"); + throw new AuthorizableCreatorException(message); } // if authorizable doesn't exist yet, it gets created and the // current authorizable gets added as a member @@ -774,7 +673,7 @@ private Set validateAssignedGroups( if (principalMapFromConfig.keySet().contains(memberOfPrincipal)) { // get authorizable intermediatePath - LinkedHashSet authorizableConfigSet = principalMapFromConfig.get(memberOfPrincipal); + Set authorizableConfigSet = principalMapFromConfig.get(memberOfPrincipal); Iterator it = authorizableConfigSet.iterator(); AuthorizableConfigBean authorizableConfigBean = null; while (it.hasNext()) { @@ -788,10 +687,9 @@ private Set validateAssignedGroups( Group newGroup = userManager.createGroup( new PrincipalImpl(memberOfPrincipal), authorizableConfigBean.getPath()); - authorizableSet.add(newGroup); + authorizableSet.add(newGroup.getID()); authorizableInstallationHistory.addNewCreatedAuthorizable(newGroup.getID()); - LOG.warn("Failed to add {} to group {} - Created group", - authorizablelID, memberOfPrincipal); + LOG.info("Created group to be able to add {} to group {} ", authorizablelID, memberOfPrincipal); } else { String message = "Failed to add group: " + authorizablelID diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configmodel/AcConfiguration.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configmodel/AcConfiguration.java new file mode 100644 index 00000000..bc32d0bc --- /dev/null +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configmodel/AcConfiguration.java @@ -0,0 +1,53 @@ +/* + * (C) Copyright 2016 Netcentric AG. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package biz.netcentric.cq.tools.actool.configmodel; + +import java.util.Map; +import java.util.Set; + +import biz.netcentric.cq.tools.actool.authorizableutils.AuthorizableConfigBean; +import biz.netcentric.cq.tools.actool.helper.AceBean; + +/** Root class of the configuration model as it is constructed from the multiple yaml files, it is a fully merged configuration. All loops + * and variables have been processed. */ +public class AcConfiguration { + + private GlobalConfiguration globalConfiguration; + + // to be changed from map to PojoClass in future versions + private Map> authorizablesMap; + + // to be changed from map to PojoClass in future versions + private Map> aceMap; + + public GlobalConfiguration getGlobalConfiguration() { + return globalConfiguration; + } + + public void setGlobalConfiguration(GlobalConfiguration globalConfiguration) { + this.globalConfiguration = globalConfiguration; + } + + public Map> getAuthorizablesConfig() { + return authorizablesMap; + } + + public void setAuthorizablesConfig(Map> authorizablesMap) { + this.authorizablesMap = authorizablesMap; + } + + public Map> getAceConfig() { + return aceMap; + } + + public void setAceConfig(Map> aceMap) { + this.aceMap = aceMap; + } + +} diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configmodel/GlobalConfiguration.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configmodel/GlobalConfiguration.java new file mode 100644 index 00000000..264c5924 --- /dev/null +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configmodel/GlobalConfiguration.java @@ -0,0 +1,78 @@ +/* + * (C) Copyright 2016 Netcentric AG. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package biz.netcentric.cq.tools.actool.configmodel; + +import java.util.Map; +import java.util.regex.Pattern; + +import org.apache.commons.lang.StringUtils; + +import biz.netcentric.cq.tools.actool.validators.GlobalConfigurationValidator; + +/** Global configuration that applies to the overall access control configuration system. */ +public class GlobalConfiguration { + + public static final String KEY_MIN_REQUIRED_VERSION = "minRequiredVersion"; + public static final String KEY_ALLOW_EXTERNAL_GROUP_NAMES_REGEX = "allowExternalGroupNamesRegEx"; + + private Pattern allowExternalGroupNamesRegEx; + private String minRequiredVersion; + + public GlobalConfiguration() { + } + + public GlobalConfiguration(Map globalConfigMap) { + + if (globalConfigMap != null) { + setAllowExternalGroupNamesRegEx((String) globalConfigMap.get(KEY_ALLOW_EXTERNAL_GROUP_NAMES_REGEX)); + setMinRequiredVersion((String) globalConfigMap.get(KEY_MIN_REQUIRED_VERSION)); + } + + } + + public void merge(GlobalConfiguration otherGlobalConfig) { + + if (otherGlobalConfig.getAllowExternalGroupNamesRegEx() != null) { + if (allowExternalGroupNamesRegEx == null) { + allowExternalGroupNamesRegEx = otherGlobalConfig.getAllowExternalGroupNamesRegEx(); + } else { + throw new IllegalArgumentException("Duplicate config for " + KEY_ALLOW_EXTERNAL_GROUP_NAMES_REGEX); + } + } + + if (otherGlobalConfig.getMinRequiredVersion() != null) { + if (minRequiredVersion == null) { + minRequiredVersion = otherGlobalConfig.getMinRequiredVersion(); + } else { + minRequiredVersion = GlobalConfigurationValidator.versionCompare(otherGlobalConfig.getMinRequiredVersion(), minRequiredVersion) > 0 + ? otherGlobalConfig.getMinRequiredVersion() : minRequiredVersion; + } + } + + } + + public Pattern getAllowExternalGroupNamesRegEx() { + return allowExternalGroupNamesRegEx; + } + + public void setAllowExternalGroupNamesRegEx(String allowExternalGroupNamesRegEx) { + this.allowExternalGroupNamesRegEx = StringUtils.isNotBlank(allowExternalGroupNamesRegEx) + ? Pattern.compile(allowExternalGroupNamesRegEx) : null; + } + + public String getMinRequiredVersion() { + return minRequiredVersion; + } + + public void setMinRequiredVersion(String requiredMinVersion) { + this.minRequiredVersion = requiredMinVersion; + } + + +} diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configreader/ConfigReader.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configreader/ConfigReader.java index 5e8235df..b43a099b 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configreader/ConfigReader.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configreader/ConfigReader.java @@ -15,6 +15,7 @@ import javax.jcr.RepositoryException; import biz.netcentric.cq.tools.actool.authorizableutils.AuthorizableConfigBean; +import biz.netcentric.cq.tools.actool.configmodel.GlobalConfiguration; import biz.netcentric.cq.tools.actool.helper.AceBean; import biz.netcentric.cq.tools.actool.validators.AceBeanValidator; import biz.netcentric.cq.tools.actool.validators.AuthorizableValidator; @@ -36,4 +37,8 @@ public Map> getUserConfigurationBeans( final Collection userConfigData, AuthorizableValidator authorizableValidator) throws AcConfigBeanValidationException; + + public GlobalConfiguration getGlobalConfiguration(final Collection yamlList); + + } \ No newline at end of file diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configreader/ConfigurationMerger.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configreader/ConfigurationMerger.java index 547c974b..ba6797d9 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configreader/ConfigurationMerger.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configreader/ConfigurationMerger.java @@ -8,36 +8,27 @@ */ package biz.netcentric.cq.tools.actool.configreader; -import java.util.List; import java.util.Map; import javax.jcr.RepositoryException; +import biz.netcentric.cq.tools.actool.configmodel.AcConfiguration; import biz.netcentric.cq.tools.actool.installationhistory.AcInstallationHistoryPojo; import biz.netcentric.cq.tools.actool.validators.exceptions.AcConfigBeanValidationException; public interface ConfigurationMerger { - /** - * Method that merges several textual AccessControlConfigurations written in - * YAML format, each comprising of a groups and ACE configuration. - * Validation ensures that no doubled defined groups and only valid section - * identifiers in configuration files are possible + /** Method that merges several textual AccessControlConfigurations written in YAML format, each comprising of a groups and ACE + * configuration. Validation ensures that no doubled defined groups and only valid section identifiers in configuration files are + * possible * - * @param newestConfigurations - * map which contains all paths and configuration in YAML format. - * key is the node path in CRX under which the respective - * configuration is stored, entry is the textual configuration - * @param history - * history object - * @return List which contains the combined groups configurations (as map - * holding sets of AuthorizableConfigBeans) as first element and the - * combined ACE configurations (as map holding sets of AceBeans) as - * second element + * @param newestConfigurations map which contains all paths and configuration in YAML format. key is the node path in CRX under which + * the respective configuration is stored, entry is the textual configuration + * @param history history object + * @return The AcConfiguration * @throws RepositoryException in case some repository error has occurred - * @throws AcConfigBeanValidationException in case the given configuration is invalid - */ - public abstract List getMergedConfigurations( + * @throws AcConfigBeanValidationException in case the given configuration is invalid */ + public abstract AcConfiguration getMergedConfigurations( final Map newestConfigurations, final AcInstallationHistoryPojo history, final ConfigReader configReader) throws RepositoryException, diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configreader/YamlConfigReader.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configreader/YamlConfigReader.java index 7b69a190..06c2425a 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configreader/YamlConfigReader.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configreader/YamlConfigReader.java @@ -32,6 +32,7 @@ import org.slf4j.LoggerFactory; import biz.netcentric.cq.tools.actool.authorizableutils.AuthorizableConfigBean; +import biz.netcentric.cq.tools.actool.configmodel.GlobalConfiguration; import biz.netcentric.cq.tools.actool.helper.AceBean; import biz.netcentric.cq.tools.actool.helper.Constants; import biz.netcentric.cq.tools.actool.helper.QueryHelper; @@ -116,11 +117,21 @@ public Map> getUserConfigurationBeans(final return principalsMap; } - private Collection getConfigSection(final String sectionName, final Collection yamlList) { + @Override + public GlobalConfiguration getGlobalConfiguration(final Collection yamlList) { + + Map globalConfigMap = (Map) getConfigSection(Constants.GLOBAL_CONFIGURATION_KEY, yamlList); + + GlobalConfiguration globalConfiguration = new GlobalConfiguration(globalConfigMap); + + return globalConfiguration; + } + + private Object getConfigSection(final String sectionName, final Collection yamlList) { final List> yamList = new ArrayList>(yamlList); for (final LinkedHashMap currMap : yamList) { if (sectionName.equals(currMap.keySet().iterator().next())) { - return (List) currMap.get(sectionName); + return currMap.get(sectionName); } } return null; diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configreader/YamlConfigurationMerger.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configreader/YamlConfigurationMerger.java index fe96733a..6d503054 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configreader/YamlConfigurationMerger.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configreader/YamlConfigurationMerger.java @@ -8,7 +8,6 @@ */ package biz.netcentric.cq.tools.actool.configreader; -import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -26,11 +25,14 @@ import org.yaml.snakeyaml.Yaml; import biz.netcentric.cq.tools.actool.authorizableutils.AuthorizableConfigBean; +import biz.netcentric.cq.tools.actool.configmodel.AcConfiguration; +import biz.netcentric.cq.tools.actool.configmodel.GlobalConfiguration; import biz.netcentric.cq.tools.actool.helper.AceBean; import biz.netcentric.cq.tools.actool.installationhistory.AcInstallationHistoryPojo; import biz.netcentric.cq.tools.actool.validators.AceBeanValidator; import biz.netcentric.cq.tools.actool.validators.AuthorizableValidator; import biz.netcentric.cq.tools.actool.validators.ConfigurationsValidator; +import biz.netcentric.cq.tools.actool.validators.GlobalConfigurationValidator; import biz.netcentric.cq.tools.actool.validators.YamlConfigurationsValidator; import biz.netcentric.cq.tools.actool.validators.exceptions.AcConfigBeanValidationException; import biz.netcentric.cq.tools.actool.validators.impl.AceBeanValidatorImpl; @@ -47,30 +49,28 @@ public class YamlConfigurationMerger implements ConfigurationMerger { YamlMacroProcessor yamlMacroProcessor; @Override - public List getMergedConfigurations( - final Map newestConfigurations, + public AcConfiguration getMergedConfigurations( + final Map configFileContentByFilename, final AcInstallationHistoryPojo history, final ConfigReader configReader) throws RepositoryException, AcConfigBeanValidationException { - final List c = new ArrayList(); + + final GlobalConfiguration globalConfiguration = new GlobalConfiguration(); final Map> mergedAuthorizablesMapfromConfig = new LinkedHashMap>(); final Map> mergedAceMapFromConfig = new LinkedHashMap>(); - final Set authorizableIdsFromAllConfigs = new HashSet(); // needed for - // detection - // of doubled - // defined - // groups in - // configurations + final Set authorizableIdsFromAllConfigs = new HashSet(); // needed for detection of doubled defined groups in + // configurations final Yaml yaml = new Yaml(); final ConfigurationsValidator configurationsValidator = new YamlConfigurationsValidator(); - for (final Map.Entry entry : newestConfigurations.entrySet()) { + for (final Map.Entry entry : configFileContentByFilename.entrySet()) { - configurationsValidator.validateMandatorySectionIdentifiersExistence(entry.getValue(), entry.getKey()); + String sourceFile = entry.getKey(); + configurationsValidator.validateMandatorySectionIdentifiersExistence(entry.getValue(), sourceFile); - final String message = "Found configuration " + entry.getKey(); + final String message = "Found configuration file " + sourceFile; LOG.info(message); history.addMessage(message); @@ -79,7 +79,7 @@ public List getMergedConfigurations( yamlList = yamlMacroProcessor.processMacros(yamlList, history); // set merged config per file to ensure it is there in case of validation errors (for success, the actual merged config is set // after this loop) - history.setMergedAndProcessedConfig("# File " + entry.getKey() + "\n" + yaml.dump(yamlList)); + history.setMergedAndProcessedConfig("# File " + sourceFile + "\n" + yaml.dump(yamlList)); final Set sectionIdentifiers = new LinkedHashSet(); @@ -89,10 +89,15 @@ public List getMergedConfigurations( sectionIdentifiers.addAll(yamlList.get(i).keySet()); } configurationsValidator.validateSectionIdentifiers( - sectionIdentifiers, entry.getKey()); + sectionIdentifiers, sourceFile); + + configurationsValidator.validateSectionContentExistence(sourceFile, yamlList); - configurationsValidator.validateSectionContentExistence( - entry.getKey(), yamlList); + try { + globalConfiguration.merge(configReader.getGlobalConfiguration(yamlList)); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Invalid global configuration in " + sourceFile + ": " + e, e); + } // build AuthorizableConfigBeans from current configurations final AuthorizableValidator authorizableValidator = new AuthorizableValidatorImpl(); @@ -121,7 +126,7 @@ public List getMergedConfigurations( if (authorizableIdsFromCurrentConfig != null) { configurationsValidator.validateDuplicateAuthorizables(authorizableIdsFromAllConfigs, authorizableIdsFromCurrentConfig, - entry.getKey()); + sourceFile); // add IDs from authorizables from current configuration to set authorizableIdsFromAllConfigs.addAll(authorizableIdsFromCurrentConfig); } @@ -144,11 +149,17 @@ public List getMergedConfigurations( // set member groups final AuthorizableMemberGroupsValidator membersValidator = new AuthorizableMemberGroupsValidator(); membersValidator.validate(mergedAuthorizablesMapfromConfig); - c.add(mergedAuthorizablesMapfromConfig); - c.add(mergedAceMapFromConfig); + + GlobalConfigurationValidator.validate(globalConfiguration); + + AcConfiguration acConfiguration = new AcConfiguration(); + acConfiguration.setGlobalConfiguration(globalConfiguration); + acConfiguration.setAuthorizablesConfig(mergedAuthorizablesMapfromConfig); + acConfiguration.setAceConfig(mergedAceMapFromConfig); - history.setMergedAndProcessedConfig("# Merged configuration of " + newestConfigurations.size() + " files \n" + yaml.dump(c)); + history.setMergedAndProcessedConfig( + "# Merged configuration of " + configFileContentByFilename.size() + " files \n" + yaml.dump(acConfiguration)); - return c; + return acConfiguration; } } diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/helper/Constants.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/helper/Constants.java index 4f63a16d..f9bc726b 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/helper/Constants.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/helper/Constants.java @@ -17,12 +17,17 @@ public class Constants { private Constants() { } + public static final String GLOBAL_CONFIGURATION_KEY = "global_config"; + public static final String GROUP_CONFIGURATION_KEY = "group_config"; public static final String USER_CONFIGURATION_KEY = "user_config"; public static final String ACE_CONFIGURATION_KEY = "ace_config"; - public static final Set VALID_CONFIG_SECTION_IDENTIFIERS = new HashSet( - Arrays.asList(GROUP_CONFIGURATION_KEY, USER_CONFIGURATION_KEY, + + public static final Set VALID_CONFIG_SECTION_IDENTIFIERS = new HashSet(Arrays.asList( + GLOBAL_CONFIGURATION_KEY, + GROUP_CONFIGURATION_KEY, + USER_CONFIGURATION_KEY, ACE_CONFIGURATION_KEY)); public static final String USER_ANONYMOUS = "anonymous"; diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installationhistory/AcInstallationHistoryPojo.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installationhistory/AcInstallationHistoryPojo.java index d478a216..dd22e559 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installationhistory/AcInstallationHistoryPojo.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installationhistory/AcInstallationHistoryPojo.java @@ -23,6 +23,7 @@ import org.slf4j.LoggerFactory; import biz.netcentric.cq.tools.actool.comparators.HistoryEntryComparator; +import biz.netcentric.cq.tools.actool.configmodel.AcConfiguration; public class AcInstallationHistoryPojo { @@ -45,6 +46,7 @@ public class AcInstallationHistoryPojo { Rendition rendition; private String mergedAndProcessedConfig; + private AcConfiguration acConfiguration; private Map configFileContentsByName; @@ -96,6 +98,14 @@ public void setMergedAndProcessedConfig(String mergedAndProcessedConfig) { this.mergedAndProcessedConfig = mergedAndProcessedConfig; } + public AcConfiguration getAcConfiguration() { + return acConfiguration; + } + + public void setAcConfiguration(AcConfiguration acConfiguration) { + this.acConfiguration = acConfiguration; + } + public Map getConfigFileContentsByName() { return configFileContentsByName; } diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installhook/AcToolInstallHookServiceImpl.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installhook/AcToolInstallHookServiceImpl.java index 352851a6..8190c5ac 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installhook/AcToolInstallHookServiceImpl.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installhook/AcToolInstallHookServiceImpl.java @@ -48,7 +48,7 @@ public AcInstallationHistoryPojo installYamlFilesFromPackage(Archive archive, Se Map configs = configFilesRetriever.getConfigFileContentFromPackage(archive); history.setCrxPackageName(getArchiveName(archive)); - aceService.installNewConfigurations(session, history, configs, authorizableInstallationHistorySet); + aceService.installConfigurationFiles(session, history, configs, authorizableInstallationHistorySet); return history; } diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/validators/GlobalConfigurationValidator.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/validators/GlobalConfigurationValidator.java new file mode 100644 index 00000000..a9032e52 --- /dev/null +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/validators/GlobalConfigurationValidator.java @@ -0,0 +1,61 @@ +/* + * (C) Copyright 2015 Netcentric AG. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package biz.netcentric.cq.tools.actool.validators; + +import org.osgi.framework.FrameworkUtil; + +import biz.netcentric.cq.tools.actool.configmodel.GlobalConfiguration; + +public class GlobalConfigurationValidator { + + public static void validate(GlobalConfiguration globalConfiguration) { + + if (globalConfiguration != null && globalConfiguration.getMinRequiredVersion() != null) { + checkForValidVersion(globalConfiguration.getMinRequiredVersion()); + } + + } + + private static void checkForValidVersion(String configuredMinRequiredVersion) { + String bundleVersion = FrameworkUtil.getBundle(GlobalConfigurationValidator.class).getVersion().toString(); + boolean isVersionValid = versionCompare(bundleVersion, configuredMinRequiredVersion) >= 0; + if (!isVersionValid) { + throw new IllegalArgumentException("AC Tool Version " + bundleVersion + " is too old for configuration (MinRequiredVersion=" + + configuredMinRequiredVersion + ")"); + } + } + + public static boolean versionIsNewerOrEqualTo(String str1, String str2) { + return versionCompare(str1, str2) >= 0; + } + + public static int versionCompare(String str1, String str2) { + String[] vals1 = str1.split("\\."); + String[] vals2 = str2.split("\\."); + int i = 0; + // set index to first non-equal ordinal or length of shortest version string + while (i < vals1.length && i < vals2.length && vals1[i].equals(vals2[i])) { + i++; + } + // compare first non-equal ordinal number + if (i < vals1.length && i < vals2.length) { + int diff; + try { + diff = Integer.valueOf(vals1[i]).compareTo(Integer.valueOf(vals2[i])); + } catch (NumberFormatException e) { + diff = vals1[i].compareTo(vals2[i]); + } + return Integer.signum(diff); + } + // the strings are equal or one string is a substring of the other + // e.g. "1.2.3" = "1.2.3" or "1.2.3" < "1.2.3.4" + return Integer.signum(vals1.length - vals2.length); + } + +} diff --git a/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/authorizableutils/impl/AuthorizableCreatorServiceImplTest.java b/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/authorizableutils/impl/AuthorizableCreatorServiceImplTest.java new file mode 100644 index 00000000..abd689ae --- /dev/null +++ b/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/authorizableutils/impl/AuthorizableCreatorServiceImplTest.java @@ -0,0 +1,115 @@ +package biz.netcentric.cq.tools.actool.authorizableutils.impl; + +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import javax.jcr.RepositoryException; + +import org.apache.jackrabbit.api.security.user.Authorizable; +import org.apache.jackrabbit.api.security.user.Group; +import org.apache.jackrabbit.api.security.user.UserManager; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.runners.MockitoJUnitRunner; +import org.mockito.stubbing.Answer; + +import biz.netcentric.cq.tools.actool.configmodel.AcConfiguration; +import biz.netcentric.cq.tools.actool.configmodel.GlobalConfiguration; +import biz.netcentric.cq.tools.actool.installationhistory.AcInstallationHistoryPojo; + +@RunWith(MockitoJUnitRunner.class) +public class AuthorizableCreatorServiceImplTest { + + public static final String TESTGROUP = "testGroup"; + + public static final String GROUP1 = "group1"; + public static final String GROUP2 = "group2"; + public static final String GROUP3 = "group3"; + public static final String EXTERNALGROUP = "externalGroup"; + + // class under test + @Spy + private AuthorizableCreatorServiceImpl cut = new AuthorizableCreatorServiceImpl(); + + private AcConfiguration acConfiguration = new AcConfiguration(); + private GlobalConfiguration globalConfiguration = new GlobalConfiguration(); + private AcInstallationHistoryPojo status = new AcInstallationHistoryPojo(); + + @Mock + private UserManager userManager; + + + @Mock + private Group testGroup; + + @Mock + private Group group1; + + @Mock + private Group group2; + + @Mock + private Group group3; + + + @Mock + private Group externalGroup; + + @Before + public void setup() throws RepositoryException { + + status.setAcConfiguration(acConfiguration); + acConfiguration.setGlobalConfiguration(globalConfiguration); + + setupAuthorizable(testGroup, TESTGROUP); + setupAuthorizable(group1, GROUP1); + setupAuthorizable(group2, GROUP2); + setupAuthorizable(group3, GROUP3); + setupAuthorizable(externalGroup, EXTERNALGROUP); + } + + private void setupAuthorizable(Authorizable authorizable, String id) throws RepositoryException { + doReturn(authorizable).when(userManager).getAuthorizable(id); + doReturn(id).when(authorizable).getID(); + } + + @Test + public void testMergeMemberOfGroups() throws Exception { + HashSet configuredGroups = new HashSet(Arrays.asList(GROUP1, GROUP2)); + HashSet groupsInRepo = new HashSet(Arrays.asList(GROUP2, GROUP3, EXTERNALGROUP)); + + globalConfiguration.setAllowExternalGroupNamesRegEx("external.*"); + + // just return the value as passed in as third argument + doAnswer(new Answer>() { + public Set answer(InvocationOnMock invocation) throws Throwable { + return (Set) invocation.getArguments()[2]; + } + }).when(cut).validateAssignedGroups(userManager, TESTGROUP, configuredGroups); + + cut.mergeMemberOfGroups(TESTGROUP, status, userManager, configuredGroups, groupsInRepo); + + verifyZeroInteractions(group2); // in configuredGroups and in groupsInRepo + verifyZeroInteractions(externalGroup); // matches external.* and hence must not be removed (even though it is not in the + // configuration) + + verify(group1).addMember(testGroup); + verifyNoMoreInteractions(group1); + + verify(group3).removeMember(testGroup); + verifyNoMoreInteractions(group3); + + } + +} diff --git a/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/configreader/YamlConfigReaderTest.java b/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/configreader/YamlConfigReaderTest.java index c895dea0..5f2168ce 100644 --- a/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/configreader/YamlConfigReaderTest.java +++ b/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/configreader/YamlConfigReaderTest.java @@ -38,7 +38,7 @@ public class YamlConfigReaderTest { @Test public void testNullActions() throws IOException, AcConfigBeanValidationException, RepositoryException { - final YamlConfigReader yamlConfigReader = new YamlConfigReader(); + final ConfigReader yamlConfigReader = new YamlConfigReader(); final List yamlList = getYamlList("test-null-actions.yaml"); final Map> groups = yamlConfigReader.getGroupConfigurationBeans(yamlList, null); final Map> acls = yamlConfigReader.getAceConfigurationBeans(yamlList, groups.keySet(), null); @@ -50,7 +50,7 @@ public void testNullActions() throws IOException, AcConfigBeanValidationExceptio @Test public void testNoActions() throws IOException, AcConfigBeanValidationException, RepositoryException { - final YamlConfigReader yamlConfigReader = new YamlConfigReader(); + final ConfigReader yamlConfigReader = new YamlConfigReader(); final List yamlList = getYamlList("test-no-actions.yaml"); final Map> groups = yamlConfigReader.getGroupConfigurationBeans(yamlList, null); final Map> acls = yamlConfigReader.getAceConfigurationBeans(yamlList, groups.keySet(), null); @@ -61,7 +61,7 @@ public void testNoActions() throws IOException, AcConfigBeanValidationException, @Test public void testMultipleAcesSamePath() throws IOException, AcConfigBeanValidationException, RepositoryException { - final YamlConfigReader yamlConfigReader = new YamlConfigReader(); + final ConfigReader yamlConfigReader = new YamlConfigReader(); final List yamlList = getYamlList("test-multiple-aces-same-path.yaml"); final Map> groups = yamlConfigReader.getGroupConfigurationBeans(yamlList, null); final Map> acls = yamlConfigReader.getAceConfigurationBeans(yamlList, groups.keySet(), null); @@ -70,7 +70,7 @@ public void testMultipleAcesSamePath() throws IOException, AcConfigBeanValidatio @Test public void testEmptyGlobVsNoGlob() throws Exception { - final YamlConfigReader yamlConfigReader = new YamlConfigReader(); + final ConfigReader yamlConfigReader = new YamlConfigReader(); final List yamlList = getYamlList("test-empty-glob.yaml"); final Map> groups = yamlConfigReader.getGroupConfigurationBeans(yamlList, null); final Map> acls = yamlConfigReader.getAceConfigurationBeans(yamlList, groups.keySet(), null); @@ -83,7 +83,7 @@ public void testEmptyGlobVsNoGlob() throws Exception { @Test public void testOptionalSections() throws Exception { - final YamlConfigReader yamlConfigReader = new YamlConfigReader(); + final ConfigReader yamlConfigReader = new YamlConfigReader(); List yamlList = getYamlList("test-no-aces.yaml"); Map> groups = yamlConfigReader.getGroupConfigurationBeans(yamlList, null); Map> acls = yamlConfigReader.getAceConfigurationBeans(yamlList, groups.keySet(), null); @@ -100,7 +100,7 @@ public void testOptionalSections() throws Exception { @Test @Ignore public void testUserManagementPrivilege() throws IOException, AcConfigBeanValidationException, RepositoryException { - final YamlConfigReader yamlConfigReader = new YamlConfigReader(); + final ConfigReader yamlConfigReader = new YamlConfigReader(); final List yamlList = getYamlList("test-rep-usermanagement.yaml"); final Map> groups = yamlConfigReader.getGroupConfigurationBeans(yamlList, null); final Map> acls = yamlConfigReader.getAceConfigurationBeans(yamlList, groups.keySet(), @@ -113,7 +113,7 @@ public void testUserManagementPrivilege() throws IOException, AcConfigBeanValida @Test public void testMemberGroups() throws IOException, AcConfigBeanValidationException, RepositoryException { - final YamlConfigReader yamlConfigReader = new YamlConfigReader(); + final ConfigReader yamlConfigReader = new YamlConfigReader(); final List yamlList = getYamlList("test-membergroups.yaml"); final Map> groups = yamlConfigReader.getGroupConfigurationBeans(yamlList, null); assertEquals("Number of groups", 4, groups.size()); diff --git a/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/configreader/YamlConfigurationMergerTest.java b/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/configreader/YamlConfigurationMergerTest.java index 824fa670..628e8119 100644 --- a/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/configreader/YamlConfigurationMergerTest.java +++ b/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/configreader/YamlConfigurationMergerTest.java @@ -13,7 +13,6 @@ import java.io.IOException; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Set; @@ -23,6 +22,7 @@ import org.junit.Test; import biz.netcentric.cq.tools.actool.authorizableutils.AuthorizableConfigBean; +import biz.netcentric.cq.tools.actool.configmodel.AcConfiguration; import biz.netcentric.cq.tools.actool.installationhistory.AcInstallationHistoryPojo; import biz.netcentric.cq.tools.actool.validators.exceptions.AcConfigBeanValidationException; @@ -45,8 +45,8 @@ public void testMemberGroups() throws IOException, RepositoryException, AcConfig final ConfigReader reader = new YamlConfigReader(); final Map configs = new HashMap(); configs.put("/etc/config", config); - final List results = merger.getMergedConfigurations(configs, mock(AcInstallationHistoryPojo.class), reader); - final Map> groups = (Map>) results.get(0); + AcConfiguration acConfiguration = merger.getMergedConfigurations(configs, mock(AcInstallationHistoryPojo.class), reader); + final Map> groups = acConfiguration.getAuthorizablesConfig(); final AuthorizableConfigBean groupA = groups.get("groupA").iterator().next(); assertEquals(3, groupA.getMemberOf().length); final AuthorizableConfigBean groupB = groups.get("groupB").iterator().next(); diff --git a/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/configreader/YamlMacroProcessorTest.java b/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/configreader/YamlMacroProcessorTest.java index 21baf6df..ebfa9bf4 100644 --- a/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/configreader/YamlMacroProcessorTest.java +++ b/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/configreader/YamlMacroProcessorTest.java @@ -74,7 +74,7 @@ public void testGroupLoop() throws IOException, AcConfigBeanValidationException, @Test public void testNestedGroupLoop() throws IOException, AcConfigBeanValidationException, RepositoryException { - final YamlConfigReader yamlConfigReader = new YamlConfigReader(); + final ConfigReader yamlConfigReader = new YamlConfigReader(); List yamlList = getYamlList("test-nested-loops.yaml"); yamlList = yamlMacroProcessor.processMacros(yamlList, acInstallationHistoryPojo); diff --git a/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/validators/GlobalConfigurationValidatorTest.java b/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/validators/GlobalConfigurationValidatorTest.java new file mode 100644 index 00000000..cb5f4a7c --- /dev/null +++ b/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/validators/GlobalConfigurationValidatorTest.java @@ -0,0 +1,28 @@ +/* + * (C) Copyright 2015 Netcentric AG. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + */ +package biz.netcentric.cq.tools.actool.validators; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class GlobalConfigurationValidatorTest { + + @Test + public void testVersionIsNewerOrEqualTo() { + assertTrue(GlobalConfigurationValidator.versionIsNewerOrEqualTo("1.0.0", "1.0.0")); + assertTrue(GlobalConfigurationValidator.versionIsNewerOrEqualTo("1.1.0", "1.0.0")); + assertTrue(GlobalConfigurationValidator.versionIsNewerOrEqualTo("2.0.0", "1.0.0")); + assertTrue(GlobalConfigurationValidator.versionIsNewerOrEqualTo("1.0.0.test", "1.0.0")); + + assertFalse(GlobalConfigurationValidator.versionIsNewerOrEqualTo("1.0.0", "1.1.0")); + } + +} diff --git a/accesscontroltool-exampleconfig-package/pom.xml b/accesscontroltool-exampleconfig-package/pom.xml index abe3e008..107a650c 100644 --- a/accesscontroltool-exampleconfig-package/pom.xml +++ b/accesscontroltool-exampleconfig-package/pom.xml @@ -15,7 +15,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 1.8.4 + 1.8.5 diff --git a/accesscontroltool-exampleconfig-package/src/main/jcr_root/apps/netcentric/actool-exampleconfig/Readme.md b/accesscontroltool-exampleconfig-package/src/main/jcr_root/apps/netcentric/actool-exampleconfig/Readme.md index d5ae50c4..16bdd8f5 100644 --- a/accesscontroltool-exampleconfig-package/src/main/jcr_root/apps/netcentric/actool-exampleconfig/Readme.md +++ b/accesscontroltool-exampleconfig-package/src/main/jcr_root/apps/netcentric/actool-exampleconfig/Readme.md @@ -2,4 +2,5 @@ The subfolders include examples for ACTool configurations. * [multipleFiles](multipleFiles): multiple config files in one directory * [runmodes](runmodes): run mode specific configurations -* [simple](simple): just one simple configuration file \ No newline at end of file +* [simple](simple): just one simple configuration file +* [realProject](realProject): configuration that can be used as a template for real projects diff --git a/accesscontroltool-exampleconfig-package/src/main/jcr_root/apps/netcentric/actool-exampleconfig/realProject/Readme.md b/accesscontroltool-exampleconfig-package/src/main/jcr_root/apps/netcentric/actool-exampleconfig/realProject/Readme.md new file mode 100644 index 00000000..bc0bfe54 --- /dev/null +++ b/accesscontroltool-exampleconfig-package/src/main/jcr_root/apps/netcentric/actool-exampleconfig/realProject/Readme.md @@ -0,0 +1,9 @@ +This includes the ACLs for a real project with three roles: + +* Content Managers (contentmanagers): read and write all content/DAM/tags + DTM cloud configs + assign users to project groups +* Power Users (powerusers): read and write all content/DAM/tags + DTM cloud configs +* Technical Support (techsupport): read all content/DAM/tags/packages + +There are also service users to read content/tags/users. + +You can use the fragment and project groups as template for your own projects. \ No newline at end of file diff --git a/accesscontroltool-exampleconfig-package/src/main/jcr_root/apps/netcentric/actool-exampleconfig/realProject/global.author/config.yaml b/accesscontroltool-exampleconfig-package/src/main/jcr_root/apps/netcentric/actool-exampleconfig/realProject/global.author/config.yaml new file mode 100644 index 00000000..0fda9c2a --- /dev/null +++ b/accesscontroltool-exampleconfig-package/src/main/jcr_root/apps/netcentric/actool-exampleconfig/realProject/global.author/config.yaml @@ -0,0 +1,108 @@ +# Global users and groups + +- group_config: + + - powerusers: + + - name: + memberOf: fragment-poweruser + path: global + + - contentmanagers: + + - name: + memberOf: fragment-contentmanager + path: global + + - techsupport: + + - name: + memberOf: fragment-technicalsupport + path: global + +- ace_config: + + - powerusers: + + - path: /content + permission: allow + actions: read,modify,create,delete,acl_read,replicate + privileges: + + - path: /content/dam + permission: allow + actions: read,modify,create,delete,acl_read,replicate + privileges: + + - path: /etc/tags + permission: allow + actions: read,modify,create,delete,acl_read,replicate + privileges: + + - path: /etc/cloudservices/dynamictagmanagement + permission: allow + actions: read,modify,create,delete,acl_read,replicate + privileges: + + - path: /home/groups/global + permission: allow + actions: read,modify,acl_read + privileges: rep:userManagement + repGlob: + + - path: /home/groups/e/everyone + permission: allow + actions: read,modify,acl_read + privileges: rep:userManagement + repGlob: + + - path: /home/users + permission: allow + actions: + privileges: jcr:all + repGlob: + + - contentmanagers: + + - path: /content + permission: allow + actions: read,modify,create,delete,acl_read,replicate + privileges: + + - path: /content/dam + permission: allow + actions: read,modify,create,delete,acl_read,replicate + privileges: + + - path: /etc/tags + permission: allow + actions: read,modify,create,delete,acl_read,replicate + privileges: + + - path: /etc/cloudservices/dynamictagmanagement + permission: allow + actions: read,modify,create,delete,acl_read,replicate + privileges: + + - techsupport: + + - path: /content + permission: allow + actions: read,acl_read + privileges: + + - path: /content/dam + permission: allow + actions: read,acl_read + privileges: + + - path: /etc/tags + permission: allow + actions: read,acl_read + privileges: + + - path: /etc/packages + permission: allow + actions: read + privileges: + repGlob: diff --git a/accesscontroltool-exampleconfig-package/src/main/jcr_root/apps/netcentric/actool-exampleconfig/realProject/serviceuser.author/config.yaml b/accesscontroltool-exampleconfig-package/src/main/jcr_root/apps/netcentric/actool-exampleconfig/realProject/serviceuser.author/config.yaml new file mode 100644 index 00000000..0491b380 --- /dev/null +++ b/accesscontroltool-exampleconfig-package/src/main/jcr_root/apps/netcentric/actool-exampleconfig/realProject/serviceuser.author/config.yaml @@ -0,0 +1,77 @@ +# Service users and groups + +- group_config: + + - system-tags: + + - name: + isMemberOf: + members: + path: s + + + - system-content: + + - name: + isMemberOf: + members: + path: s + + + - system-home: + + - name: + isMemberOf: + members: + path: s + +- user_config: + + - system-tags: + + - name: + isMemberOf: system-tags + path: s + isSystemUser: true + + + - system-content: + + - name: + isMemberOf: system-content + path: s + isSystemUser: true + + + - system-home: + + - name: + isMemberOf: system-home + path: s + isSystemUser: true + + +- ace_config: + + - system-tags: + + - path: /etc/tags + permission: allow + actions: read + privileges: + + + - system-content: + + - path: /content + permission: allow + actions: read + privileges: + + + - system-home: + + - path: /home + permission: allow + actions: read + privileges: diff --git a/accesscontroltool-exampleconfig-package/src/main/jcr_root/apps/netcentric/actool-exampleconfig/realProject/system.author/config.yaml b/accesscontroltool-exampleconfig-package/src/main/jcr_root/apps/netcentric/actool-exampleconfig/realProject/system.author/config.yaml new file mode 100644 index 00000000..0f0c85cb --- /dev/null +++ b/accesscontroltool-exampleconfig-package/src/main/jcr_root/apps/netcentric/actool-exampleconfig/realProject/system.author/config.yaml @@ -0,0 +1,876 @@ +# System configuration (all global fragments) + +- group_config: + + - fragment-basic-allow: + + - name: + memberOf: + path: f + + - fragment-restrict-for-everyone: + + - name: + memberOf: + path: f + + - fragment-siteadmin: + + - name: + memberOf: + path: f + + - fragment-dam: + + - name: + memberOf: + path: f + + - fragment-tagging: + + - name: + memberOf: + path: f + + - fragment-inbox: + + - name: + memberOf: + path: f + + - fragment-useradmin: + + - name: + memberOf: + path: f + + - fragment-tools: + + - name: + memberOf: + path: f + + - fragment-workflows: + + - name: + memberOf: + path: f + + - fragment-crxde: + + - name: + memberOf: + path: f + + - fragment-contentmanager: + + - name: + memberOf: fragment-restrict-for-everyone,fragment-basic-allow,fragment-siteadmin,fragment-dam,fragment-tagging,fragment-inbox,fragment-tools,fragment-workflows + path: f + + - fragment-poweruser: + + - name: + memberOf: fragment-restrict-for-everyone,fragment-basic-allow,fragment-siteadmin,fragment-dam,fragment-tagging,fragment-inbox,fragment-tools,fragment-useradmin,fragment-crxde,fragment-workflows + path: f + + - fragment-technicalsupport: + + - name: + memberOf: fragment-restrict-for-everyone,fragment-basic-allow,fragment-siteadmin,fragment-dam,fragment-tagging,fragment-inbox,fragment-tools,fragment-workflows,fragment-crxde + path: f + + - fragment-siteowner: + + - name: + memberOf: fragment-restrict-for-everyone,fragment-basic-allow,fragment-siteadmin,fragment-dam,fragment-tagging,fragment-inbox + path: f + + - fragment-editor: + + - name: + memberOf: fragment-restrict-for-everyone,fragment-basic-allow,fragment-siteadmin,fragment-dam,fragment-tagging,fragment-inbox + path: f + + +- ace_config: + + + - fragment-basic-allow: + + - path: / + permission: allow + actions: read + privileges: + repGlob: + + - path: /etc + permission: allow + actions: + privileges: jcr:read,jcr:readAccessControl + repGlob: "" + + - path: /etc + permission: allow + actions: + privileges: jcr:read,jcr:readAccessControl + repGlob: /jcr:* + + + - fragment-restrict-for-everyone: + + - path: / + permission: allow + actions: read + privileges: + repGlob: + + - path: /content + permission: deny + actions: + privileges: jcr:read,jcr:readAccessControl + repGlob: + + - path: /content + permission: allow + actions: + privileges: jcr:read,jcr:readAccessControl + repGlob: "" + + - path: /content + permission: allow + actions: + privileges: jcr:read,jcr:readAccessControl + repGlob: /jcr:* + + - path: /content/dam + permission: deny + actions: + privileges: jcr:read,jcr:readAccessControl + repGlob: + + - path: /content/dam + permission: allow + actions: + privileges: jcr:read,jcr:readAccessControl + repGlob: "" + + - path: /content/dam + permission: allow + actions: + privileges: jcr:read,jcr:readAccessControl + repGlob: /jcr:* + + - path: /etc/tags + permission: deny + actions: + privileges: jcr:read,jcr:readAccessControl + repGlob: + + - path: /etc/tags + permission: allow + actions: + privileges: jcr:read,jcr:readAccessControl + repGlob: "" + + - path: /etc/tags + permission: allow + actions: + privileges: jcr:read,jcr:readAccessControl + repGlob: /jcr:* + + - path: /libs/wcm/core/content/damadmin + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/wcm/core/content/siteadmin + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/wcm/core/content/misc + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/security/content/admin + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/workflow/content/console + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/tagging/content/tagadmin + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/mcm/content/admin + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/workflow/content/inbox + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/wcm/core/content/inbox + permission: deny + actions: read + privileges: + repGlob: + + - path: /etc/blueprints + permission: deny + actions: read + privileges: + repGlob: + + - path: /etc/commerce + permission: deny + actions: read + privileges: + repGlob: + + - path: /etc/dam + permission: deny + actions: read + privileges: + repGlob: + + - path: /etc/dashboards + permission: deny + actions: read + privileges: + repGlob: + + - path: /etc/docs + permission: deny + actions: read + privileges: + repGlob: + + - path: /etc/forms + permission: deny + actions: read + privileges: + repGlob: + + - path: /etc/importers + permission: deny + actions: read + privileges: + repGlob: + + - path: /etc/jobs + permission: deny + actions: read + privileges: + repGlob: + + - path: /etc/linkchecker + permission: deny + actions: read + privileges: + repGlob: + + - path: /etc/mobile + permission: deny + actions: read + privileges: + repGlob: + + - path: /etc/msm + permission: deny + actions: read + privileges: + repGlob: + + - path: /etc/notification + permission: deny + actions: read + privileges: + repGlob: + + - path: /etc/ocs + permission: deny + actions: read + privileges: + repGlob: + + - path: /etc/packages + permission: deny + actions: read + privileges: + repGlob: + + - path: /etc/replication + permission: deny + actions: read + privileges: + repGlob: + + - path: /etc/reports + permission: deny + actions: read + privileges: + repGlob: + + - path: /etc/scaffolding + permission: deny + actions: read + privileges: + repGlob: + + - path: /etc/security + permission: deny + actions: read + privileges: + repGlob: + + - path: /etc/social + permission: deny + actions: read + privileges: + repGlob: + + - path: /etc/tenants + permission: deny + actions: read + privileges: + repGlob: + + - path: /etc/versioning + permission: deny + actions: read + privileges: + repGlob: + + - path: /etc/virtual-repositories + permission: deny + actions: read + privileges: + repGlob: + + - path: /etc/watchwords + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/sites + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/projects + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/apps + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/publications + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/forms + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/assets + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/communities + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/commerce + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/tools/operations + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/tools/operations/dashboard + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/tools/operations/security + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/tools/operations/security/oauth + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/tools/operations/security/permissions + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/tools/operations/packaging + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/tools/operations/deployment + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/tools/operations/cloud + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/tools/operations/cloud/contexthub + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/tools/workflow + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/tools/operations/launches + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/tools/operations/replication + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/tools/operations/tagging + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/tools/operations/configuration + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/tools/operations/backup + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/tools/assets + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/tools/resources + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/tools/crxdelite + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/tools/webconsole + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/welcome/features/crxde + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/welcome/features/packages + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/welcome/features/share + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/welcome/features/backup + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/welcome/features/config + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/welcome/resources/cloudservices + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/welcome/resources/launches + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/welcome/resources/manuscriptsadmin + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/welcome/resources/publishingadmin + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/welcome/resources/replication + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/welcome/resources/reports + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/welcome/resources/taskmanager + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/welcome/resources/workflows + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/welcome/docs/dev + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/welcome/features/statusdump + permission: deny + actions: read + privileges: + repGlob: + + - path: /libs/granite/security + permission: deny + actions: read + privileges: + repGlob: + + + - fragment-siteadmin: + + - path: /libs/wcm/core/content/siteadmin + permission: allow + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/sites + permission: allow + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/projects + permission: allow + actions: read + privileges: + repGlob: + + + - fragment-dam: + + - path: /libs/wcm/core/content/damadmin + permission: allow + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/assets + permission: allow + actions: read + privileges: + repGlob: + + + - fragment-tagging: + + - path: /libs/cq/tagging/content/tagadmin + permission: allow + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/tools/operations + permission: allow + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/tools/operations/tagging + permission: allow + actions: read + privileges: + repGlob: + + + - fragment-inbox: + + - path: /libs/cq/workflow/content/inbox + permission: allow + actions: read + privileges: + repGlob: + + - path: /libs/wcm/core/content/inbox + permission: allow + actions: read + privileges: + repGlob: + + + - fragment-useradmin: + + - path: /libs/cq/security/content/admin + permission: allow + actions: read + privileges: + repGlob: + + - path: /home + permission: allow + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/projects + permission: allow + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/tools/operations + permission: allow + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/tools/operations/security + permission: allow + actions: read + privileges: + repGlob: + + - path: /libs/granite/security + permission: allow + actions: read + privileges: + repGlob: + + + - fragment-tools: + + - path: /libs/wcm/core/content/misc + permission: allow + actions: read + privileges: + repGlob: + + - path: /etc/blueprints + permission: allow + actions: read,modify,create,delete,acl_read,replicate + privileges: + repGlob: + + - path: /libs/cq/core/content/welcome/resources/cloudservices + permission: allow + actions: read + privileges: + repGlob: + + - path: /etc/cloudservices + permission: allow + actions: read + privileges: + repGlob: + + - path: /etc/designs + permission: allow + actions: read,modify,create,delete,acl_read,replicate + privileges: + repGlob: + + - path: /etc/linkchecker + permission: allow + actions: read,modify,create,delete,acl_read,replicate + privileges: + repGlob: + + - path: /etc/msm + permission: allow + actions: read,modify,create,delete,acl_read,replicate + privileges: + repGlob: + + - path: /etc/replication + permission: allow + actions: + privileges: jcr:read,jcr:readAccessControl + repGlob: "" + + - path: /etc/replication + permission: allow + actions: + privileges: jcr:read,jcr:readAccessControl + repGlob: /jcr:* + + - path: /etc/replication/treeactivation + permission: allow + actions: read,acl_read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/tools/operations + permission: allow + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/tools/operations/cloud + permission: allow + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/tools/operations/dashboard + permission: allow + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/tools/operations/replication + permission: allow + actions: read + privileges: + repGlob: + + + - fragment-workflows: + + - path: /libs/cq/workflow/content/console + permission: allow + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/welcome/resources/workflows + permission: allow + actions: read + privileges: + repGlob: + + - path: /etc/workflow + permission: allow + actions: read,modify,create,delete,acl_read,replicate + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/tools/operations + permission: allow + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/nav/tools/workflow + permission: allow + actions: read + privileges: + repGlob: + + - fragment-crxde: + + - path: /libs/cq/core/content/nav/tools/crxdelite + permission: allow + actions: read + privileges: + repGlob: + + - path: /libs/cq/core/content/welcome/features/crxde + permission: allow + actions: read + privileges: + repGlob: + diff --git a/accesscontroltool-oakindex-package/pom.xml b/accesscontroltool-oakindex-package/pom.xml index b45c40a0..dcc1f1fc 100644 --- a/accesscontroltool-oakindex-package/pom.xml +++ b/accesscontroltool-oakindex-package/pom.xml @@ -15,7 +15,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 1.8.4 + 1.8.5 diff --git a/accesscontroltool-package/pom.xml b/accesscontroltool-package/pom.xml index 13e4ad72..7c66b7a3 100644 --- a/accesscontroltool-package/pom.xml +++ b/accesscontroltool-package/pom.xml @@ -15,7 +15,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 1.8.4 + 1.8.5 diff --git a/docs/AdvancedFeatures.md b/docs/AdvancedFeatures.md index b96bb2a0..3daa3a02 100644 --- a/docs/AdvancedFeatures.md +++ b/docs/AdvancedFeatures.md @@ -139,3 +139,15 @@ Expressions are evaluated using javax.el expression language. The following util Normally it is ensured by validation that a configuration's group system is self-contained - this means out-of-the-box groups like ```contributor``` cannot be used. For registered users in the system this approach works well since either the users are manually assigned to groups (by a user admin) or the membership relationship is maintained by LDAP or SSO extensions. For the ```anonymous``` user on publish that is not logged in by definition, there is no hook that allows to assign it to a group in the AC Tools configuration. Therefore as an exception, it is allowed to use the user ```anonymous``` in the ```members``` attribute of a group configuration. +## Configure memberships to Dynamic Groups + +If a group configured via the AC Tool is a member of a group which is not part of the configuration, this membership is removed by the installation process. This behaviour makes sense to ensure the configuration to be self-contained and prevent unwanted "injection" of permissions into the configuration system as described by the yaml files. Therefore wherever possible groups and their dependent groups should be added to the ACTool configuration. + +An exception to this might be dynamic groups created and maintained by authors or end-users. As such groups and their memberships are not static they cannot be added easily to the configuration. For this use-case the global configuration "allowExternalGroupNamesRegEx" can be set to a regular expression that matches all group names, that may keep member AC Tool groups as member: + +``` +- global_config: + allowExternalGroupNamesRegEx: external.* # the AC Tool groups can inherit from other external.* groups +``` + + diff --git a/docs/BestPractices.md b/docs/BestPractices.md index aff3dde5..26f339ce 100644 --- a/docs/BestPractices.md +++ b/docs/BestPractices.md @@ -90,4 +90,18 @@ In a second group (fragment-project1) you can then allow to read the child node ``` +## Use fragment groups for functional aspects and content access +You should separate rights for functional aspects (e.g. access to DAM editor, CRX/DE or tag management) and content. +This means there are groups just for tool access (we call them fragment-... here) and others just for content (we call them content-.. here). This way you can enable access to required tools easily and editors only see what they need on the welcome page. + +The group that is effectively assigned to a user then includes a functional group for tooling access and content groups. + + + +In this case the user is a content manager. So the group "contentmanager" provides him the content groups + the functional group "fragment-contentmanager" to enable the right tools. The group "fragment-contentmanagers" includes functional aspects such as "fragment-dam" for access to DAM tool. It also includes the groups "fragment-restrict-for-everyone" and "fragment-basic-allow". + +* fragment-restrict-for-everyone: initially denies access to all tools and content, includes all deny rules +* fragment-basic-allow: allows access to nodes that are readable for all users (e.g. /content without subnodes) + +Please note that the functional fragment groups do not provide any content access. Read/write access to a website is provided by the groups "content-project1-read" and "content-project1-write". diff --git a/docs/Configuration.md b/docs/Configuration.md index 66baba24..6607e284 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -170,6 +170,17 @@ In case the configuration file contains ACEs for groups which are not present in All important steps performed by the service as well as all error/warning messages get written to error log and history. +## Global Configuration + +Certain configuration aspects are global and can be configured on top level (since v1.8.5). + +``` +- global_config: + minRequiredVersion: 1.8.5 # Checked as pre-condition of run + allowExternalGroupNamesRegEx: external.* # allow external groups to extend the AC Tool configuration + +``` + ## Validation First the validation of the different configuration lines is performed and gets applied while reading the file. Further validation consists of checking paths for existence as well as for double entries, checks for conflicting ACEs (e.g. allow and deny for same actions on same node), checks whether principals are existing under /home. If an issue is detected the reading is aborted and an appropriate error message is appended to the installation history and log. diff --git a/docs/images/fragments.png b/docs/images/fragments.png new file mode 100644 index 00000000..d3ca7a6c Binary files /dev/null and b/docs/images/fragments.png differ diff --git a/pom.xml b/pom.xml index b9a5c0ed..f850c1a8 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 1.8.4 + 1.8.5 pom Access Control Tool - Reactor Project