diff --git a/.gitignore b/.gitignore index 310c07f..dfb8072 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ target .project .settings .idea/ +/*.iml diff --git a/README.md b/README.md index c2a0a00..a77650e 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,22 @@ Otherwise you may enter address and credentials explicitly: ## Configuration After installation, go to the AEM *Tools* page and choose *Secure AEM* from the list on the left. The application tries to find author, publish and dispatcher URLs automatically, but you may want to confirm that they have been recognized correctly. In order to do that click *Edit* on the Settings bar and optionally correct addresses. That's it. Wait for a moment until the tests are done and check the results. - +####Osgi configuration +Application also allows Osgi configuration. To enable this option configuration for `SecureAemGlobalConfiguration` Osgi service needs to be provided (this will automatically take precedence over global config available on Secure Aem page configuration). Here is an example configuration file: +File name: `com.cognifide.secureaem.sling.SecureAemGlobalConfiguration.xml` +``` + +``` + +`author.password` and `publish.password` properties can be provided as a plain text or as encrypted values created via `/system/console/crypto` console (which is a recommended way). ## CLI version Sometimes you may want to check remote AEM instance. *Secure AEM* may be compiled in the standalone mode and used from the CLI, without any additional dependencies. In order to build application this way, enter: diff --git a/pom.xml b/pom.xml index 9d17dca..d9c0834 100644 --- a/pom.xml +++ b/pom.xml @@ -1,336 +1,358 @@ - - - org.sonatype.oss - oss-parent - 7 - - 4.0.0 - com.cognifide.secureaem - secure-aem - 1.3.3-SNAPSHOT - ${packaging.type} - Secure AEM - This application provides detailed security report for your AEM installation. After installation it's available in the 'Tools' page. - https://github.com/Cognifide/SecureCQ - - - The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - repo - - - 2013 - - UTF-8 - UTF-8 - yyyyMMdd-HHmmss - http://localhost:4502 - admin - admin - secure-aem - - - scm:git:https://github.com/Cognifide/SecureCQ.git - scm:git:https://github.com/Cognifide/SecureCQ.git - https://github.com/Cognifide/SecureCQ.git - - - Cognifide - http://www.cognifide.com - - - - Tomasz Rękawek - tomasz.rekawek@cognifide.com - Cognifide - - - - - adobe-public-releases - http://repo.adobe.com/nexus/content/groups/public - - - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.6 - - - sign-artifacts - verify - - sign - - - - - - maven-compiler-plugin - 3.5.1 - - 1.8 - 1.8 - - - - org.apache.felix - maven-scr-plugin - 1.22.0 - - - generate-scr-scrdescriptor - - scr - - - - - - org.apache.felix - maven-bundle-plugin - 3.2.0 - true - - - cq5 - ${project.artifactId} - ${project.name} - ${project.organization.name} - *;artifactId=httpclient|httpcore|gson|commons-lang3|commons-cli - - - - - maven-assembly-plugin - 2.6 - - ${assembly.name}-${project.version} - false - - src/main/assembly/${assembly.descriptor}.xml - - - - com.cognifide.secureaem.cli.Main - - - - - - package - - single - - - - - - com.cognifide.maven.plugins - maven-crx-plugin - 1.0.3 - - - - ${instance.url} - ${instance.username} - ${instance.password} - - - - org.apache.maven.plugins - maven-source-plugin - 3.0.1 - - - attach-sources - - jar - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.10.4 - - - attach-javadocs - - jar - - - - - - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - org.apache.felix - maven-scr-plugin - [1.7.2,) - - scr - - - - - - - - - - - - - - - - - org.apache.sling - org.apache.sling.api - 2.2.0 - provided - - - org.apache.felix - org.apache.felix.scr.annotations - 1.9.0 - provided - - - - javax.servlet - servlet-api - 2.4 - provided - - - javax.jcr - jcr - 2.0 - provided - - - - com.day.cq - cq-replication - 5.4.2 - provided - - - com.day.cq - cq-commons - 5.4.6 - provided - - - - org.apache.commons - commons-lang3 - 3.4 - - - commons-cli - commons-cli - 1.2 - - - org.apache.httpcomponents - httpclient - 4.2.3 - - - org.apache.httpcomponents - httpcore - 4.2.3 - - - - com.google.code.gson - gson - 2.7 - - - - - aem - - true - - - aem - bundle - - - - org.slf4j - slf4j-api - 1.5.8 - provided - - - - - cli - - - performRelease - true - - - - cli - jar - - - - org.slf4j - slf4j-simple - 1.7.21 - - - - - release-sign-artifacts - - - performRelease - true - - - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.6 - - - sign-artifacts - verify - - sign - - - - - - - - + + + org.sonatype.oss + oss-parent + 7 + + 4.0.0 + com.cognifide.secureaem + secure-aem + 1.3.3-SNAPSHOT + ${packaging.type} + Secure AEM + This application provides detailed security report for your AEM installation. After installation it's + available in the 'Tools' page. + + https://github.com/Cognifide/SecureCQ + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + 2013 + + UTF-8 + UTF-8 + yyyyMMdd-HHmmss + http://localhost:4502 + admin + admin + secure-aem + + + scm:git:https://github.com/Cognifide/SecureCQ.git + scm:git:https://github.com/Cognifide/SecureCQ.git + https://github.com/Cognifide/SecureCQ.git + + + Cognifide + http://www.cognifide.com + + + + Tomasz Rękawek + tomasz.rekawek@cognifide.com + Cognifide + + + + + adobe-public-releases + http://repo.adobe.com/nexus/content/groups/public + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + + + + maven-compiler-plugin + 3.5.1 + + 1.8 + 1.8 + + + + org.apache.felix + maven-scr-plugin + 1.22.0 + + + generate-scr-scrdescriptor + + scr + + + + + + org.apache.felix + maven-bundle-plugin + 3.2.0 + true + + + cq5 + ${project.artifactId} + ${project.name} + ${project.organization.name} + *;artifactId=httpclient|httpcore|gson|commons-lang3|commons-cli + + + + + + maven-assembly-plugin + 2.6 + + ${assembly.name}-${project.version} + false + + src/main/assembly/${assembly.descriptor}.xml + + + + com.cognifide.secureaem.cli.Main + + + + + + package + + single + + + + + + com.cognifide.maven.plugins + maven-crx-plugin + 1.0.3 + + + + ${instance.url} + ${instance.username} + ${instance.password} + + + + org.apache.maven.plugins + maven-source-plugin + 3.0.1 + + + attach-sources + + jar + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.4 + + + attach-javadocs + + jar + + + + + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + org.apache.felix + maven-scr-plugin + [1.7.2,) + + scr + + + + + + + + + + + + + + + + + org.apache.sling + org.apache.sling.api + 2.2.0 + provided + + + org.apache.sling + org.apache.sling.commons.osgi + 2.2.0 + provided + + + org.apache.felix + org.apache.felix.scr.annotations + 1.9.0 + provided + + + + javax.servlet + servlet-api + 2.4 + provided + + + javax.jcr + jcr + 2.0 + provided + + + + com.day.cq + cq-replication + 5.4.2 + provided + + + com.day.cq + cq-commons + 5.4.6 + provided + + + + org.apache.commons + commons-lang3 + 3.4 + + + commons-cli + commons-cli + 1.2 + + + org.apache.httpcomponents + httpclient + 4.2.3 + + + org.apache.httpcomponents + httpcore + 4.2.3 + + + + com.google.code.gson + gson + 2.7 + + + org.apache.sling + org.apache.sling.servlets.post + 2.3.6 + provided + + + com.adobe.granite + com.adobe.granite.crypto + 0.0.24 + + + + + + aem + + true + + + aem + bundle + + + + org.slf4j + slf4j-api + 1.5.8 + provided + + + + + cli + + + performRelease + true + + + + cli + jar + + + + org.slf4j + slf4j-simple + 1.7.21 + + + + + release-sign-artifacts + + + performRelease + true + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + + + + + + diff --git a/src/main/aem/jcr_root/apps/cognifide/secureaem/components/abstractTest/json.java b/src/main/aem/jcr_root/apps/cognifide/secureaem/components/abstractTest/json.java deleted file mode 100644 index e36d8b6..0000000 --- a/src/main/aem/jcr_root/apps/cognifide/secureaem/components/abstractTest/json.java +++ /dev/null @@ -1,4 +0,0 @@ -package apps.cognifide.secureaem.components.abstractTest; - -public class json extends com.cognifide.secureaem.sling.TestInvoker { -} diff --git a/src/main/aem/jcr_root/apps/cognifide/secureaem/renderers/mainRenderer/dialog.xml b/src/main/aem/jcr_root/apps/cognifide/secureaem/renderers/mainRenderer/dialog.xml index 2b448d7..24f6111 100644 --- a/src/main/aem/jcr_root/apps/cognifide/secureaem/renderers/mainRenderer/dialog.xml +++ b/src/main/aem/jcr_root/apps/cognifide/secureaem/renderers/mainRenderer/dialog.xml @@ -25,26 +25,40 @@ fieldLabel="Author URL" name="./author" xtype="textfield"/> - + - + diff --git a/src/main/java/com/cognifide/secureaem/GlobalConfiguration.java b/src/main/java/com/cognifide/secureaem/GlobalConfiguration.java new file mode 100644 index 0000000..cf59860 --- /dev/null +++ b/src/main/java/com/cognifide/secureaem/GlobalConfiguration.java @@ -0,0 +1,17 @@ +package com.cognifide.secureaem; + +public interface GlobalConfiguration { + String getDispatcherUrl(); + + String getAuthor(); + + String getAuthorLogin(); + + String getAuthorPassword(); + + String getPublish(); + + String getPublishLogin(); + + String getPublishPassword(); +} diff --git a/src/main/java/com/cognifide/secureaem/TestConfiguration.java b/src/main/java/com/cognifide/secureaem/TestConfiguration.java new file mode 100644 index 0000000..6cb73d3 --- /dev/null +++ b/src/main/java/com/cognifide/secureaem/TestConfiguration.java @@ -0,0 +1,11 @@ +package com.cognifide.secureaem; + +/** + * Configuration of particular test + */ +public interface TestConfiguration { + + String getStringValue(String name, String defaultValue); + + String[] getStringList(String name); +} diff --git a/src/main/java/com/cognifide/secureaem/sling/ConfigurationProvider.java b/src/main/java/com/cognifide/secureaem/sling/ConfigurationProvider.java new file mode 100644 index 0000000..01a530e --- /dev/null +++ b/src/main/java/com/cognifide/secureaem/sling/ConfigurationProvider.java @@ -0,0 +1,42 @@ +package com.cognifide.secureaem.sling; + +import com.adobe.granite.crypto.CryptoSupport; +import com.cognifide.secureaem.Configuration; +import com.cognifide.secureaem.GlobalConfiguration; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.apache.felix.scr.annotations.ReferencePolicy; +import org.apache.felix.scr.annotations.Service; +import org.apache.sling.api.SlingHttpServletRequest; + +@Component +@Service(ConfigurationProvider.class) +public class ConfigurationProvider { + + @Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY, + policy = ReferencePolicy.DYNAMIC, + referenceInterface = SecureAemGlobalConfiguration.class, + bind = "bind", unbind = "unbind") + private SecureAemGlobalConfiguration globalConfiguration; + + @Reference + private CryptoSupport cryptoSupport; + + public Configuration createConfiguration(SlingHttpServletRequest request) { + ResourceTestConfiguration testConfiguration = new ResourceTestConfiguration(request); + GlobalConfiguration globalConfig = globalConfiguration; + if (globalConfig == null) { + globalConfig = new ResourceGlobalConfiguration(request, cryptoSupport); + } + return new ConfigurationWrapper(globalConfig, testConfiguration); + } + + public void bind(SecureAemGlobalConfiguration globalConfiguration) { + this.globalConfiguration = globalConfiguration; + } + + public void unbind(SecureAemGlobalConfiguration globalConfiguration) { + this.globalConfiguration = null; + } +} diff --git a/src/main/java/com/cognifide/secureaem/sling/ConfigurationWrapper.java b/src/main/java/com/cognifide/secureaem/sling/ConfigurationWrapper.java new file mode 100644 index 0000000..8137461 --- /dev/null +++ b/src/main/java/com/cognifide/secureaem/sling/ConfigurationWrapper.java @@ -0,0 +1,62 @@ +package com.cognifide.secureaem.sling; + +import com.cognifide.secureaem.Configuration; +import com.cognifide.secureaem.GlobalConfiguration; +import com.cognifide.secureaem.TestConfiguration; + +public class ConfigurationWrapper implements Configuration { + + private GlobalConfiguration globalConfiguration; + + private TestConfiguration testConfiguration; + + public ConfigurationWrapper(GlobalConfiguration globalConfiguration, TestConfiguration testConfiguration) { + this.globalConfiguration = globalConfiguration; + this.testConfiguration = testConfiguration; + } + + @Override + public String getDispatcherUrl() { + return globalConfiguration.getDispatcherUrl(); + } + + @Override + public String getAuthor() { + return globalConfiguration.getAuthor(); + } + + @Override + public String getAuthorLogin() { + return globalConfiguration.getAuthorLogin(); + } + + @Override + public String getAuthorPassword() { + return globalConfiguration.getAuthorPassword(); + } + + @Override + public String getPublish() { + return globalConfiguration.getPublish(); + } + + @Override + public String getPublishLogin() { + return globalConfiguration.getPublishLogin(); + } + + @Override + public String getPublishPassword() { + return globalConfiguration.getPublishPassword(); + } + + @Override + public String getStringValue(String name, String defaultValue) { + return testConfiguration.getStringValue(name, defaultValue); + } + + @Override + public String[] getStringList(String name) { + return testConfiguration.getStringList(name); + } +} diff --git a/src/main/java/com/cognifide/secureaem/sling/ResourceGlobalConfiguration.java b/src/main/java/com/cognifide/secureaem/sling/ResourceGlobalConfiguration.java new file mode 100644 index 0000000..9d59037 --- /dev/null +++ b/src/main/java/com/cognifide/secureaem/sling/ResourceGlobalConfiguration.java @@ -0,0 +1,96 @@ +package com.cognifide.secureaem.sling; + +import com.adobe.granite.crypto.CryptoException; +import com.adobe.granite.crypto.CryptoSupport; +import com.cognifide.secureaem.GlobalConfiguration; +import com.cognifide.secureaem.cli.CliConfiguration; +import org.apache.commons.lang3.StringUtils; +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ValueMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ResourceGlobalConfiguration implements GlobalConfiguration { + + private static final Logger LOG = LoggerFactory.getLogger(ResourceConfiguration.class); + + private ValueMap globalConfig; + + private CryptoSupport cryptoSupport; + + public ResourceGlobalConfiguration(SlingHttpServletRequest request, CryptoSupport cryptoSupport) { + this.cryptoSupport = cryptoSupport; + Resource globalConfigRes = findGlobalConfig(request); + if (globalConfigRes != null) { + globalConfig = globalConfigRes.adaptTo(ValueMap.class); + } + } + + @Override + public String getDispatcherUrl() { + return StringUtils.removeEnd(getGlobalConfigParam("dispatcher"), "/"); + } + + @Override + public String getAuthor() { + return StringUtils.removeEnd(getGlobalConfigParam("author"), "/"); + } + + @Override public String getAuthorLogin() { + return getGlobalConfigParam("authorLogin"); + } + + @Override public String getAuthorPassword() { + return getPassword("authorPassword"); + } + + @Override + public String getPublish() { + return StringUtils.removeEnd(getGlobalConfigParam("publish"), "/"); + } + + @Override public String getPublishLogin() { + return getGlobalConfigParam("publishLogin"); + } + + @Override public String getPublishPassword() { + return getPassword("publishPassword"); + } + + private String getPassword(String paramName) { + String password = StringUtils.defaultString(getGlobalConfigParam(paramName)); + String decryptedPassword = password; + if (cryptoSupport.isProtected(password)) { + try { + decryptedPassword = cryptoSupport.unprotect(password); + } catch (CryptoException e) { + LOG.error("Failed to decrypt password {}", paramName, e); + } + } + return decryptedPassword; + } + + + private String getGlobalConfigParam(String name) { + if (globalConfig == null) { + return null; + } + return globalConfig.get(name, String.class); + } + + private Resource findGlobalConfig(SlingHttpServletRequest request) { + Resource resource = request.getResource(); + while (resource != null) { + if (resource.isResourceType("cq:Page")) { + Resource content = resource.getChild("jcr:content"); + String resourceType = content.adaptTo(ValueMap.class).get("sling:resourceType", String.class); + if ("cognifide/secureaem/renderers/mainRenderer".equals(resourceType)) { + return content.getChild("globalConfig"); + } + } + resource = resource.getParent(); + } + return null; + } +} diff --git a/src/main/java/com/cognifide/secureaem/sling/ResourceTestConfiguration.java b/src/main/java/com/cognifide/secureaem/sling/ResourceTestConfiguration.java new file mode 100644 index 0000000..1b47a7a --- /dev/null +++ b/src/main/java/com/cognifide/secureaem/sling/ResourceTestConfiguration.java @@ -0,0 +1,29 @@ +package com.cognifide.secureaem.sling; + +import com.cognifide.secureaem.TestConfiguration; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.resource.ValueMap; + +public class ResourceTestConfiguration implements TestConfiguration { + + private final ValueMap valueMap; + + public ResourceTestConfiguration(SlingHttpServletRequest request) { + this.valueMap = request.getResource().adaptTo(ValueMap.class); + } + + @Override + public String getStringValue(String name, String defaultValue) { + return getLocalConfig(name, defaultValue); + } + + @Override + public String[] getStringList(String name) { + return getLocalConfig(name, ArrayUtils.EMPTY_STRING_ARRAY); + } + + private T getLocalConfig(String name, T defaultValue) { + return this.valueMap.get(name, defaultValue); + } +} diff --git a/src/main/java/com/cognifide/secureaem/sling/SecureAemGlobalConfiguration.java b/src/main/java/com/cognifide/secureaem/sling/SecureAemGlobalConfiguration.java new file mode 100644 index 0000000..6bbcd89 --- /dev/null +++ b/src/main/java/com/cognifide/secureaem/sling/SecureAemGlobalConfiguration.java @@ -0,0 +1,129 @@ +package com.cognifide.secureaem.sling; + +import com.adobe.granite.crypto.CryptoException; +import com.adobe.granite.crypto.CryptoSupport; +import com.cognifide.secureaem.GlobalConfiguration; +import org.apache.commons.lang3.StringUtils; +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.ConfigurationPolicy; +import org.apache.felix.scr.annotations.Property; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.Service; +import org.apache.sling.commons.osgi.PropertiesUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; + +@Service(SecureAemGlobalConfiguration.class) +@Component(label = "Global Configuration for Secure Aem", + description = "This configuration is used instead of the global configuration provided in the content. To use values configured in content please disable this " + + "Component or remove configuration file.", + policy = ConfigurationPolicy.REQUIRE, + metatype = true, + immediate = true) +public class SecureAemGlobalConfiguration implements GlobalConfiguration { + + private static final Logger LOGGER = LoggerFactory.getLogger(SecureAemGlobalConfiguration.class); + + @Reference + private CryptoSupport cryptoSupport; + + @Property(label = "Dispatcher url") + private static final String DISPATCHER_URL = "dispatcher.url"; + + @Property(label = "Author url", value = "http://localhost:4502") + private static final String AUTHOR_URL = "author.url"; + + @Property(label = "Author login", value = "admin") + private static final String AUTHOR_LOGIN = "author.login"; + + @Property(label = "Author password", value = "admin", + description = "Should be encrypted with tool available through /system/console/crypto. Plain text supported but not recommended.") + private static final String AUTHOR_PASSOWRD = "author.password"; + + @Property(label = "Publish url", value = "http://localhost:4503") + private static final String PUBLISH_URL = "publish.url"; + + @Property(label = "Publish login", value = "admin") + private static final String PUBLISH_LOGIN = "publish.login"; + + @Property(label = "Publish password", value = "admin", + description = "Should be encrypted with tool available through /system/console/crypto. Plain text supported but not recommended.") + private static final String PUBLISH_PASSOWRD = "publish.password"; + + private String dispatcherUrl; + + private String authorUrl; + + private String authorLogin; + + private String authorPassowrd; + + private String publishUrl; + + private String publishLogin; + + private String publishPassword; + + @Activate + protected void activate(Map properties) { + LOGGER.info("Activating service."); + dispatcherUrl = PropertiesUtil.toString(properties.get(DISPATCHER_URL), ""); + authorUrl = PropertiesUtil.toString(properties.get(AUTHOR_URL), ""); + authorLogin = PropertiesUtil.toString(properties.get(AUTHOR_LOGIN), ""); + authorPassowrd = PropertiesUtil.toString(properties.get(AUTHOR_PASSOWRD), ""); + publishUrl = PropertiesUtil.toString(properties.get(PUBLISH_URL), ""); + publishLogin = PropertiesUtil.toString(properties.get(PUBLISH_LOGIN), ""); + publishPassword = PropertiesUtil.toString(properties.get(PUBLISH_PASSOWRD), ""); + } + + @Override + public String getDispatcherUrl() { + return dispatcherUrl; + } + + @Override + public String getAuthor() { + return authorUrl; + } + + @Override + public String getAuthorLogin() { + return authorLogin; + } + + @Override + public String getAuthorPassword() { + return getPassword(authorPassowrd); + } + + @Override + public String getPublish() { + return publishUrl; + } + + @Override + public String getPublishLogin() { + return publishLogin; + } + + @Override + public String getPublishPassword() { + return getPassword(publishPassword); + } + + + private String getPassword(String passwordToDecrypt) { + String password = StringUtils.defaultString(passwordToDecrypt); + if (cryptoSupport.isProtected(password)) { + try { + password = cryptoSupport.unprotect(password); + } catch (CryptoException e) { + LOGGER.error("Failed to decrypt password", e); + } + } + return password; + } +} diff --git a/src/main/java/com/cognifide/secureaem/sling/TestInvoker.java b/src/main/java/com/cognifide/secureaem/sling/TestInvoker.java index 07f9fbd..75a3be1 100644 --- a/src/main/java/com/cognifide/secureaem/sling/TestInvoker.java +++ b/src/main/java/com/cognifide/secureaem/sling/TestInvoker.java @@ -5,6 +5,8 @@ import javax.servlet.ServletException; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.sling.SlingServlet; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.SlingHttpServletResponse; import org.apache.sling.api.resource.Resource; @@ -14,15 +16,24 @@ import com.cognifide.secureaem.AbstractTest; import com.cognifide.secureaem.Configuration; +@SlingServlet( + methods = {"GET"}, + extensions = "json", + resourceTypes = "cognifide/secureaem/components/abstractTest" +) public class TestInvoker extends SlingSafeMethodsServlet { private static final long serialVersionUID = 1334083614379709964L; + @Reference + private ConfigurationProvider configurationPrivder; + @Override public void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException, ServletException { String testClassName = getTestClassName(request); - Configuration config = new ResourceConfiguration(request); + Configuration config = configurationPrivder.createConfiguration(request); + AbstractTest test; try { Class clazz = Class.forName(testClassName); diff --git a/src/main/java/com/cognifide/secureaem/sling/password/AuthorPasswordFieldEncryptFilter.java b/src/main/java/com/cognifide/secureaem/sling/password/AuthorPasswordFieldEncryptFilter.java new file mode 100644 index 0000000..b54f355 --- /dev/null +++ b/src/main/java/com/cognifide/secureaem/sling/password/AuthorPasswordFieldEncryptFilter.java @@ -0,0 +1,15 @@ +package com.cognifide.secureaem.sling.password; + +import org.apache.commons.lang3.StringUtils; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Service; + +@Service +@Component +public class AuthorPasswordFieldEncryptFilter implements PasswordFiledEncryptFilter { + @Override + public boolean isSupported(String propertyPath) { + return StringUtils.startsWith(propertyPath, "/etc/secureaem") + && StringUtils.endsWith(propertyPath, "authorPassword"); + } +} diff --git a/src/main/java/com/cognifide/secureaem/sling/password/PasswordFiledEncryptFilter.java b/src/main/java/com/cognifide/secureaem/sling/password/PasswordFiledEncryptFilter.java new file mode 100644 index 0000000..a0f056c --- /dev/null +++ b/src/main/java/com/cognifide/secureaem/sling/password/PasswordFiledEncryptFilter.java @@ -0,0 +1,12 @@ +package com.cognifide.secureaem.sling.password; + +public interface PasswordFiledEncryptFilter { + + /** + * Checks whether property given by propertyPath parameter should be encrypted + * @param propertyPath absolute path to property + * @return true if property should be encrypted, false otherwise + */ + boolean isSupported(String propertyPath); + +} diff --git a/src/main/java/com/cognifide/secureaem/sling/password/PasswordPropertySavePostProcessor.java b/src/main/java/com/cognifide/secureaem/sling/password/PasswordPropertySavePostProcessor.java new file mode 100644 index 0000000..31cabc9 --- /dev/null +++ b/src/main/java/com/cognifide/secureaem/sling/password/PasswordPropertySavePostProcessor.java @@ -0,0 +1,109 @@ +package com.cognifide.secureaem.sling.password; + +import com.adobe.granite.crypto.CryptoException; +import com.adobe.granite.crypto.CryptoSupport; +import org.apache.commons.lang3.StringUtils; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.apache.felix.scr.annotations.ReferencePolicy; +import org.apache.felix.scr.annotations.Service; +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.resource.ResourceResolver; +import org.apache.sling.servlets.post.Modification; +import org.apache.sling.servlets.post.SlingPostProcessor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.jcr.Property; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + + +@Component +@Service +public class PasswordPropertySavePostProcessor implements SlingPostProcessor { + + private static final Logger LOGGER = LoggerFactory.getLogger(PasswordPropertySavePostProcessor.class); + + private static final String PATH_SEPARATOR = "/"; + + @Reference( + bind = "bind", + unbind = "unbind", + cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, + referenceInterface = PasswordFiledEncryptFilter.class, + policy = ReferencePolicy.DYNAMIC) + private Collection encryptionFilters = ConcurrentHashMap.newKeySet(); + + @Reference + private CryptoSupport cryptoSupport; + + @Override + public void process(SlingHttpServletRequest slingHttpServletRequest, List list) { + List propertiesToEncrypt = list.stream() + .filter(this::isSupported) + .map(Modification::getSource) + .collect(Collectors.toList()); + + ResourceResolver resourceResolver = slingHttpServletRequest.getResourceResolver(); + Session session = resourceResolver.adaptTo(Session.class); + + if (session == null) { + LOGGER.error("Failed to create session."); + return; + } + + for (String propertyPath : propertiesToEncrypt) { + try { + this.encryptProperty(session, propertyPath); + } catch (CryptoException | RepositoryException e) { + LOGGER.error("Failed to encrypt property {}", propertyPath, e); + } + } + + } + + private boolean isSupported(Modification modification) { + return encryptionFilters + .stream() + .filter(filter -> filter.isSupported(modification.getSource())) + .findFirst() + .isPresent(); + } + + + private void encryptProperty(Session session, String propertyPath) throws CryptoException, RepositoryException { + Property propertyToBeProtected = session.getProperty(propertyPath); + if (propertyToBeProtected != null) { + String propertyValue = StringUtils.defaultString(propertyToBeProtected.getString()); + if (!cryptoSupport.isProtected(propertyValue)) { + LOGGER.info("Encrypting property: '{}'", propertyPath); + String encryptedPropertyValue = cryptoSupport.protect(propertyValue); + propertyToBeProtected.setValue(encryptedPropertyValue); + LOGGER.info("Property '{}' encrypted", propertyPath); + } + } + + } + + private String getPropertyNodePath(String propertyPath) { + return StringUtils.substringBeforeLast(propertyPath, PATH_SEPARATOR); + } + + private String getPropertyName(String propertyPath) { + return StringUtils.substringAfterLast(propertyPath, PATH_SEPARATOR); + } + + protected void bind(PasswordFiledEncryptFilter filter) { + encryptionFilters.add(filter); + } + + protected void unbind(PasswordFiledEncryptFilter filter) { + encryptionFilters.remove(filter); + } +} diff --git a/src/main/java/com/cognifide/secureaem/sling/password/PublishPasswordFieldEncryptFilter.java b/src/main/java/com/cognifide/secureaem/sling/password/PublishPasswordFieldEncryptFilter.java new file mode 100644 index 0000000..1798e4c --- /dev/null +++ b/src/main/java/com/cognifide/secureaem/sling/password/PublishPasswordFieldEncryptFilter.java @@ -0,0 +1,15 @@ +package com.cognifide.secureaem.sling.password; + +import org.apache.commons.lang3.StringUtils; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Service; + +@Service +@Component +public class PublishPasswordFieldEncryptFilter implements PasswordFiledEncryptFilter { + @Override + public boolean isSupported(String propertyPath) { + return StringUtils.startsWith(propertyPath, "/etc/secureaem") + && StringUtils.endsWith(propertyPath, "publishPassword"); + } +}