Add remember me capabilities to RAP
authorMathieu Baudier <mbaudier@argeo.org>
Sun, 19 Feb 2012 15:04:43 +0000 (15:04 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Sun, 19 Feb 2012 15:04:43 +0000 (15:04 +0000)
git-svn-id: https://svn.argeo.org/commons/trunk@5103 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

27 files changed:
basic/runtime/org.argeo.basic.nodeps/src/main/java/org/argeo/StreamUtils.java
basic/runtime/org.argeo.basic.nodeps/src/main/java/org/argeo/util/crypto/PasswordBasedEncryption.java
basic/runtime/org.argeo.basic.nodeps/src/main/java/org/argeo/util/security/DigestUtils.java [new file with mode: 0644]
demo/log4j.properties
security/modules/org.argeo.security.dao.ldap/META-INF/spring/security-ldap-services.xml
security/plugins/org.argeo.security.ui.rap/branding/afterLogout.html [new file with mode: 0644]
security/plugins/org.argeo.security.ui.rap/branding/default.htm [deleted file]
security/plugins/org.argeo.security.ui.rap/branding/empty.html [new file with mode: 0644]
security/plugins/org.argeo.security.ui.rap/branding/login.html [new file with mode: 0644]
security/plugins/org.argeo.security.ui.rap/plugin.xml
security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/LogoutEntryPoint.java [new file with mode: 0644]
security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/NullEntryPoint.java [new file with mode: 0644]
security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/RapActionBarAdvisor.java
security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/SecureEntryPoint.java
security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/AbstractSystemExecution.java
security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/rememberme/JcrPersistentTokenRepository.java [new file with mode: 0644]
server/modules/org.argeo.jackrabbit.webapp/WEB-INF/security-filters.xml
server/modules/org.argeo.jackrabbit.webapp/WEB-INF/security.xml [deleted file]
server/modules/org.argeo.jackrabbit.webapp/WEB-INF/web.xml
server/modules/org.argeo.jackrabbit.webapp/jackrabbit-webapp.properties
server/modules/org.argeo.server.rap.webapp/WEB-INF/applicationContext.xml
server/modules/org.argeo.server.rap.webapp/WEB-INF/osgi.xml
server/modules/org.argeo.server.rap.webapp/WEB-INF/security-filters.xml [new file with mode: 0644]
server/modules/org.argeo.server.rap.webapp/WEB-INF/security.xml [deleted file]
server/modules/org.argeo.server.rap.webapp/WEB-INF/web.xml
server/modules/org.argeo.server.rap.webapp/pom.xml
server/modules/org.argeo.server.rap.webapp/rap-webapp.properties [new file with mode: 0644]

index d2ccdb737af9e2bad7e00907bd871f118e7a9449..c94950d194cd97bb6a5f2bb865890cd4a2c63fa6 100644 (file)
@@ -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
+        *            <code>true</code> converts to lowercase, <code>false</code> 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;
        }
 
 }
index ab36e5d8f00e02937a6cd0d396037bf846798f88..18cccd4e1271bcf964ebc45d0d8e90ccc96a0c66 100644 (file)
@@ -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 (file)
index 0000000..34dd870
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2010 Mathieu Baudier <mbaudier@argeo.org>
+ *
+ * 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: <file> [<algorithm>]"
+                                       + " (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));
+               }
+       }
+
+}
index 06e71583d3e3b963198568bec129773eff71ec97..5be6d57f1cca36dc29c5757d01076bfa1ca97d19 100644 (file)
@@ -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
index e0851123dfa0dc465b04a93e27b1ce246797d164..525b84db0fa504b25c24fda6e2008b493a190ce5 100644 (file)
        <bean id="authenticationManager" class="org.springframework.security.providers.ProviderManager">
                <property name="providers">
                        <list>
-                               <ref bean="anonymousAuthenticationProvider" />
                                <ref bean="authByAdapterProvider" />
+<!--                           <ref bean="preAuthAuthenticationProvider" /> -->
+                               <ref bean="anonymousAuthenticationProvider" />
+                               <ref bean="rememberMeAuthenticationProvider" />
                                <ref bean="ldapAuthenticationProvider" />
                        </list>
                </property>
                <property name="key" value="${argeo.security.systemKey}" />
        </bean>
 
+<!--   <bean id="preAuthAuthenticationProvider" -->
+<!--           class="org.springframework.security.providers.preauth.PreAuthenticatedAuthenticationProvider"> -->
+<!--           <description><![CDATA[Pre-authentication]]></description> -->
+<!--   </bean> -->
+
        <bean id="anonymousAuthenticationProvider"
                class="org.springframework.security.providers.anonymous.AnonymousAuthenticationProvider">
                <description><![CDATA[Anonymous authentication]]></description>
                <property name="key" value="${argeo.security.systemKey}" />
        </bean>
 
+       <bean id="rememberMeAuthenticationProvider"
+               class="org.springframework.security.providers.rememberme.RememberMeAuthenticationProvider">
+               <description><![CDATA[Remember me authentication]]></description>
+               <property name="key" value="${argeo.security.systemKey}" />
+       </bean>
+
        <!-- Internal authentication, used by during the general authentication 
                initialization himself, in order to prevent the following dependency cycle: 
                Repository.login() <= AuthenticationManager <= LdapAuthenticationProvider 
diff --git a/security/plugins/org.argeo.security.ui.rap/branding/afterLogout.html b/security/plugins/org.argeo.security.ui.rap/branding/afterLogout.html
new file mode 100644 (file)
index 0000000..ae0901b
--- /dev/null
@@ -0,0 +1,18 @@
+<html>
+<head></head>
+<body>
+<center>
+<table height="100%">
+<tr>
+       <td style="vertical-align:middle">
+               <a 
+                       style="font-family:sans-serif;color:#0066CC;text-decoration:none;" 
+                       href="node" 
+                       title="Click to log in"
+               >Login...</a>
+       </td>
+</tr>
+</table>
+</center>
+</body>
+</html>
\ No newline at end of file
diff --git a/security/plugins/org.argeo.security.ui.rap/branding/default.htm b/security/plugins/org.argeo.security.ui.rap/branding/default.htm
deleted file mode 100644 (file)
index 6de7eb2..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-<html>
-<head></head>
-<body>
-<center>
-<table height="100%">
-<tr>
-       <td style="vertical-align:middle">
-               <a 
-                       style="font-family:sans-serif;color:#0066CC;text-decoration:none;" 
-                       href="javascript:location.reload(true);" 
-                       title="Click to log in"
-               >Login...</a>
-       </td>
-</tr>
-</table>
-</center>
-</body>
-</html>
\ No newline at end of file
diff --git a/security/plugins/org.argeo.security.ui.rap/branding/empty.html b/security/plugins/org.argeo.security.ui.rap/branding/empty.html
new file mode 100644 (file)
index 0000000..94fe28a
--- /dev/null
@@ -0,0 +1,5 @@
+<html>
+<head></head>
+<body>
+</body>
+</html>
\ No newline at end of file
diff --git a/security/plugins/org.argeo.security.ui.rap/branding/login.html b/security/plugins/org.argeo.security.ui.rap/branding/login.html
new file mode 100644 (file)
index 0000000..6de7eb2
--- /dev/null
@@ -0,0 +1,18 @@
+<html>
+<head></head>
+<body>
+<center>
+<table height="100%">
+<tr>
+       <td style="vertical-align:middle">
+               <a 
+                       style="font-family:sans-serif;color:#0066CC;text-decoration:none;" 
+                       href="javascript:location.reload(true);" 
+                       title="Click to log in"
+               >Login...</a>
+       </td>
+</tr>
+</table>
+</center>
+</body>
+</html>
\ No newline at end of file
index cd8b601d85f306effbe4199e44c0db96814d740d..27d151d6f41d9345e8b80f773da91e4af52b6d1c 100644 (file)
             class="org.argeo.security.ui.rap.AnonymousEntryPoint"
             parameter="publicWebUi">
       </entrypoint>
+      <entrypoint
+            id="org.argeo.security.ui.rap.logoutEntryPoint"
+            class="org.argeo.security.ui.rap.LogoutEntryPoint"
+            parameter="logout">
+      </entrypoint>
+      <entrypoint
+            id="org.argeo.security.ui.rap.nullEntryPoint"
+            class="org.argeo.security.ui.rap.NullEntryPoint"
+            parameter="null">
+      </entrypoint>
    </extension>
 
        <extension
             defaultEntrypointId="org.argeo.security.ui.rap.secureEntryPoint"
             title="Argeo Web UI"
             favicon="branding/favicon.ico"
-            body="branding/public.html">
+            body="branding/login.html">
+       </branding>
+       <branding
+                       id="org.argeo.security.ui.rap.branding"
+            servletName="ui"
+            defaultEntrypointId="org.argeo.security.ui.rap.secureEntryPoint"
+            title="Argeo Web UI"
+            favicon="branding/favicon.ico"
+            body="branding/login.html">
+       </branding>
+       <branding
+                       id="org.argeo.security.ui.rap.branding"
+            servletName="basicauth"
+            defaultEntrypointId="org.argeo.security.ui.rap.secureEntryPoint"
+            title="Argeo Web UI"
+            favicon="branding/favicon.ico"
+            body="branding/login.html">
        </branding>
        <branding
                        id="org.argeo.security.ui.rap.branding"
             favicon="branding/favicon.ico"
             body="branding/public.html">
        </branding>
+       <branding
+                       id="org.argeo.security.ui.rap.branding"
+            servletName="logout"
+            defaultEntrypointId="org.argeo.security.ui.rap.logoutEntryPoint"
+            title="Argeo Logout"
+            favicon="branding/favicon.ico"
+            body="branding/afterLogout.html">
+       </branding>
+       <!-- we need a servlet with thios name j_spring_security_logout for the logout filter -->
+       <branding
+                       id="org.argeo.security.ui.rap.branding"
+            servletName="j_spring_security_logout"
+            defaultEntrypointId="org.argeo.security.ui.rap.nullEntryPoint"
+            title="Argeo Logout"
+            favicon="branding/favicon.ico"
+            body="branding/empty.html">
+       </branding>
        </extension>
 
    <extension
diff --git a/security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/LogoutEntryPoint.java b/security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/LogoutEntryPoint.java
new file mode 100644 (file)
index 0000000..7f32a4e
--- /dev/null
@@ -0,0 +1,47 @@
+package org.argeo.security.ui.rap;
+
+import javax.security.auth.login.LoginException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.eclipse.equinox.security.auth.ILoginContext;
+import org.eclipse.rwt.RWT;
+import org.eclipse.rwt.lifecycle.IEntryPoint;
+import org.eclipse.ui.PlatformUI;
+import org.springframework.security.context.SecurityContextHolder;
+
+/**
+ * RAP entry point which logs out the currently authenticated user
+ */
+public class LogoutEntryPoint implements IEntryPoint {
+       private final static Log log = LogFactory.getLog(LogoutEntryPoint.class);
+
+       /**
+        * From org.springframework.security.context.
+        * HttpSessionContextIntegrationFilter
+        */
+       protected static final String SPRING_SECURITY_CONTEXT_KEY = "SPRING_SECURITY_CONTEXT";
+
+       @Override
+       public int createUI() {
+               // create display
+               PlatformUI.createDisplay();
+
+               final ILoginContext loginContext = SecureRapActivator
+                               .createLoginContext(SecureRapActivator.CONTEXT_SPRING);
+               try {
+                       loginContext.logout();
+               } catch (LoginException e) {
+                       e.printStackTrace();
+               }
+
+               RWT.getRequest().getSession()
+                               .removeAttribute(SPRING_SECURITY_CONTEXT_KEY);
+               SecurityContextHolder.clearContext();
+               RWT.getRequest().getSession().setMaxInactiveInterval(1);
+
+               if (log.isDebugEnabled())
+                       log.debug("Logged out session " + RWT.getSessionStore().getId());
+               return 0;
+       }
+}
diff --git a/security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/NullEntryPoint.java b/security/plugins/org.argeo.security.ui.rap/src/main/java/org/argeo/security/ui/rap/NullEntryPoint.java
new file mode 100644 (file)
index 0000000..7db5c56
--- /dev/null
@@ -0,0 +1,16 @@
+package org.argeo.security.ui.rap;
+
+import org.eclipse.rwt.lifecycle.IEntryPoint;
+import org.eclipse.ui.PlatformUI;
+
+/**
+ * RAP entry point which does doesing except creating the display
+ */
+public class NullEntryPoint implements IEntryPoint {
+       @Override
+       public int createUI() {
+               // create display
+               PlatformUI.createDisplay();
+               return 0;
+       }
+}
index f67ec0c07f099279a379ee9657260ac43d790232..8cb122d831f8e89df5fcc4e5d9a35898f84d4c80 100644 (file)
@@ -1,7 +1,6 @@
 package org.argeo.security.ui.rap;
 
 import org.argeo.security.ui.commands.OpenHomePerspective;
-import org.argeo.security.ui.rap.commands.UserMenu;
 import org.eclipse.core.commands.Category;
 import org.eclipse.core.commands.Command;
 import org.eclipse.jface.action.ICoolBarManager;
index e2febf0aeb9033ce74f09b33e8f1f1cf53cd8e6d..dced692949d414a7c9467d99eb234c4bcac45d28 100644 (file)
@@ -4,6 +4,8 @@ import java.security.PrivilegedAction;
 
 import javax.security.auth.Subject;
 import javax.security.auth.login.LoginException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -16,6 +18,8 @@ import org.eclipse.rwt.lifecycle.IEntryPoint;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.ui.PlatformUI;
 import org.springframework.security.BadCredentialsException;
+import org.springframework.security.context.SecurityContext;
+import org.springframework.security.context.SecurityContextHolder;
 
 /**
  * RAP entry point with login capabilities. Once the user has been
@@ -25,6 +29,12 @@ import org.springframework.security.BadCredentialsException;
 public class SecureEntryPoint implements IEntryPoint {
        private final static Log log = LogFactory.getLog(SecureEntryPoint.class);
 
+       /**
+        * From org.springframework.security.context.
+        * HttpSessionContextIntegrationFilter
+        */
+       protected static final String SPRING_SECURITY_CONTEXT_KEY = "SPRING_SECURITY_CONTEXT";
+
        /**
         * How many seconds to wait before invalidating the session if the user has
         * not yet logged in.
@@ -40,9 +50,18 @@ public class SecureEntryPoint implements IEntryPoint {
                // around too long
                RWT.getRequest().getSession().setMaxInactiveInterval(loginTimeout);
 
+               HttpServletRequest httpRequest = RWT.getRequest();
+               HttpSession httpSession = httpRequest.getSession();
+               Object contextFromSessionObject = httpSession
+                               .getAttribute(SPRING_SECURITY_CONTEXT_KEY);
+               if (contextFromSessionObject != null)
+                       SecurityContextHolder
+                                       .setContext((SecurityContext) contextFromSessionObject);
+
                if (log.isDebugEnabled())
                        log.debug("THREAD=" + Thread.currentThread().getId()
-                                       + ", sessionStore=" + RWT.getSessionStore().getId());
+                                       + ", sessionStore=" + RWT.getSessionStore().getId()
+                                       + ", remote user=" + httpRequest.getRemoteUser());
 
                // create display
                final Display display = PlatformUI.createDisplay();
@@ -55,6 +74,17 @@ public class SecureEntryPoint implements IEntryPoint {
                        try {
                                loginContext.login();
                                subject = loginContext.getSubject();
+
+                               if (httpSession.getAttribute(SPRING_SECURITY_CONTEXT_KEY) == null)
+                                       httpSession.setAttribute(SPRING_SECURITY_CONTEXT_KEY,
+                                                       SecurityContextHolder.getContext());
+
+                               // Once the user is logged in, she can have a longer session
+                               // timeout
+                               RWT.getRequest().getSession()
+                                               .setMaxInactiveInterval(sessionTimeout);
+                               if (log.isDebugEnabled())
+                                       log.debug("Authenticated " + subject);
                        } catch (LoginException e) {
                                BadCredentialsException bce = wasCausedByBadCredentials(e);
                                if (bce != null) {
@@ -67,11 +97,6 @@ public class SecureEntryPoint implements IEntryPoint {
                        }
                }
 
-               // Once the user is logged in, she can have a longer session timeout
-               RWT.getRequest().getSession().setMaxInactiveInterval(sessionTimeout);
-               if (log.isDebugEnabled())
-                       log.debug("Authenticated " + subject);
-
                final String username = subject.getPrincipals().iterator().next()
                                .getName();
                // Logout callback when the display is disposed
index 856ceee60e0a0e55fb672f781e21adbac2936595..b12629ab90ca64313ecb72317fae5408e5dd8296 100644 (file)
@@ -14,13 +14,22 @@ import org.springframework.security.context.SecurityContextHolder;
 
 /** Provides base method for executing code with system authorization. */
 public abstract class AbstractSystemExecution {
+       static {
+               // Forces Spring Security to use inheritable strategy
+               // FIXME find a better place for forcing spring security mode
+               // doesn't work for the time besing
+//             if (System.getProperty(SecurityContextHolder.SYSTEM_PROPERTY) == null)
+//                     SecurityContextHolder
+//                                     .setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
+       }
+
        private final static Log log = LogFactory
                        .getLog(AbstractSystemExecution.class);
        private AuthenticationManager authenticationManager;
        private String systemAuthenticationKey;
 
        /** Whether the current thread was authenticated by this component. */
-       private ThreadLocal<Boolean> authenticatedBySelf = new ThreadLocal<Boolean>() {
+       private InheritableThreadLocal<Boolean> authenticatedBySelf = new InheritableThreadLocal<Boolean>() {
                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 (file)
index 0000000..598c2dd
--- /dev/null
@@ -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
+
+       }
+
+}
index d7613a0ff0cd3f406df63495e12b8e054cb16708..f12f0c8048e5f62630a7d0d2cbfa144592450372 100644 (file)
@@ -8,41 +8,49 @@
 
        <bean id="springSecurityFilterChain" class="org.springframework.security.util.FilterChainProxy">
                <sec:filter-chain-map path-type="ant">
-                       <sec:filter-chain pattern="/**"
-                               filters="httpSessionContextIntegrationFilter,logoutFilter,basicProcessingFilter,anonymousProcessingFilter,securityContextHolderAwareRequestFilter,exceptionTranslationFilter,filterInvocationInterceptor" />
+                       <sec:filter-chain pattern="/webdav/**"
+                               filters="session,basic,rememberMe,anonymous,exception,interceptor" />
+                       <sec:filter-chain pattern="/remoting/**"
+                               filters="session,basic,rememberMe,anonymous,exception,interceptor" />
+                       <sec:filter-chain pattern="/public/**"
+                               filters="session,anonymous,exception,interceptorPublic" />
+                       <sec:filter-chain pattern="/pub/**"
+                               filters="session,anonymous,exception,interceptorPublic" />
+                       <sec:filter-chain pattern="/j_spring_security_logout"
+                               filters="session,logout,exception" />
                </sec:filter-chain-map>
        </bean>
 
        <!-- The actual authorization checks (called last, but first here for ease 
                of configuration) -->
-       <bean id="filterInvocationInterceptor" parent="filterInvocationInterceptorTemplate">
+       <bean id="interceptor" parent="filterInvocationInterceptorTemplate">
+               <property name="objectDefinitionSource">
+                       <value>
+                               PATTERN_TYPE_APACHE_ANT
+                               /*/*/*/**=ROLE_USER,ROLE_ADMIN
+                               /**=ROLE_ANONYMOUS
+                       </value>
+               </property>
+       </bean>
+       <bean id="interceptorPublic" parent="filterInvocationInterceptorTemplate">
                <property name="objectDefinitionSource">
                        <value>
-                               CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
                                PATTERN_TYPE_APACHE_ANT
-                               /public/**=IS_AUTHENTICATED_ANONYMOUSLY
-                               /*/*/*/**=ROLE_USER
                                /**=IS_AUTHENTICATED_ANONYMOUSLY
                        </value>
-                       <!-- <value> -->
-                       <!-- CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON -->
-                       <!-- PATTERN_TYPE_APACHE_ANT -->
-                       <!-- /config/**=ROLE_ADMINISTRATOR -->
-                       <!-- /**=IS_AUTHENTICATED_ANONYMOUSLY -->
-                       <!-- </value> -->
                </property>
        </bean>
 
        <!-- Integrates the authentication information in the http sessions -->
-       <bean id="httpSessionContextIntegrationFilter"
+       <bean id="session"
                class="org.springframework.security.context.HttpSessionContextIntegrationFilter">
-               <property name="allowSessionCreation" value="true" />
+               <property name="allowSessionCreation" value="false" />
        </bean>
 
        <!-- Processes logouts, removing both session informations and the remember-me 
                cookie from the browser -->
-       <bean id="logoutFilter" class="org.springframework.security.ui.logout.LogoutFilter">
-               <constructor-arg value="/web/" />
+       <bean id="logout" class="org.springframework.security.ui.logout.LogoutFilter">
+               <constructor-arg value="/webdav/node/main" />
                <!-- URL redirected to after logout -->
                <constructor-arg>
                        <list>
                </constructor-arg>
        </bean>
 
-       <!-- Double check, this may not be necessary -->
-       <bean id="securityContextHolderAwareRequestFilter"
-               class="org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter" />
-
        <!-- Use the remember me cookie to authenticate -->
-       <bean id="rememberMeProcessingFilter"
+       <bean id="rememberMe"
                class="org.springframework.security.ui.rememberme.RememberMeProcessingFilter">
                <property name="authenticationManager" ref="authenticationManager" />
                <property name="rememberMeServices" ref="rememberMeServices" />
                class="org.springframework.security.ui.rememberme.TokenBasedRememberMeServices">
                <property name="userDetailsService" ref="userDetailsService" />
                <property name="key" value="${argeo.security.systemKey}" />
+               <property name="tokenValiditySeconds" value="${argeo.jcr.webapp.rememberMeValidity}" />
+               <property name="alwaysRemember" value="true" />
        </bean>
 
        <!-- Basic authentication -->
-       <bean id="basicProcessingFilter"
+       <bean id="basic"
                class="org.springframework.security.ui.basicauth.BasicProcessingFilter">
                <property name="authenticationManager">
                        <ref bean="authenticationManager" />
        </bean>
 
        <!-- If everything else failed, anonymous authentication -->
-       <bean id="anonymousProcessingFilter"
+       <bean id="anonymous"
                class="org.springframework.security.providers.anonymous.AnonymousProcessingFilter">
                <property name="key" value="${argeo.security.systemKey}" />
                <property name="userAttribute" value="anonymous,ROLE_ANONYMOUS" />
        </bean>
 
        <!-- Reacts to security related exceptions -->
-       <bean id="exceptionTranslationFilter"
+       <bean id="exception"
                class="org.springframework.security.ui.ExceptionTranslationFilter">
                <property name="authenticationEntryPoint">
                        <ref bean="basicProcessingFilterEntryPoint" />
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 (file)
index 2627b75..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<beans xmlns="http://www.springframework.org/schema/beans"
-       xmlns:security="http://www.springframework.org/schema/security"
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-       xsi:schemaLocation="
-       http://www.springframework.org/schema/beans 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.4.xsd">
-
-       <security:http>
-               <security:intercept-url pattern="/remoting/*"
-                       access="ROLE_USER,ROLE_ADMIN,ROLE_ANONYMOUS" />
-               <security:intercept-url pattern="/*/node/*/**"
-                       access="ROLE_USER,ROLE_ADMIN" />
-               <security:intercept-url pattern="/**"
-                       access="ROLE_USER,ROLE_ADMIN,ROLE_ANONYMOUS" />
-               <security:http-basic />
-               <security:anonymous username="anonymous" />
-       </security:http>
-
-       <!-- LDAP -->
-       <security:ldap-authentication-provider
-               user-dn-pattern="uid={0},ou=People" group-search-base="ou=Roles"
-               group-search-filter="(member={0})" user-details-class="inetOrgPerson" />
-       <!-- <security:password-compare> -->
-       <!-- <security:password-encoder hash="{ssha}" > -->
-       <!-- <security:salt-source system-wide="test"/> -->
-       <!-- </security:password-encoder> -->
-       <!-- </security:password-compare> -->
-       <!-- </security:ldap-authentication-provider> -->
-       <security:ldap-server url="ldap://localhost:10389/dc=demo,dc=argeo,dc=org" />
-
-</beans>
\ No newline at end of file
index e24236b4125a15d351c0c46b0c3043a179867030..9e0a7528086ac9e9e4c1cf8d0a6058b41a8d9e04 100644 (file)
                <url-pattern>/remoting/*</url-pattern>
        </servlet-mapping>
 
+       <servlet-mapping>
+               <servlet-name>remoting</servlet-name>
+               <url-pattern>/pub/*</url-pattern>
+       </servlet-mapping>
+
        <!-- XML remoting
        <servlet>
                <servlet-name>xmlremoting</servlet-name>
@@ -78,7 +83,7 @@
 
        <servlet-mapping>
                <servlet-name>webdav</servlet-name>
-               <url-pattern>/public/webdav/*</url-pattern>
+               <url-pattern>/public/*</url-pattern>
        </servlet-mapping>
 
        <!-- JCR-MANAGER servlet
index ae7aa87255c7fa30407b417fa351fb241cd1384c..f0125853ea3c61f2e96418226c3b7498ae8155c1 100644 (file)
@@ -1 +1,3 @@
 argeo.security.systemKey=argeo
+
+argeo.jcr.webapp.rememberMeValidity=3600
\ No newline at end of file
index febc4b97cfea863a01633e5e99eba9067c52eca1..33686245396877c0ca516fca6fcc26a8981db049 100644 (file)
@@ -6,6 +6,15 @@
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
 
        <import resource="osgi.xml" />
-<!--   <import resource="security.xml" />-->
+       <import resource="security-filters.xml" />
+       <!-- <import resource="security.xml" /> -->
+
+       <bean
+               class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+               <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
+               <property name="locations">
+                       <value>osgibundle:rap-webapp.properties</value>
+               </property>
+       </bean>
 
 </beans>
\ No newline at end of file
index d344cc8d618affa80a631e94f539dc499d1fb421..cb31230ef39e510f502bb15443c9862b53b52c8a 100644 (file)
@@ -8,6 +8,8 @@
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd\r
        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd">\r
 \r
-       <reference id="_authenticationManager"\r
+       <reference id="authenticationManager"\r
                interface="org.springframework.security.AuthenticationManager" />\r
+       <reference id="userDetailsService"\r
+               interface="org.springframework.security.userdetails.UserDetailsService" />\r
 </beans:beans>
\ 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 (file)
index 0000000..3a6650c
--- /dev/null
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:sec="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:aop="http://www.springframework.org/schema/aop"
+       xsi:schemaLocation="
+       http://www.springframework.org/schema/beans 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.4.xsd">
+
+       <bean id="springSecurityFilterChain" class="org.springframework.security.util.FilterChainProxy">
+               <sec:filter-chain-map path-type="ant">
+                       <sec:filter-chain pattern="/ui"
+                               filters="session,basic,rememberMe,exception,interceptor" />
+                       <sec:filter-chain pattern="/basicauth"
+                               filters="session,basic,exception,interceptor" />
+                       <sec:filter-chain pattern="/node" filters="session" />
+                       <sec:filter-chain pattern="/public"
+                               filters="session,anonymous,exception,interceptorPublic" />
+                       <sec:filter-chain pattern="/j_spring_security_logout"
+                               filters="session,logout,exception" />
+               </sec:filter-chain-map>
+       </bean>
+
+       <!-- The actual authorization checks (called last, but first here for ease 
+               of configuration) -->
+       <bean id="interceptor" parent="filterInvocationInterceptorTemplate">
+               <property name="objectDefinitionSource">
+                       <value>
+                               PATTERN_TYPE_APACHE_ANT
+                               /**=ROLE_USER,ROLE_ADMIN
+                       </value>
+               </property>
+       </bean>
+       <bean id="interceptorPublic" parent="filterInvocationInterceptorTemplate">
+               <property name="objectDefinitionSource">
+                       <value>
+                               PATTERN_TYPE_APACHE_ANT
+                               /**=IS_AUTHENTICATED_ANONYMOUSLY
+                       </value>
+               </property>
+       </bean>
+
+       <!-- Integrates the authentication information in the http sessions -->
+       <bean id="session"
+               class="org.springframework.security.context.HttpSessionContextIntegrationFilter">
+               <property name="allowSessionCreation" value="true" />
+       </bean>
+
+       <!-- Processes logouts, removing both session informations and the remember-me 
+               cookie from the browser -->
+       <bean id="logout" class="org.springframework.security.ui.logout.LogoutFilter">
+               <constructor-arg value="/logout" />
+               <constructor-arg>
+                       <list>
+                               <ref bean="rememberMeServices" />
+                               <bean
+                                       class="org.springframework.security.ui.logout.SecurityContextLogoutHandler" />
+                       </list>
+               </constructor-arg>
+       </bean>
+
+       <!-- Use the remember me cookie to authenticate -->
+       <bean id="rememberMe"
+               class="org.springframework.security.ui.rememberme.RememberMeProcessingFilter">
+               <property name="authenticationManager" ref="authenticationManager" />
+               <property name="rememberMeServices" ref="rememberMeServices" />
+       </bean>
+
+       <bean id="rememberMeServices"
+               class="org.springframework.security.ui.rememberme.TokenBasedRememberMeServices">
+               <property name="userDetailsService" ref="userDetailsService" />
+               <property name="key" value="${argeo.security.systemKey}" />
+               <property name="alwaysRemember" value="true" />
+       </bean>
+
+       <!-- Basic authentication -->
+       <bean id="basic"
+               class="org.springframework.security.ui.basicauth.BasicProcessingFilter">
+               <property name="authenticationManager">
+                       <ref bean="authenticationManager" />
+               </property>
+               <property name="authenticationEntryPoint">
+                       <ref local="basicProcessingFilterEntryPoint" />
+               </property>
+               <property name="rememberMeServices" ref="rememberMeServices" />
+       </bean>
+
+       <!-- Activate basic auth when needed -->
+       <bean id="basicProcessingFilterEntryPoint"
+               class="org.springframework.security.ui.basicauth.BasicProcessingFilterEntryPoint">
+               <property name="realmName">
+                       <value>Argeo</value>
+               </property>
+       </bean>
+
+       <!-- If everything else failed, anonymous authentication -->
+       <bean id="anonymous"
+               class="org.springframework.security.providers.anonymous.AnonymousProcessingFilter">
+               <property name="key" value="${argeo.security.systemKey}" />
+               <property name="userAttribute" value="anonymous,ROLE_ANONYMOUS" />
+       </bean>
+
+       <!-- Reacts to security related exceptions -->
+       <bean id="exception"
+               class="org.springframework.security.ui.ExceptionTranslationFilter">
+               <property name="authenticationEntryPoint">
+                       <ref bean="basicProcessingFilterEntryPoint" />
+               </property>
+               <property name="accessDeniedHandler">
+                       <bean class="org.springframework.security.ui.AccessDeniedHandlerImpl">
+                               <property name="errorPage" value="/error" />
+                       </bean>
+               </property>
+       </bean>
+
+       <!-- Template for authorization checks -->
+       <bean id="filterInvocationInterceptorTemplate" abstract="true"
+               class="org.springframework.security.intercept.web.FilterSecurityInterceptor">
+               <property name="authenticationManager" ref="authenticationManager" />
+               <property name="accessDecisionManager">
+                       <bean class="org.springframework.security.vote.AffirmativeBased">
+                               <property name="allowIfAllAbstainDecisions" value="false" />
+                               <property name="decisionVoters">
+                                       <list>
+                                               <bean class="org.springframework.security.vote.RoleVoter" />
+                                               <bean class="org.springframework.security.vote.AuthenticatedVoter" />
+                                       </list>
+                               </property>
+                       </bean>
+               </property>
+       </bean>
+</beans>
\ 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 (file)
index d606d03..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<beans xmlns="http://www.springframework.org/schema/beans"
-       xmlns:security="http://www.springframework.org/schema/security"
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-       xsi:schemaLocation="
-       http://www.springframework.org/schema/beans 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.4.xsd">
-
-       <security:http>
-               <security:form-login login-page="/rap?startup=secureWebUi" />
-               <security:intercept-url pattern="/**"
-                       access="ROLE_USER,ROLE_ADMIN,ROLE_ANONYMOUS" />
-       </security:http>
-</beans>
\ No newline at end of file
index 16388e44923581cad2ad3b756a02d7bc4bc0456a..4a13fe55605542f5c495483a8c5bd8dbd8a15b81 100644 (file)
        </servlet-mapping>
 
        <!-- Security -->
-<!--   <filter>-->
-<!--           <filter-name>springSecurityFilterChain</filter-name>-->
-<!--           <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>-->
-<!--   </filter>-->
+       <filter>
+               <filter-name>springSecurityFilterChain</filter-name>
+               <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
+       </filter>
 <!---->
-<!--   <filter-mapping>-->
-<!--           <filter-name>springSecurityFilterChain</filter-name>-->
-<!--           <url-pattern>/*</url-pattern>-->
-<!--   </filter-mapping>-->
+       <filter-mapping>
+               <filter-name>springSecurityFilterChain</filter-name>
+               <url-pattern>/ui</url-pattern>
+       </filter-mapping>
+       <filter-mapping>
+               <filter-name>springSecurityFilterChain</filter-name>
+               <url-pattern>/basicauth</url-pattern>
+       </filter-mapping>
+       <filter-mapping>
+               <filter-name>springSecurityFilterChain</filter-name>
+               <url-pattern>/none</url-pattern>
+       </filter-mapping>
+       <filter-mapping>
+               <filter-name>springSecurityFilterChain</filter-name>
+               <url-pattern>/public</url-pattern>
+       </filter-mapping>
 
 </web-app>
index ee05364971bfea34b22e10629cde8912844ae51e..fcff90860a977d9ec3b116c0750b80395542a0eb 100644 (file)
                                                <Web-ContextPath>org.argeo.rap.webapp</Web-ContextPath>
                                                <Import-Package>
                                                        *,
+
+                                                       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
-                                               </Import-Package>
+                                                       org.springframework.web.servlet.mvc,
+                                                       
+                                                       
+                                               </Import-Package>
                                        </instructions>
                                </configuration>
                        </plugin>
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 (file)
index 0000000..ae7aa87
--- /dev/null
@@ -0,0 +1 @@
+argeo.security.systemKey=argeo