You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ol...@apache.org on 2021/05/27 16:58:20 UTC

[sling-org-apache-sling-commons-crypto] 01/03: SLING-10430 Add fix for POSIX newline in password files

This is an automated email from the ASF dual-hosted git repository.

olli pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-crypto.git

commit abce0d2499730ba0fff31e13c240bf6718e35180
Author: Oliver Lietz <ol...@apache.org>
AuthorDate: Thu May 27 18:56:29 2021 +0200

    SLING-10430 Add fix for POSIX newline in password files
---
 pom.xml                                            |   6 ++
 .../crypto/internal/FilePasswordProvider.java      |  22 +++--
 .../FilePasswordProviderConfiguration.java         |   6 ++
 .../crypto/internal/FilePasswordProviderTest.java  | 109 +++++++++++++++++++++
 src/test/resources/password.ascii85_newline        |   1 +
 src/test/resources/password.ascii85_newlines       |   2 +
 6 files changed, 139 insertions(+), 7 deletions(-)

diff --git a/pom.xml b/pom.xml
index 44c6285..dbdd33c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -222,6 +222,12 @@
       <scope>test</scope>
     </dependency>
     <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <version>3.10.0</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
       <groupId>com.github.stefanbirkner</groupId>
       <artifactId>system-lambda</artifactId>
       <version>1.2.0</version>
diff --git a/src/main/java/org/apache/sling/commons/crypto/internal/FilePasswordProvider.java b/src/main/java/org/apache/sling/commons/crypto/internal/FilePasswordProvider.java
index 6428c5e..26ed9b0 100644
--- a/src/main/java/org/apache/sling/commons/crypto/internal/FilePasswordProvider.java
+++ b/src/main/java/org/apache/sling/commons/crypto/internal/FilePasswordProvider.java
@@ -50,38 +50,46 @@ public class FilePasswordProvider implements PasswordProvider {
 
     private FilePasswordProviderConfiguration configuration;
 
+    private static final char NEWLINE_CHARACTER = '\n';
+
     private final Logger logger = LoggerFactory.getLogger(FilePasswordProvider.class);
 
     public FilePasswordProvider() {
     }
 
     @Activate
-    private void activate(final FilePasswordProviderConfiguration configuration) throws IOException {
+    protected void activate(final FilePasswordProviderConfiguration configuration) throws IOException {
         logger.debug("activating");
         this.configuration = configuration;
         checkConfiguration();
     }
 
     @Modified
-    private void modified(final FilePasswordProviderConfiguration configuration) throws IOException {
+    protected void modified(final FilePasswordProviderConfiguration configuration) throws IOException {
         logger.debug("modifying");
         this.configuration = configuration;
         checkConfiguration();
     }
 
     @Deactivate
-    private void deactivate() {
+    protected void deactivate() {
         logger.debug("deactivating");
         this.configuration = null;
     }
 
-    private char[] readPassword(final String path) throws IOException {
+    private char[] readPassword(final String path, final boolean fixPosixNewline) throws IOException {
         final File file = new File(path);
         final char[] buffer = new char[(int) file.length()];
         try (final BufferedReader reader = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8)) {
             final int size = reader.read(buffer);
-            final char[] password = new char[size];
-            System.arraycopy(buffer, 0, password, 0, size);
+            final int length;
+            if (fixPosixNewline && buffer[size - 1] == NEWLINE_CHARACTER) {
+                length = size - 1;
+            } else {
+                length = size;
+            }
+            final char[] password = new char[length];
+            System.arraycopy(buffer, 0, password, 0, length);
             Arrays.fill(buffer, '0');
             return password;
         }
@@ -99,7 +107,7 @@ public class FilePasswordProvider implements PasswordProvider {
     @Override
     public char @NotNull [] getPassword() {
         try {
-            return readPassword(configuration.path());
+            return readPassword(configuration.path(), configuration.fix_posixNewline());
         } catch (IOException e) {
             throw new RuntimeException(e);
         }
diff --git a/src/main/java/org/apache/sling/commons/crypto/internal/FilePasswordProviderConfiguration.java b/src/main/java/org/apache/sling/commons/crypto/internal/FilePasswordProviderConfiguration.java
index cefdfbe..e529b7f 100644
--- a/src/main/java/org/apache/sling/commons/crypto/internal/FilePasswordProviderConfiguration.java
+++ b/src/main/java/org/apache/sling/commons/crypto/internal/FilePasswordProviderConfiguration.java
@@ -40,6 +40,12 @@ import org.osgi.service.metatype.annotations.ObjectClassDefinition;
     )
     String path();
 
+    @AttributeDefinition(
+        name = "Fix POSIX newline",
+        description = "ignores the very last newline character from file content for password"
+    )
+    boolean fix_posixNewline() default false;
+
     String webconsole_configurationFactory_nameHint() default "{names} {path}";
 
 }
diff --git a/src/test/java/org/apache/sling/commons/crypto/internal/FilePasswordProviderTest.java b/src/test/java/org/apache/sling/commons/crypto/internal/FilePasswordProviderTest.java
new file mode 100644
index 0000000..5510451
--- /dev/null
+++ b/src/test/java/org/apache/sling/commons/crypto/internal/FilePasswordProviderTest.java
@@ -0,0 +1,109 @@
+/*
+ * 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 ASF 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.commons.crypto.internal;
+
+import java.io.IOException;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.ops4j.pax.exam.util.PathUtils;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class FilePasswordProviderTest {
+
+    private static final char[] PASSWORD_ASCII = "+AQ?aDes!'DBMkrCi:FE6q\\sOn=Pbmn=PK8n=PK?".toCharArray();
+
+    private static final char[] PASSWORD_ASCII_NEWLINE = "+AQ?aDes!'DBMkrCi:FE6q\\sOn=Pbmn=PK8n=PK?\n".toCharArray();
+
+    private static final char[] PASSWORD_ASCII_NEWLINES = "+AQ?aDes!'DBMkrCi:FE6q\\sOn=Pbmn=PK8n=PK?\n\n".toCharArray();
+
+    private static final char[] PASSWORD_UTF8 = " Napøleøn Sølø (DK) \uD83C\uDFC1\uD83C\uDDE9\uD83C\uDDF0".toCharArray();
+
+    @Rule
+    public ExpectedException exception = ExpectedException.none();
+
+    @Test
+    public void testComponentLifecycle() throws IOException {
+        final FilePasswordProvider provider = new FilePasswordProvider();
+        { // activate
+            final String path = String.format("%s/src/test/resources/password.ascii85", PathUtils.getBaseDir());
+            final FilePasswordProviderConfiguration configuration = mock(FilePasswordProviderConfiguration.class);
+            when(configuration.path()).thenReturn(path);
+            provider.activate(configuration);
+            assertThat(provider.getPassword()).isEqualTo(PASSWORD_ASCII);
+        }
+        { // modified
+            final String path = String.format("%s/src/test/resources/password.utf8", PathUtils.getBaseDir());
+            final FilePasswordProviderConfiguration configuration = mock(FilePasswordProviderConfiguration.class);
+            when(configuration.path()).thenReturn(path);
+            provider.modified(configuration);
+            assertThat(provider.getPassword()).isEqualTo(PASSWORD_UTF8);
+        }
+    }
+
+    @Test
+    public void testPasswordFile() throws IOException {
+        final FilePasswordProvider provider = new FilePasswordProvider();
+        final String path = String.format("%s/src/test/resources/password.ascii85", PathUtils.getBaseDir());
+        final FilePasswordProviderConfiguration configuration = mock(FilePasswordProviderConfiguration.class);
+        when(configuration.path()).thenReturn(path);
+        when(configuration.fix_posixNewline()).thenReturn(false);
+        provider.activate(configuration);
+        assertThat(provider.getPassword()).isEqualTo(PASSWORD_ASCII);
+        // enable fix for POSIX newline
+        when(configuration.fix_posixNewline()).thenReturn(true);
+        provider.modified(configuration);
+        assertThat(provider.getPassword()).isEqualTo(PASSWORD_ASCII);
+    }
+
+    @Test
+    public void testPasswordFileWithNewline() throws IOException {
+        final FilePasswordProvider provider = new FilePasswordProvider();
+        final String path = String.format("%s/src/test/resources/password.ascii85_newline", PathUtils.getBaseDir());
+        final FilePasswordProviderConfiguration configuration = mock(FilePasswordProviderConfiguration.class);
+        when(configuration.path()).thenReturn(path);
+        when(configuration.fix_posixNewline()).thenReturn(false);
+        provider.activate(configuration);
+        assertThat(provider.getPassword()).isEqualTo(PASSWORD_ASCII_NEWLINE);
+        // enable fix for POSIX newline
+        when(configuration.fix_posixNewline()).thenReturn(true);
+        provider.modified(configuration);
+        assertThat(provider.getPassword()).isEqualTo(PASSWORD_ASCII);
+    }
+
+    @Test
+    public void testPasswordFileWithNewlines() throws IOException {
+        final FilePasswordProvider provider = new FilePasswordProvider();
+        final String path = String.format("%s/src/test/resources/password.ascii85_newlines", PathUtils.getBaseDir());
+        final FilePasswordProviderConfiguration configuration = mock(FilePasswordProviderConfiguration.class);
+        when(configuration.path()).thenReturn(path);
+        when(configuration.fix_posixNewline()).thenReturn(false);
+        provider.activate(configuration);
+        assertThat(provider.getPassword()).isEqualTo(PASSWORD_ASCII_NEWLINES);
+        // enable fix for POSIX newline
+        when(configuration.fix_posixNewline()).thenReturn(true);
+        provider.modified(configuration);
+        assertThat(provider.getPassword()).isEqualTo(PASSWORD_ASCII_NEWLINE);
+    }
+
+}
diff --git a/src/test/resources/password.ascii85_newline b/src/test/resources/password.ascii85_newline
new file mode 100644
index 0000000..8884d75
--- /dev/null
+++ b/src/test/resources/password.ascii85_newline
@@ -0,0 +1 @@
++AQ?aDes!'DBMkrCi:FE6q\sOn=Pbmn=PK8n=PK?
diff --git a/src/test/resources/password.ascii85_newlines b/src/test/resources/password.ascii85_newlines
new file mode 100644
index 0000000..e33e3c4
--- /dev/null
+++ b/src/test/resources/password.ascii85_newlines
@@ -0,0 +1,2 @@
++AQ?aDes!'DBMkrCi:FE6q\sOn=Pbmn=PK8n=PK?
+