You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by en...@apache.org on 2022/09/26 18:32:43 UTC
[sling-org-apache-sling-auth-core] branch master updated: SLING-11446 Move the DefaultLoginsHealthCheck to the auth core bundle (#13)
This is an automated email from the ASF dual-hosted git repository.
enorman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-auth-core.git
The following commit(s) were added to refs/heads/master by this push:
new e8d6ba1 SLING-11446 Move the DefaultLoginsHealthCheck to the auth core bundle (#13)
e8d6ba1 is described below
commit e8d6ba1837a52f39f6d83ea77d37ac6193bb0091
Author: Eric Norman <en...@apache.org>
AuthorDate: Mon Sep 26 11:32:39 2022 -0700
SLING-11446 Move the DefaultLoginsHealthCheck to the auth core bundle (#13)
---
bnd.bnd | 6 +-
pom.xml | 22 +++-
.../core/impl/hc/DefaultLoginsHealthCheck.java | 125 +++++++++++++++++++++
.../core/impl/hc/DefaultLoginsHealthCheckTest.java | 98 ++++++++++++++++
.../apache/sling/auth/core/impl/hc/SetField.java | 30 +++++
5 files changed, 275 insertions(+), 6 deletions(-)
diff --git a/bnd.bnd b/bnd.bnd
index c910ed7..f3533ac 100644
--- a/bnd.bnd
+++ b/bnd.bnd
@@ -1,3 +1,7 @@
-Import-Package: !javax.jcr,*
+# SLING-11446 - keep hc related imports optional
+Import-Package: !javax.jcr,\
+ org.apache.felix.hc.api;resolution:=optional,\
+ org.apache.sling.jcr.api;resolution:=optional,\
+ *
DynamicImport-Package: javax.jcr
Bundle-DocURL: http://sling.apache.org/site/authentication.html
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 7284c34..947453f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -135,11 +135,23 @@
<artifactId>org.apache.sling.commons.metrics</artifactId>
<version>1.2.8</version>
</dependency>
- <dependency>
- <groupId>org.jetbrains</groupId>
- <artifactId>annotations</artifactId>
- <scope>provided</scope>
- </dependency>
+ <dependency>
+ <groupId>org.jetbrains</groupId>
+ <artifactId>annotations</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.healthcheck.api</artifactId>
+ <version>2.0.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.jcr.api</artifactId>
+ <version>2.0.4</version>
+ <scope>provided</scope>
+ </dependency>
<!-- Test Dependencies -->
<dependency>
diff --git a/src/main/java/org/apache/sling/auth/core/impl/hc/DefaultLoginsHealthCheck.java b/src/main/java/org/apache/sling/auth/core/impl/hc/DefaultLoginsHealthCheck.java
new file mode 100644
index 0000000..a02f373
--- /dev/null
+++ b/src/main/java/org/apache/sling/auth/core/impl/hc/DefaultLoginsHealthCheck.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The SF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package org.apache.sling.auth.core.impl.hc;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.jcr.Credentials;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+
+import org.apache.felix.hc.api.FormattingResultLog;
+import org.apache.felix.hc.api.HealthCheck;
+import org.apache.felix.hc.api.Result;
+import org.apache.sling.jcr.api.SlingRepository;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.ConfigurationPolicy;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** {@link HealthCheck} that runs an arbitrary script. */
+@Component(service = HealthCheck.class, name = "org.apache.sling.auth.core.DefaultLoginsHealthCheck", configurationPolicy = ConfigurationPolicy.REQUIRE)
+@Designate(ocd = DefaultLoginsHealthCheck.Config.class, factory = true)
+public class DefaultLoginsHealthCheck implements HealthCheck {
+
+ private static final Logger LOG = LoggerFactory.getLogger(DefaultLoginsHealthCheck.class);
+
+ public static final String HC_LABEL = "Health Check: Default Logins";
+
+ @ObjectClassDefinition(name = HC_LABEL, description = "Expects default logins to fail, used to verify that they are disabled on production systems")
+ @interface Config {
+
+ @AttributeDefinition(name = "Name", description = "Name of this health check.")
+ String hc_name() default "Default Logins Check"; // NOSONAR
+
+ @AttributeDefinition(name = "Tags", description = "List of tags for this health check, used to select subsets of health checks for execution e.g. by a composite health check.")
+ String[] hc_tags() default {}; // NOSONAR
+
+ @AttributeDefinition(name = "Default Logins", description = "Which credentials to check. Each one is in the format"
+ + " \"user:password\" like \"admin:admin\" for example. Do *not* put any confidential passwords here, the goal "
+ + "is just to check that the default/demo logins, which passwords are known anyway, are disabled.")
+ String[] logins() default "logins";
+
+ @AttributeDefinition
+ String webconsole_configurationFactory_nameHint() default "Default Logins Check: {logins}"; // NOSONAR
+ }
+
+ private List<String> logins;
+
+ @Reference
+ private SlingRepository repository;
+
+ @Activate
+ protected void activate(Config config) {
+ this.logins = Arrays.asList(config.logins());
+ LOG.info("Activated, logins={}", logins);
+ }
+
+ @Override
+ public Result execute() {
+ FormattingResultLog resultLog = new FormattingResultLog();
+ int checked = 0;
+ int failures = 0;
+
+ for (String login : logins) {
+ final String[] parts = login.split(":");
+ if (parts.length != 2) {
+ resultLog.warn("Expected login in the form username:password, got [{}]", login);
+ continue;
+ }
+ checked++;
+ final String username = parts[0].trim();
+ final String password = parts[1].trim();
+ final Credentials creds = new SimpleCredentials(username, password.toCharArray());
+ Session s = null;
+ try {
+ s = repository.login(creds);
+ if (s != null) {
+ failures++;
+ resultLog.warn("Login as [{}] succeeded, was expecting it to fail", username);
+ } else {
+ resultLog.debug("Login as [{}] didn't throw an Exception but returned null Session", username);
+ }
+ } catch (RepositoryException re) {
+ resultLog.debug("Login as [{}] failed, as expected", username);
+ } finally {
+ if (s != null) {
+ s.logout();
+ }
+ }
+ }
+
+ if (checked == 0) {
+ resultLog.warn("Did not check any logins, configured logins={}", logins);
+ } else if (failures != 0) {
+ resultLog.warn("Checked {} logins, {} failures", checked, failures);
+ } else {
+ resultLog.debug("Checked {} logins, all successful", checked, failures);
+ }
+
+ return new Result(resultLog);
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/auth/core/impl/hc/DefaultLoginsHealthCheckTest.java b/src/test/java/org/apache/sling/auth/core/impl/hc/DefaultLoginsHealthCheckTest.java
new file mode 100644
index 0000000..0327b6b
--- /dev/null
+++ b/src/test/java/org/apache/sling/auth/core/impl/hc/DefaultLoginsHealthCheckTest.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The SF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package org.apache.sling.auth.core.impl.hc;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+
+import javax.jcr.Credentials;
+import javax.jcr.LoginException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+
+import org.apache.felix.hc.api.Result;
+import org.apache.sling.jcr.api.SlingRepository;
+import org.junit.Test;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+public class DefaultLoginsHealthCheckTest {
+
+ private Result getTestResult(String login) throws Exception {
+ final DefaultLoginsHealthCheck c = new DefaultLoginsHealthCheck();
+ if (login == null) {
+ SetField.set(c, "logins", Arrays.asList(new String[0]));
+ } else {
+ SetField.set(c, "logins", Arrays.asList(new String[] { login }));
+ }
+
+ final SlingRepository repo = Mockito.mock(SlingRepository.class);
+ SetField.set(c, "repository", repo);
+ final Session s = Mockito.mock(Session.class);
+ Mockito.when(repo.login(ArgumentMatchers.any(Credentials.class))).thenAnswer(new Answer<Session>() {
+ @Override
+ public Session answer(InvocationOnMock invocation) throws LoginException {
+ final SimpleCredentials c = (SimpleCredentials)invocation.getArguments()[0];
+ if ("admin".equals(c.getUserID())) {
+ return s;
+ } else if ("throw".equals(c.getUserID())) {
+ throw new LoginException("Login Failed");
+ }
+ return null;
+ }
+ });
+
+ return c.execute();
+ }
+
+ @Test
+ public void testHealthCheckFails() throws Exception {
+ assertFalse("Expecting failed check", getTestResult("admin:admin").isOk());
+ }
+
+ @Test
+ public void testHealthCheckSucceeds() throws Exception {
+ assertTrue("Expecting successful check", getTestResult("FOO:bar").isOk());
+ }
+
+ @Test
+ public void testHealthCheckInvalidLogins() throws Exception {
+ Result testResult = getTestResult("FOO");
+ assertFalse("Expecting successful check", testResult.isOk());
+ assertTrue("Expected warning in the ResultLog", testResult.toString().contains("WARN Expected login in the form username:password, got [FOO]"));
+ }
+
+ @Test
+ public void testHealthCheckEmptyLogins() throws Exception {
+ Result testResult = getTestResult(null);
+ assertFalse("Expecting failed check", testResult.isOk());
+ assertTrue("Expected warning in the ResultLog", testResult.toString().contains("WARN Did not check any logins, configured logins=[]"));
+ }
+
+ @Test
+ public void testHealthCheckSucceedsWithLoginException() throws Exception {
+ Result testResult = getTestResult("throw:loginexception");
+ assertTrue("Expecting successful check", testResult.isOk());
+ assertTrue("Expected debug in the ResultLog", testResult.toString().contains("DEBUG Login as [throw] failed, as expected"));
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/auth/core/impl/hc/SetField.java b/src/test/java/org/apache/sling/auth/core/impl/hc/SetField.java
new file mode 100644
index 0000000..6df721e
--- /dev/null
+++ b/src/test/java/org/apache/sling/auth/core/impl/hc/SetField.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The SF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package org.apache.sling.auth.core.impl.hc;
+
+import java.lang.reflect.Field;
+
+public class SetField {
+
+ public static void set(Object o, String name, Object value) throws Exception {
+ final Field f = o.getClass().getDeclaredField(name);
+ f.setAccessible(true);
+ f.set(o, value);
+ }
+
+}
\ No newline at end of file