You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2022/03/22 08:15:08 UTC

[isis] 02/02: ISIS-2981: adds crypto roundtrip tests

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

ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git

commit 13d3689afa31eda419125d7fa7f12da454b2ae3f
Author: Andi Huber <ah...@apache.org>
AuthorDate: Tue Mar 22 09:14:57 2022 +0100

    ISIS-2981: adds crypto roundtrip tests
---
 viewers/wicket/viewer/pom.xml                      |   7 ++
 .../viewer/wicketapp/IsisWicketApplication.java    |   9 +-
 .../wicket/viewer/wicketapp/_CryptFactory.java     |  65 ++++++++++++
 .../wicket/viewer/wicketapp/CryptFactoryTest.java  | 117 +++++++++++++++++++++
 4 files changed, 194 insertions(+), 4 deletions(-)

diff --git a/viewers/wicket/viewer/pom.xml b/viewers/wicket/viewer/pom.xml
index 0a3b49f..9373a71 100644
--- a/viewers/wicket/viewer/pom.xml
+++ b/viewers/wicket/viewer/pom.xml
@@ -184,6 +184,13 @@
             <scope>test</scope>
         </dependency>
 
+		<!-- so we can test Cookies -->
+		<dependency>
+		  <groupId>javax.servlet</groupId>
+		  <artifactId>javax.servlet-api</artifactId>
+		  <scope>test</scope>
+		</dependency>
+
         <dependency>
         	<groupId>org.jmock</groupId>
         	<artifactId>jmock-junit4</artifactId>
diff --git a/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/wicketapp/IsisWicketApplication.java b/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/wicketapp/IsisWicketApplication.java
index fb4f5b1..e744f4e 100644
--- a/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/wicketapp/IsisWicketApplication.java
+++ b/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/wicketapp/IsisWicketApplication.java
@@ -336,13 +336,14 @@ implements
         val rememberMe = configuration.getViewer().getWicket().getRememberMe();
         val cookieKey = rememberMe.getCookieKey();
         val encryptionKey = rememberMe.getEncryptionKey().orElse(defaultEncryptionKey());
-        return new DefaultAuthenticationStrategy(cookieKey, encryptionKey);
+        return new DefaultAuthenticationStrategy(cookieKey, _CryptFactory.sunJceCrypt(encryptionKey));
     }
 
     /**
-     * As called by {@link #newAuthenticationStrategy(IsisConfiguration)}; if an encryption key for the 'rememberMe'
-     * cookie hasn't been configured, then use a different encryption key for the 'rememberMe' cookie each time the
-     * app is restarted.
+     * As called by {@link #newAuthenticationStrategy(IsisConfiguration)}.
+     * If an encryption key for the 'rememberMe' cookie hasn't been configured,
+     * then use a different encryption key for the 'rememberMe'
+     * cookie each time the app is restarted.
      */
     String defaultEncryptionKey() {
         return systemEnvironment.isPrototyping()
diff --git a/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/wicketapp/_CryptFactory.java b/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/wicketapp/_CryptFactory.java
new file mode 100644
index 0000000..cbd27a5
--- /dev/null
+++ b/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/wicketapp/_CryptFactory.java
@@ -0,0 +1,65 @@
+/*
+ *  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.isis.viewer.wicket.viewer.wicketapp;
+
+import java.security.spec.KeySpec;
+
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.wicket.core.random.DefaultSecureRandomSupplier;
+import org.apache.wicket.core.random.ISecureRandomSupplier;
+import org.apache.wicket.core.util.crypt.AESCrypt;
+import org.apache.wicket.util.crypt.ICrypt;
+import org.apache.wicket.util.crypt.SunJceCrypt;
+
+import lombok.SneakyThrows;
+import lombok.val;
+import lombok.experimental.UtilityClass;
+
+@UtilityClass
+class _CryptFactory {
+
+    ICrypt sunJceCrypt(final String encryptionKey) {
+        final byte[] salt = SunJceCrypt.randomSalt();
+        val crypt = new SunJceCrypt(salt, 1000);
+        crypt.setKey(encryptionKey);
+        return crypt;
+    }
+
+    @SneakyThrows
+    ICrypt aesCrypt(final String encryptionKey) {
+
+        final byte[] salt = SunJceCrypt.randomSalt();
+
+        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
+        KeySpec spec = new PBEKeySpec(encryptionKey.toCharArray(), salt, 65536, 256);
+        SecretKey tmp = factory.generateSecret(spec);
+        SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
+
+        ISecureRandomSupplier rGen = new DefaultSecureRandomSupplier();
+
+        return new AESCrypt(secret, rGen);
+    }
+
+    //XXX BCryptPasswordEncoder (Spring);
+
+}
diff --git a/viewers/wicket/viewer/src/test/java/org/apache/isis/viewer/wicket/viewer/wicketapp/CryptFactoryTest.java b/viewers/wicket/viewer/src/test/java/org/apache/isis/viewer/wicket/viewer/wicketapp/CryptFactoryTest.java
new file mode 100644
index 0000000..08fe26c
--- /dev/null
+++ b/viewers/wicket/viewer/src/test/java/org/apache/isis/viewer/wicket/viewer/wicketapp/CryptFactoryTest.java
@@ -0,0 +1,117 @@
+/*
+ *  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.isis.viewer.wicket.viewer.wicketapp;
+
+import java.util.stream.Stream;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.wicket.ThreadContext;
+import org.apache.wicket.authentication.strategy.DefaultAuthenticationStrategy;
+import org.apache.wicket.mock.MockWebResponse;
+import org.apache.wicket.protocol.http.servlet.ServletWebRequest;
+import org.apache.wicket.request.IExceptionMapper;
+import org.apache.wicket.request.IRequestMapper;
+import org.apache.wicket.request.Url;
+import org.apache.wicket.request.cycle.RequestCycle;
+import org.apache.wicket.request.cycle.RequestCycleContext;
+import org.apache.wicket.util.crypt.ICrypt;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.Mockito.mock;
+
+import lombok.val;
+
+/**
+ * Requires
+ * <pre>{@code
+ * <dependency>
+ *     <groupId>javax.servlet</groupId>
+ *     <artifactId>javax.servlet-api</artifactId>
+ *     <scope>test</scope>
+ * </dependency>}
+ * </pre>
+ */
+class CryptFactoryTest {
+
+    private ServletWebRequest servletWebRequest;
+    private MockWebResponse mockWebResponse;
+    private HttpServletRequest mockHttpServletRequest;
+
+    @BeforeEach
+    void setUp() throws Exception {
+
+        // quite a bit of ceremony to allow Cookie testing ...
+
+        mockHttpServletRequest = new org.springframework.mock.web.MockHttpServletRequest() {
+            @Override
+            public Cookie[] getCookies() {
+                return mockWebResponse.getCookies().toArray(new Cookie[] {});
+            }
+        };
+
+        servletWebRequest = new ServletWebRequest(mockHttpServletRequest, "", Url.parse("/"));
+        mockWebResponse = new MockWebResponse();
+
+        ThreadContext.setRequestCycle(new RequestCycle(new RequestCycleContext(
+                servletWebRequest,
+                mockWebResponse,
+                mock(IRequestMapper.class),
+                mock(IExceptionMapper.class))));
+
+    }
+
+    @ParameterizedTest(name = "{index} {0}")
+    @MethodSource("provideCryptoCandidates")
+    void authenticationStrategyRoundtrip(
+            final String displayName,
+            final ICrypt crypto) {
+
+        val strategy =
+                new DefaultAuthenticationStrategy("cookieKey", crypto);
+
+        strategy.save("hello", "world");
+        val data = strategy.load();
+
+        assertNotNull(data);
+        assertEquals(2, data.length);
+        assertEquals("hello", data[0]);
+        assertEquals("world", data[1]);
+    }
+
+    private static Stream<Arguments> provideCryptoCandidates() {
+
+        return Stream.of(
+          Arguments.of(
+                  "sunJceCrypt",
+                  _CryptFactory.sunJceCrypt("encryptionKey")),
+          Arguments.of(
+                  "aesCrypt",
+                  _CryptFactory.aesCrypt("encryptionKey"))
+        );
+    }
+
+
+}