From 28f3b2d065d9d632fd22deacd2c02c1ab0b0454c Mon Sep 17 00:00:00 2001 From: "georg.henzler" Date: Tue, 25 Oct 2016 15:03:25 +0200 Subject: [PATCH 01/28] updating poms for 1.9.2-SNAPSHOT development --- accesscontroltool-bundle/pom.xml | 2 +- accesscontroltool-exampleconfig-package/pom.xml | 2 +- accesscontroltool-oakindex-package/pom.xml | 2 +- accesscontroltool-package/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/accesscontroltool-bundle/pom.xml b/accesscontroltool-bundle/pom.xml index d22608f5..3acbae6e 100644 --- a/accesscontroltool-bundle/pom.xml +++ b/accesscontroltool-bundle/pom.xml @@ -11,7 +11,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 1.9.1-SNAPSHOT + 1.9.2-SNAPSHOT diff --git a/accesscontroltool-exampleconfig-package/pom.xml b/accesscontroltool-exampleconfig-package/pom.xml index 5c49c603..ec6e9adc 100644 --- a/accesscontroltool-exampleconfig-package/pom.xml +++ b/accesscontroltool-exampleconfig-package/pom.xml @@ -15,7 +15,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 1.9.1-SNAPSHOT + 1.9.2-SNAPSHOT diff --git a/accesscontroltool-oakindex-package/pom.xml b/accesscontroltool-oakindex-package/pom.xml index 64ee84ad..556f5007 100644 --- a/accesscontroltool-oakindex-package/pom.xml +++ b/accesscontroltool-oakindex-package/pom.xml @@ -15,7 +15,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 1.9.1-SNAPSHOT + 1.9.2-SNAPSHOT diff --git a/accesscontroltool-package/pom.xml b/accesscontroltool-package/pom.xml index 6783cd46..b2ff3b25 100644 --- a/accesscontroltool-package/pom.xml +++ b/accesscontroltool-package/pom.xml @@ -15,7 +15,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 1.9.1-SNAPSHOT + 1.9.2-SNAPSHOT diff --git a/pom.xml b/pom.xml index aab1833d..7a23e80e 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 1.9.1-SNAPSHOT + 1.9.2-SNAPSHOT pom Access Control Tool - Reactor Project From 7a81050c6469443ba62d0a86c0831073c6174deb Mon Sep 17 00:00:00 2001 From: "georg.henzler" Date: Tue, 25 Oct 2016 15:06:16 +0200 Subject: [PATCH 02/28] updating develop poms to master versions to avoid merge conflicts --- accesscontroltool-bundle/pom.xml | 2 +- accesscontroltool-exampleconfig-package/pom.xml | 2 +- accesscontroltool-oakindex-package/pom.xml | 2 +- accesscontroltool-package/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/accesscontroltool-bundle/pom.xml b/accesscontroltool-bundle/pom.xml index 3acbae6e..baa338f6 100644 --- a/accesscontroltool-bundle/pom.xml +++ b/accesscontroltool-bundle/pom.xml @@ -11,7 +11,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 1.9.2-SNAPSHOT + 1.9.1 diff --git a/accesscontroltool-exampleconfig-package/pom.xml b/accesscontroltool-exampleconfig-package/pom.xml index ec6e9adc..35f7b855 100644 --- a/accesscontroltool-exampleconfig-package/pom.xml +++ b/accesscontroltool-exampleconfig-package/pom.xml @@ -15,7 +15,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 1.9.2-SNAPSHOT + 1.9.1 diff --git a/accesscontroltool-oakindex-package/pom.xml b/accesscontroltool-oakindex-package/pom.xml index 556f5007..865b0244 100644 --- a/accesscontroltool-oakindex-package/pom.xml +++ b/accesscontroltool-oakindex-package/pom.xml @@ -15,7 +15,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 1.9.2-SNAPSHOT + 1.9.1 diff --git a/accesscontroltool-package/pom.xml b/accesscontroltool-package/pom.xml index b2ff3b25..4934eb68 100644 --- a/accesscontroltool-package/pom.xml +++ b/accesscontroltool-package/pom.xml @@ -15,7 +15,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 1.9.2-SNAPSHOT + 1.9.1 diff --git a/pom.xml b/pom.xml index 7a23e80e..89d13bc6 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 1.9.2-SNAPSHOT + 1.9.1 pom Access Control Tool - Reactor Project From fe29414f5d51e601ddb2fc31a74e368a4b73ed72 Mon Sep 17 00:00:00 2001 From: "georg.henzler" Date: Tue, 25 Oct 2016 15:06:17 +0200 Subject: [PATCH 03/28] Updating develop poms back to pre merge state --- accesscontroltool-bundle/pom.xml | 2 +- accesscontroltool-exampleconfig-package/pom.xml | 2 +- accesscontroltool-oakindex-package/pom.xml | 2 +- accesscontroltool-package/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/accesscontroltool-bundle/pom.xml b/accesscontroltool-bundle/pom.xml index baa338f6..3acbae6e 100644 --- a/accesscontroltool-bundle/pom.xml +++ b/accesscontroltool-bundle/pom.xml @@ -11,7 +11,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 1.9.1 + 1.9.2-SNAPSHOT diff --git a/accesscontroltool-exampleconfig-package/pom.xml b/accesscontroltool-exampleconfig-package/pom.xml index 35f7b855..ec6e9adc 100644 --- a/accesscontroltool-exampleconfig-package/pom.xml +++ b/accesscontroltool-exampleconfig-package/pom.xml @@ -15,7 +15,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 1.9.1 + 1.9.2-SNAPSHOT diff --git a/accesscontroltool-oakindex-package/pom.xml b/accesscontroltool-oakindex-package/pom.xml index 865b0244..556f5007 100644 --- a/accesscontroltool-oakindex-package/pom.xml +++ b/accesscontroltool-oakindex-package/pom.xml @@ -15,7 +15,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 1.9.1 + 1.9.2-SNAPSHOT diff --git a/accesscontroltool-package/pom.xml b/accesscontroltool-package/pom.xml index 4934eb68..b2ff3b25 100644 --- a/accesscontroltool-package/pom.xml +++ b/accesscontroltool-package/pom.xml @@ -15,7 +15,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 1.9.1 + 1.9.2-SNAPSHOT diff --git a/pom.xml b/pom.xml index 89d13bc6..7a23e80e 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 1.9.1 + 1.9.2-SNAPSHOT pom Access Control Tool - Reactor Project From d2140dfb2b14b5a2093b10546a3baa6e1960d20d Mon Sep 17 00:00:00 2001 From: Roland Gruber Date: Fri, 28 Oct 2016 09:00:45 +0200 Subject: [PATCH 04/28] documentation --- .../actool/aceservicejmx/AceServiceMBean.java | 22 +++++++++---------- docs/Jmx.md | 4 ++-- docs/Migration.md | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceservicejmx/AceServiceMBean.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceservicejmx/AceServiceMBean.java index 24ea1069..a02f487d 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceservicejmx/AceServiceMBean.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceservicejmx/AceServiceMBean.java @@ -24,38 +24,38 @@ public interface AceServiceMBean { boolean isReadyToStart(); - @Description("executes the installation of the ACE configuration(s)") + @Description("Executes the installation of the ACE configuration(s)") String execute(); - @Description("purges the AccessControlList of the given path, if existing") + @Description("Purges the AccessControlList of the given path, if existing") String purgeACL(@Name("path") final String path); - @Description("purges all AccessControlLists under the given path and its subpaths, if existing") + @Description("Purges all AccessControlLists under the given path and its subpaths, if existing") String purgeACLs(@Name("path") final String path); - @Description("purges all authorizables contained in configuration files and all their ACEs from the system") + @Description("Purges all authorizables contained in configuration files and all their ACEs from the system") public String purgeAllAuthorizablesFromConfigurations(); - @Description("provides status and links to the saved history logs") + @Description("Provides status and links to the saved history logs") String[] getSavedLogs() throws RepositoryException; - @Description("shows execution status of the AC Tool") + @Description("Shows execution status of the AC Tool") public boolean isExecuting(); - @Description("returns a configuration dump containing all groups and all ACLs ordered by path") + @Description("Returns a configuration dump containing all groups and all ACLs ordered by path") public String pathBasedDump(); - @Description("returns a configuration dump containing all groups and all ACEs ordered by groups") + @Description("Returns a configuration dump containing all groups and all ACEs ordered by groups (can be used as template for AC Tool configuration file)") public String groupBasedDump(); - @Description("returns links to the existing configuration files in CRX") + @Description("Returns links to the existing configuration files in CRX") public String[] getConfigurationFiles(); - @Description("returns history log which matches the provided number") + @Description("Returns history log which matches the provided number") public String showHistoryLog( @Name("historyLogNumber") @Description("number of history log") final String historyLogNumber); - @Description("purges authorizable(s) and respective ACEs from the system. Several authorizable ids have to be comma separated.") + @Description("Purges authorizable(s) and respective ACEs from the system. Several authorizable ids have to be comma separated.") public String purgeAuthorizables( @Name("authorizableIds") String authorizableIds); } diff --git a/docs/Jmx.md b/docs/Jmx.md index 53be4a2c..edfc53aa 100644 --- a/docs/Jmx.md +++ b/docs/Jmx.md @@ -31,8 +31,8 @@ The path of the config files is configured in OSGi - AC Installation Service. ### pathBasedDump() and groupBasedDump() -* path based dumps: here all ACEs in the dump are grouped by path thus representing a complete ACL. This kind of dump gets triggered by the method: pathBasedDump(). -* group based dumps: here all ACEs in the dump are grouped by their respective principal (group or user). This kind of dump gets triggered by the method: groupBasedDump(). +* Group based dump: here all ACEs in the dump are grouped by their respective principal (group or user). This kind of dump gets triggered by the method: groupBasedDump(). The result is in AC Tool config file format and can be used as template to create a configuration file. +* Path based dump: here all ACEs in the dump are grouped by path thus representing a complete ACL. This kind of dump gets triggered by the method: pathBasedDump(). The created dump can be watched directly in JMX and also gets saved in CRX under /var/statistics/achistory/dump_[Timestamp]. The number of dumps to be saved in CRX can be configured in the OSGi configuration of the dump service in the field: "Number of dumps to save" (see screenshot). diff --git a/docs/Migration.md b/docs/Migration.md index 4f59dc3e..60ff08b1 100644 --- a/docs/Migration.md +++ b/docs/Migration.md @@ -9,7 +9,7 @@ Please note that for CQ 5.6 you will need version 1.8.5 of the tool. ## 2. Export rules -Do an export using groupBasedDump() on [JMX interface](Jmx.md). The dump will provide you a Yaml export with all AC entries in your system. Save it to a file and remove all entries that should not be managed by AC Tool. E.g. you do not want to manage the system groups such as "everyone" and "administrators". +Do an export using **groupBasedDump()** on [JMX interface](Jmx.md). The dump will provide you a Yaml export with all AC entries in your system. Save it to a file and remove all entries that should not be managed by AC Tool. E.g. you do not want to manage the system groups such as "everyone" and "administrators". ## 3. Add rules to your content package From d83047ad231bc531262d8a4d0d2641a72ca57180 Mon Sep 17 00:00:00 2001 From: Roland Gruber Date: Fri, 28 Oct 2016 09:01:57 +0200 Subject: [PATCH 05/28] documentation --- docs/Jmx.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Jmx.md b/docs/Jmx.md index edfc53aa..96a602b7 100644 --- a/docs/Jmx.md +++ b/docs/Jmx.md @@ -29,7 +29,7 @@ The path of the config files is configured in OSGi - AC Installation Service. -### pathBasedDump() and groupBasedDump() +### groupBasedDump() and pathBasedDump() * Group based dump: here all ACEs in the dump are grouped by their respective principal (group or user). This kind of dump gets triggered by the method: groupBasedDump(). The result is in AC Tool config file format and can be used as template to create a configuration file. * Path based dump: here all ACEs in the dump are grouped by path thus representing a complete ACL. This kind of dump gets triggered by the method: pathBasedDump(). From c78f0552f1d5e15e78b5b61e542888dd2b4ca8f0 Mon Sep 17 00:00:00 2001 From: jochenkoschorke Date: Fri, 18 Nov 2016 09:27:48 +0100 Subject: [PATCH 06/28] #134-throwing/catching AuthorizableCreatorException to prevent rollback after authorizable installation --- .../aceservice/impl/AceServiceImpl.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) 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 2cd00964..65dce611 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 @@ -96,20 +96,18 @@ public class AceServiceImpl implements AceService { private boolean isExecuting = false; - @Property(label = "Configuration storage path", - description = "enter CRX path where ACE configuration gets stored", - name = AceServiceImpl.PROPERTY_CONFIGURATION_PATH, value = "") + @Property(label = "Configuration storage path", description = "enter CRX path where ACE configuration gets stored", name = AceServiceImpl.PROPERTY_CONFIGURATION_PATH, value = "") private String configurationPath; @Activate - public void activate(final Map properties) + public void activate(final Map properties) throws Exception { LOG.debug("Activated AceService!"); modified(properties); } @Modified - public void modified(final Map properties) { + public void modified(final Map properties) { LOG.debug("Modified AceService!"); configurationPath = PropertiesUtil.toString(properties.get(PROPERTY_CONFIGURATION_PATH), ""); } @@ -216,7 +214,7 @@ private void installAuthorizables( throws RepositoryException, Exception { // --- installation of Authorizables from configuration --- - String msg = "*** Starting installation of "+authorizablesMapfromConfig.size()+" authorizables..."; + String msg = "*** Starting installation of " + authorizablesMapfromConfig.size() + " authorizables..."; LOG.info(msg); history.addMessage(msg); @@ -243,7 +241,7 @@ private void installAuthorizables( authorizableInstallationHistory); authorizableInstallationSession.save(); } catch (Exception e) { - throw e; + throw new AuthorizableCreatorException(e); } finally { if (authorizableInstallationSession != null) { authorizableInstallationSession.logout(); @@ -262,7 +260,7 @@ private void removeObsoleteAuthorizables(AcInstallationHistoryPojo history, Sess history.addVerboseMessage("No obsolete authorizables configured"); return; } - + UserManager userManager = AccessControlUtils.getUserManagerAutoSaveDisabled(session); Set obsoleteAuthorizablesAlreadyPurged = new HashSet(); @@ -325,6 +323,7 @@ public AcInstallationHistoryPojo execute() { installConfigurationFiles(session, history, newestConfigurations, authorizableInstallationHistorySet); } catch (AuthorizableCreatorException e) { history.addError(e.toString()); + LOG.error("An exception occured while installing the authorizables from configuration: {}", e); // here no rollback of authorizables necessary since session wasn't // saved } catch (Exception e) { @@ -356,7 +355,8 @@ public AcInstallationHistoryPojo execute() { /** Common entry point for JMX and install hook. */ @Override - public void installConfigurationFiles(Session session, AcInstallationHistoryPojo history, Map configurationFileContentsByFilename, + public void installConfigurationFiles(Session session, AcInstallationHistoryPojo history, + Map configurationFileContentsByFilename, Set authorizableInstallationHistorySet) throws Exception { @@ -393,7 +393,7 @@ public void installConfigurationFiles(Session session, AcInstallationHistoryPojo } sw.stop(); long executionTime = sw.getTime(); - LOG.info("Successfully applied AC Tool configuration in "+ msHumanReadable(executionTime)); + LOG.info("Successfully applied AC Tool configuration in " + msHumanReadable(executionTime)); history.setExecutionTime(executionTime); } catch (Exception e) { history.addError(e.toString()); // ensure exception is added to history before it's persisted in log in finally clause @@ -404,7 +404,7 @@ public void installConfigurationFiles(Session session, AcInstallationHistoryPojo } catch (Exception e) { LOG.warn("Could not persist history, e=" + e, e); } - + Thread.currentThread().setName(origThreadName); isExecuting = false; } @@ -416,7 +416,7 @@ private void installMergedConfigurations( Session session, Set authorizableInstallationHistorySet, AcConfiguration acConfiguration) throws ValueFormatException, - RepositoryException, Exception { + RepositoryException, Exception { String message = "Starting installation of merged configurations..."; LOG.debug(message); From 0d3b5c9f71a888102d8203e839bcf5650ce2b699 Mon Sep 17 00:00:00 2001 From: jochenkoschorke Date: Fri, 18 Nov 2016 09:50:07 +0100 Subject: [PATCH 07/28] #134-added constructor --- .../authorizableutils/AuthorizableCreatorException.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/authorizableutils/AuthorizableCreatorException.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/authorizableutils/AuthorizableCreatorException.java index 6fc71bff..3f7bf8ad 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/authorizableutils/AuthorizableCreatorException.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/authorizableutils/AuthorizableCreatorException.java @@ -9,7 +9,11 @@ package biz.netcentric.cq.tools.actool.authorizableutils; public class AuthorizableCreatorException extends Exception { - public AuthorizableCreatorException(String message) { + public AuthorizableCreatorException(final String message) { super(message); } + + public AuthorizableCreatorException(final Throwable e) { + super(e); + } } From f982195d72acf3a7dc6e1b379881e7beab738fe7 Mon Sep 17 00:00:00 2001 From: jochenkoschorke Date: Fri, 18 Nov 2016 10:14:09 +0100 Subject: [PATCH 08/28] #134-get authorizable roots from UserConfiguration and pass them as params to validator --- .../configreader/YamlConfigurationMerger.java | 12 ++- .../impl/AuthorizableValidatorImpl.java | 71 ++++++++++++---- .../YamlConfigurationMergerTest.java | 15 +++- .../actool/validators/BeanValidatorsTest.java | 13 ++- .../validators/RestrictionValidationTest.java | 22 ++--- .../validators/ValidatorTestHelper.java | 33 +++++--- .../src/test/resources/testconfig.yaml | 81 ++++++++++++++++++- 7 files changed, 195 insertions(+), 52 deletions(-) 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 2d518f64..03ce8ca8 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 @@ -20,6 +20,7 @@ import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.Service; +import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.yaml.snakeyaml.Yaml; @@ -52,12 +53,15 @@ public class YamlConfigurationMerger implements ConfigurationMerger { @Reference ObsoleteAuthorizablesValidator obsoleteAuthorizablesValidator; + @Reference + UserConfiguration userConfiguration; + @Override public AcConfiguration getMergedConfigurations( final Map configFileContentByFilename, final AcInstallationHistoryPojo history, final ConfigReader configReader) throws RepositoryException, - AcConfigBeanValidationException { + AcConfigBeanValidationException { final GlobalConfiguration globalConfiguration = new GlobalConfiguration(); final Map> mergedAuthorizablesMapfromConfig = new LinkedHashMap>(); @@ -102,7 +106,9 @@ public AcConfiguration getMergedConfigurations( // --- authorizables config section // build AuthorizableConfigBeans from current configurations - final AuthorizableValidator authorizableValidator = new AuthorizableValidatorImpl(); + String groupsPath = (String) userConfiguration.getParameters().get("groupsPath"); + String usersPath = (String) userConfiguration.getParameters().get("usersPath"); + final AuthorizableValidator authorizableValidator = new AuthorizableValidatorImpl(groupsPath, usersPath); final Map> groupAuthorizablesMapFromConfig = configReader.getGroupConfigurationBeans( yamlRootList, authorizableValidator); // add AuthorizableConfigBeans built from current configuration to set containing AuthorizableConfigBeans from all @@ -156,7 +162,7 @@ public AcConfiguration getMergedConfigurations( // set member groups final AuthorizableMemberGroupsValidator membersValidator = new AuthorizableMemberGroupsValidator(); membersValidator.validate(mergedAuthorizablesMapfromConfig); - + GlobalConfigurationValidator.validate(globalConfiguration); AcConfiguration acConfiguration = new AcConfiguration(); diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/validators/impl/AuthorizableValidatorImpl.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/validators/impl/AuthorizableValidatorImpl.java index 52e5c0b8..97e9a454 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/validators/impl/AuthorizableValidatorImpl.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/validators/impl/AuthorizableValidatorImpl.java @@ -18,6 +18,7 @@ import biz.netcentric.cq.tools.actool.validators.exceptions.AcConfigBeanValidationException; import biz.netcentric.cq.tools.actool.validators.exceptions.InvalidAuthorizableException; import biz.netcentric.cq.tools.actool.validators.exceptions.InvalidGroupNameException; +import biz.netcentric.cq.tools.actool.validators.exceptions.InvalidIntermediatePathException; public class AuthorizableValidatorImpl implements AuthorizableValidator { @@ -25,10 +26,15 @@ public class AuthorizableValidatorImpl implements AuthorizableValidator { .getLogger(AuthorizableValidatorImpl.class); private boolean enabled = true; AuthorizableConfigBean authorizableConfigBean; + final String groupsPath; + final String usersPath; - public AuthorizableValidatorImpl() { + public AuthorizableValidatorImpl(final String groupsPath, final String usersPath) { + this.groupsPath = groupsPath; + this.usersPath = usersPath; } + @Override public void validate(final AuthorizableConfigBean authorizableConfigBean) throws AcConfigBeanValidationException { this.authorizableConfigBean = authorizableConfigBean; @@ -37,10 +43,48 @@ public void validate(final AuthorizableConfigBean authorizableConfigBean) private boolean validate() throws AcConfigBeanValidationException { if (enabled) { - return validateAuthorizableProperties(this.authorizableConfigBean) - && validateMemberOf(this.authorizableConfigBean) - && validateMembers(this.authorizableConfigBean) - && validateAuthorizableId(this.authorizableConfigBean); + return validateAuthorizableProperties(authorizableConfigBean) + && validateMemberOf(authorizableConfigBean) + && validateMembers(authorizableConfigBean) + && validateAuthorizableId(authorizableConfigBean) + && validateIntermediatePath(authorizableConfigBean); + } + return true; + } + + public boolean validateIntermediatePath( + final AuthorizableConfigBean tmpPrincipalConfigBean) + throws InvalidAuthorizableException, InvalidIntermediatePathException { + boolean isGroup = tmpPrincipalConfigBean.isGroup(); + String intermediatePath = tmpPrincipalConfigBean.getPath(); + String currentPrincipalID = tmpPrincipalConfigBean.getPrincipalID(); + final String basicErrorMessage = "Validation error while validating intermediate path of authorizable: " + + currentPrincipalID; + // we only care about paths starting with a slash. if there is none, the path is assumed to be relative + // also empty string should be possible + if (intermediatePath.startsWith("/")) { + if (!intermediatePath.startsWith(groupsPath) && !intermediatePath.startsWith(usersPath)) { + String message = basicErrorMessage + + " - the intermediate path either has to be relative (not starting with '/') or has to start with the authorizable root!"; + LOG.error(message); + throw new InvalidIntermediatePathException(message); + } + if (!isGroup && intermediatePath.startsWith(groupsPath)) { + String message = basicErrorMessage + " - the intermediate path for the user must not be the groups path: " + groupsPath; + LOG.error(message); + throw new InvalidIntermediatePathException(message); + } + if (isGroup && intermediatePath.startsWith(usersPath)) { + String message = basicErrorMessage + " - the intermediate path for the group must not be the users path: " + usersPath; + LOG.error(message); + throw new InvalidIntermediatePathException(message); + } + if (intermediatePath.equals(groupsPath) || intermediatePath.equals(usersPath)) { + String message = basicErrorMessage + + " - the intermediate path must not be equal to the authorizable root but has to specify a subfolder of it!"; + LOG.error(message); + throw new InvalidIntermediatePathException(message); + } } return true; } @@ -80,12 +124,10 @@ public boolean validateAuthorizableProperties( } } - - - return true; } + @Override public boolean validateMemberOf( final AuthorizableConfigBean tmpPrincipalConfigBean) throws InvalidGroupNameException { @@ -122,7 +164,7 @@ public boolean validateMemberOf( public boolean validateMembers( final AuthorizableConfigBean tmpPrincipalConfigBean) - throws InvalidGroupNameException { + throws InvalidGroupNameException { final String currentPrincipal = tmpPrincipalConfigBean.getPrincipalID(); final String currentEntryValue = tmpPrincipalConfigBean .getMembersStringFromConfig(); @@ -154,16 +196,17 @@ public boolean validateMembers( return true; } + @Override public boolean validateAuthorizableId( final AuthorizableConfigBean tmpPrincipalConfigBean) - throws InvalidGroupNameException { + throws InvalidGroupNameException { final String currentPrincipal = tmpPrincipalConfigBean.getPrincipalID(); - if (Validators.isValidAuthorizableId((String) currentPrincipal)) { - tmpPrincipalConfigBean.setPrincipalID((String) currentPrincipal); + if (Validators.isValidAuthorizableId(currentPrincipal)) { + tmpPrincipalConfigBean.setPrincipalID(currentPrincipal); } else { final String message = "Validation error while reading group data: invalid group name: " - + (String) currentPrincipal; + + currentPrincipal; LOG.error(message); throw new InvalidGroupNameException(message); @@ -178,7 +221,7 @@ public void setBean(final AuthorizableConfigBean authorizableConfigBean) { @Override public void disable() { - this.enabled = false; + enabled = false; } 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 5d6a092e..00195ac9 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 @@ -18,8 +18,11 @@ import javax.jcr.RepositoryException; +import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters; +import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration; import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; import biz.netcentric.cq.tools.actool.configmodel.AcConfiguration; import biz.netcentric.cq.tools.actool.configmodel.AuthorizableConfigBean; @@ -28,7 +31,7 @@ import biz.netcentric.cq.tools.actool.validators.impl.ObsoleteAuthorizablesValidatorImpl; /** Tests the YamlConfigurationMerger - * + * * @author Roland Gruber */ public class YamlConfigurationMergerTest { @@ -39,6 +42,16 @@ public void setup() { merger = new YamlConfigurationMerger(); merger.yamlMacroProcessor = new YamlMacroProcessorImpl(); merger.obsoleteAuthorizablesValidator = new ObsoleteAuthorizablesValidatorImpl(); + + UserConfiguration userConfiguration = Mockito.mock(UserConfiguration.class); + + Map map = new HashMap<>(); + map.put("groupsPath", "/home/groups"); + map.put("usersPath", "/home/users"); + ConfigurationParameters configurationParameters = ConfigurationParameters.of(map); + + Mockito.when(userConfiguration.getParameters()).thenReturn(configurationParameters); + merger.userConfiguration = userConfiguration; } @Test diff --git a/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/validators/BeanValidatorsTest.java b/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/validators/BeanValidatorsTest.java index 8098365a..da743128 100644 --- a/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/validators/BeanValidatorsTest.java +++ b/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/validators/BeanValidatorsTest.java @@ -42,7 +42,6 @@ import biz.netcentric.cq.tools.actool.validators.impl.AceBeanValidatorImpl; import biz.netcentric.cq.tools.actool.validators.impl.AuthorizableValidatorImpl; - public class BeanValidatorsTest { @Mock @@ -60,7 +59,6 @@ public class BeanValidatorsTest { @InjectMocks ConfigReader yamlConfigReader = new YamlConfigReader(); - List aclList; Set groupsFromConfig; List aceBeanList = new ArrayList(); @@ -68,32 +66,33 @@ public class BeanValidatorsTest { @Before public void setup() throws IOException, RepositoryException, - AcConfigBeanValidationException { + AcConfigBeanValidationException { initMocks(this); doReturn(session).when(repository).loginAdministrative(null); accessControlPolicy = mock(AccessControlList.class, withSettings().extraInterfaces(JackrabbitAccessControlList.class)); - doReturn(new String[]{"rep:glob"}).when((JackrabbitAccessControlList)accessControlPolicy).getRestrictionNames(); + doReturn(new String[] { "rep:glob" }).when((JackrabbitAccessControlList) accessControlPolicy).getRestrictionNames(); doReturn(accessControlManager).when(session).getAccessControlManager(); - doReturn(new AccessControlPolicy[]{accessControlPolicy}).when(accessControlManager).getPolicies("/"); + doReturn(new AccessControlPolicy[] { accessControlPolicy }).when(accessControlManager).getPolicies("/"); doThrow(new RepositoryException("invalid permission")).when(accessControlManager).privilegeFromName("read"); doThrow(new RepositoryException("invalid permission")).when(accessControlManager).privilegeFromName("jcr_all"); final List yamlList = ValidatorTestHelper.getYamlList("testconfig.yaml"); - final AuthorizableValidator authorizableValidator = new AuthorizableValidatorImpl(); + final AuthorizableValidator authorizableValidator = new AuthorizableValidatorImpl("/home/groups", "/home/users"); authorizableValidator.disable(); groupsFromConfig = yamlConfigReader.getGroupConfigurationBeans( yamlList, authorizableValidator).keySet(); + ValidatorTestHelper.createAuthorizableTestBeans(yamlList, yamlConfigReader, authorizableBeanList); ValidatorTestHelper.createAceTestBeans(yamlList, yamlConfigReader, groupsFromConfig, aceBeanList); } @Test public void testAuthorizableBeans() { - final AuthorizableValidator authorizableValidator = new AuthorizableValidatorImpl(); + final AuthorizableValidator authorizableValidator = new AuthorizableValidatorImpl("/home/groups", "/home/users"); for (final AuthorizableConfigBean authorizableBean : authorizableBeanList) { assertEquals( ValidatorTestHelper.getSimpleValidationException(authorizableBean, diff --git a/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/validators/RestrictionValidationTest.java b/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/validators/RestrictionValidationTest.java index bed415fc..22c49593 100644 --- a/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/validators/RestrictionValidationTest.java +++ b/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/validators/RestrictionValidationTest.java @@ -33,11 +33,10 @@ import biz.netcentric.cq.tools.actool.validators.exceptions.AcConfigBeanValidationException; import biz.netcentric.cq.tools.actool.validators.impl.AceBeanValidatorImpl; import biz.netcentric.cq.tools.actool.validators.impl.AuthorizableValidatorImpl; -/** - * Contains unit tests checking support of different restrictions - * @author jochenkoschorkej - * - */ + +/** Contains unit tests checking support of different restrictions + * + * @author jochenkoschorkej */ public class RestrictionValidationTest { @Mock @@ -62,7 +61,7 @@ public class RestrictionValidationTest { @Before public void setup() throws IOException, RepositoryException, - AcConfigBeanValidationException { + AcConfigBeanValidationException { initMocks(this); doReturn(session).when(repository).loginAdministrative(null); @@ -71,7 +70,7 @@ public void setup() throws IOException, RepositoryException, withSettings().extraInterfaces(JackrabbitAccessControlList.class)); doReturn(accessControlManager).when(session).getAccessControlManager(); - doReturn(new AccessControlPolicy[]{accessControlPolicy}).when(accessControlManager).getPolicies("/"); + doReturn(new AccessControlPolicy[] { accessControlPolicy }).when(accessControlManager).getPolicies("/"); doThrow(new RepositoryException("invalid permission")).when(accessControlManager).privilegeFromName("read"); doThrow(new RepositoryException("invalid permission")).when(accessControlManager).privilegeFromName("jcr_all"); @@ -79,7 +78,7 @@ public void setup() throws IOException, RepositoryException, private void setupBeansFromTestYaml(final String path) throws IOException, AcConfigBeanValidationException, RepositoryException { final List yamlList = ValidatorTestHelper.getYamlList(path); - final AuthorizableValidator authorizableValidator = new AuthorizableValidatorImpl(); + final AuthorizableValidator authorizableValidator = new AuthorizableValidatorImpl("/home/groups", "/home/users"); authorizableValidator.disable(); groupsFromConfig = yamlConfigReader.getGroupConfigurationBeans( yamlList, authorizableValidator).keySet(); @@ -89,7 +88,7 @@ private void setupBeansFromTestYaml(final String path) throws IOException, AcCon @Test public void testAceBeansOnlyRepGlobRestrictionSupported() throws IOException, AcConfigBeanValidationException, RepositoryException { - doReturn(new String[]{"rep:glob"}).when((JackrabbitAccessControlList)accessControlPolicy).getRestrictionNames(); + doReturn(new String[] { "rep:glob" }).when((JackrabbitAccessControlList) accessControlPolicy).getRestrictionNames(); setupBeansFromTestYaml("testRestrictionsConfigs/test-restrictions1.yaml"); testExceptions(); } @@ -97,14 +96,15 @@ public void testAceBeansOnlyRepGlobRestrictionSupported() throws IOException, Ac @Test public void testAceBeansOnlyNtNamesRestrictionSupported() throws IOException, AcConfigBeanValidationException, RepositoryException { setupBeansFromTestYaml("testRestrictionsConfigs/test-restrictions2.yaml"); - doReturn(new String[]{"rep:ntNames"}).when((JackrabbitAccessControlList)accessControlPolicy).getRestrictionNames(); + doReturn(new String[] { "rep:ntNames" }).when((JackrabbitAccessControlList) accessControlPolicy).getRestrictionNames(); testExceptions(); } @Test public void testAceBeansAllRestrictionsSupported() throws IOException, AcConfigBeanValidationException, RepositoryException { setupBeansFromTestYaml("testRestrictionsConfigs/test-restrictions3.yaml"); - doReturn(new String[]{"rep:ntNames", "rep:glob", "rep:prefixes"}).when((JackrabbitAccessControlList)accessControlPolicy).getRestrictionNames(); + doReturn(new String[] { "rep:ntNames", "rep:glob", "rep:prefixes" }).when((JackrabbitAccessControlList) accessControlPolicy) + .getRestrictionNames(); testExceptions(); } diff --git a/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/validators/ValidatorTestHelper.java b/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/validators/ValidatorTestHelper.java index a0d1a0b5..35ebb7fa 100644 --- a/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/validators/ValidatorTestHelper.java +++ b/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/validators/ValidatorTestHelper.java @@ -21,26 +21,34 @@ import biz.netcentric.cq.tools.actool.validators.exceptions.AcConfigBeanValidationException; import biz.netcentric.cq.tools.actool.validators.impl.AceBeanValidatorImpl; import biz.netcentric.cq.tools.actool.validators.impl.AuthorizableValidatorImpl; -/** - * Helper class containing static methods used in validator-related unit tests based on test yaml files - * @author jochenkoschorkej + +/** Helper class containing static methods used in validator-related unit tests based on test yaml files * - */ + * @author jochenkoschorkej */ public class ValidatorTestHelper { - private ValidatorTestHelper(){} + private ValidatorTestHelper() { + } - static void createAuthorizableTestBeans(final List yamlList, ConfigReader yamlConfigReader, List authorizableBeanList) - throws AcConfigBeanValidationException { - final AuthorizableValidator authorizableValidator = new AuthorizableValidatorImpl(); + static void createAuthorizableTestBeans(final List yamlList, ConfigReader yamlConfigReader, + List authorizableBeanList) + throws AcConfigBeanValidationException { + final AuthorizableValidator authorizableValidator = new AuthorizableValidatorImpl("/home/groups", "/home/users"); authorizableValidator.disable(); - final Map> authorizablesMap = yamlConfigReader + final Map> groupsMap = yamlConfigReader .getGroupConfigurationBeans(yamlList, authorizableValidator); - for (final Entry> authorizableEntrySet : authorizablesMap + final Map> usersMap = yamlConfigReader + .getUserConfigurationBeans(yamlList, authorizableValidator); + for (final Entry> authorizableEntrySet : groupsMap + .entrySet()) { + authorizableBeanList.addAll(authorizableEntrySet.getValue()); + } + for (final Entry> authorizableEntrySet : usersMap .entrySet()) { authorizableBeanList.addAll(authorizableEntrySet.getValue()); } } + static String getTestConfigAsString(final String resourceName) throws IOException { final ClassLoader classloader = Thread.currentThread() @@ -52,8 +60,9 @@ static String getTestConfigAsString(final String resourceName) return stringWriter.toString(); } - static void createAceTestBeans(final List yamlList, ConfigReader yamlConfigReader, Set groupsFromConfig, List aceBeanList) - throws RepositoryException, AcConfigBeanValidationException { + static void createAceTestBeans(final List yamlList, ConfigReader yamlConfigReader, Set groupsFromConfig, + List aceBeanList) + throws RepositoryException, AcConfigBeanValidationException { final AceBeanValidator aceBeanValidator = new AceBeanValidatorImpl( groupsFromConfig); diff --git a/accesscontroltool-bundle/src/test/resources/testconfig.yaml b/accesscontroltool-bundle/src/test/resources/testconfig.yaml index 7c10e10a..5e86ac62 100644 --- a/accesscontroltool-bundle/src/test/resources/testconfig.yaml +++ b/accesscontroltool-bundle/src/test/resources/testconfig.yaml @@ -32,22 +32,95 @@ - group_D: + - isMemberOf: group_B + path: /home/groups/g + + - group_E: + - isMemberOf: + path: relativePath - - group_C: + - group_F: - - isMemberOf: group_B - path: /home/groups/g + - isMemberOf: + + - group_G: + + - isMemberOf: + path: /home/groups + assertedException: InvalidIntermediatePathException + + + - group_I: + + - isMemberOf: + path: /home/group + assertedException: InvalidIntermediatePathException + + - group_K: + + - isMemberOf: + path: /home + assertedException: InvalidIntermediatePathException + + - group_L: + + - isMemberOf: + path: / + assertedException: InvalidIntermediatePathException + + - group_M: + + - isMemberOf: + path: /home/users/m + assertedException: InvalidIntermediatePathException # invalid isMemberOf-name name - - group_F: + - group_N: - isMemberOf: group_Ö path: /home/groups/g assertedException: InvalidGroupNameException +- user_config: + + - user_A: + + - isMemberOf: + path: /home/groups + password: secret + assertedException: InvalidIntermediatePathException + + - user_B: + + - isMemberOf: + path: /home/users + password: secret + assertedException: InvalidIntermediatePathException + + - user_C: + + - isMemberOf: + path: /home/user + password: secret + assertedException: InvalidIntermediatePathException + + - user_D: + + - isMemberOf: + path: /home/groups/u + password: secret + assertedException: InvalidIntermediatePathException + + - user_E: + + - isMemberOf: + path: /home/users/u + assertedException: InvalidAuthorizableException + + - ace_config: From bc924727778187bf507030472f9247ac1df01220 Mon Sep 17 00:00:00 2001 From: jochenkoschorke Date: Fri, 18 Nov 2016 10:14:38 +0100 Subject: [PATCH 09/28] #134-added exception for invalid intermediate path --- .../exceptions/InvalidIntermediatePathException.java | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/validators/exceptions/InvalidIntermediatePathException.java diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/validators/exceptions/InvalidIntermediatePathException.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/validators/exceptions/InvalidIntermediatePathException.java new file mode 100644 index 00000000..342222e9 --- /dev/null +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/validators/exceptions/InvalidIntermediatePathException.java @@ -0,0 +1,10 @@ +package biz.netcentric.cq.tools.actool.validators.exceptions; + +public class InvalidIntermediatePathException extends + AcConfigBeanValidationException { + + public InvalidIntermediatePathException(String message) { + super(message); + } + +} From 4ee3db611dfd28474c88e6c1701035104cad9aca Mon Sep 17 00:00:00 2001 From: jochenkoschorke Date: Fri, 18 Nov 2016 15:36:53 +0100 Subject: [PATCH 10/28] #134-changed comment --- .../tools/actool/validators/impl/AuthorizableValidatorImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/validators/impl/AuthorizableValidatorImpl.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/validators/impl/AuthorizableValidatorImpl.java index 97e9a454..b9b967f1 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/validators/impl/AuthorizableValidatorImpl.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/validators/impl/AuthorizableValidatorImpl.java @@ -61,7 +61,6 @@ public boolean validateIntermediatePath( final String basicErrorMessage = "Validation error while validating intermediate path of authorizable: " + currentPrincipalID; // we only care about paths starting with a slash. if there is none, the path is assumed to be relative - // also empty string should be possible if (intermediatePath.startsWith("/")) { if (!intermediatePath.startsWith(groupsPath) && !intermediatePath.startsWith(usersPath)) { String message = basicErrorMessage @@ -69,6 +68,7 @@ public boolean validateIntermediatePath( LOG.error(message); throw new InvalidIntermediatePathException(message); } + if (!isGroup && intermediatePath.startsWith(groupsPath)) { String message = basicErrorMessage + " - the intermediate path for the user must not be the groups path: " + groupsPath; LOG.error(message); From 1cad7a25a43f7f5f6b0d04c09efbc0366ad69fcc Mon Sep 17 00:00:00 2001 From: jochenkoschorke Date: Fri, 18 Nov 2016 15:54:37 +0100 Subject: [PATCH 11/28] #134-added constants for autRoots --- .../configreader/YamlConfigurationMerger.java | 10 ++-------- .../cq/tools/actool/helper/AcHelper.java | 7 ++++--- .../cq/tools/actool/helper/Constants.java | 9 ++++++--- .../YamlConfigurationMergerTest.java | 17 ++--------------- 4 files changed, 14 insertions(+), 29 deletions(-) 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 03ce8ca8..fe72f5ad 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 @@ -20,7 +20,6 @@ import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.Service; -import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.yaml.snakeyaml.Yaml; @@ -29,6 +28,7 @@ import biz.netcentric.cq.tools.actool.configmodel.AceBean; import biz.netcentric.cq.tools.actool.configmodel.AuthorizableConfigBean; import biz.netcentric.cq.tools.actool.configmodel.GlobalConfiguration; +import biz.netcentric.cq.tools.actool.helper.Constants; import biz.netcentric.cq.tools.actool.installationhistory.AcInstallationHistoryPojo; import biz.netcentric.cq.tools.actool.validators.AceBeanValidator; import biz.netcentric.cq.tools.actool.validators.AuthorizableValidator; @@ -53,9 +53,6 @@ public class YamlConfigurationMerger implements ConfigurationMerger { @Reference ObsoleteAuthorizablesValidator obsoleteAuthorizablesValidator; - @Reference - UserConfiguration userConfiguration; - @Override public AcConfiguration getMergedConfigurations( final Map configFileContentByFilename, @@ -105,10 +102,7 @@ public AcConfiguration getMergedConfigurations( // --- authorizables config section - // build AuthorizableConfigBeans from current configurations - String groupsPath = (String) userConfiguration.getParameters().get("groupsPath"); - String usersPath = (String) userConfiguration.getParameters().get("usersPath"); - final AuthorizableValidator authorizableValidator = new AuthorizableValidatorImpl(groupsPath, usersPath); + final AuthorizableValidator authorizableValidator = new AuthorizableValidatorImpl(Constants.GROUPS_ROOT, Constants.USERS_ROOT); final Map> groupAuthorizablesMapFromConfig = configReader.getGroupConfigurationBeans( yamlRootList, authorizableValidator); // add AuthorizableConfigBeans built from current configuration to set containing AuthorizableConfigBeans from all diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/helper/AcHelper.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/helper/AcHelper.java index c29b7ae9..8f9e9cab 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/helper/AcHelper.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/helper/AcHelper.java @@ -49,10 +49,10 @@ private AcHelper() { /** By default ACEs with denies are sorted up to the top of the list, this follows the best practice to order denies always before * allows - this makes by default allows always take precedence over denies. - * + * * Denies should be used sparsely: Normally there is exactly one group that includes all deny-ACEs for to-be-secured content and many * groups with allow-ACEs, that selectively allow what has been denied by the "global deny" group. - * + * * For some special cases (e.g. when working with restrictions that limit a preceding allow) it is possible to specify "keepOrder=true", * for those cases the natural order from the config file is kept when {@link #ACE_ORDER_ACTOOL_BEST_PRACTICE} is used. */ public static int ACE_ORDER_ACTOOL_BEST_PRACTICE = 1; @@ -116,7 +116,8 @@ public static Principal getPrincipal(final Session session, principal = getPrincipalForName(session, principalName); if (principal == null) { - final String query = "/jcr:root/home/groups//*[(@jcr:primaryType = 'rep:Group') and jcr:like(@rep:principalName, 'cn=" + final String query = "/jcr:root" + Constants.GROUPS_ROOT + + "//*[(@jcr:primaryType = 'rep:Group') and jcr:like(@rep:principalName, 'cn=" + principalName + "%')]"; LOG.debug("Fallback query did not return results for principalName={}, using second fallback query (ldap name): {}", principalName, query); 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 b82a8e82..1a4ee85b 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 @@ -26,12 +26,15 @@ private Constants() { public static final String OBSOLETE_AUTHORIZABLES_KEY = "obsolete_authorizables"; public static final Set VALID_CONFIG_SECTION_IDENTIFIERS = new HashSet(Arrays.asList( - GLOBAL_CONFIGURATION_KEY, - GROUP_CONFIGURATION_KEY, - USER_CONFIGURATION_KEY, + GLOBAL_CONFIGURATION_KEY, + GROUP_CONFIGURATION_KEY, + USER_CONFIGURATION_KEY, ACE_CONFIGURATION_KEY, OBSOLETE_AUTHORIZABLES_KEY)); public static final String USER_ANONYMOUS = "anonymous"; + public static final String GROUPS_ROOT = "/home/groups"; + public static final String USERS_ROOT = "/home/users"; + } 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 00195ac9..c2b7dff0 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 @@ -18,11 +18,8 @@ import javax.jcr.RepositoryException; -import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters; -import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration; import org.junit.Before; import org.junit.Test; -import org.mockito.Mockito; import biz.netcentric.cq.tools.actool.configmodel.AcConfiguration; import biz.netcentric.cq.tools.actool.configmodel.AuthorizableConfigBean; @@ -42,16 +39,6 @@ public void setup() { merger = new YamlConfigurationMerger(); merger.yamlMacroProcessor = new YamlMacroProcessorImpl(); merger.obsoleteAuthorizablesValidator = new ObsoleteAuthorizablesValidatorImpl(); - - UserConfiguration userConfiguration = Mockito.mock(UserConfiguration.class); - - Map map = new HashMap<>(); - map.put("groupsPath", "/home/groups"); - map.put("usersPath", "/home/users"); - ConfigurationParameters configurationParameters = ConfigurationParameters.of(map); - - Mockito.when(userConfiguration.getParameters()).thenReturn(configurationParameters); - merger.userConfiguration = userConfiguration; } @Test @@ -66,8 +53,8 @@ public void testMemberGroups() throws IOException, RepositoryException, AcConfig assertEquals(3, groupA.getMemberOf().length); final AuthorizableConfigBean groupB = groups.get("groupB").iterator().next(); assertEquals(2, groupB.getMemberOf().length); - final AuthorizableConfigBean groupC = groups.get("groupC").iterator().next(); - final AuthorizableConfigBean groupD = groups.get("groupD").iterator().next(); + groups.get("groupC").iterator().next(); + groups.get("groupD").iterator().next(); } } From 10f3e1b8258020aff8c467b4f057cd4c73fb2d5f Mon Sep 17 00:00:00 2001 From: jochenkoschorke Date: Fri, 18 Nov 2016 15:55:26 +0100 Subject: [PATCH 12/28] #134-extended check for equality with authRoot + slash --- .../validators/impl/AuthorizableValidatorImpl.java | 3 ++- .../src/test/resources/testconfig.yaml | 14 +++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/validators/impl/AuthorizableValidatorImpl.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/validators/impl/AuthorizableValidatorImpl.java index b9b967f1..497dfa9e 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/validators/impl/AuthorizableValidatorImpl.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/validators/impl/AuthorizableValidatorImpl.java @@ -79,7 +79,8 @@ public boolean validateIntermediatePath( LOG.error(message); throw new InvalidIntermediatePathException(message); } - if (intermediatePath.equals(groupsPath) || intermediatePath.equals(usersPath)) { + if (intermediatePath.equals(groupsPath) || intermediatePath.equals(usersPath) || intermediatePath.equals(groupsPath + "/") + || intermediatePath.equals(usersPath + "/")) { String message = basicErrorMessage + " - the intermediate path must not be equal to the authorizable root but has to specify a subfolder of it!"; LOG.error(message); diff --git a/accesscontroltool-bundle/src/test/resources/testconfig.yaml b/accesscontroltool-bundle/src/test/resources/testconfig.yaml index 5e86ac62..75e35183 100644 --- a/accesscontroltool-bundle/src/test/resources/testconfig.yaml +++ b/accesscontroltool-bundle/src/test/resources/testconfig.yaml @@ -77,9 +77,15 @@ path: /home/users/m assertedException: InvalidIntermediatePathException - # invalid isMemberOf-name name - group_N: + - isMemberOf: + path: /home/groups/ + assertedException: InvalidIntermediatePathException + + # invalid isMemberOf-name name + - group_O: + - isMemberOf: group_Ö path: /home/groups/g assertedException: InvalidGroupNameException @@ -119,6 +125,12 @@ - isMemberOf: path: /home/users/u assertedException: InvalidAuthorizableException + + - user_F: + + - isMemberOf: + path: /home/users/ + assertedException: InvalidAuthorizableException - ace_config: From 4bf7a01f923bc06bdef281b9fba64e41d433329b Mon Sep 17 00:00:00 2001 From: jochenkoschorke Date: Fri, 18 Nov 2016 15:55:50 +0100 Subject: [PATCH 13/28] #143-added nullcheck for session --- .../cq/tools/actool/aceservice/impl/AceServiceImpl.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) 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 65dce611..599ec018 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 @@ -330,8 +330,9 @@ public AcInstallationHistoryPojo execute() { // in case an installation of an ACE configuration // threw an exception, logout from this session // otherwise changes made on the ACLs would get persisted - - session.logout(); + if (session != null) { + session.logout(); + } LOG.error("Exception in AceServiceImpl: {}", e); history.addError(e.toString()); @@ -348,7 +349,9 @@ public AcInstallationHistoryPojo execute() { } } } finally { - session.logout(); + if (session != null) { + session.logout(); + } } return history; } From 5f6fd31b29148f936454f47d8989e6c55bfdafcf Mon Sep 17 00:00:00 2001 From: jochenkoschorke Date: Fri, 18 Nov 2016 16:00:38 +0100 Subject: [PATCH 14/28] #134-using global constants --- .../impl/AuthorizableCreatorServiceImpl.java | 87 +++++++++---------- 1 file changed, 42 insertions(+), 45 deletions(-) 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 f8bd976c..5017f51d 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 @@ -61,8 +61,6 @@ public class AuthorizableCreatorServiceImpl implements 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 String PATH_SEGMENT_SYSTEMUSERS = "system"; private static final String PRINCIPAL_EVERYONE = "everyone"; @@ -76,9 +74,9 @@ public void createNewAuthorizables( Map> principalMapFromConfig, final Session session, AcInstallationHistoryPojo status, AuthorizableInstallationHistory authorizableInstallationHistory) - throws AccessDeniedException, - UnsupportedRepositoryOperationException, RepositoryException, - AuthorizableCreatorException { + throws AccessDeniedException, + UnsupportedRepositoryOperationException, RepositoryException, + AuthorizableCreatorException { this.status = status; this.principalMapFromConfig = principalMapFromConfig; @@ -112,9 +110,9 @@ private void installAuthorizableConfigurationBean(final Session session, AuthorizableConfigBean authorizableConfigBean, AcInstallationHistoryPojo history, AuthorizableInstallationHistory authorizableInstallationHistory) - throws AccessDeniedException, - UnsupportedRepositoryOperationException, RepositoryException, - AuthorizableExistsException, AuthorizableCreatorException { + throws AccessDeniedException, + UnsupportedRepositoryOperationException, RepositoryException, + AuthorizableExistsException, AuthorizableCreatorException { String principalId = authorizableConfigBean.getPrincipalID(); LOG.debug("- start installation of authorizable: {}", principalId); @@ -208,8 +206,6 @@ private void migrateFromOldGroup(AuthorizableConfigBean authorizableConfigBean, } - - private void handleIntermediatePath(final Session session, AuthorizableConfigBean principalConfigBean, AcInstallationHistoryPojo history, @@ -225,8 +221,8 @@ private void handleIntermediatePath(final Session session, .substring(0, existingAuthorizable.getPath().lastIndexOf("/")); // Relative paths need to be prefixed with /home/groups (issue #10) String authorizablePathFromBean = principalConfigBean.getPath(); - if (StringUtils.isNotEmpty(authorizablePathFromBean) && authorizablePathFromBean.charAt(0) != '/') { - authorizablePathFromBean = (principalConfigBean.isGroup() ? PATH_HOME_GROUPS : PATH_HOME_USERS) + if (StringUtils.isNotEmpty(authorizablePathFromBean) && (authorizablePathFromBean.charAt(0) != '/')) { + authorizablePathFromBean = (principalConfigBean.isGroup() ? Constants.GROUPS_ROOT : Constants.USERS_ROOT) + (principalConfigBean.isSystemUser() && !authorizablePathFromBean.startsWith(PATH_SEGMENT_SYSTEMUSERS) ? "/" + PATH_SEGMENT_SYSTEMUSERS : "") + "/" + authorizablePathFromBean; @@ -234,8 +230,10 @@ private void handleIntermediatePath(final Session session, if (!StringUtils.equals(intermediatedPathOfExistingAuthorizable, authorizablePathFromBean)) { StringBuilder message = new StringBuilder(); message.append("found change of intermediate path:\n" - + "existing authorizable: " + existingAuthorizable.getID() + " has intermediate path: " + intermediatedPathOfExistingAuthorizable +"\n" - + "authorizable from config: " + principalConfigBean.getPrincipalID() + " has intermediate path: " + authorizablePathFromBean + + "existing authorizable: " + existingAuthorizable.getID() + " has intermediate path: " + + intermediatedPathOfExistingAuthorizable + "\n" + + "authorizable from config: " + principalConfigBean.getPrincipalID() + " has intermediate path: " + + authorizablePathFromBean + "\n"); // save members of existing group before deletion @@ -291,16 +289,16 @@ private void handleIntermediatePath(final Session session, * @throws AccessDeniedException */ private void deleteOldIntermediatePath(final Session session, Node oldIntermediateNode) throws RepositoryException, - PathNotFoundException, VersionException, LockException, - ConstraintViolationException, AccessDeniedException { + PathNotFoundException, VersionException, LockException, + ConstraintViolationException, AccessDeniedException { // if '/home/groups' or '/home/users' was intermediatedNode, these must // not get deleted! // also node to be deleted has to be empty, so no other authorizables // stored under this path get deleted - while (!StringUtils.equals(PATH_HOME_GROUPS, + while (!StringUtils.equals(Constants.GROUPS_ROOT, oldIntermediateNode.getPath()) - && !StringUtils.equals(PATH_HOME_USERS, + && !StringUtils.equals(Constants.USERS_ROOT, oldIntermediateNode.getPath()) && !oldIntermediateNode.hasNodes()) { // delete old intermediatedPath @@ -351,8 +349,8 @@ private Authorizable createNewAuthorizable( AcInstallationHistoryPojo status, AuthorizableInstallationHistory authorizableInstallationHistory, UserManager userManager, ValueFactory vf, Session session) - throws AuthorizableExistsException, RepositoryException, - AuthorizableCreatorException { + throws AuthorizableExistsException, RepositoryException, + AuthorizableCreatorException { boolean isGroup = principalConfigBean.isGroup(); String principalId = principalConfigBean.getPrincipalID(); @@ -405,8 +403,8 @@ void mergeMemberOfGroups(String principalId, AcInstallationHistoryPojo status, UserManager userManager, Set membershipGroupsFromConfig, Set membershipGroupsFromRepository) - throws RepositoryException, AuthorizableExistsException, - AuthorizableCreatorException { + throws RepositoryException, AuthorizableExistsException, + AuthorizableCreatorException { LOG.debug("mergeMemberOfGroups() for {}", principalId); // membership to everyone cannot be removed or added => take it out from both lists @@ -434,7 +432,7 @@ void mergeMemberOfGroups(String principalId, Iterator toBeRemovedMembersIt = toBeRemovedMembers.iterator(); while (toBeRemovedMembersIt.hasNext()) { String groupId = toBeRemovedMembersIt.next(); - if (ignoredMembershipsPattern != null && ignoredMembershipsPattern.matcher(groupId).find()) { + if ((ignoredMembershipsPattern != null) && ignoredMembershipsPattern.matcher(groupId).find()) { toBeSkippedFromRemovalMembers.add(groupId); toBeRemovedMembersIt.remove(); } @@ -483,8 +481,8 @@ private Authorizable createNewGroup( AuthorizableInstallationHistory authorizableInstallationHistory, ValueFactory vf, Map> principalMapFromConfig, Session session) - throws AuthorizableExistsException, RepositoryException, - AuthorizableCreatorException { + throws AuthorizableExistsException, RepositoryException, + AuthorizableCreatorException { String groupID = principalConfigBean.getPrincipalID(); String intermediatePath = principalConfigBean.getPath(); @@ -501,14 +499,13 @@ private Authorizable createNewGroup( addMembersToReferencingAuthorizables(newGroup, principalConfigBean, userManager); - setAuthorizableProperties(newGroup, vf, principalConfigBean, session); return newGroup; } private void setAuthorizableProperties(Authorizable authorizable, ValueFactory vf, AuthorizableConfigBean principalConfigBean, Session session) - throws RepositoryException { + throws RepositoryException { String profileContent = principalConfigBean.getProfileContent(); if (StringUtils.isNotBlank(profileContent)) { @@ -537,7 +534,7 @@ private void setAuthorizableProperties(Authorizable authorizable, ValueFactory v authorizable.setProperty("profile/aboutMe", vf.createValue(description)); } } - + private Authorizable createNewUser( final UserManager userManager, AuthorizableConfigBean principalConfigBean, @@ -545,8 +542,8 @@ private Authorizable createNewUser( AuthorizableInstallationHistory authorizableInstallationHistory, ValueFactory vf, Map> principalMapFromConfig, Session session) - throws AuthorizableExistsException, RepositoryException, - AuthorizableCreatorException { + throws AuthorizableExistsException, RepositoryException, + AuthorizableCreatorException { String principalId = principalConfigBean.getPrincipalID(); String password = principalConfigBean.getPassword(); boolean isSystemUser = principalConfigBean.isSystemUser(); @@ -587,7 +584,7 @@ private void addMembersToReferencingAuthorizables(Authorizable authorizable, Aut // using reflection with fallback to create a system user in order to be backwards compatible private User userManagerCreateSystemUserViaReflection(UserManager userManager, String userID, String intermediatePath, AcInstallationHistoryPojo status) - throws RepositoryException { + throws RepositoryException { // make sure all relative intermediate paths get the prefix suffix (but don't touch absolute paths) String systemPrefix = "system/"; @@ -611,7 +608,7 @@ private User userManagerCreateSystemUserViaReflection(UserManager userManager, S } /** Validates the authorizables in 'membersOf' array of a given authorizable. Validation fails if an authorizable is a user. - * + * * If an authorizable contained in membersOf array doesn't exist it gets created and the current authorizable gets added as a member. * * @param userManager @@ -623,7 +620,7 @@ private User userManagerCreateSystemUserViaReflection(UserManager userManager, S Set validateAssignedGroups( final UserManager userManager, final String authorizablelID, final Set isMemberOf) throws RepositoryException, - AuthorizableCreatorException { + AuthorizableCreatorException { Set authorizableSet = new HashSet(); for (String memberOfPrincipal : isMemberOf) { @@ -711,17 +708,17 @@ public void performRollback(SlingRepository repository, history.addWarning("performing Groups rollback!"); for (String authorizableName : newCreatedAuthorizables) { - Authorizable authorizable = userManager.getAuthorizable(authorizableName); - if (authorizable != null) { - authorizable.remove(); - message = "removed authorizable " + authorizableName + " from the system!"; - LOG.info(message); - history.addWarning(message); - } else { - message = "Can't remove authorizable " + authorizableName + " from the system!"; - LOG.error(message); - history.addError(message); - } + Authorizable authorizable = userManager.getAuthorizable(authorizableName); + if (authorizable != null) { + authorizable.remove(); + message = "removed authorizable " + authorizableName + " from the system!"; + LOG.info(message); + history.addWarning(message); + } else { + message = "Can't remove authorizable " + authorizableName + " from the system!"; + LOG.error(message); + history.addError(message); + } } } @@ -783,7 +780,7 @@ public void performRollback(SlingRepository repository, if (authorizable.hasProperty("profile/givenName")) { authorizableName = authorizable .getProperty("profile/givenName")[0] - .getString(); + .getString(); } if (snapshotBean.getName().equals(authorizableName)) { history.addMessage("No change found in name of authorizable: " From 75daeaed691acc51cb5b345229f089389f44fd43 Mon Sep 17 00:00:00 2001 From: jochenkoschorke Date: Mon, 28 Nov 2016 00:18:56 +0100 Subject: [PATCH 15/28] #135-removed final modifier --- .../authorizableutils/AuthorizableCreatorException.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/authorizableutils/AuthorizableCreatorException.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/authorizableutils/AuthorizableCreatorException.java index 3f7bf8ad..5e1999fa 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/authorizableutils/AuthorizableCreatorException.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/authorizableutils/AuthorizableCreatorException.java @@ -9,11 +9,11 @@ package biz.netcentric.cq.tools.actool.authorizableutils; public class AuthorizableCreatorException extends Exception { - public AuthorizableCreatorException(final String message) { + public AuthorizableCreatorException(String message) { super(message); } - public AuthorizableCreatorException(final Throwable e) { + public AuthorizableCreatorException(Throwable e) { super(e); } } From dd90c9f363f8d8034a34ced87cea553fa5383c2d Mon Sep 17 00:00:00 2001 From: Konrad Windszus Date: Thu, 26 Jan 2017 14:39:07 +0100 Subject: [PATCH 16/28] clarify that really all *.yaml files are processed by the install hook --- docs/ApplyConfig.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/ApplyConfig.md b/docs/ApplyConfig.md index 932d50aa..4a8251a9 100644 --- a/docs/ApplyConfig.md +++ b/docs/ApplyConfig.md @@ -28,9 +28,9 @@ If you use the content-package-maven-plugin enable the installation hook via: ``` -The ```*.yaml``` files are installed directly from the package content and respect the [run mode semantics](Configuration.md). +The `*.yaml` files are installed directly from the package content and respect the [run mode semantics](Configuration.md). Otherwise there is no limitation, so the YAML files will be picked up from anywhere in the package (as long as the parent node does not contain a `.` followed by one or multiple not matching run modes). -Although it is not necessary that the YAML files are covered by the filter rules of the ```filter.xml```, this is recommended practice. That way you can see afterwards in the repository which YAML files have been processed. However if you would not let the ```filter.xml``` cover your YAML files, those files would still be processed by the installation hook. +Although it is not necessary that the YAML files are covered by the filter rules of the `filter.xml`, this is recommended practice. That way you can see afterwards in the repository which YAML files have been processed. However if you would not let the `filter.xml` cover your YAML files, those files would still be processed by the installation hook. ## JMX From 2f1fb050098dd862cc557f4cdd2c2ca7c721004e Mon Sep 17 00:00:00 2001 From: Konrad Windszus Date: Thu, 26 Jan 2017 14:41:53 +0100 Subject: [PATCH 17/28] fix md syntax errors --- docs/AdvancedFeatures.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/AdvancedFeatures.md b/docs/AdvancedFeatures.md index 53522205..facd626a 100644 --- a/docs/AdvancedFeatures.md +++ b/docs/AdvancedFeatures.md @@ -137,7 +137,7 @@ Expressions are evaluated using javax.el expression language. The following util ## Configure permissions for anonymous (since 1.8.2) -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. +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 From 1f084ec42ddbf28ba13555fbb10e25b705b7d217 Mon Sep 17 00:00:00 2001 From: Konrad Windszus Date: Thu, 26 Jan 2017 14:44:18 +0100 Subject: [PATCH 18/28] document limitation of only allowing to set ACEs on group Ids #141 --- docs/Configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index 6f5c9507..577fdd2d 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -107,7 +107,7 @@ Group memberships can be set on user entry or group entry or both. ## Configuration of ACEs -The configurations are done per principal followed by indented settings for each ACE. This data includes +The configurations are done per principal (currently on group ids are supported due to [issue #141](https://github.com/Netcentric/accesscontroltool/issues/141)) followed by indented settings for each ACE. This data includes property | comment | required --- | --- | --- From f4aa68cb8e50f99bae5baf63e88f1f03c50f77b9 Mon Sep 17 00:00:00 2001 From: Marat Saitov Date: Thu, 26 Jan 2017 17:32:58 +0100 Subject: [PATCH 19/28] #143 Possibility to disable saving the history --- .../installationhistory/impl/AcHistoryServiceImpl.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installationhistory/impl/AcHistoryServiceImpl.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installationhistory/impl/AcHistoryServiceImpl.java index 9a08eaab..dcf23b3f 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installationhistory/impl/AcHistoryServiceImpl.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installationhistory/impl/AcHistoryServiceImpl.java @@ -65,6 +65,14 @@ public void activate(@SuppressWarnings("rawtypes") final Map properties) @Override public void persistHistory(AcInstallationHistoryPojo history) { + + if (nrOfSavedHistories == 0) { + String message = "History hasn't been persisted, configured number of histories is " + nrOfSavedHistories; + history.addVerboseMessage(message); + LOG.info(message); + return; + } + Session session = null; try { From aa76d36a0cc9ec20860c3fa34ba3bccc0b6a64cc Mon Sep 17 00:00:00 2001 From: "georg.henzler" Date: Mon, 6 Feb 2017 12:22:01 +0000 Subject: [PATCH 20/28] #127 Support partial update of ACEs (path-based) --- .../tools/actool/aceservice/AceService.java | 18 +- .../aceservice/impl/AceServiceImpl.java | 478 +++++++++++------- .../actool/aceservicejmx/AceServiceMBean.java | 4 + .../impl/AceServiceMBeanImpl.java | 14 +- .../tools/actool/acls/AceBeanInstaller.java | 7 +- .../actool/acls/AceBeanInstallerImpl.java | 37 +- .../impl/AuthorizableCreatorServiceImpl.java | 4 +- .../configreader/ConfigFilesRetriever.java | 9 +- .../ConfigFilesRetrieverImpl.java | 29 +- .../tools/actool/dumpservice/Dumpservice.java | 13 +- .../dumpservice/impl/DumpserviceImpl.java | 129 ++--- .../actool/helper/AccessControlUtils.java | 18 +- .../actool/installhook/AcToolInstallHook.java | 2 +- .../installhook/AcToolInstallHookService.java | 4 +- .../AcToolInstallHookServiceImpl.java | 7 +- .../aceservice/impl/AceServiceImplTest.java | 28 + 16 files changed, 495 insertions(+), 306 deletions(-) create mode 100644 accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/aceservice/impl/AceServiceImplTest.java 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 777db89e..832b8305 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 @@ -11,15 +11,23 @@ import java.util.Map; import java.util.Set; -import javax.jcr.Session; - import biz.netcentric.cq.tools.actool.authorizableutils.AuthorizableInstallationHistory; import biz.netcentric.cq.tools.actool.installationhistory.AcInstallationHistoryPojo; public interface AceService { + /** Applies the full configuration as stored at the path configured at PID biz.netcentric.cq.tools.actool.aceservice.impl.AceServiceImpl + * to the repository. + * + * @return the history */ public AcInstallationHistoryPojo execute(); + /** Applies parts of the history + * + * @param restrictedToPaths only apply ACLs to root paths as given + * @return the history */ + public AcInstallationHistoryPojo execute(String[] restrictedToPaths); + /** method that indicates whether the service is ready for installation (if at least one configurations was found in repository) * * @return {@code true} if ready, otherwise {@code false} */ @@ -62,13 +70,13 @@ public interface AceService { /** Common entry point for JMX and install hook. * - * @param session * @param history * @param configurationFileContentsByFilename * @param authorizableInstallationHistorySet + * @param restrictedToPaths only apply ACLs to root paths as given * @throws Exception */ - public void installConfigurationFiles(Session session, AcInstallationHistoryPojo history, + public void installConfigurationFiles(AcInstallationHistoryPojo history, Map configurationFileContentsByFilename, - Set authorizableInstallationHistorySet) throws Exception; + Set authorizableInstallationHistorySet, String[] restrictedToPaths) 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 2cd00964..592fa8f9 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 @@ -13,13 +13,13 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; -import javax.jcr.Node; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.UnsupportedRepositoryOperationException; @@ -69,6 +69,7 @@ public class AceServiceImpl implements AceService { private static final Logger LOG = LoggerFactory.getLogger(AceServiceImpl.class); static final String PROPERTY_CONFIGURATION_PATH = "AceService.configurationPath"; + static final String PROPERTY_INTERMEDIATE_SAVES = "intermediateSaves"; @Reference AuthorizableCreatorService authorizableCreatorService; @@ -101,6 +102,9 @@ public class AceServiceImpl implements AceService { name = AceServiceImpl.PROPERTY_CONFIGURATION_PATH, value = "") private String configurationPath; + @Property(label = "Use intermedate saves", description = "Saves ACLs for each path individually - this can be used to avoid problems with large changesets and MongoDB (OAK-5557), however the rollback is disabled then.", name = AceServiceImpl.PROPERTY_INTERMEDIATE_SAVES, value = "") + private boolean intermediateSaves; + @Activate public void activate(final Map properties) throws Exception { @@ -112,44 +116,133 @@ public void activate(final Map properties) public void modified(final Map properties) { LOG.debug("Modified AceService!"); configurationPath = PropertiesUtil.toString(properties.get(PROPERTY_CONFIGURATION_PATH), ""); + LOG.info("Conifg " + PROPERTY_CONFIGURATION_PATH + "=" + configurationPath); + intermediateSaves = PropertiesUtil.toBoolean(properties.get(PROPERTY_INTERMEDIATE_SAVES), false); + LOG.info("Conifg " + PROPERTY_INTERMEDIATE_SAVES + "=" + intermediateSaves); } - private void installAcConfiguration( - AcConfiguration acConfiguration, AcInstallationHistoryPojo history, - final Session session, - Set authorizableHistorySet, - Map> repositoryDumpAceMap) throws Exception { + @Override + public AcInstallationHistoryPojo execute() { + return execute(null); + } - Map> authorizablesMapfromConfig = acConfiguration.getAuthorizablesConfig(); - Map> aceMapFromConfig = acConfiguration.getAceConfig(); + @Override + public AcInstallationHistoryPojo execute(String[] restrictedToPaths) { - if (aceMapFromConfig == null) { - String message = "ace config not found in YAML file! installation aborted!"; - LOG.error(message); - throw new IllegalArgumentException(message); + AcInstallationHistoryPojo history = new AcInstallationHistoryPojo(); + if (isExecuting) { + history.addError("AC Tool is already executing."); + return history; } - Set authorizablesToRemoveAcesFor = getAuthorizablesToRemoveAcesFor(authorizablesMapfromConfig); + Set authorizableInstallationHistorySet = new LinkedHashSet(); + try { + String rootPath = getConfigurationRootPath(); + Map newestConfigurations = configFilesRetriever.getConfigFileContentFromNode(rootPath); + installConfigurationFiles(history, newestConfigurations, authorizableInstallationHistorySet, restrictedToPaths); + } catch (AuthorizableCreatorException e) { + history.addError(e.toString()); + // here no rollback of authorizables necessary since session wasn't + // saved + } catch (Exception e) { + // in case an installation of an ACE configuration + // threw an exception, logout from this session + // otherwise changes made on the ACLs would get persisted + + LOG.error("Exception in AceServiceImpl: {}", e); + history.addError(e.toString()); + + if (!intermediateSaves) { + for (AuthorizableInstallationHistory authorizableInstallationHistory : authorizableInstallationHistorySet) { + try { + String message = "performing authorizable installation rollback(s)"; + LOG.info(message); + history.addMessage(message); + authorizableCreatorService.performRollback(repository, + authorizableInstallationHistory, history); + } catch (RepositoryException e1) { + LOG.error("Exception: ", e1); + } + } + } else { + String message = "Rollback is disabled due to configuration option intermediateSaves=true"; + LOG.info(message); + history.addMessage(message); + } - removeAcesForAuthorizables(history, session, authorizablesToRemoveAcesFor, repositoryDumpAceMap); - installAuthorizables(history, authorizableHistorySet, authorizablesMapfromConfig); - installAces(history, session, aceMapFromConfig); + } + return history; } - private Set getAuthorizablesToRemoveAcesFor(Map> authorizablesMapfromConfig) { - Set authorizablesToRemoveAcesFor = new HashSet(authorizablesMapfromConfig.keySet()); - Set authorizablesToBeMigrated = collectAuthorizablesToBeMigrated(authorizablesMapfromConfig); - Collection invalidAuthorizablesInConfig = CollectionUtils.intersection(authorizablesToRemoveAcesFor, authorizablesToBeMigrated); - if (!invalidAuthorizablesInConfig.isEmpty()) { - String message = "If migrateFrom feature is used, groups that shall be migrated from must not be present in regular configuration (offending groups: " - + invalidAuthorizablesInConfig + ")"; + /** Common entry point for JMX and install hook. */ + @Override + public void installConfigurationFiles(AcInstallationHistoryPojo history, Map configurationFileContentsByFilename, + Set authorizableInstallationHistorySet, String[] restrictedToPaths) + throws Exception { + + String origThreadName = Thread.currentThread().getName(); + try { + Thread.currentThread().setName(origThreadName + "-ACTool-Config-Worker"); + StopWatch sw = new StopWatch(); + sw.start(); + isExecuting = true; + String message = "*** Applying AC Tool Configuration..."; + LOG.info(message); + history.addMessage(message); + + if (configurationFileContentsByFilename != null) { + + history.setConfigFileContentsByName(configurationFileContentsByFilename); + + AcConfiguration acConfiguration = configurationMerger.getMergedConfigurations(configurationFileContentsByFilename, history, + configReader); + history.setAcConfiguration(acConfiguration); + + installMergedConfigurations(history, authorizableInstallationHistorySet, acConfiguration, restrictedToPaths); + + // this runs as "own transaction" after session.save() of ACLs + removeObsoleteAuthorizables(history, acConfiguration.getObsoleteAuthorizables()); + + } + sw.stop(); + long executionTime = sw.getTime(); + LOG.info("Successfully applied AC Tool configuration in " + msHumanReadable(executionTime)); + history.setExecutionTime(executionTime); + } catch (Exception e) { + history.addError(e.toString()); // ensure exception is added to history before it's persisted in log in finally clause + throw e; // handling is different depending on JMX or install hook case + } finally { + try { + acHistoryService.persistHistory(history); + } catch (Exception e) { + LOG.warn("Could not persist history, e=" + e, e); + } + + Thread.currentThread().setName(origThreadName); + isExecuting = false; + } + + } + + private void installAcConfiguration( + AcConfiguration acConfiguration, AcInstallationHistoryPojo history, + Set authorizableHistorySet, + Map> repositoryDumpAceMap, String[] restrictedToPaths) throws Exception { + + if (acConfiguration.getAceConfig() == null) { + String message = "ACE config not found in YAML file! installation aborted!"; LOG.error(message); throw new IllegalArgumentException(message); } - authorizablesToRemoveAcesFor.addAll(authorizablesToBeMigrated); - return authorizablesToRemoveAcesFor; + + + installAuthorizables(history, authorizableHistorySet, acConfiguration.getAuthorizablesConfig()); + + installAces(history, acConfiguration, repositoryDumpAceMap, restrictedToPaths); } + + private Set collectAuthorizablesToBeMigrated(Map> authorizablesMapfromConfig) { Set authorizablesToBeMigrated = new HashSet(); for (String principalStr : authorizablesMapfromConfig.keySet()) { @@ -164,41 +257,169 @@ private Set collectAuthorizablesToBeMigrated(Map authorizablesInConfig, - Map> repositoryDumpAceMap) throws UnsupportedRepositoryOperationException, RepositoryException { + private void removeAcesForPathsNotInConfig(AcInstallationHistoryPojo history, Session session, Set authorizablesInConfig, + Map> repositoryDumpAceMap, Set acePathsFromConfig) + throws UnsupportedRepositoryOperationException, RepositoryException { + + int countAcesCleaned = 0; + int countPathsCleaned = 0; + Set relevantPathsForCleanup = getRelevantPathsForAceCleanup(authorizablesInConfig, repositoryDumpAceMap, + acePathsFromConfig); + + for (String relevantPath: relevantPathsForCleanup) { + // delete ACE if authorizable *is* in config, but the path *is not* in config + int countRemoved = AccessControlUtils.deleteAllEntriesForAuthorizableFromACL(session, + relevantPath, authorizablesInConfig.toArray(new String[authorizablesInConfig.size()])); + String message = "Cleaned (deleted) " + countRemoved + " ACEs of path " + relevantPath + + " from all ACEs for configured authorizables"; + LOG.info(message); + history.addMessage(message); + if (countRemoved > 0) { + countPathsCleaned++; + } + countAcesCleaned += countRemoved; + } + + if (countAcesCleaned > 0) { + String message = "Cleaned " + countAcesCleaned + " ACEs from " + countPathsCleaned + + " paths in repository (ACEs that belong to users in the AC Config, " + + "but resided at paths that are not contained in AC Config)"; + LOG.info(message); + history.addMessage(message); + } + + } + + private Set getRelevantPathsForAceCleanup(Set authorizablesInConfig, Map> repositoryDumpAceMap, + Set acePathsFromConfig) { // loop through all ACLs found in the repository + Set relevantPathsForCleanup = new HashSet(); for (Map.Entry> entry : repositoryDumpAceMap.entrySet()) { Set existingAcl = entry.getValue(); - for (AceBean existingAce : existingAcl) { - // if the ACL from repo contains an ACE regarding an - // authorizable from the groups config then delete all ACEs from - // this authorizable from current ACL - if (authorizablesInConfig.contains(existingAce.getPrincipalName())) { - AccessControlUtils.deleteAllEntriesForAuthorizableFromACL(session, - existingAce.getJcrPath(), existingAce.getPrincipalName()); - String message = "deleted all ACEs of authorizable " - + existingAce.getPrincipalName() - + " from ACL of path: " + existingAce.getJcrPath(); - LOG.debug(message); - history.addVerboseMessage(message); + for (AceBean existingAceFromDump : existingAcl) { + String jcrPath = existingAceFromDump.getJcrPath(); + + if (acePathsFromConfig.contains(jcrPath)) { + LOG.debug("Path {} is explicitly listed in config and hence that ACL is handled later, " + + "not preceding cleanup needed here", jcrPath); + continue; + } + + if (!authorizablesInConfig.contains(existingAceFromDump.getPrincipalName())) { + LOG.debug("Authorizable {} is not contained in config, hence not cleaning its ACE from non-config-contained " + + "path {}", existingAceFromDump.getPrincipalName(), jcrPath); + continue; } + relevantPathsForCleanup.add(jcrPath); } } + return relevantPathsForCleanup; } - private void installAces( - AcInstallationHistoryPojo history, - final Session session, - Map> aceMapFromConfig) throws Exception { + private Set collectJcrPaths(Map> aceMapFromConfig) { + Set jcrPathsInAceConfig = new HashSet(); + for (Set aces : aceMapFromConfig.values()) { + for (AceBean aceBean : aces) { + String path = aceBean.getJcrPath(); + jcrPathsInAceConfig.add(path); + } + } + return jcrPathsInAceConfig; + } + + boolean isRelevantPath(String path, String[] restrictedToPaths) { + if (restrictedToPaths == null || restrictedToPaths.length == 0) { + return true; + } + boolean isRelevant = false; + for (String restrictedToPath : restrictedToPaths) { + if (path.startsWith(restrictedToPath)) { + isRelevant = true; + } + } + return isRelevant; + } + + private Set getAuthorizablesToRemoveAcesFor(Map> authorizablesMapfromConfig) { + Set authorizablesToRemoveAcesFor = new HashSet(authorizablesMapfromConfig.keySet()); + Set authorizablesToBeMigrated = collectAuthorizablesToBeMigrated(authorizablesMapfromConfig); + Collection invalidAuthorizablesInConfig = CollectionUtils.intersection(authorizablesToRemoveAcesFor, authorizablesToBeMigrated); + if (!invalidAuthorizablesInConfig.isEmpty()) { + String message = "If migrateFrom feature is used, groups that shall be migrated from must not be present in regular configuration (offending groups: " + + invalidAuthorizablesInConfig + ")"; + LOG.error(message); + throw new IllegalArgumentException(message); + } + authorizablesToRemoveAcesFor.addAll(authorizablesToBeMigrated); + return authorizablesToRemoveAcesFor; + } + + private void installAces(AcInstallationHistoryPojo history, + AcConfiguration acConfiguration, Map> repositoryDumpAceMap, String[] restrictedToPaths) throws Exception { + + + Map> aceMapFromConfig = acConfiguration.getAceConfig(); + // --- installation of ACEs from configuration --- Map> pathBasedAceMapFromConfig = AcHelper .getPathBasedAceMap(aceMapFromConfig, AcHelper.ACE_ORDER_ACTOOL_BEST_PRACTICE); - String msg = "*** Starting installation of " + collectAceCount(aceMapFromConfig) + " ACLs for " + aceMapFromConfig.size() - + " authorizables in content nodes..."; - LOG.info(msg); - history.addMessage(msg); - aceBeanInstaller.installPathBasedACEs(pathBasedAceMapFromConfig, session, history); + Session session = null; + try { + session = repository.loginAdministrative(null); + + Set authorizablesToRemoveAcesFor = getAuthorizablesToRemoveAcesFor(acConfiguration.getAuthorizablesConfig()); + removeAcesForPathsNotInConfig(history, session, authorizablesToRemoveAcesFor, repositoryDumpAceMap, + collectJcrPaths(aceMapFromConfig)); + + Map> filteredPathBasedAceMapFromConfig = filterForRestrictedPaths(pathBasedAceMapFromConfig, + restrictedToPaths, history); + + String msg = "*** Starting installation of " + collectAceCount(filteredPathBasedAceMapFromConfig) + " ACLs for " + + filteredPathBasedAceMapFromConfig.size() + + " paths in content nodes..."; + LOG.info(msg); + history.addMessage(msg); + aceBeanInstaller.installPathBasedACEs(filteredPathBasedAceMapFromConfig, session, history, authorizablesToRemoveAcesFor, + intermediateSaves); + + // if everything went fine (no exceptions), save the session + // thus persisting the changed ACLs + history.addVerboseMessage( + "Finished (transient) installation of access control configuration without errors, saving now..."); + session.save(); + history.addMessage("Persisted changes of ACLs"); + } finally { + if (session != null) { + session.logout(); + } + } + + + } + + private Map> filterForRestrictedPaths(Map> pathBasedAceMapFromConfig, + String[] restrictedToPaths, AcInstallationHistoryPojo history) { + if (restrictedToPaths == null || restrictedToPaths.length == 0) { + return pathBasedAceMapFromConfig; + } + + Map> filteredPathBasedAceMapFromConfig = new HashMap>(); + for (final String path : pathBasedAceMapFromConfig.keySet()) { + boolean isRelevant = isRelevantPath(path, restrictedToPaths); + if(isRelevant) { + filteredPathBasedAceMapFromConfig.put(path, pathBasedAceMapFromConfig.get(path)); + } + } + + int skipped = pathBasedAceMapFromConfig.keySet().size() - filteredPathBasedAceMapFromConfig.keySet().size(); + String message = "Will install AC Config at " + filteredPathBasedAceMapFromConfig.keySet().size() + " paths (skipping " + skipped + + " due to paths restriction " + Arrays.toString(restrictedToPaths) + ")"; + + history.addMessage(message); + LOG.info(message); + + return filteredPathBasedAceMapFromConfig; } private int collectAceCount(Map> aceMapFromConfig) { @@ -216,6 +437,9 @@ private void installAuthorizables( throws RepositoryException, Exception { // --- installation of Authorizables from configuration --- + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + String msg = "*** Starting installation of "+authorizablesMapfromConfig.size()+" authorizables..."; LOG.info(msg); history.addMessage(msg); @@ -250,14 +474,18 @@ private void installAuthorizables( } } - String message = "Finished installation of authorizables without errors"; + String message = "Finished installation of authorizables without errors in " + + AcInstallationHistoryPojo.msHumanReadable(stopWatch.getTime()); history.addMessage(message); LOG.info(message); } - private void removeObsoleteAuthorizables(AcInstallationHistoryPojo history, Session session, Set obsoleteAuthorizables) { + private void removeObsoleteAuthorizables(AcInstallationHistoryPojo history, Set obsoleteAuthorizables) { + Session session = null; try { + session = repository.loginAdministrative(null); + if (obsoleteAuthorizables.isEmpty()) { history.addVerboseMessage("No obsolete authorizables configured"); return; @@ -291,6 +519,7 @@ private void removeObsoleteAuthorizables(AcInstallationHistoryPojo history, Sess history.addMessage("(" + obsoleteAuthorizablesAlreadyPurged.size() + " have been purged already)"); } + String purgeAuthorizablesResultMsg = purgeAuthorizables(obsoleteAuthorizables, session); LOG.info(purgeAuthorizablesResultMsg); history.addVerboseMessage(purgeAuthorizablesResultMsg); // this message is too long for regular log @@ -299,123 +528,18 @@ private void removeObsoleteAuthorizables(AcInstallationHistoryPojo history, Sess String excMsg = "Could not purge obsolete authorizables " + obsoleteAuthorizables + ": " + e; history.addError(excMsg); LOG.error(excMsg, e); - } - - } - - /** Executes the installation of the existing configurations - entry point for JMX execute() method. */ - @Override - public AcInstallationHistoryPojo execute() { - - AcInstallationHistoryPojo history = new AcInstallationHistoryPojo(); - if (isExecuting) { - history.addError("AC Tool is already executing."); - return history; - } - - Session session = null; - - Set authorizableInstallationHistorySet = new LinkedHashSet(); - - try { - session = repository.loginAdministrative(null); - String rootPath = getConfigurationRootPath(); - Node rootNode = session.getNode(rootPath); - Map newestConfigurations = configFilesRetriever.getConfigFileContentFromNode(rootNode); - installConfigurationFiles(session, history, newestConfigurations, authorizableInstallationHistorySet); - } catch (AuthorizableCreatorException e) { - history.addError(e.toString()); - // here no rollback of authorizables necessary since session wasn't - // saved - } catch (Exception e) { - // in case an installation of an ACE configuration - // threw an exception, logout from this session - // otherwise changes made on the ACLs would get persisted - - session.logout(); - - LOG.error("Exception in AceServiceImpl: {}", e); - history.addError(e.toString()); - - for (AuthorizableInstallationHistory authorizableInstallationHistory : authorizableInstallationHistorySet) { - try { - String message = "performing authorizable installation rollback(s)"; - LOG.info(message); - history.addMessage(message); - authorizableCreatorService.performRollback(repository, - authorizableInstallationHistory, history); - } catch (RepositoryException e1) { - LOG.error("Exception: ", e1); - } - } } finally { - session.logout(); - } - return history; - } - - /** Common entry point for JMX and install hook. */ - @Override - public void installConfigurationFiles(Session session, AcInstallationHistoryPojo history, Map configurationFileContentsByFilename, - Set authorizableInstallationHistorySet) - throws Exception { - - String origThreadName = Thread.currentThread().getName(); - try { - Thread.currentThread().setName(origThreadName + "-ACTool-Config-Worker"); - StopWatch sw = new StopWatch(); - sw.start(); - isExecuting = true; - String message = "*** Applying AC Tool Configuration..."; - LOG.info(message); - history.addMessage(message); - - if (configurationFileContentsByFilename != null) { - - history.setConfigFileContentsByName(configurationFileContentsByFilename); - - AcConfiguration acConfiguration = configurationMerger.getMergedConfigurations(configurationFileContentsByFilename, history, - configReader); - history.setAcConfiguration(acConfiguration); - - installMergedConfigurations(history, session, authorizableInstallationHistorySet, acConfiguration); - - // if everything went fine (no exceptions), save the session - // thus persisting the changed ACLs - history.addVerboseMessage( - "Finished (transient) installation of access control configuration without errors, saving now..."); - session.save(); - history.addMessage("Persisted changes of ACLs"); - - // this runs as "own transaction" after session.save() of ACLs - removeObsoleteAuthorizables(history, session, acConfiguration.getObsoleteAuthorizables()); - - } - sw.stop(); - long executionTime = sw.getTime(); - LOG.info("Successfully applied AC Tool configuration in "+ msHumanReadable(executionTime)); - history.setExecutionTime(executionTime); - } catch (Exception e) { - history.addError(e.toString()); // ensure exception is added to history before it's persisted in log in finally clause - throw e; // handling is different depending on JMX or install hook case - } finally { - try { - acHistoryService.persistHistory(history); - } catch (Exception e) { - LOG.warn("Could not persist history, e=" + e, e); + if (session != null) { + session.logout(); } - - Thread.currentThread().setName(origThreadName); - isExecuting = false; } } private void installMergedConfigurations( AcInstallationHistoryPojo history, - Session session, Set authorizableInstallationHistorySet, - AcConfiguration acConfiguration) throws ValueFormatException, + AcConfiguration acConfiguration, String[] restrictedToPaths) throws ValueFormatException, RepositoryException, Exception { String message = "Starting installation of merged configurations..."; @@ -424,31 +548,25 @@ private void installMergedConfigurations( Map> repositoryDumpAceMap = null; LOG.debug("Building dump from repository (to compare delta with config to be installed)"); - repositoryDumpAceMap = dumpservice.createAclDumpMap( - session, AcHelper.PATH_BASED_ORDER, + repositoryDumpAceMap = dumpservice.createAclDumpMap(AcHelper.PATH_BASED_ORDER, AcHelper.ACE_ORDER_NONE, - dumpservice.getQueryExcludePaths()).getAceDump(); + new String[0], true).getAceDump(); - installAcConfiguration(acConfiguration, history, session, authorizableInstallationHistorySet, repositoryDumpAceMap); + installAcConfiguration(acConfiguration, history, authorizableInstallationHistorySet, repositoryDumpAceMap, restrictedToPaths); } @Override public boolean isReadyToStart() { String rootPath = getConfigurationRootPath(); - Session session = null; try { - session = repository.loginAdministrative(null); - Node rootNode = session.getNode(rootPath); - return !configFilesRetriever.getConfigFileContentFromNode(rootNode).isEmpty(); - } catch (Exception e) { + return !configFilesRetriever.getConfigFileContentFromNode(rootPath).isEmpty(); - } finally { - if (session != null) { - session.logout(); - } + } catch (Exception e) { + LOG.warn("Could not retrieve config file content for root path " + configurationPath); + return false; } - return false; + } @Override @@ -461,7 +579,6 @@ public String purgeACL(String path) { PurgeHelper.purgeAcl(session, path); session.save(); } catch (Exception e) { - // TO DO: Logging flag = false; message = e.toString(); LOG.error("Exception: ", e); @@ -630,19 +747,11 @@ public String getConfigurationRootPath() { @Override public Set getCurrentConfigurationPaths() { - Session session = null; Set paths = new LinkedHashSet(); - try { - session = repository.loginAdministrative(null); - Node rootNode = session.getNode(configurationPath); - paths = configFilesRetriever.getConfigFileContentFromNode(rootNode).keySet(); + paths = configFilesRetriever.getConfigFileContentFromNode(configurationPath).keySet(); } catch (Exception e) { - - } finally { - if (session != null) { - session.logout(); - } + LOG.warn("Could not retrieve config file content for root path " + configurationPath); } return paths; } @@ -650,8 +759,7 @@ public Set getCurrentConfigurationPaths() { public Set getAllAuthorizablesFromConfig(Session session) throws Exception { AcInstallationHistoryPojo history = new AcInstallationHistoryPojo(); - Node rootNode = session.getNode(configurationPath); - Map newestConfigurations = configFilesRetriever.getConfigFileContentFromNode(rootNode); + Map newestConfigurations = configFilesRetriever.getConfigFileContentFromNode(configurationPath); 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/aceservicejmx/AceServiceMBean.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceservicejmx/AceServiceMBean.java index 24ea1069..2cdc673f 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceservicejmx/AceServiceMBean.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceservicejmx/AceServiceMBean.java @@ -27,6 +27,10 @@ public interface AceServiceMBean { @Description("executes the installation of the ACE configuration(s)") String execute(); + @Description("executes the installation of the ACE configuration(s), but restricted to given paths") + String execute( + @Name("paths") @Description("comma separated list of paths to apply the configuration to, other paths will be skipped") String restrictedToPaths); + @Description("purges the AccessControlList of the given path, if existing") String purgeACL(@Name("path") final String path); diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceservicejmx/impl/AceServiceMBeanImpl.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceservicejmx/impl/AceServiceMBeanImpl.java index 98b6cd03..97b961e5 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceservicejmx/impl/AceServiceMBeanImpl.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/aceservicejmx/impl/AceServiceMBeanImpl.java @@ -12,6 +12,7 @@ import javax.management.NotCompliantMBeanException; +import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.time.StopWatch; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Properties; @@ -21,13 +22,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.adobe.granite.jmx.annotation.AnnotatedStandardMBean; + import biz.netcentric.cq.tools.actool.aceservice.AceService; import biz.netcentric.cq.tools.actool.aceservicejmx.AceServiceMBean; import biz.netcentric.cq.tools.actool.dumpservice.Dumpservice; import biz.netcentric.cq.tools.actool.installationhistory.AcHistoryService; -import com.adobe.granite.jmx.annotation.AnnotatedStandardMBean; - @Service @Component(immediate = true) @Properties({ @@ -57,6 +58,15 @@ public String execute() { return aceService.execute().toString(); } + @Override + public String execute(String paths) { + String[] restrictedToPaths = null; + if (StringUtils.isNotBlank(paths)) { + restrictedToPaths = paths.split(","); + } + return aceService.execute(restrictedToPaths).toString(); + } + @Override public boolean isReadyToStart() { return aceService.isReadyToStart(); diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/acls/AceBeanInstaller.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/acls/AceBeanInstaller.java index 726d4410..feebcbf5 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/acls/AceBeanInstaller.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/acls/AceBeanInstaller.java @@ -25,7 +25,10 @@ public interface AceBeanInstaller { * * @param pathBasedAceMapFromConfig map containing the ACE data from the merged configurations path based * @param session the jcr session - * @param history history object */ - void installPathBasedACEs(final Map> pathBasedAceMapFromConfig, final Session session, final AcInstallationHistoryPojo history) throws Exception; + * @param history history object + * @param authorizablesToRemoveAcesFor + * @param intermediateSaves whether the session should be saved after each path (for each ACL) */ + void installPathBasedACEs(final Map> pathBasedAceMapFromConfig, final Session session, + final AcInstallationHistoryPojo history, Set authorizablesToRemoveAcesFor, boolean intermediateSaves) throws Exception; } \ No newline at end of file diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/acls/AceBeanInstallerImpl.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/acls/AceBeanInstallerImpl.java index 8466d867..47bb7bb7 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/acls/AceBeanInstallerImpl.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/acls/AceBeanInstallerImpl.java @@ -58,15 +58,22 @@ public class AceBeanInstallerImpl implements AceBeanInstaller { public void installPathBasedACEs( final Map> pathBasedAceMapFromConfig, final Session session, - final AcInstallationHistoryPojo history) throws Exception { + final AcInstallationHistoryPojo history, Set authorizablesToRemoveAcesFor, + boolean intermediateSaves) throws Exception { final Set paths = pathBasedAceMapFromConfig.keySet(); - LOG.debug("Paths in merged config = {}", paths); final String msg = "Found " + paths.size() + " paths in config"; LOG.debug(msg); history.addVerboseMessage(msg); + LOG.trace("Paths with ACEs: {}", paths); + + if (intermediateSaves) { + final String messageSave = "Will save ACL for each path to session due to configuration option intermediateSaves=true - rollback functionality is disabled."; + LOG.info(messageSave); + history.addMessage(messageSave); + } // loop through all nodes from config for (final String path : paths) { @@ -90,19 +97,21 @@ public void installPathBasedACEs( new AcePermissionComparator()); orderedAceBeanSetFromConfig.addAll(aceBeanSetFromConfig); - // remove ACL of that path from ACLs from repo so that after the - // loop has ended only paths are left which are not contained in - // current config - for (final AceBean bean : orderedAceBeanSetFromConfig) { - AccessControlUtils.deleteAllEntriesForAuthorizableFromACL(session, - path, bean.getPrincipalName()); - final String message = "deleted all ACEs of authorizable " - + bean.getPrincipalName() - + " from ACL of path: " + path; - LOG.debug(message); - history.addVerboseMessage(message); - } + // Remove all config contained auhtorizables from ACL of this path + int countRemoved = AccessControlUtils.deleteAllEntriesForAuthorizableFromACL(session, + path, authorizablesToRemoveAcesFor.toArray(new String[authorizablesToRemoveAcesFor.size()])); + final String message = "Deleted " + countRemoved + " ACEs for configured authorizables from path " + path; + LOG.debug(message); + history.addVerboseMessage(message); + writeAcBeansToRepository(session, history, orderedAceBeanSetFromConfig); + + if (intermediateSaves) { + final String messageSave = "Saved session for path " + path; + LOG.debug(messageSave); + history.addVerboseMessage(messageSave); + session.save(); + } } } 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 f8bd976c..e4050024 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 @@ -84,9 +84,9 @@ public void createNewAuthorizables( this.principalMapFromConfig = principalMapFromConfig; this.authorizableInstallationHistory = authorizableInstallationHistory; - Set groupsFromConfigurations = principalMapFromConfig.keySet(); + Set authorizableFromConfigurations = principalMapFromConfig.keySet(); - for (String principalId : groupsFromConfigurations) { + for (String principalId : authorizableFromConfigurations) { Set currentPrincipalData = principalMapFromConfig .get(principalId); diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configreader/ConfigFilesRetriever.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configreader/ConfigFilesRetriever.java index 46e672df..043a35e1 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configreader/ConfigFilesRetriever.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configreader/ConfigFilesRetriever.java @@ -2,8 +2,6 @@ import java.util.Map; -import javax.jcr.Node; - import org.apache.jackrabbit.vault.fs.io.Archive; /** Retrieves the contents of a AC tool yaml config file from either a package directly (used by install hook) or from the JCR node structure @@ -12,12 +10,13 @@ * @author ghenzler */ public interface ConfigFilesRetriever { - /** Returns yaml configurations using a given root node. This will only return configuration entries which apply to the current run mode. + /** Returns yaml configurations using a given root node. This will only return configuration entries which apply to the current run + * mode. * - * @param rootNode the root node in the JCR to start looking for yaml-files + * @param rootPath the root path in the JCR to start looking for yaml-files * @return map of yaml configurations by their path location * @throws Exception if things go wrong */ - Map getConfigFileContentFromNode(Node rootNode) throws Exception; + Map getConfigFileContentFromNode(String rootPath) throws Exception; /** Returns yaml configurations from a package. This will only return configuration entries which apply to the current run mode * diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configreader/ConfigFilesRetrieverImpl.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configreader/ConfigFilesRetrieverImpl.java index f49a272c..8ad4caab 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configreader/ConfigFilesRetrieverImpl.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/configreader/ConfigFilesRetrieverImpl.java @@ -12,6 +12,7 @@ import java.util.Set; import javax.jcr.Node; +import javax.jcr.Session; import javax.jcr.nodetype.NodeType; import org.apache.commons.io.IOUtils; @@ -22,6 +23,7 @@ import org.apache.jackrabbit.commons.JcrUtils; import org.apache.jackrabbit.vault.fs.io.Archive; import org.apache.jackrabbit.vault.fs.io.Archive.Entry; +import org.apache.sling.jcr.api.SlingRepository; import org.apache.sling.settings.SlingSettingsService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -35,13 +37,30 @@ public class ConfigFilesRetrieverImpl implements ConfigFilesRetriever { @Reference private SlingSettingsService slingSettingsService; + + @Reference + private SlingRepository repository; + @Override - public Map getConfigFileContentFromNode(Node rootNode) throws Exception { - if (rootNode == null) { - throw new IllegalArgumentException("No configuration path configured! please check the configuration of AcService!"); + public Map getConfigFileContentFromNode(String rootPath) throws Exception { + + Session session = null; + try { + session = repository.loginAdministrative(null); + + Node rootNode = session.getNode(rootPath); + + if (rootNode == null) { + throw new IllegalArgumentException("No configuration path configured! please check the configuration of AcService!"); + } + Map configurations = getConfigurations(new NodeInJcr(rootNode)); + return configurations; + } finally { + if (session != null) { + session.logout(); + } } - Map configurations = getConfigurations(new NodeInJcr(rootNode)); - return configurations; + } @Override diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/dumpservice/Dumpservice.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/dumpservice/Dumpservice.java index f796c7b8..83f2cc86 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/dumpservice/Dumpservice.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/dumpservice/Dumpservice.java @@ -15,7 +15,6 @@ import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.UnsupportedRepositoryOperationException; -import javax.jcr.ValueFormatException; import biz.netcentric.cq.tools.actool.configmodel.AuthorizableConfigBean; import biz.netcentric.cq.tools.actool.helper.AclBean; @@ -28,7 +27,7 @@ public Set getGroupBeans(Session session) public boolean isIncludeUsers(); - /** returns the paths under jcr:root witch are excluded from search for rep:policy nodes in OSGi configuration + /** returns the paths under jcr:root which are excluded from search for rep:policy nodes in OSGi configuration * * @return String array containing the paths */ public String[] getQueryExcludePaths(); @@ -42,16 +41,14 @@ public Set getACLDumpBeans(final Session session) /** returns a Map with holds either principal or path based ACE data * - * @param session the jcr session * @param keyOrder either principals (AceHelper.PRINCIPAL_BASED_ORDERING) or node paths (AceHelper.PATH_BASED_ORDERING) as keys * @param aclOrdering specifies whether the allow and deny ACEs within an ACL should be divided in separate blocks (first deny then * allow) + * @param isIncludeUsers * @return AceDumpData */ - public AceDumpData createAclDumpMap(final Session session, - final int keyOrder, final int aclOrdering, - final String[] excludePaths) throws ValueFormatException, - IllegalArgumentException, IllegalStateException, - RepositoryException; + public AceDumpData createAclDumpMap( final int keyOrder, final int aclOrdering, + final String[] excludePaths, + final boolean isIncludeUsers) throws RepositoryException; /** method that return a dump comprising of all groups and all aces in path based view * diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/dumpservice/impl/DumpserviceImpl.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/dumpservice/impl/DumpserviceImpl.java index c7d99809..682dcc1a 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/dumpservice/impl/DumpserviceImpl.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/dumpservice/impl/DumpserviceImpl.java @@ -253,15 +253,14 @@ private String getCompleteDump(int keyOrder, int aclOrderInMap) { LOG.info("Starting to create dump for " + (keyOrder == AcHelper.PRINCIPAL_BASED_ORDER ? "PRINCIPAL_BASED_ORDER" : "PATH_BASED_ORDER")); try { - session = repository.loginAdministrative(null); - AceDumpData aceDumpData = createAclDumpMap(session, + + AceDumpData aceDumpData = createAclDumpMap( keyOrder, AcHelper.ACE_ORDER_ACTOOL_BEST_PRACTICE, // this ORDER is important to keep the ORDER of denies with "keepOrder" // attribute that is automatically added if needed queryExcludePaths); Map> aclDumpMap = aceDumpData.getAceDump(); - // Map> legacyAclDumpMap = - // aceDumpData.getLegacyAceDump(); + session = repository.loginAdministrative(null); Set groupBeans = getGroupBeans(session); Set usersFromACEs = getUsersFromAces(keyOrder, session, aclDumpMap); Set userBeans = getUserBeans(usersFromACEs); @@ -269,10 +268,9 @@ private String getCompleteDump(int keyOrder, int aclOrderInMap) { String configurationDumpAsString = getConfigurationDumpAsString(aceDumpData, groupBeans, userBeans, aclOrderInMap); return configurationDumpAsString; - } catch (ValueFormatException e) { - LOG.error("ValueFormatException in AceServiceImpl: {}", e); + } catch (IllegalStateException e) { - LOG.error("IllegalStateException in AceServiceImpl: {}", e); + LOG.error("IllegalStateException in DumpServiceImpl: {}", e); } catch (IOException e) { LOG.error("IOException in AceServiceImpl: {}", e); } catch (RepositoryException e) { @@ -395,13 +393,11 @@ public Set getACLDumpBeans(final Session session) return accessControBeanSet; } - @Override - public AceDumpData createAclDumpMap(final Session session, - final int keyOrder, final int aclOrdering, + public AceDumpData createAclDumpMap(final int keyOrder, final int aclOrdering, final String[] excludePaths) throws ValueFormatException, IllegalArgumentException, IllegalStateException, RepositoryException { - return createAclDumpMap(session, keyOrder, aclOrdering, excludePaths, includeUsersInDumps); + return createAclDumpMap(keyOrder, aclOrdering, excludePaths, includeUsersInDumps); } /** returns a Map with holds either principal or path based ACE data @@ -413,75 +409,82 @@ public AceDumpData createAclDumpMap(final Session session, * @param isFilterACEs * @param isIncludeUsers * @return - * @throws ValueFormatException - * @throws IllegalStateException * @throws RepositoryException */ - private AceDumpData createAclDumpMap(final Session session, - final int keyOrder, final int aclOrdering, + @Override + public AceDumpData createAclDumpMap(final int keyOrder, final int aclOrdering, final String[] excludePaths, - final boolean isIncludeUsers) throws ValueFormatException, - IllegalArgumentException, IllegalStateException, - RepositoryException { - AceDumpData aceDumpData = new AceDumpData(); - UserManager um = ((JackrabbitSession) session).getUserManager(); - Map> aceMap = null; - Map> legacyAceMap = new TreeMap>(); + final boolean isIncludeUsers) throws RepositoryException { + Session session = null; + try { + session = repository.loginAdministrative(null); - if (keyOrder == AcHelper.PRINCIPAL_BASED_ORDER) { // principal based - aceMap = new TreeMap>(); - } else if (keyOrder == AcHelper.PATH_BASED_ORDER) { // path based - aceMap = new TreeMap>(); - } + AceDumpData aceDumpData = new AceDumpData(); + UserManager um = ((JackrabbitSession) session).getUserManager(); + Map> aceMap = null; + Map> legacyAceMap = new TreeMap>(); - Set aclBeanSet = getACLDumpBeans(session); + if (keyOrder == AcHelper.PRINCIPAL_BASED_ORDER) { // principal based + aceMap = new TreeMap>(); + } else if (keyOrder == AcHelper.PATH_BASED_ORDER) { // path based + aceMap = new TreeMap>(); + } - // build a set containing all ACE found in the original order - for (AclBean aclBean : aclBeanSet) { + Set aclBeanSet = getACLDumpBeans(session); - if (aclBean.getAcl() == null) { - continue; - } + // build a set containing all ACE found in the original order + for (AclBean aclBean : aclBeanSet) { - boolean allowExistsInListEarlier = false; - for (AccessControlEntry ace : aclBean.getAcl() - .getAccessControlEntries()) { - if (!(ace instanceof JackrabbitAccessControlEntry)) { - throw new IllegalStateException("AC entry is not a JackrabbitAccessControlEntry: " + ace); + if (aclBean.getAcl() == null) { + continue; } - AceWrapper tmpBean = new AceWrapper((JackrabbitAccessControlEntry) ace, aclBean.getJcrPath()); - AceBean tmpAceBean = AcHelper.getAceBean(tmpBean); - - // sets keepOrder true if ACE deny entries are found that are not at top of list - if (tmpAceBean.isAllow()) { - allowExistsInListEarlier = true; - } else { - if (allowExistsInListEarlier && !tmpAceBean.isAllow()) { - tmpAceBean.setKeepOrder(true); + + boolean allowExistsInListEarlier = false; + for (AccessControlEntry ace : aclBean.getAcl() + .getAccessControlEntries()) { + if (!(ace instanceof JackrabbitAccessControlEntry)) { + throw new IllegalStateException("AC entry is not a JackrabbitAccessControlEntry: " + ace); + } + AceWrapper tmpBean = new AceWrapper((JackrabbitAccessControlEntry) ace, aclBean.getJcrPath()); + AceBean tmpAceBean = AcHelper.getAceBean(tmpBean); + + // sets keepOrder true if ACE deny entries are found that are not at top of list + if (tmpAceBean.isAllow()) { + allowExistsInListEarlier = true; + } else { + if (allowExistsInListEarlier && !tmpAceBean.isAllow()) { + tmpAceBean.setKeepOrder(true); + } } - } - Authorizable authorizable = um.getAuthorizable(tmpAceBean - .getPrincipalName()); + Authorizable authorizable = um.getAuthorizable(tmpAceBean + .getPrincipalName()); - // if this group exists under home - if (authorizable != null) { - if (authorizable.isGroup() || isIncludeUsers) { - addBeanToMap(keyOrder, aclOrdering, aceMap, tmpAceBean); + // if this group exists under home + if (authorizable != null) { + if (authorizable.isGroup() || isIncludeUsers) { + addBeanToMap(keyOrder, aclOrdering, aceMap, tmpAceBean); + } } + // otherwise put in map holding legacy ACEs + else { + addBeanToMap(keyOrder, aclOrdering, legacyAceMap, + tmpAceBean); + } + } - // otherwise put in map holding legacy ACEs - else { - addBeanToMap(keyOrder, aclOrdering, legacyAceMap, - tmpAceBean); - } + } + + aceDumpData.setAceDump(aceMap); + aceDumpData.setLegacyAceDump(legacyAceMap); + return aceDumpData; + + } finally { + if (session != null) { + session.logout(); } } - aceDumpData.setAceDump(aceMap); - aceDumpData.setLegacyAceDump(legacyAceMap); - - return aceDumpData; } private void addBeanToMap(final int keyOrder, final int aclOrdering, diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/helper/AccessControlUtils.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/helper/AccessControlUtils.java index 0c6b2308..9a44870e 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/helper/AccessControlUtils.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/helper/AccessControlUtils.java @@ -26,7 +26,7 @@ import javax.jcr.security.AccessControlPolicyIterator; import javax.jcr.security.Privilege; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.ArrayUtils; import org.apache.jackrabbit.api.JackrabbitSession; import org.apache.jackrabbit.api.security.JackrabbitAccessControlEntry; import org.apache.jackrabbit.api.security.JackrabbitAccessControlList; @@ -230,9 +230,10 @@ public static Set getPrivilegeSet(String[] privNames, /** @param session admin session * @param path valid node path in CRX - * @param authorizableID ID of authorizable to be deleted from ACL of node specified by path */ - public static void deleteAllEntriesForAuthorizableFromACL(final Session session, - final String path, String authorizableID) + * @param authorizableIds ids of authorizables to be deleted from ACL of node specified by path + * @return count ACEs that were removed */ + public static int deleteAllEntriesForAuthorizableFromACL(final Session session, + final String path, String[] authorizableIds) throws UnsupportedRepositoryOperationException, RepositoryException { final AccessControlManager accessControlManager = session .getAccessControlManager(); @@ -241,21 +242,24 @@ public static void deleteAllEntriesForAuthorizableFromACL(final Session session, accessControlManager, path); if (acl == null) { // do nothing, if there is no content node at the given path - return; + return 0; } // get ACEs of the node final AccessControlEntry[] aces = acl.getAccessControlEntries(); + int countRemoved = 0; // loop thorough ACEs and find the one of the given principal for (final AccessControlEntry ace : aces) { final JackrabbitAccessControlEntry jace = (JackrabbitAccessControlEntry) ace; - if (StringUtils.equals(jace.getPrincipal().getName(), - authorizableID)) { + + if (ArrayUtils.contains(authorizableIds, jace.getPrincipal().getName())) { acl.removeAccessControlEntry(jace); // bind new policy accessControlManager.setPolicy(path, acl); + countRemoved++; } } + return countRemoved; } /** Retrieves JackrabbitAccessControlList for path. diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installhook/AcToolInstallHook.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installhook/AcToolInstallHook.java index 80f48a29..aa222fcd 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installhook/AcToolInstallHook.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installhook/AcToolInstallHook.java @@ -58,7 +58,7 @@ public void execute(InstallContext context) throws PackageException { AcInstallationHistoryPojo history; try { history = acService.installYamlFilesFromPackage(context - .getPackage().getArchive(), context.getSession()); + .getPackage().getArchive()); } catch (Exception e) { log("Exception while installing configurations: " + e, diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installhook/AcToolInstallHookService.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installhook/AcToolInstallHookService.java index 22efd52e..568ad251 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installhook/AcToolInstallHookService.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installhook/AcToolInstallHookService.java @@ -8,15 +8,13 @@ */ package biz.netcentric.cq.tools.actool.installhook; -import javax.jcr.Session; - import org.apache.jackrabbit.vault.fs.io.Archive; import biz.netcentric.cq.tools.actool.installationhistory.AcInstallationHistoryPojo; public interface AcToolInstallHookService { - public AcInstallationHistoryPojo installYamlFilesFromPackage(Archive archive, Session session) + public AcInstallationHistoryPojo installYamlFilesFromPackage(Archive archive) throws Exception; } \ No newline at end of file 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 7b64261f..91e2d691 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 @@ -13,8 +13,6 @@ import java.util.Properties; import java.util.Set; -import javax.jcr.Session; - import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.Service; @@ -40,14 +38,15 @@ public class AcToolInstallHookServiceImpl implements AcToolInstallHookService { private ConfigFilesRetriever configFilesRetriever; @Override - public AcInstallationHistoryPojo installYamlFilesFromPackage(Archive archive, Session session) + public AcInstallationHistoryPojo installYamlFilesFromPackage(Archive archive) throws Exception { AcInstallationHistoryPojo history = new AcInstallationHistoryPojo(); Set authorizableInstallationHistorySet = new LinkedHashSet(); Map configs = configFilesRetriever.getConfigFileContentFromPackage(archive); history.setCrxPackageName(getArchiveName(archive)); - aceService.installConfigurationFiles(session, history, configs, authorizableInstallationHistorySet); + String[] restrictedToPaths = null; // never use path restriction for hook usage for now + aceService.installConfigurationFiles(history, configs, authorizableInstallationHistorySet, restrictedToPaths); return history; } diff --git a/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/aceservice/impl/AceServiceImplTest.java b/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/aceservice/impl/AceServiceImplTest.java new file mode 100644 index 00000000..6abfaded --- /dev/null +++ b/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/aceservice/impl/AceServiceImplTest.java @@ -0,0 +1,28 @@ +package biz.netcentric.cq.tools.actool.aceservice.impl; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +public class AceServiceImplTest { + + @Test + public void testIsRelevantPath() { + AceServiceImpl aceServiceImpl = new AceServiceImpl(); + + String[] restrictedToPaths = new String[] { "/content/site1", "/content/site3" }; + assertTrue(aceServiceImpl.isRelevantPath("/content/site1", restrictedToPaths)); + assertTrue(aceServiceImpl.isRelevantPath("/content/site1/page", restrictedToPaths)); + + assertFalse(aceServiceImpl.isRelevantPath("/content/site2", restrictedToPaths)); + assertFalse(aceServiceImpl.isRelevantPath("/content/site2/page", restrictedToPaths)); + + assertTrue(aceServiceImpl.isRelevantPath("/content/site3", restrictedToPaths)); + assertTrue(aceServiceImpl.isRelevantPath("/content/site3/page", restrictedToPaths)); + + assertFalse(aceServiceImpl.isRelevantPath("/etc/cloudservices", restrictedToPaths)); + + } + +} From 3c00475de1ed5588b0b58ef2fc4a3cfb1ddc25e0 Mon Sep 17 00:00:00 2001 From: "georg.henzler" Date: Mon, 6 Feb 2017 13:52:30 +0000 Subject: [PATCH 21/28] #127 Fix to ensure root path matches exact folder or siblings only --- .../cq/tools/actool/aceservice/impl/AceServiceImpl.java | 2 +- .../cq/tools/actool/aceservice/impl/AceServiceImplTest.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) 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 592fa8f9..a37dbad4 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 @@ -333,7 +333,7 @@ boolean isRelevantPath(String path, String[] restrictedToPaths) { } boolean isRelevant = false; for (String restrictedToPath : restrictedToPaths) { - if (path.startsWith(restrictedToPath)) { + if (path.matches("^" + restrictedToPath + "(/.*|$)")) { isRelevant = true; } } diff --git a/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/aceservice/impl/AceServiceImplTest.java b/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/aceservice/impl/AceServiceImplTest.java index 6abfaded..ca9ac218 100644 --- a/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/aceservice/impl/AceServiceImplTest.java +++ b/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/aceservice/impl/AceServiceImplTest.java @@ -13,6 +13,7 @@ public void testIsRelevantPath() { String[] restrictedToPaths = new String[] { "/content/site1", "/content/site3" }; assertTrue(aceServiceImpl.isRelevantPath("/content/site1", restrictedToPaths)); + assertFalse(aceServiceImpl.isRelevantPath("/content/site1ButNotSameRoot", restrictedToPaths)); assertTrue(aceServiceImpl.isRelevantPath("/content/site1/page", restrictedToPaths)); assertFalse(aceServiceImpl.isRelevantPath("/content/site2", restrictedToPaths)); From 61a24f4ad8e25da915c13c0d13e4ccae566364e4 Mon Sep 17 00:00:00 2001 From: "georg.henzler" Date: Mon, 6 Feb 2017 16:47:19 +0000 Subject: [PATCH 22/28] #145 just use non-verbose history for if verbose history becomes too large --- .../installationhistory/impl/HistoryUtils.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installationhistory/impl/HistoryUtils.java b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installationhistory/impl/HistoryUtils.java index dc524b6c..e7cee9bd 100644 --- a/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installationhistory/impl/HistoryUtils.java +++ b/accesscontroltool-bundle/src/main/java/biz/netcentric/cq/tools/actool/installationhistory/impl/HistoryUtils.java @@ -78,7 +78,7 @@ public static Node persistHistory(final Session session, if (StringUtils.isNotBlank(history.getCrxPackageName())) { name += "_via_" + history.getCrxPackageName(); } else { - name += "_via_jmx"; + name += "_via_api"; } Node newHistoryNode = safeGetNode(acHistoryRootNode, name, NODETYPE_NT_UNSTRUCTURED); @@ -119,8 +119,15 @@ public static void setHistoryNodeProperties(final Node historyNode, historyNode.setProperty(PROPERTY_SUCCESS, history.isSuccess()); historyNode.setProperty(PROPERTY_EXECUTION_TIME, history.getExecutionTime()); - historyNode.setProperty(PROPERTY_MESSAGES, - history.getVerboseMessageHistory()); + + String messageHistory = history.getVerboseMessageHistory(); + + // 16777216 bytes = ~ 16MB was the error in #145, assuming chars*2, hence 16777216 / 2 = 8MB, using 7MB to consider the BSON + // overhead + if (messageHistory.length() > (7 * 1024 * 1024)) { + messageHistory = history.getMessageHistory(); // just use non-verbose history for this case + } + historyNode.setProperty(PROPERTY_MESSAGES, messageHistory); historyNode.setProperty(PROPERTY_TIMESTAMP, history .getInstallationDate().getTime()); historyNode.setProperty(PROPERTY_SLING_RESOURCE_TYPE, From 7b921b2237fde37dc52810c54cd38536249e76c8 Mon Sep 17 00:00:00 2001 From: "georg.henzler" Date: Mon, 6 Feb 2017 18:52:32 +0000 Subject: [PATCH 23/28] #128 allowing empty path for authorizables --- .../impl/AuthorizableCreatorServiceImpl.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) 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 4a2e1256..00a4e0de 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 @@ -227,7 +227,8 @@ private void handleIntermediatePath(final Session session, ? "/" + PATH_SEGMENT_SYSTEMUSERS : "") + "/" + authorizablePathFromBean; } - if (!StringUtils.equals(intermediatedPathOfExistingAuthorizable, authorizablePathFromBean)) { + if (!StringUtils.equals(intermediatedPathOfExistingAuthorizable, authorizablePathFromBean) + && StringUtils.isNotBlank(principalConfigBean.getPath())) { StringBuilder message = new StringBuilder(); message.append("found change of intermediate path:\n" + "existing authorizable: " + existingAuthorizable.getID() + " has intermediate path: " @@ -490,8 +491,13 @@ private Authorizable createNewGroup( // create new Group Group newGroup = null; try { - newGroup = userManager.createGroup(new PrincipalImpl(groupID), - intermediatePath); + PrincipalImpl principalForNewGroup = new PrincipalImpl(groupID); + if (StringUtils.isNotBlank(intermediatePath)) { + newGroup = userManager.createGroup(principalForNewGroup, intermediatePath); + } else { + newGroup = userManager.createGroup(principalForNewGroup); + } + } catch (AuthorizableExistsException e) { LOG.warn("Group {} already exists in system!", groupID); newGroup = (Group) userManager.getAuthorizable(groupID); From f02a0abc0b7b2bd06bc2bea2d1c74563b1eba22d Mon Sep 17 00:00:00 2001 From: "georg.henzler" Date: Mon, 6 Feb 2017 20:40:59 +0000 Subject: [PATCH 24/28] #129 unit test --- .../AcePermissionComparatorTest.java | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/comparators/AcePermissionComparatorTest.java diff --git a/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/comparators/AcePermissionComparatorTest.java b/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/comparators/AcePermissionComparatorTest.java new file mode 100644 index 00000000..de2b0b53 --- /dev/null +++ b/accesscontroltool-bundle/src/test/java/biz/netcentric/cq/tools/actool/comparators/AcePermissionComparatorTest.java @@ -0,0 +1,112 @@ +package biz.netcentric.cq.tools.actool.comparators; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import org.apache.commons.lang.StringUtils; +import org.junit.Test; + +import biz.netcentric.cq.tools.actool.configmodel.AceBean; + +public class AcePermissionComparatorTest { + + @Test + public void testReorderingWithoutKeepOrder() { + + // keep order if it is correct already + assertEquals("no1-no2-no3-no4", toComparableString(toAutoSortedSet( + getAceBean("no1", "deny", false), + getAceBean("no2", "deny", false), + getAceBean("no3", "allow", false), + getAceBean("no4", "allow", false)))); + + // only allows - order not changed + assertEquals("no1-no2-no3", toComparableString(toAutoSortedSet( + getAceBean("no1", "allow", false), + getAceBean("no2", "allow", false), + getAceBean("no3", "allow", false)))); + + // one allow many denies - order changed + assertEquals("no2-no3-no4-no1", toComparableString(toAutoSortedSet( + getAceBean("no1", "allow", false), + getAceBean("no2", "deny", false), + getAceBean("no3", "deny", false), + getAceBean("no4", "deny", false)))); + + // one deny reorder + assertEquals("no4-no1-no2-no3", toComparableString(toAutoSortedSet( + getAceBean("no1", "allow", false), + getAceBean("no2", "allow", false), + getAceBean("no3", "allow", false), + getAceBean("no4", "deny", false)))); + + // many mixed ones + assertEquals("no2-no4-no6-no8-no1-no3-no5-no7", toComparableString(toAutoSortedSet( + getAceBean("no1", "allow", false), + getAceBean("no2", "deny", false), + getAceBean("no3", "allow", false), + getAceBean("no4", "deny", false), + getAceBean("no5", "allow", false), + getAceBean("no6", "deny", false), + getAceBean("no7", "allow", false), + getAceBean("no8", "deny", false)))); + + } + + @Test + public void testReorderingWithKeepOrder() { + + // simple case - one order kept + assertEquals("no2-no3-no1-no4", toComparableString(toAutoSortedSet( + getAceBean("no1", "allow", false), + getAceBean("no2", "deny", false), + getAceBean("no3", "deny", false), + getAceBean("no4", "deny", true)))); + + // two item keep order + assertEquals("no3-no1-no2-no4", toComparableString(toAutoSortedSet( + getAceBean("no1", "allow", false), + getAceBean("no2", "deny", true), + getAceBean("no3", "deny", false), + getAceBean("no4", "deny", true)))); + + // all items keep order + assertEquals("no1-no2-no3-no4", toComparableString(toAutoSortedSet( + getAceBean("no1", "allow", true), + getAceBean("no2", "deny", true), + getAceBean("no3", "allow", true), + getAceBean("no4", "deny", true)))); + + } + + private Set toAutoSortedSet(AceBean... beans) { + + final Set orderedAceBeanSet = new TreeSet(new AcePermissionComparator()); + orderedAceBeanSet.addAll(Arrays.asList(beans)); + return orderedAceBeanSet; + + } + + private String toComparableString(Set beans) { + List ids = new ArrayList(); + for (AceBean bean : beans) { + ids.add(bean.getPrincipalName()); + } + return StringUtils.join(ids, "-"); + + } + + private AceBean getAceBean(String id, String permission, boolean keepOrder) { + AceBean aceBean = new AceBean(); + aceBean.setPrincipal(id); + aceBean.setPermission(permission); + aceBean.setKeepOrder(keepOrder); + return aceBean; + } + +} From afe91f2d37e6044fc97c45a09c39d583cd521bc7 Mon Sep 17 00:00:00 2001 From: "georg.henzler" Date: Mon, 6 Feb 2017 20:51:27 +0000 Subject: [PATCH 25/28] #149 documentation --- docs/AdvancedFeatures.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/AdvancedFeatures.md b/docs/AdvancedFeatures.md index facd626a..d2dcf074 100644 --- a/docs/AdvancedFeatures.md +++ b/docs/AdvancedFeatures.md @@ -192,3 +192,10 @@ The following examples shows a legitimate example of using `keepOrder: true`: sling:resourceTypes: myproj/iframe ``` This example gives the group `myproj-editor` edit rights for all content in folder `myproj`, except for the iframe component. + +## Intermediate save() calls during ACL installation + +For large installations (> 1000 groups) that use MongoDB, the system possibly may get into an invalid state as OAK does not correctly fire the post commit hook for very large change sets (OAK-5557). To circumvent this issue it is possible since v1.9.2 to configure the OSGi property "intermediateSaves=true" of PID biz.netcentric.cq.tools.actool.aceservice.impl.AceServiceImpl. + +NOTE: This is never necessary when using TarMK and also it should only be used for MongoMK for large installations that do not contain a fix for OAK-5557 yet as the rollback functionality is lost when enabling intermediate saves. + From 3289ab9b0e268ff39e9bbb064b62be9bfa8a4220 Mon Sep 17 00:00:00 2001 From: "georg.henzler" Date: Mon, 6 Feb 2017 20:54:14 +0000 Subject: [PATCH 26/28] #149 documentation --- docs/AdvancedFeatures.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/AdvancedFeatures.md b/docs/AdvancedFeatures.md index d2dcf074..bd1260c8 100644 --- a/docs/AdvancedFeatures.md +++ b/docs/AdvancedFeatures.md @@ -195,7 +195,7 @@ This example gives the group `myproj-editor` edit rights for all content in fold ## Intermediate save() calls during ACL installation -For large installations (> 1000 groups) that use MongoDB, the system possibly may get into an invalid state as OAK does not correctly fire the post commit hook for very large change sets (OAK-5557). To circumvent this issue it is possible since v1.9.2 to configure the OSGi property "intermediateSaves=true" of PID biz.netcentric.cq.tools.actool.aceservice.impl.AceServiceImpl. +For large installations (> 1000 groups) that use MongoDB, the system possibly may get into an invalid state as older versions of OAK (AEM 6.1/6.2 ootb) do not always correctly fire the post commit hook for very large change sets (OAK-5557). To circumvent this issue it is possible since v1.9.2 to configure the OSGi property `intermediateSaves=true` of PID `biz.netcentric.cq.tools.actool.aceservice.impl.AceServiceImpl`. NOTE: This is never necessary when using TarMK and also it should only be used for MongoMK for large installations that do not contain a fix for OAK-5557 yet as the rollback functionality is lost when enabling intermediate saves. From dffda4f4fbc1b40cb4c0f4a83aa51bfdf6cb092d Mon Sep 17 00:00:00 2001 From: "georg.henzler" Date: Mon, 6 Feb 2017 22:09:04 +0100 Subject: [PATCH 27/28] updating poms for 1.9.2 branch with snapshot versions --- accesscontroltool-bundle/pom.xml | 2 +- accesscontroltool-exampleconfig-package/pom.xml | 2 +- accesscontroltool-oakindex-package/pom.xml | 2 +- accesscontroltool-package/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/accesscontroltool-bundle/pom.xml b/accesscontroltool-bundle/pom.xml index 3acbae6e..1157a0ae 100644 --- a/accesscontroltool-bundle/pom.xml +++ b/accesscontroltool-bundle/pom.xml @@ -11,7 +11,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 1.9.2-SNAPSHOT + 1.9.2-rc-SNAPSHOT diff --git a/accesscontroltool-exampleconfig-package/pom.xml b/accesscontroltool-exampleconfig-package/pom.xml index ec6e9adc..8e31cf43 100644 --- a/accesscontroltool-exampleconfig-package/pom.xml +++ b/accesscontroltool-exampleconfig-package/pom.xml @@ -15,7 +15,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 1.9.2-SNAPSHOT + 1.9.2-rc-SNAPSHOT diff --git a/accesscontroltool-oakindex-package/pom.xml b/accesscontroltool-oakindex-package/pom.xml index 556f5007..49c06192 100644 --- a/accesscontroltool-oakindex-package/pom.xml +++ b/accesscontroltool-oakindex-package/pom.xml @@ -15,7 +15,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 1.9.2-SNAPSHOT + 1.9.2-rc-SNAPSHOT diff --git a/accesscontroltool-package/pom.xml b/accesscontroltool-package/pom.xml index b2ff3b25..7cc2fb1f 100644 --- a/accesscontroltool-package/pom.xml +++ b/accesscontroltool-package/pom.xml @@ -15,7 +15,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 1.9.2-SNAPSHOT + 1.9.2-rc-SNAPSHOT diff --git a/pom.xml b/pom.xml index 7a23e80e..c3f58ade 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 1.9.2-SNAPSHOT + 1.9.2-rc-SNAPSHOT pom Access Control Tool - Reactor Project From 344e2117bf7c50907b5380d95f7f7685c24b4c96 Mon Sep 17 00:00:00 2001 From: "georg.henzler" Date: Mon, 6 Feb 2017 22:09:57 +0100 Subject: [PATCH 28/28] updating poms for branch'release/1.9.2' with non-snapshot versions --- accesscontroltool-bundle/pom.xml | 2 +- accesscontroltool-exampleconfig-package/pom.xml | 2 +- accesscontroltool-oakindex-package/pom.xml | 2 +- accesscontroltool-package/pom.xml | 2 +- pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/accesscontroltool-bundle/pom.xml b/accesscontroltool-bundle/pom.xml index 1157a0ae..c04f74a1 100644 --- a/accesscontroltool-bundle/pom.xml +++ b/accesscontroltool-bundle/pom.xml @@ -11,7 +11,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 1.9.2-rc-SNAPSHOT + 1.9.2 diff --git a/accesscontroltool-exampleconfig-package/pom.xml b/accesscontroltool-exampleconfig-package/pom.xml index 8e31cf43..827afcae 100644 --- a/accesscontroltool-exampleconfig-package/pom.xml +++ b/accesscontroltool-exampleconfig-package/pom.xml @@ -15,7 +15,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 1.9.2-rc-SNAPSHOT + 1.9.2 diff --git a/accesscontroltool-oakindex-package/pom.xml b/accesscontroltool-oakindex-package/pom.xml index 49c06192..e7d6be4b 100644 --- a/accesscontroltool-oakindex-package/pom.xml +++ b/accesscontroltool-oakindex-package/pom.xml @@ -15,7 +15,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 1.9.2-rc-SNAPSHOT + 1.9.2 diff --git a/accesscontroltool-package/pom.xml b/accesscontroltool-package/pom.xml index 7cc2fb1f..6e770252 100644 --- a/accesscontroltool-package/pom.xml +++ b/accesscontroltool-package/pom.xml @@ -15,7 +15,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 1.9.2-rc-SNAPSHOT + 1.9.2 diff --git a/pom.xml b/pom.xml index c3f58ade..ed9889a6 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ biz.netcentric.cq.tools.accesscontroltool accesscontroltool - 1.9.2-rc-SNAPSHOT + 1.9.2 pom Access Control Tool - Reactor Project