From 0c7d4e488bf4e357ca33329c87a29baae63a5be9 Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Sun, 19 Feb 2012 15:04:43 +0000 Subject: [PATCH] Add remember me capabilities to RAP git-svn-id: https://svn.argeo.org/commons/trunk@5103 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc --- .../src/main/java/org/argeo/StreamUtils.java | 88 +++++++++++- .../util/crypto/PasswordBasedEncryption.java | 8 ++ .../org/argeo/util/security/DigestUtils.java | 130 +++++++++++++++++ demo/log4j.properties | 2 + .../spring/security-ldap-services.xml | 15 +- .../branding/afterLogout.html | 18 +++ .../branding/empty.html | 5 + .../branding/{default.htm => login.html} | 0 .../org.argeo.security.ui.rap/plugin.xml | 45 +++++- .../security/ui/rap/LogoutEntryPoint.java | 47 +++++++ .../argeo/security/ui/rap/NullEntryPoint.java | 16 +++ .../security/ui/rap/RapActionBarAdvisor.java | 1 - .../security/ui/rap/SecureEntryPoint.java | 37 ++++- .../core/AbstractSystemExecution.java | 20 ++- .../JcrPersistentTokenRepository.java | 30 ++++ .../WEB-INF/security-filters.xml | 54 ++++---- .../WEB-INF/security.xml | 32 ----- .../WEB-INF/web.xml | 7 +- .../jackrabbit-webapp.properties | 2 + .../WEB-INF/applicationContext.xml | 11 +- .../WEB-INF/osgi.xml | 4 +- .../WEB-INF/security-filters.xml | 131 ++++++++++++++++++ .../WEB-INF/security.xml | 14 -- .../WEB-INF/web.xml | 28 ++-- .../org.argeo.server.rap.webapp/pom.xml | 20 ++- .../rap-webapp.properties | 1 + 26 files changed, 669 insertions(+), 97 deletions(-) create mode 100644 basic/runtime/org.argeo.basic.nodeps/src/main/java/org/argeo/util/security/DigestUtils.java create mode 100644 security/plugins/org.argeo.security.ui.rap/branding/afterLogout.html create mode 100644 security/plugins/org.argeo.security.ui.rap/branding/empty.html rename security/plugins/org.argeo.security.ui.rap/branding/{default.htm => login.html} (100%) create mode 100644 security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/LogoutEntryPoint.java create mode 100644 security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/NullEntryPoint.java create mode 100644 security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/rememberme/JcrPersistentTokenRepository.java delete mode 100644 server/modules/org.argeo.jackrabbit.webapp/WEB-INF/security.xml create mode 100644 server/modules/org.argeo.server.rap.webapp/WEB-INF/security-filters.xml delete mode 100644 server/modules/org.argeo.server.rap.webapp/WEB-INF/security.xml create mode 100644 server/modules/org.argeo.server.rap.webapp/rap-webapp.properties diff --git a/basic/runtime/org.argeo.basic.nodeps/src/main/java/org/argeo/StreamUtils.java b/basic/runtime/org.argeo.basic.nodeps/src/main/java/org/argeo/StreamUtils.java index d2ccdb737..c94950d19 100644 --- a/basic/runtime/org.argeo.basic.nodeps/src/main/java/org/argeo/StreamUtils.java +++ b/basic/runtime/org.argeo.basic.nodeps/src/main/java/org/argeo/StreamUtils.java @@ -10,6 +10,10 @@ import java.io.Writer; public class StreamUtils { private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; + /* + * APACHE COMMONS IO (inspired) + */ + /** @return the number of bytes */ public static Long copy(InputStream in, OutputStream out) throws IOException { @@ -75,8 +79,90 @@ public class StreamUtils { } } - private StreamUtils() { + /* + * APACHE COMMONS CODEC (forked) + */ + /** + * Used to build output as Hex + */ + private static final char[] DIGITS_LOWER = { '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + + /** + * Used to build output as Hex + */ + private static final char[] DIGITS_UPPER = { '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + /** + * Converts an array of bytes into a String representing the hexadecimal + * values of each byte in order. The returned String will be double the + * length of the passed array, as it takes two characters to represent any + * given byte. + * + * @param data + * a byte[] to convert to Hex characters + * @return A String containing hexadecimal characters + * @since 1.4 + */ + public static String encodeHexString(byte[] data) { + return new String(encodeHex(data)); + } + + /** + * Converts an array of bytes into an array of characters representing the + * hexadecimal values of each byte in order. The returned array will be + * double the length of the passed array, as it takes two characters to + * represent any given byte. + * + * @param data + * a byte[] to convert to Hex characters + * @return A char[] containing hexadecimal characters + */ + public static char[] encodeHex(byte[] data) { + return encodeHex(data, true); + } + + /** + * Converts an array of bytes into an array of characters representing the + * hexadecimal values of each byte in order. The returned array will be + * double the length of the passed array, as it takes two characters to + * represent any given byte. + * + * @param data + * a byte[] to convert to Hex characters + * @param toLowerCase + * true converts to lowercase, false to + * uppercase + * @return A char[] containing hexadecimal characters + * @since 1.4 + */ + public static char[] encodeHex(byte[] data, boolean toLowerCase) { + return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER); + } + + /** + * Converts an array of bytes into an array of characters representing the + * hexadecimal values of each byte in order. The returned array will be + * double the length of the passed array, as it takes two characters to + * represent any given byte. + * + * @param data + * a byte[] to convert to Hex characters + * @param toDigits + * the output alphabet + * @return A char[] containing hexadecimal characters + * @since 1.4 + */ + protected static char[] encodeHex(byte[] data, char[] toDigits) { + int l = data.length; + char[] out = new char[l << 1]; + // two characters form the hex value. + for (int i = 0, j = 0; i < l; i++) { + out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; + out[j++] = toDigits[0x0F & data[i]]; + } + return out; } } diff --git a/basic/runtime/org.argeo.basic.nodeps/src/main/java/org/argeo/util/crypto/PasswordBasedEncryption.java b/basic/runtime/org.argeo.basic.nodeps/src/main/java/org/argeo/util/crypto/PasswordBasedEncryption.java index ab36e5d8f..18cccd4e1 100644 --- a/basic/runtime/org.argeo.basic.nodeps/src/main/java/org/argeo/util/crypto/PasswordBasedEncryption.java +++ b/basic/runtime/org.argeo.basic.nodeps/src/main/java/org/argeo/util/crypto/PasswordBasedEncryption.java @@ -40,10 +40,18 @@ public class PasswordBasedEncryption { private final Cipher ecipher; private final Cipher dcipher; + /** + * This is up to the caller to clear the passed array. Neither copy of nor + * reference to the passed array is kept + */ public PasswordBasedEncryption(char[] password) { this(password, DEFAULT_SALT_8, DEFAULT_IV_16); } + /** + * This is up to the caller to clear the passed array. Neither copies of nor + * references to the passed arrays are kept + */ public PasswordBasedEncryption(char[] password, byte[] passwordSalt, byte[] initializationVector) { try { diff --git a/basic/runtime/org.argeo.basic.nodeps/src/main/java/org/argeo/util/security/DigestUtils.java b/basic/runtime/org.argeo.basic.nodeps/src/main/java/org/argeo/util/security/DigestUtils.java new file mode 100644 index 000000000..34dd870e3 --- /dev/null +++ b/basic/runtime/org.argeo.basic.nodeps/src/main/java/org/argeo/util/security/DigestUtils.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2010 Mathieu Baudier + * + * Licensed 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.argeo.util.security; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import org.argeo.ArgeoException; +import org.argeo.StreamUtils; + +/** Utilities around cryptographic digests */ +public class DigestUtils { + private static Boolean debug = true; + // TODO: make it writable + private final static Integer byteBufferCapacity = 100 * 1024;// 100 KB + + public static String digest(String algorithm, InputStream in) { + try { + MessageDigest digest = MessageDigest.getInstance(algorithm); + // ReadableByteChannel channel = Channels.newChannel(in); + // ByteBuffer bb = ByteBuffer.allocateDirect(byteBufferCapacity); + // while (channel.read(bb) > 0) + // digest.update(bb); + byte[] buffer = new byte[byteBufferCapacity]; + int read = 0; + while ((read = in.read(buffer)) > 0) { + digest.update(buffer, 0, read); + } + + byte[] checksum = digest.digest(); + String res = StreamUtils.encodeHexString(checksum); + return res; + } catch (Exception e) { + throw new ArgeoException("Cannot digest with algorithm " + + algorithm, e); + } finally { + StreamUtils.closeQuietly(in); + } + } + + public static String digest(String algorithm, File file) { + FileInputStream fis = null; + FileChannel fc = null; + try { + fis = new FileInputStream(file); + fc = fis.getChannel(); + + // Get the file's size and then map it into memory + int sz = (int) fc.size(); + ByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, sz); + return digest(algorithm, bb); + } catch (IOException e) { + throw new ArgeoException("Cannot digest " + file + + " with algorithm " + algorithm, e); + } finally { + StreamUtils.closeQuietly(fis); + if (fc.isOpen()) + try { + fc.close(); + } catch (IOException e) { + // silent + } + } + } + + protected static String digest(String algorithm, ByteBuffer bb) { + long begin = System.currentTimeMillis(); + try { + MessageDigest digest = MessageDigest.getInstance(algorithm); + digest.update(bb); + byte[] checksum = digest.digest(); + String res = StreamUtils.encodeHexString(checksum); + long end = System.currentTimeMillis(); + if (debug) + System.out.println((end - begin) + " ms / " + + ((end - begin) / 1000) + " s"); + return res; + } catch (NoSuchAlgorithmException e) { + throw new ArgeoException("Cannot digest with algorithm " + + algorithm, e); + } + } + + public static void main(String[] args) { + File file; + if (args.length > 0) + file = new File(args[0]); + else { + System.err.println("Usage: []" + + " (see http://java.sun.com/j2se/1.5.0/" + + "docs/guide/security/CryptoSpec.html#AppA)"); + return; + } + + if (args.length > 1) { + String algorithm = args[1]; + System.out.println(digest(algorithm, file)); + } else { + String algorithm = "MD5"; + System.out.println(algorithm + ": " + digest(algorithm, file)); + algorithm = "SHA"; + System.out.println(algorithm + ": " + digest(algorithm, file)); + algorithm = "SHA-256"; + System.out.println(algorithm + ": " + digest(algorithm, file)); + algorithm = "SHA-512"; + System.out.println(algorithm + ": " + digest(algorithm, file)); + } + } + +} diff --git a/demo/log4j.properties b/demo/log4j.properties index 06e71583d..5be6d57f1 100644 --- a/demo/log4j.properties +++ b/demo/log4j.properties @@ -12,6 +12,8 @@ log4j.logger.org.apache.coyote=INFO log4j.logger.org.apache.directory.server=ERROR log4j.logger.org.apache.jackrabbit.core.query.lucene=ERROR +log4j.logger.org.springframework.security.context=DEBUG + ## Appenders # console is set to be a ConsoleAppender. log4j.appender.console=org.apache.log4j.ConsoleAppender diff --git a/security/modules/org.argeo.security.dao.ldap/META-INF/spring/security-ldap-services.xml b/security/modules/org.argeo.security.dao.ldap/META-INF/spring/security-ldap-services.xml index e0851123d..525b84db0 100644 --- a/security/modules/org.argeo.security.dao.ldap/META-INF/spring/security-ldap-services.xml +++ b/security/modules/org.argeo.security.dao.ldap/META-INF/spring/security-ldap-services.xml @@ -12,8 +12,10 @@ - + + + @@ -26,12 +28,23 @@ + + + + + + + + + + + + authenticatedBySelf = new ThreadLocal() { + private InheritableThreadLocal authenticatedBySelf = new InheritableThreadLocal() { protected Boolean initialValue() { return false; } @@ -35,12 +44,12 @@ public abstract class AbstractSystemExecution { return; SecurityContext securityContext = SecurityContextHolder.getContext(); Authentication currentAuth = securityContext.getAuthentication(); - if (currentAuth != null) + if (currentAuth != null){ throw new ArgeoException( "System execution on an already authenticated thread: " + currentAuth + ", THREAD=" + Thread.currentThread().getId()); - + } Subject subject = Subject.getSubject(AccessController.getContext()); if (subject != null && !subject.getPrincipals(Authentication.class).isEmpty()) @@ -75,7 +84,10 @@ public abstract class AbstractSystemExecution { } } - /** Whether the current thread was authenticated by this component. */ + /** + * Whether the current thread was authenticated by this component or a + * parent thread. + */ protected Boolean isAuthenticatedBySelf() { return authenticatedBySelf.get(); } diff --git a/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/rememberme/JcrPersistentTokenRepository.java b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/rememberme/JcrPersistentTokenRepository.java new file mode 100644 index 000000000..598c2dd9c --- /dev/null +++ b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/rememberme/JcrPersistentTokenRepository.java @@ -0,0 +1,30 @@ +package org.argeo.security.jcr.rememberme; + +import java.util.Date; + +import org.springframework.security.ui.rememberme.PersistentRememberMeToken; +import org.springframework.security.ui.rememberme.PersistentTokenRepository; + +public class JcrPersistentTokenRepository implements PersistentTokenRepository { + + public void createNewToken(PersistentRememberMeToken token) { + // TODO Auto-generated method stub + + } + + public void updateToken(String series, String tokenValue, Date lastUsed) { + // TODO Auto-generated method stub + + } + + public PersistentRememberMeToken getTokenForSeries(String seriesId) { + // TODO Auto-generated method stub + return null; + } + + public void removeUserTokens(String username) { + // TODO Auto-generated method stub + + } + +} diff --git a/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/security-filters.xml b/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/security-filters.xml index d7613a0ff..f12f0c804 100644 --- a/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/security-filters.xml +++ b/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/security-filters.xml @@ -8,41 +8,49 @@ - + + + + + - + + + + PATTERN_TYPE_APACHE_ANT + /*/*/*/**=ROLE_USER,ROLE_ADMIN + /**=ROLE_ANONYMOUS + + + + - CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON PATTERN_TYPE_APACHE_ANT - /public/**=IS_AUTHENTICATED_ANONYMOUSLY - /*/*/*/**=ROLE_USER /**=IS_AUTHENTICATED_ANONYMOUSLY - - - - - - - - + - - + + @@ -53,12 +61,8 @@ - - - - @@ -68,10 +72,12 @@ class="org.springframework.security.ui.rememberme.TokenBasedRememberMeServices"> + + - @@ -91,14 +97,14 @@ - - diff --git a/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/security.xml b/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/security.xml deleted file mode 100644 index 2627b7583..000000000 --- a/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/security.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/web.xml b/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/web.xml index e24236b41..9e0a75280 100644 --- a/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/web.xml +++ b/server/modules/org.argeo.jackrabbit.webapp/WEB-INF/web.xml @@ -40,6 +40,11 @@ /remoting/* + + remoting + /pub/* + + + + + + + + + osgibundle:rap-webapp.properties + + \ No newline at end of file diff --git a/server/modules/org.argeo.server.rap.webapp/WEB-INF/osgi.xml b/server/modules/org.argeo.server.rap.webapp/WEB-INF/osgi.xml index d344cc8d6..cb31230ef 100644 --- a/server/modules/org.argeo.server.rap.webapp/WEB-INF/osgi.xml +++ b/server/modules/org.argeo.server.rap.webapp/WEB-INF/osgi.xml @@ -8,6 +8,8 @@ http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd"> - + \ No newline at end of file diff --git a/server/modules/org.argeo.server.rap.webapp/WEB-INF/security-filters.xml b/server/modules/org.argeo.server.rap.webapp/WEB-INF/security-filters.xml new file mode 100644 index 000000000..3a6650cd9 --- /dev/null +++ b/server/modules/org.argeo.server.rap.webapp/WEB-INF/security-filters.xml @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + PATTERN_TYPE_APACHE_ANT + /**=ROLE_USER,ROLE_ADMIN + + + + + + + PATTERN_TYPE_APACHE_ANT + /**=IS_AUTHENTICATED_ANONYMOUSLY + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Argeo + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/server/modules/org.argeo.server.rap.webapp/WEB-INF/security.xml b/server/modules/org.argeo.server.rap.webapp/WEB-INF/security.xml deleted file mode 100644 index d606d0300..000000000 --- a/server/modules/org.argeo.server.rap.webapp/WEB-INF/security.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/server/modules/org.argeo.server.rap.webapp/WEB-INF/web.xml b/server/modules/org.argeo.server.rap.webapp/WEB-INF/web.xml index 16388e449..4a13fe556 100644 --- a/server/modules/org.argeo.server.rap.webapp/WEB-INF/web.xml +++ b/server/modules/org.argeo.server.rap.webapp/WEB-INF/web.xml @@ -37,14 +37,26 @@ - - - - + + springSecurityFilterChain + org.springframework.web.filter.DelegatingFilterProxy + - - - - + + springSecurityFilterChain + /ui + + + springSecurityFilterChain + /basicauth + + + springSecurityFilterChain + /none + + + springSecurityFilterChain + /public + diff --git a/server/modules/org.argeo.server.rap.webapp/pom.xml b/server/modules/org.argeo.server.rap.webapp/pom.xml index ee0536497..fcff90860 100644 --- a/server/modules/org.argeo.server.rap.webapp/pom.xml +++ b/server/modules/org.argeo.server.rap.webapp/pom.xml @@ -20,16 +20,32 @@ org.argeo.rap.webapp *, + + org.springframework.beans.factory.config, + org.eclipse.equinox.http.servlet, org.springframework.osgi.web.context.support, org.springframework.security, + org.springframework.security.context, + org.springframework.security.intercept.web, + org.springframework.security.providers.anonymous, + org.springframework.security.ui, + org.springframework.security.ui.basicauth, + org.springframework.security.ui.logout, + org.springframework.security.ui.rememberme, org.springframework.security.ui.webapp, + org.springframework.security.userdetails, + org.springframework.security.util, + org.springframework.security.vote, + org.springframework.security.wrapper, org.springframework.web.context, org.springframework.web.filter, org.springframework.web.servlet, org.springframework.web.servlet.handler, - org.springframework.web.servlet.mvc - + org.springframework.web.servlet.mvc, + + + diff --git a/server/modules/org.argeo.server.rap.webapp/rap-webapp.properties b/server/modules/org.argeo.server.rap.webapp/rap-webapp.properties new file mode 100644 index 000000000..ae7aa8725 --- /dev/null +++ b/server/modules/org.argeo.server.rap.webapp/rap-webapp.properties @@ -0,0 +1 @@ +argeo.security.systemKey=argeo -- 2.30.2