diff --git a/accesscontroltool-bundle/pom.xml b/accesscontroltool-bundle/pom.xml index 9bbe4959..dff37a6c 100644 --- a/accesscontroltool-bundle/pom.xml +++ b/accesscontroltool-bundle/pom.xml @@ -11,7 +11,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 2.0.5 + 2.0.6 diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceinstaller/BaseAceBeanInstaller.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceinstaller/BaseAceBeanInstaller.java index d06385e2..3a26637b 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceinstaller/BaseAceBeanInstaller.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceinstaller/BaseAceBeanInstaller.java @@ -89,7 +89,8 @@ public void installPathBasedACEs( orderedAceBeanSetFromConfig.addAll(aceBeanSetFromConfig); Set principalsToRemoveAcesForAtThisPath = acConfiguration.getAuthorizablesConfig() - .removeUnmanagedPrincipalNamesAtPath(path, principalsToRemoveAcesFor); + .removeUnmanagedPrincipalNamesAtPath(path, principalsToRemoveAcesFor, + acConfiguration.getGlobalConfiguration().getDefaultUnmanagedAcePathsRegex()); installAcl(orderedAceBeanSetFromConfig, path, principalsToRemoveAcesForAtThisPath, session, history); if (intermediateSaves && session.hasPendingChanges()) { @@ -100,7 +101,7 @@ public void installPathBasedACEs( if (history.getMissingParentPathsForInitialContent() > 0) { history.addWarning(LOG, "There were " + history.getMissingParentPathsForInitialContent() - + " parent paths missing for creation of intial content (those paths were skipped, see verbose log for details)"); + + " parent paths missing for creation of initial content (those paths were skipped, see verbose log for details)"); } history.addMessage(LOG, "ACL Update Statistics: Changed=" + history.getCountAclsChanged() + " Unchanged=" + history.getCountAclsUnchanged() diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/authorizableinstaller/impl/AuthorizableInstallerServiceImpl.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/authorizableinstaller/impl/AuthorizableInstallerServiceImpl.java index 32af8267..ba39f5ba 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/authorizableinstaller/impl/AuthorizableInstallerServiceImpl.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/authorizableinstaller/impl/AuthorizableInstallerServiceImpl.java @@ -224,8 +224,11 @@ private Set removeRegularUsers(Set allMembersFromRepo, UserManag private Set removeExternalMembersUnmanagedByConfiguration(AcConfiguration acConfiguration, AuthorizableConfigBean authorizableConfigBean, Set relevantMembersInRepo, InstallationLogger installLog) { Set relevantMembers = new HashSet(relevantMembersInRepo); - Pattern unmanagedExternalMembersRegex = acConfiguration.getGlobalConfiguration() - .getDefaultUnmanagedExternalMembersRegex(); + + Pattern unmanagedExternalMembersRegex = authorizableConfigBean.getUnmanagedExternalMembersRegex(); + if (unmanagedExternalMembersRegex == null) { + unmanagedExternalMembersRegex = acConfiguration.getGlobalConfiguration().getDefaultUnmanagedExternalMembersRegex(); + } Set unmanagedMembers = new HashSet(); if (unmanagedExternalMembersRegex != null) { @@ -413,13 +416,13 @@ private void applyGroupMembershipConfigIsMemberOf(InstallationLogger installLog, AuthorizableConfigBean authorizableConfigBean, UserManager userManager, Session session, Set authorizablesFromConfigurations) throws RepositoryException, AuthorizableCreatorException { String[] memberOf = authorizableConfigBean.getMemberOf(); - String authorizableId = authorizableConfigBean.getAuthorizableId(); - Authorizable currentGroupFromRepository = userManager.getAuthorizable(authorizableId); + Authorizable currentGroupFromRepository = userManager.getAuthorizable(authorizableConfigBean.getAuthorizableId()); Set membershipGroupsFromConfig = getMembershipGroupsFromConfig(memberOf); Set membershipGroupsFromRepository = getMembershipGroupsFromRepository(currentGroupFromRepository); - applyGroupMembershipConfigIsMemberOf(authorizableId, acConfiguration, installLog, userManager, session, membershipGroupsFromConfig, + applyGroupMembershipConfigIsMemberOf(authorizableConfigBean, acConfiguration, installLog, userManager, session, + membershipGroupsFromConfig, membershipGroupsFromRepository, authorizablesFromConfigurations); } @@ -479,7 +482,7 @@ private Set getMembershipGroupsFromConfig(String[] memberOf) { } @SuppressWarnings("unchecked") - void applyGroupMembershipConfigIsMemberOf(String authorizableId, + void applyGroupMembershipConfigIsMemberOf(AuthorizableConfigBean authorizableConfigBean, AcConfiguration acConfiguration, InstallationLogger installLog, UserManager userManager, Session session, Set membershipGroupsFromConfig, @@ -491,6 +494,7 @@ void applyGroupMembershipConfigIsMemberOf(String authorizableId, membershipGroupsFromConfig.remove(PRINCIPAL_EVERYONE); membershipGroupsFromRepository.remove(PRINCIPAL_EVERYONE); + String authorizableId = authorizableConfigBean.getAuthorizableId(); installLog.addVerboseMessage(LOG, "Authorizable " + authorizableId + " isMemberOf(repo)=" + membershipGroupsFromRepository); installLog.addVerboseMessage(LOG, "Authorizable " + authorizableId + " isMemberOf(conifg)=" + membershipGroupsFromConfig); @@ -508,8 +512,10 @@ void applyGroupMembershipConfigIsMemberOf(String authorizableId, validatedMembershipGroupsFromConfig); Set unmanagedMembers = new HashSet(); - Pattern unmanagedExternalIsMemberOfRegex = acConfiguration.getGlobalConfiguration() - .getDefaultUnmanagedExternalIsMemberOfRegex(); + Pattern unmanagedExternalIsMemberOfRegex = authorizableConfigBean.getUnmanagedExternalIsMemberOfRegex(); + if (unmanagedExternalIsMemberOfRegex == null) { + unmanagedExternalIsMemberOfRegex = acConfiguration.getGlobalConfiguration().getDefaultUnmanagedExternalIsMemberOfRegex(); + } Iterator toBeRemovedMembersIt = toBeRemovedMembers.iterator(); while (toBeRemovedMembersIt.hasNext()) { diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configmodel/AuthorizableConfigBean.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configmodel/AuthorizableConfigBean.java index ed9bf75b..8cec397c 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configmodel/AuthorizableConfigBean.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configmodel/AuthorizableConfigBean.java @@ -11,6 +11,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.regex.Pattern; import org.apache.commons.lang.StringUtils; @@ -42,12 +43,15 @@ public class AuthorizableConfigBean implements AcDumpElement { private String migrateFrom; private String unmanagedAcePathsRegex; + private Pattern unmanagedExternalIsMemberOfRegex; + private Pattern unmanagedExternalMembersRegex; private boolean isGroup = true; private boolean isSystemUser = false; private String disabled; + public String getAuthorizableId() { return authorizableId; } @@ -248,6 +252,22 @@ public void setUnmanagedAcePathsRegex(String unmanagedAcePathsRegex) { this.unmanagedAcePathsRegex = unmanagedAcePathsRegex; } + public Pattern getUnmanagedExternalIsMemberOfRegex() { + return unmanagedExternalIsMemberOfRegex; + } + + public void setUnmanagedExternalIsMemberOfRegex(String unmanagedExternalIsMemberOfRegex) { + this.unmanagedExternalIsMemberOfRegex = GlobalConfiguration.stringToRegex(unmanagedExternalIsMemberOfRegex); + } + + public Pattern getUnmanagedExternalMembersRegex() { + return unmanagedExternalMembersRegex; + } + + public void setUnmanagedExternalMembersRegex(String unmanagedExternalMembersRegex) { + this.unmanagedExternalMembersRegex = GlobalConfiguration.stringToRegex(unmanagedExternalMembersRegex); + } + @Override public String toString() { final StringBuilder sb = new StringBuilder(); @@ -259,10 +279,11 @@ public String toString() { return sb.toString(); } - public boolean managesPath(String path) { - if (StringUtils.isNotBlank(unmanagedAcePathsRegex) + public boolean managesPath(String path, String defaultUnmanagedAcePathsRegex) { + String effectiveUnmanagedAcePathsRegex = StringUtils.defaultIfEmpty(unmanagedAcePathsRegex, defaultUnmanagedAcePathsRegex); + if (StringUtils.isNotBlank(effectiveUnmanagedAcePathsRegex) && StringUtils.isNotBlank(path) /* not supporting repository permissions here */) { - boolean pathIsManaged = !path.matches(unmanagedAcePathsRegex); + boolean pathIsManaged = !path.matches(effectiveUnmanagedAcePathsRegex); return pathIsManaged; } else { return true; // default diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configmodel/AuthorizablesConfig.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configmodel/AuthorizablesConfig.java index f4cd200e..0fb5cca0 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configmodel/AuthorizablesConfig.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configmodel/AuthorizablesConfig.java @@ -63,12 +63,12 @@ public String getPrincipalNameForAuthorizableId(String authorizableId) { return principalName; } - public Set removeUnmanagedPrincipalNamesAtPath(String path, Set principals) { + public Set removeUnmanagedPrincipalNamesAtPath(String path, Set principals, String defaultUnmanagedAcePathsRegex) { Set filteredPrincipals = new HashSet(); for (String principal : principals) { AuthorizableConfigBean authorizableConfig = getAuthorizableConfigByPrincipalName(principal); - if (authorizableConfig.managesPath(path)) { + if (authorizableConfig.managesPath(path, defaultUnmanagedAcePathsRegex)) { filteredPrincipals.add(principal); } } 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 index 13ef062a..7eed2e7a 100644 --- 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 @@ -25,6 +25,7 @@ public class GlobalConfiguration { public static final String KEY_DEFAULT_UNMANAGED_EXTERNAL_ISMEMBEROF_REGEX = "defaultUnmanagedExternalIsMemberOfRegex"; public static final String KEY_DEFAULT_UNMANAGED_EXTERNAL_MEMBERS_REGEX = "defaultUnmanagedExternalMembersRegex"; + public static final String KEY_DEFAULT_UNMANAGED_ACE_PATHS_REGEX = "defaultUnmanagedAcePathsRegex"; @Deprecated public static final String KEY_KEEP_EXISTING_MEMBERSHIPS_FOR_GROUP_NAMES_REGEX = "keepExistingMembershipsForGroupNamesRegEx"; @@ -34,6 +35,7 @@ public class GlobalConfiguration { private Pattern defaultUnmanagedExternalIsMemberOfRegex; private Pattern defaultUnmanagedExternalMembersRegex; + private String defaultUnmanagedAcePathsRegex; public GlobalConfiguration() { } @@ -47,7 +49,9 @@ public GlobalConfiguration(Map globalConfigMap) { + " (since v2.0.0) - please adjust your configuration."); } - + + setDefaultUnmanagedAcePathsRegex((String) globalConfigMap.get(KEY_DEFAULT_UNMANAGED_ACE_PATHS_REGEX)); + setDefaultUnmanagedExternalIsMemberOfRegex((String) globalConfigMap.get(KEY_DEFAULT_UNMANAGED_EXTERNAL_ISMEMBEROF_REGEX)); setDefaultUnmanagedExternalMembersRegex((String) globalConfigMap.get(KEY_DEFAULT_UNMANAGED_EXTERNAL_MEMBERS_REGEX)); @@ -75,6 +79,13 @@ public GlobalConfiguration(Map globalConfigMap) { public void merge(GlobalConfiguration otherGlobalConfig) { + if (otherGlobalConfig.getDefaultUnmanagedAcePathsRegex() != null) { + if (defaultUnmanagedAcePathsRegex == null) { + defaultUnmanagedAcePathsRegex = otherGlobalConfig.getDefaultUnmanagedAcePathsRegex(); + } else { + throw new IllegalArgumentException("Duplicate config for " + KEY_DEFAULT_UNMANAGED_ACE_PATHS_REGEX); + } + } if (otherGlobalConfig.getDefaultUnmanagedExternalIsMemberOfRegex() != null) { if (defaultUnmanagedExternalIsMemberOfRegex == null) { defaultUnmanagedExternalIsMemberOfRegex = otherGlobalConfig.getDefaultUnmanagedExternalIsMemberOfRegex(); @@ -139,7 +150,15 @@ public void setDefaultUnmanagedExternalMembersRegex(String defaultUnmanagedExter this.defaultUnmanagedExternalMembersRegex = stringToRegex(defaultUnmanagedExternalMembersRegex); } - private Pattern stringToRegex(String regex) { + public String getDefaultUnmanagedAcePathsRegex() { + return defaultUnmanagedAcePathsRegex; + } + + public void setDefaultUnmanagedAcePathsRegex(String defaultUnmanagedAcePathsRegex) { + this.defaultUnmanagedAcePathsRegex = defaultUnmanagedAcePathsRegex; + } + + static Pattern stringToRegex(String regex) { return StringUtils.isNotBlank(regex) ? Pattern.compile(regex) : null; } 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 8d8d3655..027d6281 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 @@ -70,6 +70,8 @@ public class YamlConfigReader implements ConfigReader { private static final String GROUP_CONFIG_PROPERTY_MIGRATE_FROM = "migrateFrom"; private static final String GROUP_CONFIG_PROPERTY_UNMANAGED_ACE_PATHS_REGEX = "unmanagedAcePathsRegex"; + private static final String GROUP_CONFIG_PROPERTY_UNMANAGED_EXTERNAL_ISMEMBEROF_REGEX = "unmanagedExternalIsMemberOfRegex"; + private static final String GROUP_CONFIG_PROPERTY_UNMANAGED_EXTERNAL_MEMBERS_REGEX = "unmanagedExternalMembersRegex"; private static final String USER_CONFIG_PROPERTY_IS_SYSTEM_USER = "isSystemUser"; @@ -364,6 +366,10 @@ protected void setupAuthorizableBean( authorizableConfigBean.setUnmanagedAcePathsRegex(getMapValueAsString(currentPrincipalDataMap, GROUP_CONFIG_PROPERTY_UNMANAGED_ACE_PATHS_REGEX)); + authorizableConfigBean.setUnmanagedExternalIsMemberOfRegex(getMapValueAsString(currentPrincipalDataMap, + GROUP_CONFIG_PROPERTY_UNMANAGED_EXTERNAL_ISMEMBEROF_REGEX)); + authorizableConfigBean.setUnmanagedExternalMembersRegex(getMapValueAsString(currentPrincipalDataMap, + GROUP_CONFIG_PROPERTY_UNMANAGED_EXTERNAL_MEMBERS_REGEX)); authorizableConfigBean.setIsGroup(isGroupSection); authorizableConfigBean.setIsSystemUser(Boolean.valueOf(getMapValueAsString(currentPrincipalDataMap, diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configreader/YamlMacroElEvaluator.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configreader/YamlMacroElEvaluator.java index a588b45a..e8f8d74b 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configreader/YamlMacroElEvaluator.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configreader/YamlMacroElEvaluator.java @@ -109,7 +109,8 @@ public ElFunctionMapper() { StringUtils.class.getMethod("contains", new Class[] { String.class, String.class }), StringUtils.class.getMethod("endsWith", new Class[] { String.class, String.class }), StringUtils.class.getMethod("startsWith", new Class[] { String.class, String.class }), - StringUtils.class.getMethod("replace", new Class[] { String.class, String.class, String.class }) + StringUtils.class.getMethod("replace", new Class[] { String.class, String.class, String.class }), + StringUtils.class.getMethod("length", new Class[] { String.class }) }; for (Method method : exportedMethods) { functionMap.put(method.getName(), method); diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/history/InstallationLogger.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/history/InstallationLogger.java index c5a647a2..85338465 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/history/InstallationLogger.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/history/InstallationLogger.java @@ -11,10 +11,6 @@ public interface InstallationLogger extends InstallationLog { void addVerboseMessage(Logger log, String message); - void addError(final String error); - - void addError(Logger log, String error); - void addError(String error, Throwable e); void addError(Logger log, String error, Throwable e); diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/history/PersistableInstallationLogger.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/history/PersistableInstallationLogger.java index 4f84a77c..b454bedc 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/history/PersistableInstallationLogger.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/history/PersistableInstallationLogger.java @@ -138,20 +138,10 @@ public void addError(Logger log, String error, Throwable e) { addError(error, e); } - @Override - public void addError(Logger log, String error) { - log.error(error); - addError(error); - } - public void addError(final String error, Throwable e) { - addError(error + " / e=" + e); - } - - @Override - public void addError(final String error) { + String fullErrorValue = error + " / e=" + e; errors.add(new HistoryEntry(msgIndex, new Timestamp( - new Date().getTime()), MSG_IDENTIFIER_ERROR + error)); + new Date().getTime()), MSG_IDENTIFIER_ERROR + fullErrorValue)); success = false; msgIndex++; } diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/history/ProgressTrackerListenerInstallationLogger.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/history/ProgressTrackerListenerInstallationLogger.java index 1467ac40..f637419f 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/history/ProgressTrackerListenerInstallationLogger.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/history/ProgressTrackerListenerInstallationLogger.java @@ -24,12 +24,6 @@ protected void addMessage(String message) { super.addMessage(message); } - @Override - public void addError(String error) { - listener.onError(ProgressTrackerListener.Mode.TEXT, MSG_IDENTIFIER_ERROR + error, null); - super.addError(error); - } - @Override public void addError(String error, Throwable t) { Exception e; diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/impl/AcInstallationServiceImpl.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/impl/AcInstallationServiceImpl.java index 3254cdb2..496c3b09 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/impl/AcInstallationServiceImpl.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/impl/AcInstallationServiceImpl.java @@ -214,8 +214,7 @@ public void installConfigurationFiles(PersistableInstallationLogger installLog, LOG.info("Successfully applied AC Tool configuration in " + msHumanReadable(executionTime)); installLog.setExecutionTime(executionTime); } catch (Exception e) { - // TODO: separate exception - installLog.addError(e.toString()); // ensure exception is added to installLog before it's persisted in log in finally clause + installLog.addError("Could not process yaml files", e); // ensure exception is added to installLog before it's persisted in log in finally clause throw e; // handling is different depending on JMX or install hook case } finally { try { @@ -255,9 +254,9 @@ private void removeAcesForPathsNotInConfig(InstallationLogger installLog, Sessio acConfiguration.getAceConfig()); for (String relevantPath : relevantPathsForCleanup) { - // TODO: why is acconfiguration retrieved from log? Set principalsToRemoveAcesForAtThisPath = acConfiguration.getAuthorizablesConfig() - .removeUnmanagedPrincipalNamesAtPath(relevantPath, principalsInConfig); + .removeUnmanagedPrincipalNamesAtPath(relevantPath, principalsInConfig, + acConfiguration.getGlobalConfiguration().getDefaultUnmanagedAcePathsRegex()); // delete ACE if principal *is* in config, but the path *is not* in config int countRemoved = AccessControlUtils.deleteAllEntriesForPrincipalsFromACL(session, diff --git a/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/authorizableinstaller/impl/AuthorizableInstallerServiceImplTest.java b/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/authorizableinstaller/impl/AuthorizableInstallerServiceImplTest.java index b4d371f4..956ecee3 100644 --- a/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/authorizableinstaller/impl/AuthorizableInstallerServiceImplTest.java +++ b/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/authorizableinstaller/impl/AuthorizableInstallerServiceImplTest.java @@ -161,7 +161,11 @@ public Set answer(InvocationOnMock invocation) throws Throwable { }).when(cut).validateAssignedGroups(userManager, acConfiguration.getAuthorizablesConfig(), null, TESTGROUP, configuredGroups, status); Set authorizablesInConfig = new HashSet(asList(GROUP1)); - cut.applyGroupMembershipConfigIsMemberOf(TESTGROUP, acConfiguration, status, userManager, null, configuredGroups, groupsInRepo, + + AuthorizableConfigBean authorizableConfigBean = new AuthorizableConfigBean(); + authorizableConfigBean.setAuthorizableId(TESTGROUP); + cut.applyGroupMembershipConfigIsMemberOf(authorizableConfigBean, acConfiguration, status, userManager, null, configuredGroups, + groupsInRepo, authorizablesInConfig); verifyZeroInteractions(group2); // in configuredGroups and in groupsInRepo diff --git a/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/configmodel/AuthorizablesConfigTest.java b/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/configmodel/AuthorizablesConfigTest.java index 75c6ad56..d0eb55ed 100644 --- a/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/configmodel/AuthorizablesConfigTest.java +++ b/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/configmodel/AuthorizablesConfigTest.java @@ -1,55 +1,84 @@ package biz.netcentric.cq.tools.actool.configmodel; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; import java.util.Arrays; +import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; +import org.junit.Before; import org.junit.Test; public class AuthorizablesConfigTest { - @Test - public void testRemoveUnmanagedPrincipalNamesAtPath() { - AuthorizablesConfig authorizablesConfig = new AuthorizablesConfig(); + AuthorizablesConfig authorizablesConfig; + AuthorizableConfigBean beanTestGroupAllManaged; + AuthorizableConfigBean testgroupPartlyManaged; + AuthorizableConfigBean beanEveryone; + + @Before + public void setup() { + authorizablesConfig = new AuthorizablesConfig(); - AuthorizableConfigBean beanTestGroupAllManaged = getBean("testgroupAllManaged", null); + beanTestGroupAllManaged = getBean("testgroupAllManaged", null); authorizablesConfig.add(beanTestGroupAllManaged); - AuthorizableConfigBean testgroupPartlyManaged = getBean("testgroupPartlyManaged", "/content/dam/geometrixx.*"); + testgroupPartlyManaged = getBean("testgroupPartlyManaged", "/content/dam/geometrixx.*"); authorizablesConfig.add(testgroupPartlyManaged); // example for negative look-ahead to only manage certain paths as useful for everyone - AuthorizableConfigBean beanEveryone = getBean("everyone", "^(?!/etc/linkchecker|/etc/test).*" /* - * only manage /etc/linkchecker and - * /etc/test - */ ); + beanEveryone = getBean("everyone", "^(?!/etc/linkchecker|/etc/test).*" /* + * only manage /etc/linkchecker and /etc/test + */ ); authorizablesConfig.add(beanEveryone); + } + + @Test + public void testRemoveUnmanagedPrincipalNamesAtPath() { Set principalSet = principalSet(beanTestGroupAllManaged.getPrincipalName(), testgroupPartlyManaged.getPrincipalName(), beanEveryone.getPrincipalName()); - Set onlyManagedPrincipalNames = authorizablesConfig.removeUnmanagedPrincipalNamesAtPath("/etc/linkchecker", principalSet); - assertTrue(onlyManagedPrincipalNames.contains("testgroupAllManaged")); - assertTrue(onlyManagedPrincipalNames.contains("testgroupPartlyManaged")); - assertTrue(onlyManagedPrincipalNames.contains("everyone")); - - onlyManagedPrincipalNames = authorizablesConfig.removeUnmanagedPrincipalNamesAtPath("/etc", principalSet); - assertTrue(onlyManagedPrincipalNames.contains("testgroupAllManaged")); - assertTrue(onlyManagedPrincipalNames.contains("testgroupPartlyManaged")); - assertFalse(onlyManagedPrincipalNames.contains("everyone")); - - onlyManagedPrincipalNames = authorizablesConfig.removeUnmanagedPrincipalNamesAtPath("/content/geometrixx", principalSet); - assertTrue(onlyManagedPrincipalNames.contains("testgroupAllManaged")); - assertTrue(onlyManagedPrincipalNames.contains("testgroupPartlyManaged")); - assertFalse(onlyManagedPrincipalNames.contains("everyone")); - - onlyManagedPrincipalNames = authorizablesConfig.removeUnmanagedPrincipalNamesAtPath("/content/dam/geometrixx", principalSet); - assertTrue(onlyManagedPrincipalNames.contains("testgroupAllManaged")); - assertFalse(onlyManagedPrincipalNames.contains("testgroupPartlyManaged")); - assertFalse(onlyManagedPrincipalNames.contains("everyone")); + Set onlyManagedPrincipalNames = authorizablesConfig.removeUnmanagedPrincipalNamesAtPath("/etc/linkchecker", principalSet, + null); + assertEquals(principalSet("testgroupAllManaged", "testgroupPartlyManaged", "everyone"), onlyManagedPrincipalNames); + + onlyManagedPrincipalNames = authorizablesConfig.removeUnmanagedPrincipalNamesAtPath("/etc", principalSet, null); + assertEquals(principalSet("testgroupAllManaged", "testgroupPartlyManaged"), onlyManagedPrincipalNames); + + onlyManagedPrincipalNames = authorizablesConfig.removeUnmanagedPrincipalNamesAtPath("/content/geometrixx", principalSet, null); + assertEquals(principalSet("testgroupAllManaged", "testgroupPartlyManaged"), onlyManagedPrincipalNames); + + onlyManagedPrincipalNames = authorizablesConfig.removeUnmanagedPrincipalNamesAtPath("/content/dam/geometrixx", principalSet, null); + assertEquals(principalSet("testgroupAllManaged"), onlyManagedPrincipalNames); + + } + + @Test + public void testRemoveUnmanagedPrincipalNamesAtPathUsingGlobalConfig() { + + Set principalSet = principalSet(beanTestGroupAllManaged.getPrincipalName(), testgroupPartlyManaged.getPrincipalName(), + beanEveryone.getPrincipalName()); + + Set onlyManagedPrincipalNames = authorizablesConfig.removeUnmanagedPrincipalNamesAtPath("/etc/linkchecker", principalSet, + "/etc/.*"); + + // "testgroupPartlyManaged", "everyone" are still in since they define their own unmanagedAcePathsRegex + assertEquals(principalSet("testgroupPartlyManaged", "everyone"), onlyManagedPrincipalNames); + + // without any individual unmanagedAcePathsRegex set but defaultUnmanagedAcePathsRegex set to a matching regex, there will be never + // anything removed from this path + testgroupPartlyManaged.setUnmanagedAcePathsRegex(null); + beanEveryone.setUnmanagedAcePathsRegex(null); + onlyManagedPrincipalNames = authorizablesConfig.removeUnmanagedPrincipalNamesAtPath("/etc/linkchecker", principalSet, + "/etc/.*"); + assertEquals(Collections.emptySet(), onlyManagedPrincipalNames); + + // without default restriction all principals have to be returned + onlyManagedPrincipalNames = authorizablesConfig.removeUnmanagedPrincipalNamesAtPath("/etc/linkchecker", principalSet, + null); + assertEquals(principalSet("testgroupAllManaged", "testgroupPartlyManaged", "everyone"), onlyManagedPrincipalNames); } diff --git a/accesscontroltool-exampleconfig-package/pom.xml b/accesscontroltool-exampleconfig-package/pom.xml index b8ffe5ad..5815bcd6 100644 --- a/accesscontroltool-exampleconfig-package/pom.xml +++ b/accesscontroltool-exampleconfig-package/pom.xml @@ -15,7 +15,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 2.0.5 + 2.0.6 diff --git a/accesscontroltool-oakindex-package/pom.xml b/accesscontroltool-oakindex-package/pom.xml index e29723ac..97346d3e 100644 --- a/accesscontroltool-oakindex-package/pom.xml +++ b/accesscontroltool-oakindex-package/pom.xml @@ -15,7 +15,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 2.0.5 + 2.0.6 diff --git a/accesscontroltool-package/pom.xml b/accesscontroltool-package/pom.xml index d7097633..fb88572b 100644 --- a/accesscontroltool-package/pom.xml +++ b/accesscontroltool-package/pom.xml @@ -15,7 +15,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 2.0.5 + 2.0.6 diff --git a/docs/AdvancedFeatures.md b/docs/AdvancedFeatures.md index 34527026..bc0db2fe 100644 --- a/docs/AdvancedFeatures.md +++ b/docs/AdvancedFeatures.md @@ -147,6 +147,7 @@ Expressions are evaluated using javax.el expression language. The following util - contains(str,fragmentStr) - endsWith(str,fragmentStr) - startsWith(str,fragmentStr) +- length(str) ### Variables @@ -179,9 +180,13 @@ Variables can also be declared to be an array and used in a loop: NOTE: The scope of a variable is always limited to the lines in the very same yaml file following the definition till it is either redefined or the end of the yaml file is reached (this limitation will supposably be lifted with [#257][i257]). -## Configure permissions for anonymous +## Configure permissions for built-in users or groups (like anonymous) -To configure permissions for out-of-the-box anonymous user, it's best to create a custom group and add user `anonymous` to the `members` attribute of that group. The ACEs added to the custom group will then be effective for anonyomous user. +To configure permissions for already existing users, it's best to create a custom group and add this user to the `members` attribute of that group. The ACEs added to the custom group will then be effective for that user as well. + +This is not an option for the [`everyone` group](https://jackrabbit.apache.org/oak/docs/security/user/default.html#Everyone_Group) as it is neither allowed to put groups/users as members to this group (because implicitly every principal is member of this group) nor to put this group as member to another group (to prevent cycles, compare with [OAK-7323](https://issues.apache.org/jira/browse/OAK-7323)). + +Another alternative is to list the built-in user in the YAML file (with the correct path and system user flag) and leverage `unmanagedAcePathsRegex` as outlined below. This is currently the only option to extend rights for `everyone`. ## Configure memberships of/towards externally managed groups @@ -196,6 +201,8 @@ The AC Tool manages relationships between authorizables of the configuration (th That way relationships that are created programmatically or manually can be left intact and the AC Tool does not remove them. Also this allows to have two configuration sets at different root paths. +Additionally, it is also possible to set `unmanagedExternalIsMemberOfRegex` and `unmanagedExternalMembersRegex` directly on the authorizable definition (then only effective locally to the authorizable). + ### Examples ### * `defaultUnmanagedExternalMembersRegex: .*` allow arbitrary groups to inherit from ACTool managed groups and keep those (unmanaged) relations even though relationship hasn't been established through the ACTool. Might be useful in a multi-tenant setup where each tenant maintains his own list of groups (e.g. via ACTool in dedicated packages) and wants to inherit from some fragments being set up by the global YAML file. @@ -203,16 +210,33 @@ That way relationships that are created programmatically or manually can be left ## Limiting where the AC Tool creates and removes ACEs -The property `unmanagedAcePathsRegx` for authorizable configurations (users or groups) can be used to ensure certain paths are not managed by the AC Tool: +The property `unmanagedAcePathsRegex` for authorizable configurations (users or groups) can be used to ensure certain paths are not managed by the AC Tool. This property must contain a regular expression which is matched against all ACE paths bound to the authorizable found in the system. All ACEs with matching paths are not touched. By setting the global config `defaultUnmanagedAcePathsRegex` it is possible to exclude certain areas of the JCR totally from removing (and creating once #244 is fixed) at all. +### Examples ``` - testgroup: - name: "Test Group" unmanagedAcePathsRegex: /content/dam/.* ``` +That way for `testgroup`, ACEs in `/content/dam/` will be left untouched for this particular group. -That way for `testgroup`, ACE in `/content/dam/` will be left as they are for this particular group. +You can use negative lookaheads to whitelist management of certain paths: +``` +- user_config: + - version-manager-service: + # the user does exist already, make sure the path is set correctly + - path: /home/users/system/wcm + isSystemUser: true + # everything outside /conf should not be managed by the ac tool + unmanagedAcePathsRegex: /(?!conf).* +``` + +Example for setting it globally: +``` +- global_config: + defaultUnmanagedAcePathsRegex: /content/project2.* # will never change any ACLs underneath this root path +``` ## Automatically purge obsolete groups and users The root element `obsolete_authorizables` can be used to automatically purge authorizables that are not in use anymore: diff --git a/docs/Configuration.md b/docs/Configuration.md index 5d97c0da..2aaaf8f7 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -38,11 +38,12 @@ Groups are specified in the **group_config**. A group record in the configuratio property | comment | required --- | --- | --- -name | Name of the group as shown in UI | optional, if empty group id is taken +name | Name of the group as shown in UI. Sets the property `profile/givenName` of that group. | optional description | Description of the group | optional externalId | Required for AC setups since AEM 6.2 SP1 that synchronize groups from LDAP to AEM. The value has to be in format LDAP-DN;IDP-NAME where LDAP-DN is the full distinguished name and IDP-NAME is configured in OSGI config PID org.apache.jackrabbit.oak.security.authentication.ldap.impl.LdapIdentityProvider property "provider-name". Example: `externalId: "cn=group-name,ou=mydepart,ou=Groups,dc=comp,dc=com;IDPNAME"`. Since v1.9.3 | optional -path | Path of the group either relative or absolute | optional +path | Path of the intermediate node either relative or absolute. If relative, /home/groups is automatically prefixed. By default some implementation specific path is choosen. Usually the full group path the concatenated (intermediate) path and the authorizable id | optional isMemberOf | comma separated list of groups this groups is a member of | optional +memberOf | same meaning as `isMemberOf`. This property is *deprecated*, please use `isMemberOf` instead. Only supported for backwards-compatibility reasons | optional members | comma separated list of groups that are member of this group (allows to specify the relationshipo from the other side, however prefer `isMemberOf` over members if possible) | optional migrateFrom | a group name assigned member users are taken over from | optional unmanaged* Properties | Only use sparsely and with care, see [Advanced Features](AdvancedFeatures.md) | optional @@ -75,7 +76,7 @@ Users can be configured in the same way as groups in the **user_config** section property | comment | required --- | --- | --- -name | Works mostly like for groups, except that the string is split up in first and last name using the last space found in string. For instance "Johann Sebastian Bach" will result in first name "Johann Sebastian" and last name "Bach". For names where the split has to be explicitly configured, use a comma: "Van der Broek, Sebastian" will result in first name "Sebastian" and last name "Van der Broek" | optional +name | Works mostly like for groups, except that the string is split up in first and last name using the last space found in string. For instance "Johann Sebastian Bach" will result in first name "Johann Sebastian" and last name "Bach". For names where the split has to be explicitly configured, use a comma: "Van der Broek, Sebastian" will result in first name "Sebastian" and last name "Van der Broek". Sets the properties `profile/familyName` and `profile/givenName` of the user. | optional description, path, isMemberOf | Work exactly as for groups | optional password | The PW for the user. Can be stored in plain text (only to be used for test users). If a password value is enclosed in brackets, then it will be automatically decrypted using com.adobe.granite.crypto.CryptoSupport. `/system/console/crypto` on target instance can be used to get encrypted password. Encrypted password (together with braces) should also be enclosed in double quotes. | Required for non-system users, otherwise must not be set isSystemUser | Create users as system user (AEM 6.1 and later) | optional diff --git a/pom.xml b/pom.xml index b363ba3f..5990c5cb 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 2.0.5 + 2.0.6 pom Access Control Tool - Reactor Project