New project conventions
authorMathieu Baudier <mbaudier@argeo.org>
Tue, 25 Nov 2014 13:10:18 +0000 (13:10 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Tue, 25 Nov 2014 13:10:18 +0000 (13:10 +0000)
git-svn-id: https://svn.argeo.org/commons/trunk@7530 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

76 files changed:
org.argeo.security.core/.classpath
org.argeo.security.core/bnd.bnd [new file with mode: 0644]
org.argeo.security.core/build.properties
org.argeo.security.core/ext/test/log4j.properties [new file with mode: 0644]
org.argeo.security.core/ext/test/org/argeo/security/PasswordSandbox.java [new file with mode: 0644]
org.argeo.security.core/ext/test/org/argeo/security/crypto/PasswordBasedEncryptionTest.java [new file with mode: 0644]
org.argeo.security.core/src/main/java/org/argeo/security/NodeAuthenticationToken.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/OsAuthenticationToken.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/SecurityUtils.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/SystemAuthentication.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/SystemExecutionService.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/UserAdminService.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/core/AbstractSystemExecution.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/core/AsyncSystemTaskExecutor.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/core/AuthenticatedApplicationContextInitialization.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/core/AuthenticationProvidersRegister.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/core/ConsoleCallbackHandler.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/core/InternalAuthentication.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/core/KeyBasedSystemExecutionService.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/core/MatchingAuthenticationProvider.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/core/OsAuthenticationProvider.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/core/OsgiModuleLabel.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/core/SimpleRoleRegistration.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/crypto/AbstractKeyring.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/crypto/CryptoKeyring.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/crypto/KeyringLoginModule.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/crypto/PBEKeySpecCallback.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/crypto/PasswordBasedEncryption.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrKeyring.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrSecurityModel.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrUserDetails.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/jcr/OsJcrAuthenticationProvider.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/jcr/OsJcrUserAdminService.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/jcr/RemoteJcrAuthenticationProvider.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/jcr/RemoteJcrRepositoryWrapper.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/jcr/SecureThreadBoundSession.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/jcr/SimpleJcrSecurityModel.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/jcr/rememberme/JcrPersistentTokenRepository.java [deleted file]
org.argeo.security.core/src/main/java/org/argeo/security/log4j/SecureLogger.java [deleted file]
org.argeo.security.core/src/org/argeo/security/NodeAuthenticationToken.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/OsAuthenticationToken.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/SecurityUtils.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/SystemAuthentication.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/SystemExecutionService.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/UserAdminService.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/core/AbstractSystemExecution.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/core/AsyncSystemTaskExecutor.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/core/AuthenticatedApplicationContextInitialization.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/core/AuthenticationProvidersRegister.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/core/ConsoleCallbackHandler.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/core/InternalAuthentication.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/core/KeyBasedSystemExecutionService.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/core/MatchingAuthenticationProvider.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/core/OsAuthenticationProvider.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/core/OsgiModuleLabel.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/core/SimpleRoleRegistration.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/crypto/AbstractKeyring.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/crypto/CryptoKeyring.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/crypto/KeyringLoginModule.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/crypto/PBEKeySpecCallback.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/crypto/PasswordBasedEncryption.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/jcr/JcrKeyring.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/jcr/JcrSecurityModel.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/jcr/JcrUserDetails.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/jcr/OsJcrAuthenticationProvider.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/jcr/OsJcrUserAdminService.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/jcr/RemoteJcrAuthenticationProvider.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/jcr/RemoteJcrRepositoryWrapper.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/jcr/SecureThreadBoundSession.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/jcr/SimpleJcrSecurityModel.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/jcr/rememberme/JcrPersistentTokenRepository.java [new file with mode: 0644]
org.argeo.security.core/src/org/argeo/security/log4j/SecureLogger.java [new file with mode: 0644]
org.argeo.security.core/src/test/java/org/argeo/security/PasswordSandbox.java [deleted file]
org.argeo.security.core/src/test/java/org/argeo/security/crypto/PasswordBasedEncryptionTest.java [deleted file]
org.argeo.security.core/src/test/resources/log4j.properties [deleted file]
org.argeo.security.core/src/test/resources/org/argeo/security/json/gandalf2.json [deleted file]

index b639b537ce71a6eb713cbcf7b5a6df2c86d477e2..91d2c42e58d8e05a91c95579ff06f2e61c1384e9 100644 (file)
@@ -1,10 +1,10 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
-       <classpathentry kind="src" output="target/classes" path="src/main/java"/>
-       <classpathentry kind="src" output="target/classes" path="src/main/resources"/>
-       <classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
-       <classpathentry kind="src" output="target/test-classes" path="src/test/resources"/>
-       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>>>
-       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
-       <classpathentry kind="output" path="bin"/>
+       <classpathentry kind="src" path="src" />
+       <classpathentry kind="src" path="ext/test" />
+       <classpathentry kind="con"
+               path="org.eclipse.pde.core.requiredPlugins" />
+       <classpathentry kind="con"
+               path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6" />
+       <classpathentry kind="output" path="bin" />
 </classpath>
diff --git a/org.argeo.security.core/bnd.bnd b/org.argeo.security.core/bnd.bnd
new file mode 100644 (file)
index 0000000..2bbd9c3
--- /dev/null
@@ -0,0 +1,4 @@
+Bundle-ActivationPolicy: lazy
+Import-Package:org.bouncycastle.*;resolution:=optional,\
+javax.jcr.security,\
+*
index 788870d9887d430ea38c38645987e478e1bcbbea..c988eeb5db5dea3582d9474f22569b49d7ccf1cc 100644 (file)
@@ -7,7 +7,6 @@ additional.bundles = org.springframework.transaction,\
                      org.apache.log4j,\
                      slf4j.api,\
                      slf4j.org.apache.commons.logging
-source.. = src/main/java/,\
-           src/main/resources/,\
-           src/test/java/,\
-           src/test/resources/
+source.. = src/,\
+           ext/test/
+bin.. = bin/
diff --git a/org.argeo.security.core/ext/test/log4j.properties b/org.argeo.security.core/ext/test/log4j.properties
new file mode 100644 (file)
index 0000000..b33daa9
--- /dev/null
@@ -0,0 +1,28 @@
+log4j.rootLogger=WARN, console
+
+## Levels
+log4j.logger.org.argeo=DEBUG
+
+log4j.logger.org.hibernate=WARN
+
+log4j.logger.org.springframework=WARN
+#log4j.logger.org.springframework.web=DEBUG
+#log4j.logger.org.springframework.jms=WARN
+#log4j.logger.org.springframework.security=WARN
+
+log4j.logger.org.apache.activemq=WARN
+log4j.logger.org.apache.activemq.transport=WARN
+log4j.logger.org.apache.activemq.ActiveMQMessageConsumer=INFO
+log4j.logger.org.apache.activemq.ActiveMQMessageProducer=INFO
+
+log4j.logger.org.apache.catalina=INFO
+log4j.logger.org.apache.coyote=INFO
+log4j.logger.org.apache.tomcat=INFO
+
+## Appenders
+# console is set to be a ConsoleAppender.
+log4j.appender.console=org.apache.log4j.ConsoleAppender
+
+# console uses PatternLayout.
+log4j.appender.console.layout=org.apache.log4j.PatternLayout
+log4j.appender.console.layout.ConversionPattern= %-5p %d{ISO8601} %m - %c%n
diff --git a/org.argeo.security.core/ext/test/org/argeo/security/PasswordSandbox.java b/org.argeo.security.core/ext/test/org/argeo/security/PasswordSandbox.java
new file mode 100644 (file)
index 0000000..d8a0846
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.security;
+
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.codec.binary.Hex;
+import org.springframework.security.providers.ldap.authenticator.LdapShaPasswordEncoder;
+
+public class PasswordSandbox {
+       public static void main(String[] args) {
+               try {
+                       // Tested password
+                       String pwdPlain = "demo";
+
+                       // Check Java generated values
+                       LdapShaPasswordEncoder lspe = new LdapShaPasswordEncoder();
+                       String pwdLdapShaBase64 = lspe.encodePassword(pwdPlain, null);
+                       System.out.println("pwdLdapShaBase64:\t\t" + pwdLdapShaBase64);
+
+                       String pwdShaBase64 = pwdLdapShaBase64.substring("{SHA}".length());
+                       System.out.println("pwdShaBase64:\t\t\t" + pwdShaBase64);
+
+                       byte[] pwdShaArray = Base64.decodeBase64(pwdShaBase64.getBytes());
+                       String pwdShaHex = new String(Hex.encodeHex(pwdShaArray));
+                       System.out.println("pwdShaHex:\t\t\t" + pwdShaHex);
+
+                       // Check that we can use JavaScript generated values in Hex
+                       String jsShaHex = "89e495e7941cf9e40e6980d14a16bf023ccd4c91";
+                       System.out.println("jsShaHex:\t\t\t" + pwdShaHex);
+                       System.out.println("pwdShaHex==jsShaHex:\t\t"
+                                       + (pwdShaHex.equals(jsShaHex)));
+
+                       byte[] jsShaArray = Hex.decodeHex(jsShaHex.toCharArray());
+                       String jsShaBase64 = new String(Base64.encodeBase64(jsShaArray));
+                       System.out.println("jsShaBase64:\t\t\t" + jsShaBase64);
+                       System.out.println("pwdShaBase64==jsShaBase64:\t"
+                                       + (pwdShaBase64.equals(jsShaBase64)));
+               } catch (DecoderException e) {
+                       e.printStackTrace();
+               }
+
+       }
+
+}
\ No newline at end of file
diff --git a/org.argeo.security.core/ext/test/org/argeo/security/crypto/PasswordBasedEncryptionTest.java b/org.argeo.security.core/ext/test/org/argeo/security/crypto/PasswordBasedEncryptionTest.java
new file mode 100644 (file)
index 0000000..6973f57
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.security.crypto;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import javax.crypto.CipherOutputStream;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.PBEParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import javax.xml.bind.DatatypeConverter;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.StreamUtils;
+import org.argeo.security.crypto.PasswordBasedEncryption;
+
+public class PasswordBasedEncryptionTest extends TestCase {
+       private final static Log log = LogFactory
+                       .getLog(PasswordBasedEncryptionTest.class);
+
+       public void testEncryptDecrypt() {
+               final String password = "test long password since they are safer";
+               PasswordBasedEncryption pbeEnc = new PasswordBasedEncryption(
+                               password.toCharArray());
+               String message = "Hello World!";
+               log.info("Password:\t'" + password + "'");
+               log.info("Message:\t'" + message + "'");
+               byte[] encrypted = pbeEnc.encryptString(message);
+               log.info("Encrypted:\t'"
+                               + DatatypeConverter.printBase64Binary(encrypted) + "'");
+               PasswordBasedEncryption pbeDec = new PasswordBasedEncryption(
+                               password.toCharArray());
+               InputStream in = null;
+               in = new ByteArrayInputStream(encrypted);
+               String decrypted = pbeDec.decryptAsString(in);
+               log.info("Decrypted:\t'" + decrypted + "'");
+               StreamUtils.closeQuietly(in);
+               assertEquals(message, decrypted);
+       }
+
+       public void testPBEWithMD5AndDES() throws Exception {
+               String password = "test";
+               String message = "Hello World!";
+
+               byte[] salt = { (byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c,
+                               (byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99 };
+
+               int count = 1024;
+
+               String cipherAlgorithm = "PBEWithMD5AndDES";
+               String secretKeyAlgorithm = "PBEWithMD5AndDES";
+               SecretKeyFactory keyFac = SecretKeyFactory
+                               .getInstance(secretKeyAlgorithm);
+               PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray());
+               PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, count);
+               SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
+               Cipher ecipher = Cipher.getInstance(cipherAlgorithm);
+               ecipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);
+               Cipher dcipher = Cipher.getInstance(cipherAlgorithm);
+               dcipher.init(Cipher.DECRYPT_MODE, pbeKey, pbeParamSpec);
+
+               byte[] encrypted = ecipher.doFinal(message.getBytes());
+               byte[] decrypted = dcipher.doFinal(encrypted);
+               assertEquals(message, new String(decrypted));
+
+       }
+
+       public void testPBEWithSHA1AndAES() throws Exception {
+               String password = "test";
+               String message = "Hello World!";
+
+               byte[] salt = { (byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c,
+                               (byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99 };
+               byte[] iv = { (byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c,
+                               (byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99,
+                               (byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c,
+                               (byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99 };
+
+               int count = 1024;
+               // int keyLength = 256;
+               int keyLength = 128;
+
+               String cipherAlgorithm = "AES/CBC/PKCS5Padding";
+               String secretKeyAlgorithm = "PBKDF2WithHmacSHA1";
+               SecretKeyFactory keyFac = SecretKeyFactory
+                               .getInstance(secretKeyAlgorithm);
+               PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt,
+                               count, keyLength);
+               SecretKey tmp = keyFac.generateSecret(pbeKeySpec);
+               SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
+               Cipher ecipher = Cipher.getInstance(cipherAlgorithm);
+               ecipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(iv));
+
+               // decrypt
+               keyFac = SecretKeyFactory.getInstance(secretKeyAlgorithm);
+               pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt, count,
+                               keyLength);
+               tmp = keyFac.generateSecret(pbeKeySpec);
+               secret = new SecretKeySpec(tmp.getEncoded(), "AES");
+               // AlgorithmParameters params = ecipher.getParameters();
+               // byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
+               Cipher dcipher = Cipher.getInstance(cipherAlgorithm);
+               dcipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
+
+               byte[] encrypted = ecipher.doFinal(message.getBytes());
+               byte[] decrypted = dcipher.doFinal(encrypted);
+               assertEquals(message, new String(decrypted));
+
+               ByteArrayOutputStream out = new ByteArrayOutputStream();
+               CipherOutputStream cipherOut = new CipherOutputStream(out, ecipher);
+               cipherOut.write(message.getBytes());
+               StreamUtils.closeQuietly(cipherOut);
+               byte[] enc = out.toByteArray();
+
+               ByteArrayInputStream in = new ByteArrayInputStream(enc);
+               CipherInputStream cipherIn = new CipherInputStream(in, dcipher);
+               ByteArrayOutputStream dec = new ByteArrayOutputStream();
+               StreamUtils.copy(cipherIn, dec);
+               assertEquals(message, new String(dec.toByteArray()));
+       }
+}
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/NodeAuthenticationToken.java b/org.argeo.security.core/src/main/java/org/argeo/security/NodeAuthenticationToken.java
deleted file mode 100644 (file)
index 1870675..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.security;
-
-import org.springframework.security.GrantedAuthority;
-import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
-
-/** Credentials required for the authentication to a node. */
-public class NodeAuthenticationToken extends
-               UsernamePasswordAuthenticationToken {
-       private static final long serialVersionUID = 1955222132884795213L;
-       private final String url;
-
-       /** Non authenticated local constructor */
-       public NodeAuthenticationToken(Object principal, Object credentials) {
-               super(principal, credentials);
-               this.url = null;
-       }
-
-       /** Non authenticated remote constructor */
-       public NodeAuthenticationToken(Object principal, Object credentials,
-                       String url) {
-               super(principal, credentials);
-               this.url = url;
-       }
-
-       /** Authenticated constructor */
-       public NodeAuthenticationToken(NodeAuthenticationToken sat,
-                       GrantedAuthority[] authorities) {
-               super(sat.getPrincipal(), sat.getCredentials(), authorities);
-               this.url = sat.getUrl();
-       }
-
-       public String getUrl() {
-               return url;
-       }
-
-       public Boolean isRemote() {
-               return url != null;
-       }
-}
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/OsAuthenticationToken.java b/org.argeo.security.core/src/main/java/org/argeo/security/OsAuthenticationToken.java
deleted file mode 100644 (file)
index b3727b2..0000000
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.security;
-
-import java.security.AccessController;
-import java.security.Principal;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Set;
-
-import javax.security.auth.Subject;
-
-import org.argeo.ArgeoException;
-import org.argeo.OperatingSystem;
-import org.springframework.security.Authentication;
-import org.springframework.security.GrantedAuthority;
-import org.springframework.security.GrantedAuthorityImpl;
-import org.springframework.security.userdetails.UserDetails;
-
-/** Abstracts principals provided by com.sun.security.auth.module login modules. */
-public class OsAuthenticationToken implements Authentication {
-       private static final long serialVersionUID = -7544626794250917244L;
-
-       final Class<? extends Principal> osUserPrincipalClass;
-       final Class<? extends Principal> osUserIdPrincipalClass;
-       final Class<? extends Principal> osGroupIdPrincipalClass;
-
-       private List<GrantedAuthority> grantedAuthorities;
-
-       private UserDetails details;
-
-       /** Request */
-       public OsAuthenticationToken(GrantedAuthority[] grantedAuthorities) {
-               this.grantedAuthorities = grantedAuthorities != null ? Arrays
-                               .asList(grantedAuthorities) : null;
-               ClassLoader cl = getClass().getClassLoader();
-               switch (OperatingSystem.os) {
-               case OperatingSystem.WINDOWS:
-                       osUserPrincipalClass = getPrincipalClass(cl,
-                                       "com.sun.security.auth.NTUserPrincipal");
-                       osUserIdPrincipalClass = getPrincipalClass(cl,
-                                       "com.sun.security.auth.NTSidUserPrincipal");
-                       osGroupIdPrincipalClass = getPrincipalClass(cl,
-                                       "com.sun.security.auth.NTSidGroupPrincipal");
-                       break;
-               case OperatingSystem.NIX:
-                       osUserPrincipalClass = getPrincipalClass(cl,
-                                       "com.sun.security.auth.UnixPrincipal");
-                       osUserIdPrincipalClass = getPrincipalClass(cl,
-                                       "com.sun.security.auth.UnixNumericUserPrincipal");
-                       osGroupIdPrincipalClass = getPrincipalClass(cl,
-                                       "com.sun.security.auth.UnixNumericGroupPrincipal");
-                       break;
-               case OperatingSystem.SOLARIS:
-                       osUserPrincipalClass = getPrincipalClass(cl,
-                                       "com.sun.security.auth.SolarisPrincipal");
-                       osUserIdPrincipalClass = getPrincipalClass(cl,
-                                       "com.sun.security.auth.SolarisNumericUserPrincipal");
-                       osGroupIdPrincipalClass = getPrincipalClass(cl,
-                                       "com.sun.security.auth.SolarisNumericGroupPrincipal");
-                       break;
-
-               default:
-                       throw new ArgeoException("Unsupported operating system "
-                                       + OperatingSystem.os);
-               }
-
-       }
-
-       /** Authenticated */
-       public OsAuthenticationToken() {
-               this(null);
-       }
-
-       /** @return the name, or null if not yet logged */
-       public String getName() {
-               Subject subject = Subject.getSubject(AccessController.getContext());
-               if (subject == null)
-                       return null;
-               return getUser().getName();
-       }
-
-       /**
-        * Should not be called during authentication since group IDs are not yet
-        * available {@link Subject} has been set
-        */
-       public GrantedAuthority[] getAuthorities() {
-               // grantedAuthorities should not be null at this stage
-               List<GrantedAuthority> gas = new ArrayList<GrantedAuthority>(
-                               grantedAuthorities);
-               for (Principal groupPrincipal : getGroupsIds()) {
-                       gas.add(new GrantedAuthorityImpl("OSGROUP_"
-                                       + groupPrincipal.getName()));
-               }
-               return gas.toArray(new GrantedAuthority[gas.size()]);
-       }
-
-       public UserDetails getDetails() {
-               return details;
-       }
-
-       public void setDetails(UserDetails details) {
-               this.details = details;
-       }
-
-       public boolean isAuthenticated() {
-               return grantedAuthorities != null;
-       }
-
-       public void setAuthenticated(boolean isAuthenticated)
-                       throws IllegalArgumentException {
-               if (grantedAuthorities != null)
-                       grantedAuthorities.clear();
-               grantedAuthorities = null;
-       }
-
-       @SuppressWarnings("unchecked")
-       protected static Class<? extends Principal> getPrincipalClass(
-                       ClassLoader cl, String className) {
-               try {
-                       return (Class<? extends Principal>) cl.loadClass(className);
-               } catch (ClassNotFoundException e) {
-                       throw new ArgeoException("Cannot load principal class", e);
-               }
-       }
-
-       public Object getPrincipal() {
-               return getUser();
-       }
-
-       public Principal getUser() {
-               Subject subject = getSubject();
-               Set<? extends Principal> userPrincipals = subject
-                               .getPrincipals(osUserPrincipalClass);
-               if (userPrincipals == null || userPrincipals.size() == 0)
-                       throw new ArgeoException("No OS principal");
-               if (userPrincipals.size() > 1)
-                       throw new ArgeoException("More than one OS principal");
-               Principal user = userPrincipals.iterator().next();
-               return user;
-       }
-
-       public Principal getUserId() {
-               Subject subject = getSubject();
-               Set<? extends Principal> userIdsPrincipals = subject
-                               .getPrincipals(osUserIdPrincipalClass);
-               if (userIdsPrincipals == null || userIdsPrincipals.size() == 0)
-                       throw new ArgeoException("No user id principal");
-               if (userIdsPrincipals.size() > 1)
-                       throw new ArgeoException("More than one user id principal");
-               Principal userId = userIdsPrincipals.iterator().next();
-               return userId;
-       }
-
-       public Set<? extends Principal> getGroupsIds() {
-               Subject subject = getSubject();
-               return (Set<? extends Principal>) subject
-                               .getPrincipals(osGroupIdPrincipalClass);
-       }
-
-       /** @return the subject always non null */
-       protected Subject getSubject() {
-               Subject subject = Subject.getSubject(AccessController.getContext());
-               if (subject == null)
-                       throw new ArgeoException("No subject in JAAS context");
-               return subject;
-       }
-
-       public Object getCredentials() {
-               return "";
-       }
-
-}
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/SecurityUtils.java b/org.argeo.security.core/src/main/java/org/argeo/security/SecurityUtils.java
deleted file mode 100644 (file)
index e5b8ae7..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.security;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import org.springframework.security.Authentication;
-import org.springframework.security.GrantedAuthority;
-import org.springframework.security.context.SecurityContext;
-import org.springframework.security.context.SecurityContextHolder;
-import org.springframework.security.providers.anonymous.AnonymousAuthenticationToken;
-
-/** Static utilities */
-public class SecurityUtils {
-
-       private SecurityUtils() {
-       }
-
-       /** Whether the current thread has the admin role */
-       public static boolean hasCurrentThreadAuthority(String authority) {
-               SecurityContext securityContext = SecurityContextHolder.getContext();
-               if (securityContext != null) {
-                       Authentication authentication = securityContext.getAuthentication();
-                       if (authentication != null) {
-                               for (GrantedAuthority ga : authentication.getAuthorities())
-                                       if (ga.getAuthority().equals(authority))
-                                               return true;
-                       }
-               }
-               return false;
-       }
-
-       /**
-        * @return the authenticated username or null if not authenticated /
-        *         anonymous
-        */
-       public static String getCurrentThreadUsername() {
-               SecurityContext securityContext = SecurityContextHolder.getContext();
-               if (securityContext != null) {
-                       Authentication authentication = securityContext.getAuthentication();
-                       if (authentication != null) {
-                               if (authentication instanceof AnonymousAuthenticationToken) {
-                                       return null;
-                               }
-                               return authentication.getName();
-                       }
-               }
-               return null;
-       }
-
-       /**
-        * Returns the display name of the user details (by calling toString() on
-        * it)
-        */
-       public static String getUserDetailsDisplayName() {
-               SecurityContext securityContext = SecurityContextHolder.getContext();
-               if (securityContext != null) {
-                       Authentication authentication = securityContext.getAuthentication();
-                       if (authentication != null) {
-                               if (authentication instanceof AnonymousAuthenticationToken) {
-                                       return null;
-                               }
-                               Object details = authentication.getDetails();
-                               if (details != null)
-                                       return details.toString();
-                               return authentication.getName();
-                       }
-               }
-               return null;
-       }
-
-       /**
-        * Converts an array of Spring Security {@link GrantedAuthority} to a
-        * read-only list of strings, for portability and integration
-        */
-       public static List<String> authoritiesToStringList(
-                       GrantedAuthority[] authorities) {
-               List<String> lst = new ArrayList<String>();
-               for (GrantedAuthority ga : authorities)
-                       lst.add(ga.getAuthority());
-               return Collections.unmodifiableList(lst);
-       }
-}
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/SystemAuthentication.java b/org.argeo.security.core/src/main/java/org/argeo/security/SystemAuthentication.java
deleted file mode 100644 (file)
index 2722c1f..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.security;
-
-/**
- * Marks a system authentication, that is which did not require a login process.
- */
-public interface SystemAuthentication {
-
-}
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/SystemExecutionService.java b/org.argeo.security.core/src/main/java/org/argeo/security/SystemExecutionService.java
deleted file mode 100644 (file)
index 075a6c3..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.security;
-
-import java.util.concurrent.Callable;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Future;
-
-/**
- * Allows to execute code authenticated as a system user (that is not a real
- * person). The {@link Executor} interface is not used directly in order to
- * allow future extension of this interface and to simplify its publication
- * (e.g. as an OSGi service) and interception.
- */
-public interface SystemExecutionService extends Executor {
-       /**
-        * Executes this {@link Runnable} within a system authenticated context.
-        * Implementations should make sure that this method is properly secured via
-        * Java permissions since it could access everything without credentials.
-        */
-       public void execute(Runnable runnable);
-
-       /**
-        * Executes this {@link Callable} within a system authenticated context.
-        * Implementations should make sure that this method is properly secured via
-        * Java permissions since it could access everything without credentials.
-        */
-       public <T> Future<T> submit(Callable<T> task);
-}
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/UserAdminService.java b/org.argeo.security.core/src/main/java/org/argeo/security/UserAdminService.java
deleted file mode 100644 (file)
index 0a84cf6..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.security;
-
-import java.util.Set;
-
-import org.springframework.security.userdetails.UserDetailsManager;
-
-/** Enrich {@link UserDetailsManager} in order to provide roles semantics. */
-public interface UserAdminService extends UserDetailsManager {
-       /**
-        * Usernames must match this regexp pattern ({@value #USERNAME_PATTERN}).
-        * Thanks to <a href=
-        * "http://www.mkyong.com/regular-expressions/how-to-validate-username-with-regular-expression/"
-        * >this tip</a> (modified to add upper-case, add '@')
-        */
-       //public final static String USERNAME_PATTERN = "^[a-zA-Z0-9_-@]{3,64}$";
-
-       /**
-        * Email addresses must match this regexp pattern ({@value #EMAIL_PATTERN}.
-        * Thanks to <a href=
-        * "http://www.mkyong.com/regular-expressions/how-to-validate-email-address-with-regular-expression/"
-        * >this tip</a>.
-        */
-       public final static String EMAIL_PATTERN = "^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";
-
-       /*
-        * USERS
-        */
-       /** List all users. */
-       public Set<String> listUsers();
-
-       /** List users having this role (except the super user). */
-       public Set<String> listUsersInRole(String role);
-
-       /** Synchronize with the underlying DAO. */
-       public void synchronize();
-
-       /*
-        * ROLES
-        */
-       public void newRole(String role);
-
-       public Set<String> listEditableRoles();
-
-       public void deleteRole(String role);
-}
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/core/AbstractSystemExecution.java b/org.argeo.security.core/src/main/java/org/argeo/security/core/AbstractSystemExecution.java
deleted file mode 100644 (file)
index b84f3de..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.security.core;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.ArgeoException;
-import org.argeo.security.SystemAuthentication;
-import org.springframework.security.Authentication;
-import org.springframework.security.AuthenticationManager;
-import org.springframework.security.context.SecurityContext;
-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 being
-//             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>() {
-               protected Boolean initialValue() {
-                       return false;
-               }
-       };
-
-       /**
-        * Authenticate the calling thread to the underlying
-        * {@link AuthenticationManager}
-        */
-       protected void authenticateAsSystem() {
-               if (authenticatedBySelf.get())
-                       return;
-               SecurityContext securityContext = SecurityContextHolder.getContext();
-               Authentication currentAuth = securityContext.getAuthentication();
-               if (currentAuth != null) {
-                       if (!(currentAuth instanceof SystemAuthentication))
-                               throw new ArgeoException(
-                                               "System execution on an already authenticated thread: "
-                                                               + currentAuth + ", THREAD="
-                                                               + Thread.currentThread().getId());
-                       return;
-               }
-               // Subject subject = Subject.getSubject(AccessController.getContext());
-               // if (subject != null
-               // && !subject.getPrincipals(Authentication.class).isEmpty())
-               // throw new ArgeoException(
-               // "There is already an authenticated subject: " + subject);
-
-               String key = systemAuthenticationKey != null ? systemAuthenticationKey
-                               : System.getProperty(
-                                               InternalAuthentication.SYSTEM_KEY_PROPERTY,
-                                               InternalAuthentication.SYSTEM_KEY_DEFAULT);
-               if (key == null)
-                       throw new ArgeoException("No system key defined");
-               Authentication auth = authenticationManager
-                               .authenticate(new InternalAuthentication(key));
-               securityContext.setAuthentication(auth);
-               authenticatedBySelf.set(true);
-               if (log.isTraceEnabled())
-                       log.trace("System authenticated");
-       }
-
-       // /** Removes the authentication from the calling thread. */
-       // protected void deauthenticateAsSystem() {
-       // // remove the authentication
-       // // SecurityContext securityContext = SecurityContextHolder.getContext();
-       // // securityContext.setAuthentication(null);
-       // // authenticatedBySelf.set(false);
-       // if (log.isTraceEnabled()) {
-       // log.trace("System deauthenticated");
-       // // Thread.dumpStack();
-       // }
-       // }
-
-       /**
-        * Whether the current thread was authenticated by this component or a
-        * parent thread.
-        */
-       protected Boolean isAuthenticatedBySelf() {
-               return authenticatedBySelf.get();
-       }
-
-       public void setAuthenticationManager(
-                       AuthenticationManager authenticationManager) {
-               this.authenticationManager = authenticationManager;
-       }
-
-       public void setSystemAuthenticationKey(String systemAuthenticationKey) {
-               this.systemAuthenticationKey = systemAuthenticationKey;
-       }
-
-}
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/core/AsyncSystemTaskExecutor.java b/org.argeo.security.core/src/main/java/org/argeo/security/core/AsyncSystemTaskExecutor.java
deleted file mode 100644 (file)
index 0e400c8..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.security.core;
-
-import org.argeo.security.SystemExecutionService;
-import org.springframework.core.task.SimpleAsyncTaskExecutor;
-
-/**
- * Asynchronous Spring TaskExecutor (for use in JMS for example) wrapping a
- * {@link SystemExecutionService}.
- */
-public class AsyncSystemTaskExecutor extends SimpleAsyncTaskExecutor {
-       private static final long serialVersionUID = -8035527542087963068L;
-
-       private SystemExecutionService systemExecutionService;
-
-       public AsyncSystemTaskExecutor() {
-               super();
-       }
-
-       public AsyncSystemTaskExecutor(String threadNamePrefix) {
-               super(threadNamePrefix);
-       }
-
-       @Override
-       public Thread createThread(final Runnable runnable) {
-               Runnable systemExecutionRunnable = new Runnable() {
-
-                       public void run() {
-                               systemExecutionService.execute(runnable);
-
-                       }
-               };
-               return super.createThread(systemExecutionRunnable);
-       }
-
-       public void setSystemExecutionService(
-                       SystemExecutionService systemExecutionService) {
-               this.systemExecutionService = systemExecutionService;
-       }
-
-}
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/core/AuthenticatedApplicationContextInitialization.java b/org.argeo.security.core/src/main/java/org/argeo/security/core/AuthenticatedApplicationContextInitialization.java
deleted file mode 100644 (file)
index 97dd6ca..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.security.core;
-
-import java.beans.PropertyDescriptor;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.springframework.beans.BeansException;
-import org.springframework.beans.PropertyValues;
-import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
-import org.springframework.context.ApplicationEvent;
-import org.springframework.context.ApplicationListener;
-import org.springframework.context.event.ContextRefreshedEvent;
-
-/**
- * Executes with a system authentication the instantiation and initialization
- * methods of the application context where it has been defined.
- */
-public class AuthenticatedApplicationContextInitialization extends
-               AbstractSystemExecution implements InstantiationAwareBeanPostProcessor,
-               ApplicationListener {
-       // private Log log = LogFactory
-       // .getLog(AuthenticatedApplicationContextInitialization.class);
-       /** If non empty, restricts to these beans */
-       private List<String> beanNames = new ArrayList<String>();
-
-       @SuppressWarnings("rawtypes")
-       public Object postProcessBeforeInstantiation(Class beanClass,
-                       String beanName) throws BeansException {
-               // we authenticate when any bean is instantiated
-               // we will deauthenticate only when the application context has been
-               // refreshed in order to be able to deal with factory beans has well
-               if (!isAuthenticatedBySelf()) {
-                       if (beanNames.size() == 0)
-                               authenticateAsSystem();
-                       else if (beanNames.contains(beanName))
-                               authenticateAsSystem();
-               }
-               return null;
-       }
-
-       public boolean postProcessAfterInstantiation(Object bean, String beanName)
-                       throws BeansException {
-               return true;
-       }
-
-       public PropertyValues postProcessPropertyValues(PropertyValues pvs,
-                       PropertyDescriptor[] pds, Object bean, String beanName)
-                       throws BeansException {
-               return pvs;
-       }
-
-       public Object postProcessBeforeInitialization(Object bean, String beanName)
-                       throws BeansException {
-               // authenticateAsSystem();
-               return bean;
-       }
-
-       public Object postProcessAfterInitialization(Object bean, String beanName)
-                       throws BeansException {
-               // NOTE: in case there was an exception in on the initialization method
-               // we expect the underlying thread to die and thus the system
-               // authentication to be lost. We have currently no way to catch the
-               // exception and perform the deauthentication by ourselves.
-               // deauthenticateAsSystem();
-               return bean;
-       }
-
-       public void onApplicationEvent(ApplicationEvent event) {
-               if (event instanceof ContextRefreshedEvent) {
-                       // make sure that we have deauthenticated after the application
-                       // context was initialized/refreshed
-                       // deauthenticateAsSystem();
-               }
-       }
-
-       public void setBeanNames(List<String> beanNames) {
-               this.beanNames = beanNames;
-       }
-
-}
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/core/AuthenticationProvidersRegister.java b/org.argeo.security.core/src/main/java/org/argeo/security/core/AuthenticationProvidersRegister.java
deleted file mode 100644 (file)
index 317815e..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.security.core;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.springframework.beans.factory.InitializingBean;
-
-/**
- * Maintains a list of authentication providers injected in to a provider
- * manager, in order to avoid issues with OSGi services and use packages.
- */
-public class AuthenticationProvidersRegister implements InitializingBean {
-       private Log log = LogFactory.getLog(AuthenticationProvidersRegister.class);
-
-       private List<Object> providers = new ArrayList<Object>();
-       private List<Object> defaultProviders = new ArrayList<Object>();
-
-       public void register(Object authenticationProvider,
-                       Map<String, String> parameters) {
-               providers.add(authenticationProvider);
-               if (log.isTraceEnabled())
-                       log.trace("Registered authentication provider " + parameters);
-       }
-
-       public void unregister(Object authenticationProvider,
-                       Map<String, String> parameters) {
-               providers.remove(authenticationProvider);
-               if (log.isTraceEnabled())
-                       log.trace("Unregistered authentication provider " + parameters);
-       }
-
-       public List<Object> getProviders() {
-               return providers;
-       }
-
-       public void setDefaultProviders(
-                       List<Object> defaultProviders) {
-               this.defaultProviders = defaultProviders;
-       }
-
-       public void afterPropertiesSet() throws Exception {
-               providers.addAll(defaultProviders);
-       }
-
-}
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/core/ConsoleCallbackHandler.java b/org.argeo.security.core/src/main/java/org/argeo/security/core/ConsoleCallbackHandler.java
deleted file mode 100644 (file)
index faa81b0..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-package org.argeo.security.core;
-
-import java.io.Console;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.Arrays;
-import java.util.Locale;
-
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.NameCallback;
-import javax.security.auth.callback.PasswordCallback;
-import javax.security.auth.callback.TextOutputCallback;
-import javax.security.auth.callback.UnsupportedCallbackException;
-
-import org.argeo.ArgeoException;
-import org.argeo.util.LocaleCallback;
-
-/** Callback handler to be used with a command line UI. */
-public class ConsoleCallbackHandler implements CallbackHandler {
-
-       @Override
-       public void handle(Callback[] callbacks) throws IOException,
-                       UnsupportedCallbackException {
-               Console console = System.console();
-               if (console == null)
-                       throw new ArgeoException("No console available");
-
-               PrintWriter writer = console.writer();
-               for (int i = 0; i < callbacks.length; i++) {
-                       if (callbacks[i] instanceof TextOutputCallback) {
-                               TextOutputCallback callback = (TextOutputCallback) callbacks[i];
-                               writer.write(callback.getMessage());
-                       } else if (callbacks[i] instanceof NameCallback) {
-                               NameCallback callback = (NameCallback) callbacks[i];
-                               writer.write(callback.getPrompt());
-                               if (callback.getDefaultName() != null)
-                                       writer.write(" (" + callback.getDefaultName() + ")");
-                               writer.write(" : ");
-                               String answer = console.readLine();
-                               if (callback.getDefaultName() != null
-                                               && answer.trim().equals(""))
-                                       callback.setName(callback.getDefaultName());
-                               else
-                                       callback.setName(answer);
-                       } else if (callbacks[i] instanceof PasswordCallback) {
-                               PasswordCallback callback = (PasswordCallback) callbacks[i];
-                               writer.write(callback.getPrompt());
-                               char[] answer = console.readPassword();
-                               callback.setPassword(answer);
-                               Arrays.fill(answer, ' ');
-                       } else if (callbacks[i] instanceof LocaleCallback) {
-                               LocaleCallback callback = (LocaleCallback) callbacks[i];
-                               writer.write(callback.getPrompt());
-                               writer.write("\n");
-                               for (int j = 0; j < callback.getAvailableLocales().size(); j++) {
-                                       Locale locale = callback.getAvailableLocales().get(j);
-                                       writer.print(j + " : " + locale.getDisplayName() + "\n");
-                               }
-                               writer.write("(" + callback.getDefaultIndex() + ") : ");
-                               String answer = console.readLine();
-                               if (answer.trim().equals(""))
-                                       callback.setSelectedIndex(callback.getDefaultIndex());
-                               else
-                                       callback.setSelectedIndex(new Integer(answer.trim()));
-                       }
-               }
-       }
-
-}
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/core/InternalAuthentication.java b/org.argeo.security.core/src/main/java/org/argeo/security/core/InternalAuthentication.java
deleted file mode 100644 (file)
index 267ddd3..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.security.core;
-
-import org.argeo.security.SystemAuthentication;
-import org.springframework.security.GrantedAuthority;
-import org.springframework.security.GrantedAuthorityImpl;
-import org.springframework.security.adapters.PrincipalSpringSecurityUserToken;
-
-/** A token base on a system key used to request a system authentication. */
-public class InternalAuthentication extends PrincipalSpringSecurityUserToken
-               implements SystemAuthentication {
-       private static final long serialVersionUID = -6783376375615949315L;
-       /** 'admin' for consistency with JCR */
-       public final static String DEFAULT_SYSTEM_USERNAME = "admin";
-       public final static String DEFAULT_SYSTEM_ROLE = "ROLE_SYSTEM";
-       public final static String SYSTEM_KEY_PROPERTY = "argeo.security.systemKey";
-       public final static String SYSTEM_KEY_DEFAULT = "argeo";
-
-       public InternalAuthentication(String key, String systemUsername,
-                       String systemRole) {
-               super(
-                               key,
-                               systemUsername,
-                               key,
-                               new GrantedAuthority[] { new GrantedAuthorityImpl(systemRole) },
-                               systemUsername);
-       }
-
-       public InternalAuthentication(String key) {
-               this(key, DEFAULT_SYSTEM_USERNAME, DEFAULT_SYSTEM_ROLE);
-       }
-
-}
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/core/KeyBasedSystemExecutionService.java b/org.argeo.security.core/src/main/java/org/argeo/security/core/KeyBasedSystemExecutionService.java
deleted file mode 100644 (file)
index 6c85df1..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.security.core;
-
-import java.util.concurrent.Callable;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.FutureTask;
-
-import org.argeo.ArgeoException;
-import org.argeo.security.SystemExecutionService;
-
-/**
- * Implementation of a {@link SystemExecutionService} using a key-based
- * {@link InternalAuthentication}
- */
-public class KeyBasedSystemExecutionService extends AbstractSystemExecution
-               implements SystemExecutionService {
-       public void execute(Runnable runnable) {
-               try {
-                       wrapWithSystemAuthentication(Executors.callable(runnable)).call();
-               } catch (RuntimeException e) {
-                       throw e;
-               } catch (Exception e) {
-                       throw new ArgeoException(
-                                       "Exception when running system authenticated task", e);
-               }
-       }
-
-       public <T> Future<T> submit(Callable<T> task) {
-               FutureTask<T> future = new FutureTask<T>(
-                               wrapWithSystemAuthentication(task));
-               future.run();
-               return future;
-       }
-
-       protected <T> Callable<T> wrapWithSystemAuthentication(
-                       final Callable<T> runnable) {
-               return new Callable<T>() {
-
-                       public T call() throws Exception {
-                               authenticateAsSystem();
-                               try {
-                                       return runnable.call();
-                               } finally {
-//                                     deauthenticateAsSystem();
-                               }
-                       }
-               };
-       }
-}
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/core/MatchingAuthenticationProvider.java b/org.argeo.security.core/src/main/java/org/argeo/security/core/MatchingAuthenticationProvider.java
deleted file mode 100644 (file)
index 0471151..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.security.core;
-
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Properties;
-
-import org.springframework.core.io.Resource;
-import org.springframework.security.AuthenticationException;
-import org.springframework.security.BadCredentialsException;
-import org.springframework.security.GrantedAuthority;
-import org.springframework.security.GrantedAuthorityImpl;
-import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
-import org.springframework.security.providers.dao.AbstractUserDetailsAuthenticationProvider;
-import org.springframework.security.userdetails.User;
-import org.springframework.security.userdetails.UserDetails;
-
-/** @deprecated */
-@Deprecated
-public class MatchingAuthenticationProvider extends
-               AbstractUserDetailsAuthenticationProvider {
-
-       private Resource mapping;
-       private Properties properties;
-
-       private List<String> defaultRoles = new ArrayList<String>();
-
-       @Override
-       protected void doAfterPropertiesSet() throws Exception {
-               properties = new Properties();
-               InputStream propIn = mapping.getInputStream();
-               try {
-                       properties.load(propIn);
-               } finally {
-                       propIn.close();
-               }
-       }
-
-       @Override
-       protected void additionalAuthenticationChecks(UserDetails userDetails,
-                       UsernamePasswordAuthenticationToken authentication)
-                       throws AuthenticationException {
-               if (!userDetails.getPassword().equals(authentication.getCredentials()))
-                       throw new BadCredentialsException(
-                                       "Invalid credentails provided by "
-                                                       + authentication.getName());
-       }
-
-       @Override
-       protected UserDetails retrieveUser(String username,
-                       UsernamePasswordAuthenticationToken authentication)
-                       throws AuthenticationException {
-               String value = properties.getProperty(username);
-               if (value == null)
-                       throw new BadCredentialsException("User " + username
-                                       + " is not registered");
-               List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
-               for (String role : defaultRoles)
-                       grantedAuthorities.add(new GrantedAuthorityImpl(role));
-               return new User(
-                               username,
-                               value,
-                               true,
-                               true,
-                               true,
-                               true,
-                               grantedAuthorities
-                                               .toArray(new GrantedAuthority[grantedAuthorities.size()]));
-       }
-
-       public void setMapping(Resource mapping) {
-               this.mapping = mapping;
-       }
-
-       public void setDefaultRoles(List<String> defaultRoles) {
-               this.defaultRoles = defaultRoles;
-       }
-
-}
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/core/OsAuthenticationProvider.java b/org.argeo.security.core/src/main/java/org/argeo/security/core/OsAuthenticationProvider.java
deleted file mode 100644 (file)
index 0e29ecd..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.security.core;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.argeo.security.OsAuthenticationToken;
-import org.springframework.security.Authentication;
-import org.springframework.security.AuthenticationException;
-import org.springframework.security.GrantedAuthority;
-import org.springframework.security.GrantedAuthorityImpl;
-import org.springframework.security.providers.AuthenticationProvider;
-
-/**
- * Validates an OS authentication. The id is that it will always be
- * authenticated since we are always runnign within an OS, but the fact that the
- * {@link Authentication} works properly depends on the proper OS login module
- * having been called as well. TODO make it more configurable (base roles, is
- * admin)
- */
-public class OsAuthenticationProvider implements AuthenticationProvider {
-       final static String osUserRole = "ROLE_OS_USER";
-       final static String userRole = "ROLE_USER";
-       final static String adminRole = "ROLE_ADMIN";
-
-       final static Boolean isAdmin = true;
-
-       public Authentication authenticate(Authentication authentication)
-                       throws AuthenticationException {
-               return new OsAuthenticationToken(getBaseAuthorities());
-       }
-
-       public static GrantedAuthority[] getBaseAuthorities() {
-               List<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
-               auths.add(new GrantedAuthorityImpl(osUserRole));
-               auths.add(new GrantedAuthorityImpl(userRole));
-               if (isAdmin)
-                       auths.add(new GrantedAuthorityImpl(adminRole));
-               return auths.toArray(new GrantedAuthority[auths.size()]);
-       }
-
-       @SuppressWarnings("rawtypes")
-       public boolean supports(Class authentication) {
-               return OsAuthenticationToken.class.isAssignableFrom(authentication);
-       }
-
-}
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/core/OsgiModuleLabel.java b/org.argeo.security.core/src/main/java/org/argeo/security/core/OsgiModuleLabel.java
deleted file mode 100644 (file)
index 45c9e16..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-package org.argeo.security.core;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.Constants;
-
-/**
- * Logs the name and version of an OSGi bundle based on its
- * {@link BundleContext}.
- */
-public class OsgiModuleLabel {
-       private final static Log log = LogFactory.getLog(OsgiModuleLabel.class);
-
-       private Bundle bundle;
-
-       public OsgiModuleLabel() {
-       }
-
-       /** Sets without logging. */
-       public OsgiModuleLabel(Bundle bundle) {
-               this.bundle = bundle;
-       }
-
-       /**
-        * Retrieved bundle from a bundle context and logs it. Typically to be set
-        * as a Spring bean.
-        */
-       public void setBundleContext(BundleContext bundleContext) {
-               this.bundle = bundleContext.getBundle();
-               log.info(msg());
-       }
-
-       public String msg() {
-               String name = bundle.getHeaders().get(Constants.BUNDLE_NAME).toString();
-               String symbolicName = bundle.getSymbolicName();
-               String version = bundle.getVersion().toString();
-               return name + " v" + version + " (" + symbolicName + ")";
-       }
-}
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/core/SimpleRoleRegistration.java b/org.argeo.security.core/src/main/java/org/argeo/security/core/SimpleRoleRegistration.java
deleted file mode 100644 (file)
index aa8a5f0..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-package org.argeo.security.core;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.security.UserAdminService;
-
-/**
- * Register one or many roles via a user admin service. Does nothing if the role
- * is already registered.
- */
-public class SimpleRoleRegistration implements Runnable {
-       private final static Log log = LogFactory
-                       .getLog(SimpleRoleRegistration.class);
-
-       private String role;
-       private List<String> roles = new ArrayList<String>();
-       private UserAdminService userAdminService;
-
-       @Override
-       public void run() {
-               Set<String> existingRoles = userAdminService.listEditableRoles();
-               if (role != null && !existingRoles.contains(role))
-                       newRole(role);
-               for (String r : roles) {
-                       if (!existingRoles.contains(r))
-                               newRole(r);
-               }
-       }
-
-       protected void newRole(String r) {
-               userAdminService.newRole(r);
-               log.info("Added role " + r + " required by application.");
-       }
-
-       public void register(UserAdminService userAdminService, Map<?, ?> properties) {
-               this.userAdminService = userAdminService;
-               run();
-       }
-
-       public void setRole(String role) {
-               this.role = role;
-       }
-
-       public void setRoles(List<String> roles) {
-               this.roles = roles;
-       }
-
-       public void setUserAdminService(UserAdminService userAdminService) {
-               this.userAdminService = userAdminService;
-       }
-
-}
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/crypto/AbstractKeyring.java b/org.argeo.security.core/src/main/java/org/argeo/security/crypto/AbstractKeyring.java
deleted file mode 100644 (file)
index daa1ebd..0000000
+++ /dev/null
@@ -1,282 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.security.crypto;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.CharArrayWriter;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.io.Reader;
-import java.io.Writer;
-import java.security.AccessController;
-import java.security.MessageDigest;
-import java.security.Provider;
-import java.security.Security;
-import java.util.Arrays;
-import java.util.Iterator;
-
-import javax.crypto.SecretKey;
-import javax.security.auth.Subject;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.PasswordCallback;
-import javax.security.auth.callback.TextOutputCallback;
-import javax.security.auth.callback.UnsupportedCallbackException;
-import javax.security.auth.login.LoginContext;
-import javax.security.auth.login.LoginException;
-
-import org.argeo.ArgeoException;
-import org.argeo.StreamUtils;
-import org.argeo.util.security.Keyring;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-
-/** username / password based keyring. TODO internationalize */
-public abstract class AbstractKeyring implements Keyring, CryptoKeyring {
-       static {
-               Security.addProvider(new BouncyCastleProvider());
-       }
-
-       public final static String DEFAULT_KEYRING_LOGIN_CONTEXT = "KEYRING";
-
-       private String loginContextName = DEFAULT_KEYRING_LOGIN_CONTEXT;
-       private CallbackHandler defaultCallbackHandler;
-
-       private String charset = "UTF-8";
-
-       /**
-        * Default provider is bouncy castle, in order to have consistent behaviour
-        * across implementations
-        */
-       private String securityProviderName = "BC";
-
-       /**
-        * Whether the keyring has already been created in the past with a master
-        * password
-        */
-       protected abstract Boolean isSetup();
-
-       /**
-        * Setup the keyring persistently, {@link #isSetup()} must return true
-        * afterwards
-        */
-       protected abstract void setup(char[] password);
-
-       /** Populates the key spec callback */
-       protected abstract void handleKeySpecCallback(PBEKeySpecCallback pbeCallback);
-
-       protected abstract void encrypt(String path, InputStream unencrypted);
-
-       protected abstract InputStream decrypt(String path);
-
-       /** Triggers lazy initialization */
-       protected SecretKey getSecretKey() {
-               Subject subject = Subject.getSubject(AccessController.getContext());
-               // we assume only one secrete key is available
-               Iterator<SecretKey> iterator = subject.getPrivateCredentials(
-                               SecretKey.class).iterator();
-               if (!iterator.hasNext()) {// not initialized
-                       CallbackHandler callbackHandler = new KeyringCallbackHandler();
-                       try {
-                               LoginContext loginContext = new LoginContext(loginContextName,
-                                               subject, callbackHandler);
-                               loginContext.login();
-                               // FIXME will login even if password is wrong
-                               iterator = subject.getPrivateCredentials(SecretKey.class)
-                                               .iterator();
-                               return iterator.next();
-                       } catch (LoginException e) {
-                               throw new ArgeoException("Keyring login failed", e);
-                       }
-
-               } else {
-                       SecretKey secretKey = iterator.next();
-                       if (iterator.hasNext())
-                               throw new ArgeoException(
-                                               "More than one secret key in private credentials");
-                       return secretKey;
-               }
-       }
-
-       public InputStream getAsStream(String path) {
-               return decrypt(path);
-       }
-
-       public void set(String path, InputStream in) {
-               encrypt(path, in);
-       }
-
-       public char[] getAsChars(String path) {
-               InputStream in = getAsStream(path);
-               CharArrayWriter writer = null;
-               Reader reader = null;
-               try {
-                       writer = new CharArrayWriter();
-                       reader = new InputStreamReader(in, charset);
-                       StreamUtils.copy(reader, writer);
-                       return writer.toCharArray();
-               } catch (IOException e) {
-                       throw new ArgeoException("Cannot decrypt to char array", e);
-               } finally {
-                       StreamUtils.closeQuietly(reader);
-                       StreamUtils.closeQuietly(in);
-                       StreamUtils.closeQuietly(writer);
-               }
-       }
-
-       public void set(String path, char[] arr) {
-               ByteArrayOutputStream out = new ByteArrayOutputStream();
-               ByteArrayInputStream in = null;
-               Writer writer = null;
-               try {
-                       writer = new OutputStreamWriter(out, charset);
-                       writer.write(arr);
-                       writer.flush();
-                       in = new ByteArrayInputStream(out.toByteArray());
-                       set(path, in);
-               } catch (IOException e) {
-                       throw new ArgeoException("Cannot encrypt to char array", e);
-               } finally {
-                       StreamUtils.closeQuietly(writer);
-                       StreamUtils.closeQuietly(out);
-                       StreamUtils.closeQuietly(in);
-               }
-       }
-
-       protected Provider getSecurityProvider() {
-               return Security.getProvider(securityProviderName);
-       }
-
-       public void setLoginContextName(String loginContextName) {
-               this.loginContextName = loginContextName;
-       }
-
-       public void setDefaultCallbackHandler(CallbackHandler defaultCallbackHandler) {
-               this.defaultCallbackHandler = defaultCallbackHandler;
-       }
-
-       public void setCharset(String charset) {
-               this.charset = charset;
-       }
-
-       public void setSecurityProviderName(String securityProviderName) {
-               this.securityProviderName = securityProviderName;
-       }
-
-       @Deprecated
-       protected static byte[] hash(char[] password, byte[] salt,
-                       Integer iterationCount) {
-               ByteArrayOutputStream out = null;
-               OutputStreamWriter writer = null;
-               try {
-                       out = new ByteArrayOutputStream();
-                       writer = new OutputStreamWriter(out, "UTF-8");
-                       writer.write(password);
-                       MessageDigest pwDigest = MessageDigest.getInstance("SHA-256");
-                       pwDigest.reset();
-                       pwDigest.update(salt);
-                       byte[] btPass = pwDigest.digest(out.toByteArray());
-                       for (int i = 0; i < iterationCount; i++) {
-                               pwDigest.reset();
-                               btPass = pwDigest.digest(btPass);
-                       }
-                       return btPass;
-               } catch (Exception e) {
-                       throw new ArgeoException("Cannot hash", e);
-               } finally {
-                       StreamUtils.closeQuietly(out);
-                       StreamUtils.closeQuietly(writer);
-               }
-
-       }
-
-       /**
-        * Convenience method using the underlying callback to ask for a password
-        * (typically used when the password is not saved in the keyring)
-        */
-       protected char[] ask() {
-               PasswordCallback passwordCb = new PasswordCallback("Password", false);
-               Callback[] dialogCbs = new Callback[] { passwordCb };
-               try {
-                       defaultCallbackHandler.handle(dialogCbs);
-                       char[] password = passwordCb.getPassword();
-                       return password;
-               } catch (Exception e) {
-                       throw new ArgeoException("Cannot ask for a password", e);
-               }
-
-       }
-
-       class KeyringCallbackHandler implements CallbackHandler {
-               public void handle(Callback[] callbacks) throws IOException,
-                               UnsupportedCallbackException {
-                       // checks
-                       if (callbacks.length != 2)
-                               throw new IllegalArgumentException(
-                                               "Keyring required 2 and only 2 callbacks: {PasswordCallback,PBEKeySpecCallback}");
-                       if (!(callbacks[0] instanceof PasswordCallback))
-                               throw new UnsupportedCallbackException(callbacks[0]);
-                       if (!(callbacks[1] instanceof PBEKeySpecCallback))
-                               throw new UnsupportedCallbackException(callbacks[0]);
-
-                       PasswordCallback passwordCb = (PasswordCallback) callbacks[0];
-                       PBEKeySpecCallback pbeCb = (PBEKeySpecCallback) callbacks[1];
-
-                       if (isSetup()) {
-                               Callback[] dialogCbs = new Callback[] { passwordCb };
-                               defaultCallbackHandler.handle(dialogCbs);
-                       } else {// setup keyring
-                               TextOutputCallback textCb1 = new TextOutputCallback(
-                                               TextOutputCallback.INFORMATION,
-                                               "Enter a master password which will protect your private data");
-                               TextOutputCallback textCb2 = new TextOutputCallback(
-                                               TextOutputCallback.INFORMATION,
-                                               "(for example your credentials to third-party services)");
-                               TextOutputCallback textCb3 = new TextOutputCallback(
-                                               TextOutputCallback.INFORMATION,
-                                               "Don't forget this password since the data cannot be read without it");
-                               PasswordCallback confirmPasswordCb = new PasswordCallback(
-                                               "Confirm password", false);
-                               // first try
-                               Callback[] dialogCbs = new Callback[] { textCb1, textCb2,
-                                               textCb3, passwordCb, confirmPasswordCb };
-                               defaultCallbackHandler.handle(dialogCbs);
-
-                               // if passwords different, retry (except if cancelled)
-                               while (passwordCb.getPassword() != null
-                                               && !Arrays.equals(passwordCb.getPassword(),
-                                                               confirmPasswordCb.getPassword())) {
-                                       TextOutputCallback textCb = new TextOutputCallback(
-                                                       TextOutputCallback.ERROR,
-                                                       "The passwords do not match");
-                                       dialogCbs = new Callback[] { textCb, passwordCb,
-                                                       confirmPasswordCb };
-                                       defaultCallbackHandler.handle(dialogCbs);
-                               }
-
-                               if (passwordCb.getPassword() != null) {// not cancelled
-                                       setup(passwordCb.getPassword());
-                               }
-                       }
-
-                       if (passwordCb.getPassword() != null)
-                               handleKeySpecCallback(pbeCb);
-               }
-
-       }
-}
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/crypto/CryptoKeyring.java b/org.argeo.security.core/src/main/java/org/argeo/security/crypto/CryptoKeyring.java
deleted file mode 100644 (file)
index d25eccd..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.security.crypto;
-
-import org.argeo.util.security.Keyring;
-
-/**
- * Advanced keyring based on cryptography that can easily be centralized and
- * coordinated with {@link KeyringLoginModule} (since they ar ein the same
- * package)
- */
-public interface CryptoKeyring extends Keyring {
-
-}
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/crypto/KeyringLoginModule.java b/org.argeo.security.core/src/main/java/org/argeo/security/crypto/KeyringLoginModule.java
deleted file mode 100644 (file)
index 34b7d40..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.security.crypto;
-
-import java.security.AccessController;
-import java.util.Map;
-import java.util.Set;
-
-import javax.crypto.SecretKey;
-import javax.crypto.SecretKeyFactory;
-import javax.crypto.spec.PBEKeySpec;
-import javax.crypto.spec.SecretKeySpec;
-import javax.security.auth.Subject;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.CallbackHandler;
-import javax.security.auth.callback.PasswordCallback;
-import javax.security.auth.login.LoginException;
-import javax.security.auth.spi.LoginModule;
-
-/** Adds a secret key to the private credentials */
-public class KeyringLoginModule implements LoginModule {
-       private Subject subject;
-       private CallbackHandler callbackHandler;
-       private SecretKey secretKey;
-
-       public void initialize(Subject subject, CallbackHandler callbackHandler,
-                       Map<String, ?> sharedState, Map<String, ?> options) {
-               this.subject = subject;
-               if (subject == null) {
-                       subject = Subject.getSubject(AccessController.getContext());
-               }
-               this.callbackHandler = callbackHandler;
-       }
-
-       public boolean login() throws LoginException {
-               Set<SecretKey> pbes = subject.getPrivateCredentials(SecretKey.class);
-               if (pbes.size() > 0)
-                       return true;
-               PasswordCallback pc = new PasswordCallback("Master password", false);
-               PBEKeySpecCallback pbeCb = new PBEKeySpecCallback();
-               Callback[] callbacks = { pc, pbeCb };
-               try {
-                       callbackHandler.handle(callbacks);
-                       char[] password = pc.getPassword();
-
-                       SecretKeyFactory keyFac = SecretKeyFactory.getInstance(pbeCb
-                                       .getSecretKeyFactory());
-                       PBEKeySpec keySpec;
-                       if (pbeCb.getKeyLength() != null)
-                               keySpec = new PBEKeySpec(password, pbeCb.getSalt(),
-                                               pbeCb.getIterationCount(), pbeCb.getKeyLength());
-                       else
-                               keySpec = new PBEKeySpec(password, pbeCb.getSalt(),
-                                               pbeCb.getIterationCount());
-
-                       String secKeyEncryption = pbeCb.getSecretKeyEncryption();
-                       if (secKeyEncryption != null) {
-                               SecretKey tmp = keyFac.generateSecret(keySpec);
-                               secretKey = new SecretKeySpec(tmp.getEncoded(),
-                                               secKeyEncryption);
-                       } else {
-                               secretKey = keyFac.generateSecret(keySpec);
-                       }
-               } catch (Exception e) {
-                       LoginException le = new LoginException("Cannot login keyring");
-                       le.initCause(e);
-                       throw le;
-               }
-               return true;
-       }
-
-       public boolean commit() throws LoginException {
-               if (secretKey != null)
-                       subject.getPrivateCredentials().add(secretKey);
-               return true;
-       }
-
-       public boolean abort() throws LoginException {
-               return true;
-       }
-
-       public boolean logout() throws LoginException {
-               Set<PasswordBasedEncryption> pbes = subject
-                               .getPrivateCredentials(PasswordBasedEncryption.class);
-               pbes.clear();
-               return true;
-       }
-
-}
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/crypto/PBEKeySpecCallback.java b/org.argeo.security.core/src/main/java/org/argeo/security/crypto/PBEKeySpecCallback.java
deleted file mode 100644 (file)
index e964366..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.security.crypto;
-
-import javax.crypto.spec.PBEKeySpec;
-import javax.security.auth.callback.Callback;
-import javax.security.auth.callback.PasswordCallback;
-
-/**
- * All information required to set up a {@link PBEKeySpec} bar the password
- * itself (use a {@link PasswordCallback})
- */
-public class PBEKeySpecCallback implements Callback {
-       private String secretKeyFactory;
-       private byte[] salt;
-       private Integer iterationCount;
-       /** Can be null for some algorithms */
-       private Integer keyLength;
-       /** Can be null, will trigger secret key encryption if not */
-       private String secretKeyEncryption;
-
-       private String encryptedPasswordHashCipher;
-       private byte[] encryptedPasswordHash;
-
-       public void set(String secretKeyFactory, byte[] salt,
-                       Integer iterationCount, Integer keyLength,
-                       String secretKeyEncryption) {
-               this.secretKeyFactory = secretKeyFactory;
-               this.salt = salt;
-               this.iterationCount = iterationCount;
-               this.keyLength = keyLength;
-               this.secretKeyEncryption = secretKeyEncryption;
-//             this.encryptedPasswordHashCipher = encryptedPasswordHashCipher;
-//             this.encryptedPasswordHash = encryptedPasswordHash;
-       }
-
-       public String getSecretKeyFactory() {
-               return secretKeyFactory;
-       }
-
-       public byte[] getSalt() {
-               return salt;
-       }
-
-       public Integer getIterationCount() {
-               return iterationCount;
-       }
-
-       public Integer getKeyLength() {
-               return keyLength;
-       }
-
-       public String getSecretKeyEncryption() {
-               return secretKeyEncryption;
-       }
-
-       public String getEncryptedPasswordHashCipher() {
-               return encryptedPasswordHashCipher;
-       }
-
-       public byte[] getEncryptedPasswordHash() {
-               return encryptedPasswordHash;
-       }
-
-}
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/crypto/PasswordBasedEncryption.java b/org.argeo.security.core/src/main/java/org/argeo/security/crypto/PasswordBasedEncryption.java
deleted file mode 100644 (file)
index aec25ac..0000000
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.security.crypto;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.security.GeneralSecurityException;
-import java.security.InvalidKeyException;
-import java.security.Key;
-import java.security.Security;
-
-import javax.crypto.Cipher;
-import javax.crypto.CipherInputStream;
-import javax.crypto.CipherOutputStream;
-import javax.crypto.SecretKey;
-import javax.crypto.SecretKeyFactory;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.PBEKeySpec;
-import javax.crypto.spec.SecretKeySpec;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.ArgeoException;
-import org.argeo.StreamUtils;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-
-/** Simple password based encryption / decryption */
-public class PasswordBasedEncryption {
-       private final static Log log = LogFactory
-                       .getLog(PasswordBasedEncryption.class);
-
-       static {
-               Security.addProvider(new BouncyCastleProvider());
-       }
-
-       public final static Integer DEFAULT_ITERATION_COUNT = 1024;
-       /** Stronger with 256, but causes problem with Oracle JVM */
-       public final static Integer DEFAULT_SECRETE_KEY_LENGTH = 256;
-       public final static Integer DEFAULT_SECRETE_KEY_LENGTH_RESTRICTED = 128;
-       public final static String DEFAULT_SECRETE_KEY_FACTORY = "PBKDF2WithHmacSHA1";
-       public final static String DEFAULT_SECRETE_KEY_ENCRYPTION = "AES";
-       public final static String DEFAULT_CIPHER_NAME = "AES/CBC/PKCS5Padding";
-       public final static String DEFAULT_CHARSET = "UTF-8";
-
-       private Integer iterationCount = DEFAULT_ITERATION_COUNT;
-       private Integer secreteKeyLength = DEFAULT_SECRETE_KEY_LENGTH;
-       private String secreteKeyFactoryName = DEFAULT_SECRETE_KEY_FACTORY;
-       private String secreteKeyEncryption = DEFAULT_SECRETE_KEY_ENCRYPTION;
-       private String cipherName = DEFAULT_CIPHER_NAME;
-
-       private static byte[] DEFAULT_SALT_8 = { (byte) 0xA9, (byte) 0x9B,
-                       (byte) 0xC8, (byte) 0x32, (byte) 0x56, (byte) 0x35, (byte) 0xE3,
-                       (byte) 0x03 };
-       private static byte[] DEFAULT_IV_16 = { (byte) 0xA9, (byte) 0x9B,
-                       (byte) 0xC8, (byte) 0x32, (byte) 0x56, (byte) 0x35, (byte) 0xE3,
-                       (byte) 0x03, (byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32,
-                       (byte) 0x56, (byte) 0x35, (byte) 0xE3, (byte) 0x03 };
-
-       private Key key;
-       private Cipher ecipher;
-       private Cipher dcipher;
-
-       /**
-        * Default provider is bouncy castle, in order to have consistent behaviour
-        * across implementations
-        */
-       private String securityProviderName = "BC";
-
-       /**
-        * 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 {
-                       initKeyAndCiphers(password, passwordSalt, initializationVector);
-               } catch (InvalidKeyException e) {
-                       Integer previousSecreteKeyLength = secreteKeyLength;
-                       secreteKeyLength = DEFAULT_SECRETE_KEY_LENGTH_RESTRICTED;
-                       log.warn("'" + e.getMessage() + "', will use " + secreteKeyLength
-                                       + " secrete key length instead of "
-                                       + previousSecreteKeyLength);
-                       try {
-                               initKeyAndCiphers(password, passwordSalt, initializationVector);
-                       } catch (Exception e1) {
-                               throw new ArgeoException(
-                                               "Cannot get secret key (with restricted length)", e1);
-                       }
-               } catch (Exception e) {
-                       throw new ArgeoException("Cannot get secret key", e);
-               }
-       }
-
-       protected void initKeyAndCiphers(char[] password, byte[] passwordSalt,
-                       byte[] initializationVector) throws GeneralSecurityException {
-               byte[] salt = new byte[8];
-               System.arraycopy(passwordSalt, 0, salt, 0, salt.length);
-               // for (int i = 0; i < password.length && i < salt.length; i++)
-               // salt[i] = (byte) password[i];
-               byte[] iv = new byte[16];
-               System.arraycopy(initializationVector, 0, iv, 0, iv.length);
-
-               SecretKeyFactory keyFac = SecretKeyFactory
-                               .getInstance(getSecretKeyFactoryName());
-               PBEKeySpec keySpec = new PBEKeySpec(password, salt,
-                               getIterationCount(), getKeyLength());
-               String secKeyEncryption = getSecretKeyEncryption();
-               if (secKeyEncryption != null) {
-                       SecretKey tmp = keyFac.generateSecret(keySpec);
-                       key = new SecretKeySpec(tmp.getEncoded(), getSecretKeyEncryption());
-               } else {
-                       key = keyFac.generateSecret(keySpec);
-               }
-               ecipher = Cipher.getInstance(getCipherName(), securityProviderName);
-               ecipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
-               dcipher = Cipher.getInstance(getCipherName());
-               dcipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
-       }
-
-       public void encrypt(InputStream decryptedIn, OutputStream encryptedOut)
-                       throws IOException {
-               try {
-                       CipherOutputStream out = new CipherOutputStream(encryptedOut,
-                                       ecipher);
-                       StreamUtils.copy(decryptedIn, out);
-                       StreamUtils.closeQuietly(out);
-               } catch (IOException e) {
-                       throw e;
-               } catch (Exception e) {
-                       throw new ArgeoException("Cannot encrypt", e);
-               } finally {
-                       StreamUtils.closeQuietly(decryptedIn);
-               }
-       }
-
-       public void decrypt(InputStream encryptedIn, OutputStream decryptedOut)
-                       throws IOException {
-               try {
-                       CipherInputStream decryptedIn = new CipherInputStream(encryptedIn,
-                                       dcipher);
-                       StreamUtils.copy(decryptedIn, decryptedOut);
-               } catch (IOException e) {
-                       throw e;
-               } catch (Exception e) {
-                       throw new ArgeoException("Cannot decrypt", e);
-               } finally {
-                       StreamUtils.closeQuietly(encryptedIn);
-               }
-       }
-
-       public byte[] encryptString(String str) {
-               ByteArrayOutputStream out = null;
-               ByteArrayInputStream in = null;
-               try {
-                       out = new ByteArrayOutputStream();
-                       in = new ByteArrayInputStream(str.getBytes(DEFAULT_CHARSET));
-                       encrypt(in, out);
-                       return out.toByteArray();
-               } catch (Exception e) {
-                       throw new ArgeoException("Cannot encrypt", e);
-               } finally {
-                       StreamUtils.closeQuietly(out);
-               }
-       }
-
-       /** Closes the input stream */
-       public String decryptAsString(InputStream in) {
-               ByteArrayOutputStream out = null;
-               try {
-                       out = new ByteArrayOutputStream();
-                       decrypt(in, out);
-                       return new String(out.toByteArray(), DEFAULT_CHARSET);
-               } catch (Exception e) {
-                       throw new ArgeoException("Cannot decrypt", e);
-               } finally {
-                       StreamUtils.closeQuietly(out);
-               }
-       }
-
-       protected Key getKey() {
-               return key;
-       }
-
-       protected Cipher getEcipher() {
-               return ecipher;
-       }
-
-       protected Cipher getDcipher() {
-               return dcipher;
-       }
-
-       protected Integer getIterationCount() {
-               return iterationCount;
-       }
-
-       protected Integer getKeyLength() {
-               return secreteKeyLength;
-       }
-
-       protected String getSecretKeyFactoryName() {
-               return secreteKeyFactoryName;
-       }
-
-       protected String getSecretKeyEncryption() {
-               return secreteKeyEncryption;
-       }
-
-       protected String getCipherName() {
-               return cipherName;
-       }
-
-       public void setIterationCount(Integer iterationCount) {
-               this.iterationCount = iterationCount;
-       }
-
-       public void setSecreteKeyLength(Integer keyLength) {
-               this.secreteKeyLength = keyLength;
-       }
-
-       public void setSecreteKeyFactoryName(String secreteKeyFactoryName) {
-               this.secreteKeyFactoryName = secreteKeyFactoryName;
-       }
-
-       public void setSecreteKeyEncryption(String secreteKeyEncryption) {
-               this.secreteKeyEncryption = secreteKeyEncryption;
-       }
-
-       public void setCipherName(String cipherName) {
-               this.cipherName = cipherName;
-       }
-
-       public void setSecurityProviderName(String securityProviderName) {
-               this.securityProviderName = securityProviderName;
-       }
-}
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrKeyring.java b/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrKeyring.java
deleted file mode 100644 (file)
index 1b9f244..0000000
+++ /dev/null
@@ -1,294 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.security.jcr;
-
-import java.io.ByteArrayInputStream;
-import java.io.CharArrayReader;
-import java.io.InputStream;
-import java.io.Reader;
-import java.security.SecureRandom;
-
-import javax.crypto.Cipher;
-import javax.crypto.CipherInputStream;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.IvParameterSpec;
-import javax.jcr.Binary;
-import javax.jcr.Node;
-import javax.jcr.Property;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-
-import org.apache.commons.io.IOUtils;
-import org.argeo.ArgeoException;
-import org.argeo.jcr.ArgeoNames;
-import org.argeo.jcr.ArgeoTypes;
-import org.argeo.jcr.JcrUtils;
-import org.argeo.jcr.UserJcrUtils;
-import org.argeo.security.crypto.AbstractKeyring;
-import org.argeo.security.crypto.PBEKeySpecCallback;
-
-/** JCR based implementation of a keyring */
-public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
-       /**
-        * Stronger with 256, but causes problem with Oracle JVM, force 128 in this
-        * case
-        */
-       public final static Long DEFAULT_SECRETE_KEY_LENGTH = 256l;
-       public final static String DEFAULT_SECRETE_KEY_FACTORY = "PBKDF2WithHmacSHA1";
-       public final static String DEFAULT_SECRETE_KEY_ENCRYPTION = "AES";
-       public final static String DEFAULT_CIPHER_NAME = "AES/CBC/PKCS5Padding";
-
-       private Integer iterationCountFactor = 200;
-       private Long secreteKeyLength = DEFAULT_SECRETE_KEY_LENGTH;
-       private String secreteKeyFactoryName = DEFAULT_SECRETE_KEY_FACTORY;
-       private String secreteKeyEncryption = DEFAULT_SECRETE_KEY_ENCRYPTION;
-       private String cipherName = DEFAULT_CIPHER_NAME;
-
-       private Session session;
-
-       /**
-        * When setup is called the session has not yet been saved and we don't want
-        * to save it since there maybe other data which would be inconsistent. So
-        * we keep a reference to this node which will then be used (an reset to
-        * null) when handling the PBE callback. We keep one per thread in case
-        * multiple users are accessing the same instance of a keyring.
-        */
-       private ThreadLocal<Node> notYetSavedKeyring = new ThreadLocal<Node>() {
-
-               @Override
-               protected Node initialValue() {
-                       return null;
-               }
-       };
-
-       @Override
-       protected Boolean isSetup() {
-               try {
-                       if (notYetSavedKeyring.get() != null)
-                               return true;
-
-                       Node userHome = UserJcrUtils.getUserHome(session);
-                       return userHome.hasNode(ARGEO_KEYRING);
-               } catch (RepositoryException e) {
-                       throw new ArgeoException("Cannot check whether keyring is setup", e);
-               }
-       }
-
-       @Override
-       protected void setup(char[] password) {
-               Binary binary = null;
-               InputStream in = null;
-               try {
-                       Node userHome = UserJcrUtils.getUserHome(session);
-                       if (userHome.hasNode(ARGEO_KEYRING))
-                               throw new ArgeoException("Keyring already setup");
-                       Node keyring = userHome.addNode(ARGEO_KEYRING);
-                       keyring.addMixin(ArgeoTypes.ARGEO_PBE_SPEC);
-
-                       // deterministic salt and iteration count based on username
-                       String username = session.getUserID();
-                       byte[] salt = new byte[8];
-                       byte[] usernameBytes = username.getBytes();
-                       for (int i = 0; i < salt.length; i++) {
-                               if (i < usernameBytes.length)
-                                       salt[i] = usernameBytes[i];
-                               else
-                                       salt[i] = 0;
-                       }
-                       in = new ByteArrayInputStream(salt);
-                       binary = session.getValueFactory().createBinary(in);
-                       keyring.setProperty(ARGEO_SALT, binary);
-
-                       Integer iterationCount = username.length() * iterationCountFactor;
-                       keyring.setProperty(ARGEO_ITERATION_COUNT, iterationCount);
-
-                       // default algo
-                       // TODO check if algo and key length are available, use DES if not
-                       keyring.setProperty(ARGEO_SECRET_KEY_FACTORY, secreteKeyFactoryName);
-                       keyring.setProperty(ARGEO_KEY_LENGTH, secreteKeyLength);
-                       keyring.setProperty(ARGEO_SECRET_KEY_ENCRYPTION,
-                                       secreteKeyEncryption);
-                       keyring.setProperty(ARGEO_CIPHER, cipherName);
-
-                       // encrypted password hash
-                       // IOUtils.closeQuietly(in);
-                       // JcrUtils.closeQuietly(binary);
-                       // byte[] btPass = hash(password, salt, iterationCount);
-                       // in = new ByteArrayInputStream(btPass);
-                       // binary = session.getValueFactory().createBinary(in);
-                       // keyring.setProperty(ARGEO_PASSWORD, binary);
-
-                       notYetSavedKeyring.set(keyring);
-               } catch (Exception e) {
-                       throw new ArgeoException("Cannot setup keyring", e);
-               } finally {
-                       JcrUtils.closeQuietly(binary);
-                       IOUtils.closeQuietly(in);
-                       // JcrUtils.discardQuietly(session);
-               }
-       }
-
-       @Override
-       protected void handleKeySpecCallback(PBEKeySpecCallback pbeCallback) {
-               try {
-                       Node userHome = UserJcrUtils.getUserHome(session);
-                       Node keyring;
-                       if (userHome.hasNode(ARGEO_KEYRING))
-                               keyring = userHome.getNode(ARGEO_KEYRING);
-                       else if (notYetSavedKeyring.get() != null)
-                               keyring = notYetSavedKeyring.get();
-                       else
-                               throw new ArgeoException("Keyring not setup");
-
-                       pbeCallback.set(keyring.getProperty(ARGEO_SECRET_KEY_FACTORY)
-                                       .getString(), JcrUtils.getBinaryAsBytes(keyring
-                                       .getProperty(ARGEO_SALT)),
-                                       (int) keyring.getProperty(ARGEO_ITERATION_COUNT).getLong(),
-                                       (int) keyring.getProperty(ARGEO_KEY_LENGTH).getLong(),
-                                       keyring.getProperty(ARGEO_SECRET_KEY_ENCRYPTION)
-                                                       .getString());
-
-                       if (notYetSavedKeyring.get() != null)
-                               notYetSavedKeyring.remove();
-               } catch (RepositoryException e) {
-                       throw new ArgeoException("Cannot handle key spec callback", e);
-               }
-       }
-
-       /** The parent node must already exist at this path. */
-       @Override
-       protected synchronized void encrypt(String path, InputStream unencrypted) {
-               // should be called first for lazy initialization
-               SecretKey secretKey = getSecretKey();
-
-               Binary binary = null;
-               InputStream in = null;
-               try {
-                       Cipher cipher = createCipher();
-                       Node node;
-                       if (!session.nodeExists(path)) {
-                               String parentPath = JcrUtils.parentPath(path);
-                               if (!session.nodeExists(parentPath))
-                                       throw new ArgeoException("No parent node of " + path);
-                               Node parentNode = session.getNode(parentPath);
-                               node = parentNode.addNode(JcrUtils.nodeNameFromPath(path));
-                       } else {
-                               node = session.getNode(path);
-                       }
-                       node.addMixin(ArgeoTypes.ARGEO_ENCRYPTED);
-                       SecureRandom random = new SecureRandom();
-                       byte[] iv = new byte[16];
-                       random.nextBytes(iv);
-                       cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));
-                       JcrUtils.setBinaryAsBytes(node, ARGEO_IV, iv);
-
-                       in = new CipherInputStream(unencrypted, cipher);
-                       binary = session.getValueFactory().createBinary(in);
-                       node.setProperty(Property.JCR_DATA, binary);
-                       session.save();
-               } catch (Exception e) {
-                       throw new ArgeoException("Cannot encrypt", e);
-               } finally {
-                       IOUtils.closeQuietly(unencrypted);
-                       IOUtils.closeQuietly(in);
-                       JcrUtils.closeQuietly(binary);
-               }
-       }
-
-       @Override
-       protected synchronized InputStream decrypt(String path) {
-               Binary binary = null;
-               InputStream encrypted = null;
-               Reader reader = null;
-               try {
-                       if (!session.nodeExists(path)) {
-                               char[] password = ask();
-                               reader = new CharArrayReader(password);
-                               return new ByteArrayInputStream(IOUtils.toByteArray(reader));
-                       } else {
-                               // should be called first for lazy initialisation
-                               SecretKey secretKey = getSecretKey();
-
-                               Cipher cipher = createCipher();
-
-                               Node node = session.getNode(path);
-                               if (node.hasProperty(ARGEO_IV)) {
-                                       byte[] iv = JcrUtils.getBinaryAsBytes(node
-                                                       .getProperty(ARGEO_IV));
-                                       cipher.init(Cipher.DECRYPT_MODE, secretKey,
-                                                       new IvParameterSpec(iv));
-                               } else {
-                                       cipher.init(Cipher.DECRYPT_MODE, secretKey);
-                               }
-
-                               binary = node.getProperty(Property.JCR_DATA).getBinary();
-                               encrypted = binary.getStream();
-                               return new CipherInputStream(encrypted, cipher);
-                       }
-               } catch (Exception e) {
-                       throw new ArgeoException("Cannot decrypt", e);
-               } finally {
-                       IOUtils.closeQuietly(encrypted);
-                       IOUtils.closeQuietly(reader);
-                       JcrUtils.closeQuietly(binary);
-               }
-       }
-
-       protected Cipher createCipher() {
-               try {
-                       Node userHome = UserJcrUtils.getUserHome(session);
-                       if (!userHome.hasNode(ARGEO_KEYRING))
-                               throw new ArgeoException("Keyring not setup");
-                       Node keyring = userHome.getNode(ARGEO_KEYRING);
-                       Cipher cipher = Cipher.getInstance(keyring
-                                       .getProperty(ARGEO_CIPHER).getString(),
-                                       getSecurityProvider());
-                       return cipher;
-               } catch (Exception e) {
-                       throw new ArgeoException("Cannot get cipher", e);
-               }
-       }
-
-       public synchronized void changePassword(char[] oldPassword,
-                       char[] newPassword) {
-               // TODO decrypt with old pw / encrypt with new pw all argeo:encrypted
-       }
-
-       public synchronized void setSession(Session session) {
-               this.session = session;
-       }
-
-       public void setIterationCountFactor(Integer iterationCountFactor) {
-               this.iterationCountFactor = iterationCountFactor;
-       }
-
-       public void setSecreteKeyLength(Long keyLength) {
-               this.secreteKeyLength = keyLength;
-       }
-
-       public void setSecreteKeyFactoryName(String secreteKeyFactoryName) {
-               this.secreteKeyFactoryName = secreteKeyFactoryName;
-       }
-
-       public void setSecreteKeyEncryption(String secreteKeyEncryption) {
-               this.secreteKeyEncryption = secreteKeyEncryption;
-       }
-
-       public void setCipherName(String cipherName) {
-               this.cipherName = cipherName;
-       }
-
-}
\ No newline at end of file
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrSecurityModel.java b/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrSecurityModel.java
deleted file mode 100644 (file)
index e9ab89c..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.security.jcr;
-
-import java.util.List;
-
-import javax.jcr.Node;
-import javax.jcr.Session;
-
-/**
- * Manages data expected by the Argeo security model, such as user home and
- * profile.
- */
-public interface JcrSecurityModel {
-       /**
-        * To be called before user details are loaded. Make sure than any logged in
-        * user has a home directory with full access and a profile with information
-        * about him (read access)
-        * 
-        * @return the user profile (whose parent is the user home), never null
-        */
-       public Node sync(Session session, String username, List<String> roles);
-}
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrUserDetails.java b/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrUserDetails.java
deleted file mode 100644 (file)
index 2f7b97b..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.security.jcr;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.jcr.Node;
-import javax.jcr.Property;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-
-import org.argeo.jcr.ArgeoNames;
-import org.argeo.jcr.UserJcrUtils;
-import org.springframework.security.BadCredentialsException;
-import org.springframework.security.DisabledException;
-import org.springframework.security.GrantedAuthority;
-import org.springframework.security.GrantedAuthorityImpl;
-import org.springframework.security.LockedException;
-import org.springframework.security.userdetails.User;
-
-/** User details based on a user profile node. */
-public class JcrUserDetails extends User implements ArgeoNames {
-       private static final long serialVersionUID = -8142764995842559646L;
-       private final String homePath;
-       private final String securityWorkspace;
-
-       /** Human readable user name */
-       private String displayName;
-
-       protected JcrUserDetails(String securityWorkspace, String homePath,
-                       String username, String password, boolean enabled,
-                       boolean accountNonExpired, boolean credentialsNonExpired,
-                       boolean accountNonLocked, GrantedAuthority[] authorities)
-                       throws IllegalArgumentException {
-               super(username, password, enabled, accountNonExpired,
-                               credentialsNonExpired, accountNonLocked, authorities);
-               this.homePath = homePath;
-               this.securityWorkspace = securityWorkspace;
-       }
-
-       public JcrUserDetails(Node userProfile, String password,
-                       GrantedAuthority[] authorities) throws RepositoryException {
-               super(
-                               userProfile.getProperty(ARGEO_USER_ID).getString(),
-                               password,
-                               userProfile.getProperty(ARGEO_ENABLED).getBoolean(),
-                               userProfile.getProperty(ARGEO_ACCOUNT_NON_EXPIRED).getBoolean(),
-                               userProfile.getProperty(ARGEO_CREDENTIALS_NON_EXPIRED)
-                                               .getBoolean(), userProfile.getProperty(
-                                               ARGEO_ACCOUNT_NON_LOCKED).getBoolean(), authorities);
-               // human readable name
-               if (userProfile.hasProperty(Property.JCR_TITLE)) {
-                       displayName = userProfile.getProperty(Property.JCR_TITLE)
-                                       .getString();
-                       if (displayName.trim().equals(""))
-                               displayName = null;
-               }
-               if (displayName == null)
-                       displayName = userProfile.getProperty(ARGEO_USER_ID).getString();
-               // home is defined as the parent of the profile
-               homePath = userProfile.getParent().getPath();
-               securityWorkspace = userProfile.getSession().getWorkspace().getName();
-       }
-
-       /**
-        * Convenience constructor
-        * 
-        * @param session
-        *            the security session
-        * @param username
-        *            the username
-        * @param password
-        *            the password, can be null
-        * @param authorities
-        *            the granted authorities
-        */
-       public JcrUserDetails(Session session, String username, String password,
-                       GrantedAuthority[] authorities) throws RepositoryException {
-               this(UserJcrUtils.getUserProfile(session, username),
-                               password != null ? password : "", authorities);
-       }
-
-       /**
-        * Check the account status in JCR, throwing the exceptions expected by
-        * Spring security if needed.
-        */
-       public static void checkAccountStatus(Node userProfile) {
-               try {
-                       if (!userProfile.getProperty(ARGEO_ENABLED).getBoolean())
-                               throw new DisabledException(userProfile.getPath()
-                                               + " is disabled");
-                       if (!userProfile.getProperty(ARGEO_ACCOUNT_NON_LOCKED).getBoolean())
-                               throw new LockedException(userProfile.getPath() + " is locked");
-               } catch (RepositoryException e) {
-                       throw new BadCredentialsException("Cannot check account status", e);
-               }
-       }
-
-       /** Clone immutable with new roles */
-       public JcrUserDetails cloneWithNewRoles(List<String> roles) {
-               List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
-               for (String role : roles) {
-                       authorities.add(new GrantedAuthorityImpl(role));
-               }
-               return new JcrUserDetails(securityWorkspace, homePath, getUsername(),
-                               getPassword(), isEnabled(), isAccountNonExpired(),
-                               isAccountNonExpired(), isAccountNonLocked(),
-                               authorities.toArray(new GrantedAuthority[authorities.size()]));
-       }
-
-       /** Clone immutable with new password */
-       public JcrUserDetails cloneWithNewPassword(String password) {
-               return new JcrUserDetails(securityWorkspace, homePath, getUsername(),
-                               password, isEnabled(), isAccountNonExpired(),
-                               isAccountNonExpired(), isAccountNonLocked(), getAuthorities());
-       }
-
-       public String getHomePath() {
-               return homePath;
-       }
-
-       /** Not yet API */
-       public String getSecurityWorkspace() {
-               return securityWorkspace;
-       }
-
-       /** The human readable name of this user */
-       public String getDisplayName() {
-               return displayName;
-       }
-
-       @Override
-       public String toString() {
-               return getDisplayName();
-       }
-
-}
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/jcr/OsJcrAuthenticationProvider.java b/org.argeo.security.core/src/main/java/org/argeo/security/jcr/OsJcrAuthenticationProvider.java
deleted file mode 100644 (file)
index aa95e32..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.security.jcr;
-
-import javax.jcr.Node;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-
-import org.argeo.ArgeoException;
-import org.argeo.jcr.JcrUtils;
-import org.argeo.security.OsAuthenticationToken;
-import org.argeo.security.SecurityUtils;
-import org.argeo.security.core.OsAuthenticationProvider;
-import org.springframework.security.Authentication;
-import org.springframework.security.AuthenticationException;
-import org.springframework.security.BadCredentialsException;
-import org.springframework.security.GrantedAuthority;
-import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
-import org.springframework.security.userdetails.UserDetails;
-
-/** Relies on OS to authenticate and additionally setup JCR */
-public class OsJcrAuthenticationProvider extends OsAuthenticationProvider {
-       private Repository repository;
-       private Session nodeSession;
-
-       private UserDetails userDetails;
-       private JcrSecurityModel jcrSecurityModel = new SimpleJcrSecurityModel();
-
-       private final static String JVM_OSUSER = System.getProperty("user.name");
-
-       public void init() {
-               try {
-                       nodeSession = repository.login();
-               } catch (RepositoryException e) {
-                       throw new ArgeoException("Cannot initialize", e);
-               }
-       }
-
-       public void destroy() {
-               JcrUtils.logoutQuietly(nodeSession);
-       }
-
-       public Authentication authenticate(Authentication authentication)
-                       throws AuthenticationException {
-               if (authentication instanceof UsernamePasswordAuthenticationToken) {
-                       // deal with remote access to internal server
-                       // FIXME very primitive and unsecure at this sSession adminSession
-                       // =tage
-                       // consider using the keyring for username / password authentication
-                       // or certificate
-                       UsernamePasswordAuthenticationToken upat = (UsernamePasswordAuthenticationToken) authentication;
-                       if (!upat.getPrincipal().toString().equals(JVM_OSUSER))
-                               throw new BadCredentialsException("Wrong credentials");
-                       UsernamePasswordAuthenticationToken authen = new UsernamePasswordAuthenticationToken(
-                                       authentication.getPrincipal(),
-                                       authentication.getCredentials(), getBaseAuthorities());
-                       authen.setDetails(userDetails);
-                       return authen;
-               } else if (authentication instanceof OsAuthenticationToken) {
-                       OsAuthenticationToken authen = (OsAuthenticationToken) super
-                                       .authenticate(authentication);
-                       try {
-                               // WARNING: at this stage we assume that the java properties
-                               // will have the same value
-                               GrantedAuthority[] authorities = getBaseAuthorities();
-                               String username = JVM_OSUSER;
-                               Node userProfile = jcrSecurityModel.sync(nodeSession, username,
-                                               SecurityUtils.authoritiesToStringList(authorities));
-                               JcrUserDetails.checkAccountStatus(userProfile);
-
-                               userDetails = new JcrUserDetails(userProfile, authen
-                                               .getCredentials().toString(), authorities);
-                               authen.setDetails(userDetails);
-                               return authen;
-                       } catch (RepositoryException e) {
-                               JcrUtils.discardQuietly(nodeSession);
-                               throw new ArgeoException(
-                                               "Unexpected exception when synchronizing OS and JCR security ",
-                                               e);
-                       }
-               } else {
-                       throw new ArgeoException("Unsupported authentication "
-                                       + authentication.getClass());
-               }
-       }
-
-       public void setRepository(Repository repository) {
-               this.repository = repository;
-       }
-
-       public void setJcrSecurityModel(JcrSecurityModel jcrSecurityModel) {
-               this.jcrSecurityModel = jcrSecurityModel;
-       }
-
-       @SuppressWarnings("rawtypes")
-       public boolean supports(Class authentication) {
-               return OsAuthenticationToken.class.isAssignableFrom(authentication)
-                               || UsernamePasswordAuthenticationToken.class
-                                               .isAssignableFrom(authentication);
-       }
-}
\ No newline at end of file
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/jcr/OsJcrUserAdminService.java b/org.argeo.security.core/src/main/java/org/argeo/security/jcr/OsJcrUserAdminService.java
deleted file mode 100644 (file)
index c25bdb8..0000000
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.security.jcr;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import javax.jcr.Node;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-
-import org.argeo.ArgeoException;
-import org.argeo.jcr.JcrUtils;
-import org.argeo.jcr.UserJcrUtils;
-import org.argeo.security.UserAdminService;
-import org.springframework.dao.DataAccessException;
-import org.springframework.security.userdetails.User;
-import org.springframework.security.userdetails.UserDetails;
-import org.springframework.security.userdetails.UsernameNotFoundException;
-
-/**
- * Dummy user service to be used when running as a single OS user (typically
- * desktop). TODO integrate with JCR user / groups
- */
-public class OsJcrUserAdminService implements UserAdminService {
-       private Repository repository;
-
-       /** In memory roles provided by applications. */
-       private List<String> roles = new ArrayList<String>();
-
-       // private Session adminSession;
-
-       public void init() {
-               // try {
-               // adminSession = repository.login();
-               // } catch (RepositoryException e) {
-               // throw new ArgeoException("Cannot initialize", e);
-               // }
-       }
-
-       public void destroy() {
-               // JcrUtils.logoutQuietly(adminSession);
-       }
-
-       /** <b>Unsupported</b> */
-       public void createUser(UserDetails user) {
-               throw new UnsupportedOperationException();
-       }
-
-       /** Does nothing */
-       public void updateUser(UserDetails user) {
-
-       }
-
-       /** <b>Unsupported</b> */
-       public void deleteUser(String username) {
-               throw new UnsupportedOperationException();
-       }
-
-       /** <b>Unsupported</b> */
-       public void changePassword(String oldPassword, String newPassword) {
-               throw new UnsupportedOperationException();
-       }
-
-       public boolean userExists(String username) {
-               if (getSPropertyUsername().equals(username))
-                       return true;
-               else
-                       return false;
-       }
-
-       public UserDetails loadUserByUsername(String username)
-                       throws UsernameNotFoundException, DataAccessException {
-               if (getSPropertyUsername().equals(username)) {
-                       UserDetails userDetails;
-                       if (repository != null) {
-                               Session adminSession = null;
-                               try {
-                                       adminSession = repository.login();
-                                       Node userProfile = UserJcrUtils.getUserProfile(
-                                                       adminSession, username);
-                                       userDetails = new JcrUserDetails(userProfile, "",
-                                                       OsJcrAuthenticationProvider.getBaseAuthorities());
-                               } catch (RepositoryException e) {
-                                       throw new ArgeoException(
-                                                       "Cannot retrieve user profile for " + username, e);
-                               } finally {
-                                       JcrUtils.logoutQuietly(adminSession);
-                               }
-                       } else {
-                               userDetails = new User(username, "", true, true, true, true,
-                                               OsJcrAuthenticationProvider.getBaseAuthorities());
-                       }
-                       return userDetails;
-               } else {
-                       throw new UnsupportedOperationException();
-               }
-       }
-
-       protected final String getSPropertyUsername() {
-               return System.getProperty("user.name");
-       }
-
-       public Set<String> listUsers() {
-               Set<String> set = new HashSet<String>();
-               set.add(getSPropertyUsername());
-               return set;
-       }
-
-       public Set<String> listUsersInRole(String role) {
-               Set<String> set = new HashSet<String>();
-               set.add(getSPropertyUsername());
-               return set;
-       }
-
-       /** Does nothing */
-       public void synchronize() {
-       }
-
-       /** <b>Unsupported</b> */
-       public void newRole(String role) {
-               roles.add(role);
-       }
-
-       public Set<String> listEditableRoles() {
-               return new HashSet<String>(roles);
-       }
-
-       /** <b>Unsupported</b> */
-       public void deleteRole(String role) {
-               roles.remove(role);
-       }
-
-       public void setRepository(Repository repository) {
-               this.repository = repository;
-       }
-}
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/jcr/RemoteJcrAuthenticationProvider.java b/org.argeo.security.core/src/main/java/org/argeo/security/jcr/RemoteJcrAuthenticationProvider.java
deleted file mode 100644 (file)
index 87208b2..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.security.jcr;
-
-import java.util.ArrayList;
-import java.util.Dictionary;
-import java.util.Hashtable;
-import java.util.List;
-
-import javax.jcr.Node;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.RepositoryFactory;
-import javax.jcr.Session;
-import javax.jcr.SimpleCredentials;
-import javax.jcr.Value;
-
-import org.argeo.ArgeoException;
-import org.argeo.jcr.ArgeoJcrConstants;
-import org.argeo.jcr.ArgeoNames;
-import org.argeo.jcr.UserJcrUtils;
-import org.argeo.security.NodeAuthenticationToken;
-import org.osgi.framework.BundleContext;
-import org.springframework.security.Authentication;
-import org.springframework.security.AuthenticationException;
-import org.springframework.security.BadCredentialsException;
-import org.springframework.security.GrantedAuthority;
-import org.springframework.security.GrantedAuthorityImpl;
-import org.springframework.security.providers.AuthenticationProvider;
-
-/** Connects to a JCR repository and delegates authentication to it. */
-public class RemoteJcrAuthenticationProvider implements AuthenticationProvider,
-               ArgeoNames {
-       private RepositoryFactory repositoryFactory;
-       private BundleContext bundleContext;
-
-       public final static String ROLE_REMOTE = "ROLE_REMOTE";
-
-       public Authentication authenticate(Authentication authentication)
-                       throws AuthenticationException {
-               NodeAuthenticationToken siteAuth = (NodeAuthenticationToken) authentication;
-               String url = siteAuth.getUrl();
-               if (url == null)// TODO? login on own node
-                       throw new ArgeoException("No url set in " + siteAuth);
-               Session session;
-
-               Node userProfile;
-               try {
-                       SimpleCredentials sp = new SimpleCredentials(siteAuth.getName(),
-                                       siteAuth.getCredentials().toString().toCharArray());
-                       // get repository
-                       Repository repository = new RemoteJcrRepositoryWrapper(
-                                       repositoryFactory, url, sp);
-                       if (bundleContext != null) {
-                               Dictionary<String, String> serviceProperties = new Hashtable<String, String>();
-                               serviceProperties.put(ArgeoJcrConstants.JCR_REPOSITORY_ALIAS,
-                                               ArgeoJcrConstants.ALIAS_NODE);
-                               serviceProperties
-                                               .put(ArgeoJcrConstants.JCR_REPOSITORY_URI, url);
-                               bundleContext.registerService(Repository.class.getName(),
-                                               repository, serviceProperties);
-                       }
-                       // Repository repository = ArgeoJcrUtils.getRepositoryByUri(
-                       // repositoryFactory, url);
-                       // if (repository == null)
-                       // throw new ArgeoException("Cannot connect to " + url);
-
-                       session = repository.login(sp, null);
-
-                       userProfile = UserJcrUtils.getUserProfile(session, sp.getUserID());
-                       JcrUserDetails.checkAccountStatus(userProfile);
-
-                       // Node userHome = UserJcrUtils.getUserHome(session);
-                       // if (userHome == null ||
-                       // !userHome.hasNode(ArgeoNames.ARGEO_PROFILE))
-                       // throw new ArgeoException("No profile for user "
-                       // + siteAuth.getName() + " in security workspace "
-                       // + siteAuth.getSecurityWorkspace() + " of "
-                       // + siteAuth.getUrl());
-                       // userProfile = userHome.getNode(ArgeoNames.ARGEO_PROFILE);
-               } catch (RepositoryException e) {
-                       throw new BadCredentialsException(
-                                       "Cannot authenticate " + siteAuth, e);
-               }
-
-               try {
-                       // Node userHome = UserJcrUtils.getUserHome(session);
-                       // retrieve remote roles
-                       List<GrantedAuthority> authoritiesList = new ArrayList<GrantedAuthority>();
-                       if (userProfile != null
-                                       && userProfile.hasProperty(ArgeoNames.ARGEO_REMOTE_ROLES)) {
-                               Value[] roles = userProfile.getProperty(
-                                               ArgeoNames.ARGEO_REMOTE_ROLES).getValues();
-                               for (int i = 0; i < roles.length; i++)
-                                       authoritiesList.add(new GrantedAuthorityImpl(roles[i]
-                                                       .getString()));
-                       }
-                       authoritiesList.add(new GrantedAuthorityImpl(ROLE_REMOTE));
-
-                       // create authenticated objects
-                       GrantedAuthority[] authorities = authoritiesList
-                                       .toArray(new GrantedAuthority[authoritiesList.size()]);
-                       JcrUserDetails userDetails = new JcrUserDetails(userProfile,
-                                       siteAuth.getCredentials().toString(), authorities);
-                       NodeAuthenticationToken authenticated = new NodeAuthenticationToken(
-                                       siteAuth, authorities);
-                       authenticated.setDetails(userDetails);
-                       return authenticated;
-               } catch (RepositoryException e) {
-                       throw new ArgeoException(
-                                       "Unexpected exception when authenticating to " + url, e);
-               }
-       }
-
-       @SuppressWarnings("rawtypes")
-       public boolean supports(Class authentication) {
-               return NodeAuthenticationToken.class.isAssignableFrom(authentication);
-       }
-
-       public void setRepositoryFactory(RepositoryFactory repositoryFactory) {
-               this.repositoryFactory = repositoryFactory;
-       }
-
-       public void setBundleContext(BundleContext bundleContext) {
-               this.bundleContext = bundleContext;
-       }
-
-}
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/jcr/RemoteJcrRepositoryWrapper.java b/org.argeo.security.core/src/main/java/org/argeo/security/jcr/RemoteJcrRepositoryWrapper.java
deleted file mode 100644 (file)
index f0ad3a3..0000000
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.security.jcr;
-
-import javax.jcr.Credentials;
-import javax.jcr.LoginException;
-import javax.jcr.NoSuchWorkspaceException;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.RepositoryFactory;
-import javax.jcr.Session;
-import javax.jcr.SimpleCredentials;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.ArgeoException;
-import org.argeo.jcr.ArgeoJcrUtils;
-import org.argeo.jcr.JcrRepositoryWrapper;
-import org.argeo.security.NodeAuthenticationToken;
-import org.argeo.security.SystemAuthentication;
-import org.springframework.security.Authentication;
-import org.springframework.security.context.SecurityContextHolder;
-import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
-
-/**
- * Wrapper around a remote Jackrabbit repository which allows to simplify
- * configuration and intercept some actions. It exposes itself as a
- * {@link Repository}.
- */
-public class RemoteJcrRepositoryWrapper extends JcrRepositoryWrapper {
-       private final static Log log = LogFactory
-                       .getLog(RemoteJcrRepositoryWrapper.class);
-
-       private String uri = null;
-
-       private RepositoryFactory repositoryFactory;
-
-       // remote
-       private Credentials remoteSystemCredentials = null;
-
-       /**
-        * Empty constructor, {@link #init()} should be called after properties have
-        * been set
-        */
-       public RemoteJcrRepositoryWrapper() {
-       }
-
-       /**
-        * Embedded constructor, calling the {@link #init()} method.
-        * 
-        * @param alias
-        *            if not null the repository will be published under this alias
-        */
-       public RemoteJcrRepositoryWrapper(RepositoryFactory repositoryFactory,
-                       String uri, Credentials remoteSystemCredentials) {
-               this.repositoryFactory = repositoryFactory;
-               this.uri = uri;
-               this.remoteSystemCredentials = remoteSystemCredentials;
-               init();
-       }
-
-       public void init() {
-               Repository repository = createJackrabbitRepository();
-               setRepository(repository);
-       }
-
-       /** Actually creates the new repository. */
-       protected Repository createJackrabbitRepository() {
-               long begin = System.currentTimeMillis();
-               try {
-                       if (uri == null || uri.trim().equals(""))
-                               throw new ArgeoException("Remote URI not set");
-
-                       Repository repository = ArgeoJcrUtils.getRepositoryByUri(
-                                       repositoryFactory, uri);
-                       if (repository == null)
-                               throw new ArgeoException("Remote JCR repository " + uri
-                                               + " not found");
-                       double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
-                       log.info("Created remote JCR repository in " + duration
-                                       + " s from URI " + uri);
-                       // we assume that the data model of the remote repository has
-                       // been properly initialized
-                       return repository;
-               } catch (Exception e) {
-                       throw new ArgeoException("Cannot create remote JCR repository "
-                                       + uri, e);
-               }
-       }
-
-       /** Shutdown the repository */
-       public void destroy() throws Exception {
-               super.destroy();
-       }
-
-       /** Central login method */
-       public Session login(Credentials credentials, String workspaceName)
-                       throws LoginException, NoSuchWorkspaceException,
-                       RepositoryException {
-
-               // retrieve credentials for remote
-               if (credentials == null) {
-                       Authentication authentication = SecurityContextHolder.getContext()
-                                       .getAuthentication();
-                       if (authentication != null) {
-                               if (authentication instanceof UsernamePasswordAuthenticationToken) {
-                                       UsernamePasswordAuthenticationToken upat = (UsernamePasswordAuthenticationToken) authentication;
-                                       credentials = new SimpleCredentials(upat.getName(), upat
-                                                       .getCredentials().toString().toCharArray());
-                               } else if ((authentication instanceof SystemAuthentication)
-                                               || (authentication instanceof NodeAuthenticationToken)) {
-                                       credentials = remoteSystemCredentials;
-                               }
-                       }
-               }
-
-               return super.login(credentials, workspaceName);
-       }
-
-       public void setUri(String uri) {
-               this.uri = uri;
-       }
-
-       public void setRepositoryFactory(RepositoryFactory repositoryFactory) {
-               this.repositoryFactory = repositoryFactory;
-       }
-
-       public void setRemoteSystemCredentials(Credentials remoteSystemCredentials) {
-               this.remoteSystemCredentials = remoteSystemCredentials;
-       }
-
-}
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/jcr/SecureThreadBoundSession.java b/org.argeo.security.core/src/main/java/org/argeo/security/jcr/SecureThreadBoundSession.java
deleted file mode 100644 (file)
index b398774..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.security.jcr;
-
-import javax.jcr.Session;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.jcr.spring.ThreadBoundSession;
-import org.springframework.security.Authentication;
-import org.springframework.security.context.SecurityContextHolder;
-
-/**
- * Thread bounded JCR session factory which checks authentication and is
- * autoconfigured in Spring.
- */
-public class SecureThreadBoundSession extends ThreadBoundSession {
-       private final static Log log = LogFactory
-                       .getLog(SecureThreadBoundSession.class);
-
-       @Override
-       protected Session preCall(Session session) {
-               Authentication authentication = SecurityContextHolder.getContext()
-                               .getAuthentication();
-               if (authentication != null) {
-                       String userID = session.getUserID();
-                       String currentUserName = authentication.getName();
-                       if (currentUserName != null) {
-                               if (!userID.equals(currentUserName)) {
-                                       log.warn("Current session has user ID " + userID
-                                                       + " while logged is user is " + currentUserName
-                                                       + "(authentication=" + authentication + ")"
-                                                       + ". Re-login.");
-                                       // TODO throw an exception
-                                       return login();
-                               }
-                       }
-               }
-               return super.preCall(session);
-       }
-
-}
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/jcr/SimpleJcrSecurityModel.java b/org.argeo.security.core/src/main/java/org/argeo/security/jcr/SimpleJcrSecurityModel.java
deleted file mode 100644 (file)
index fc01587..0000000
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.security.jcr;
-
-import java.util.List;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.Value;
-import javax.jcr.security.Privilege;
-import javax.jcr.version.VersionManager;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.ArgeoException;
-import org.argeo.jcr.ArgeoJcrConstants;
-import org.argeo.jcr.ArgeoNames;
-import org.argeo.jcr.ArgeoTypes;
-import org.argeo.jcr.JcrUtils;
-import org.argeo.jcr.UserJcrUtils;
-
-/**
- * Manages data expected by the Argeo security model, such as user home and
- * profile.
- */
-public class SimpleJcrSecurityModel implements JcrSecurityModel {
-       private final static Log log = LogFactory
-                       .getLog(SimpleJcrSecurityModel.class);
-       // ArgeoNames not implemented as interface in order to ease derivation by
-       // Jackrabbit bundles
-
-       /** The home base path. */
-       private String homeBasePath = "/home";
-
-       public synchronized Node sync(Session session, String username,
-                       List<String> roles) {
-               // TODO check user name validity (e.g. should not start by ROLE_)
-
-               try {
-                       Node userHome = UserJcrUtils.getUserHome(session, username);
-                       if (userHome == null) {
-                               String homePath = generateUserPath(homeBasePath, username);
-                               userHome = JcrUtils.mkdirs(session, homePath);
-                               // userHome = JcrUtils.mkfolders(session, homePath);
-                               userHome.addMixin(ArgeoTypes.ARGEO_USER_HOME);
-                               userHome.setProperty(ArgeoNames.ARGEO_USER_ID, username);
-                               session.save();
-
-                               JcrUtils.clearAccessControList(session, homePath, username);
-                               JcrUtils.addPrivilege(session, homePath, username,
-                                               Privilege.JCR_ALL);
-                       } else {
-                               // for backward compatibility with pre 1.0 security model
-                               if (userHome.hasNode(ArgeoNames.ARGEO_PROFILE)) {
-                                       userHome.getNode(ArgeoNames.ARGEO_PROFILE).remove();
-                                       userHome.getSession().save();
-                               }
-                       }
-
-                       // Remote roles
-                       if (roles != null) {
-                               // writeRemoteRoles(userHome, roles);
-                       }
-
-                       Node userProfile = UserJcrUtils.getUserProfile(session, username);
-                       if (userProfile == null) {
-                               String personPath = generateUserPath(
-                                               ArgeoJcrConstants.PEOPLE_BASE_PATH, username);
-                               Node personBase = JcrUtils.mkdirs(session, personPath);
-                               userProfile = personBase.addNode(ArgeoNames.ARGEO_PROFILE);
-                               userProfile.addMixin(ArgeoTypes.ARGEO_USER_PROFILE);
-                               userProfile.setProperty(ArgeoNames.ARGEO_USER_ID, username);
-                               userProfile.setProperty(ArgeoNames.ARGEO_ENABLED, true);
-                               userProfile.setProperty(ArgeoNames.ARGEO_ACCOUNT_NON_EXPIRED,
-                                               true);
-                               userProfile.setProperty(ArgeoNames.ARGEO_ACCOUNT_NON_LOCKED,
-                                               true);
-                               userProfile.setProperty(
-                                               ArgeoNames.ARGEO_CREDENTIALS_NON_EXPIRED, true);
-                               session.save();
-
-                               JcrUtils.clearAccessControList(session, userProfile.getPath(),
-                                               username);
-                               JcrUtils.addPrivilege(session, userProfile.getPath(), username,
-                                               Privilege.JCR_READ);
-
-                               VersionManager versionManager = session.getWorkspace()
-                                               .getVersionManager();
-                               if (versionManager.isCheckedOut(userProfile.getPath()))
-                                       versionManager.checkin(userProfile.getPath());
-
-                       }
-
-                       // Remote roles
-                       if (roles != null) {
-                               writeRemoteRoles(userProfile, roles);
-                       }
-                       return userProfile;
-               } catch (RepositoryException e) {
-                       JcrUtils.discardQuietly(session);
-                       throw new ArgeoException("Cannot sync node security model for "
-                                       + username, e);
-               }
-       }
-
-       /** Generate path for a new user home */
-       protected String generateUserPath(String base, String username) {
-               int atIndex = username.indexOf('@');
-               if (atIndex > 0) {
-                       String domain = username.substring(0, atIndex);
-                       String name = username.substring(atIndex + 1);
-                       return base + '/' + JcrUtils.firstCharsToPath(domain, 2) + '/'
-                                       + domain + '/' + JcrUtils.firstCharsToPath(name, 2) + '/'
-                                       + name;
-               } else if (atIndex == 0 || atIndex == (username.length() - 1)) {
-                       throw new ArgeoException("Unsupported username " + username);
-               } else {
-                       return base + '/' + JcrUtils.firstCharsToPath(username, 2) + '/'
-                                       + username;
-               }
-       }
-
-       /** Write remote roles used by remote access in the home directory */
-       protected void writeRemoteRoles(Node userHome, List<String> roles)
-                       throws RepositoryException {
-               boolean writeRoles = false;
-               if (userHome.hasProperty(ArgeoNames.ARGEO_REMOTE_ROLES)) {
-                       Value[] remoteRoles = userHome.getProperty(
-                                       ArgeoNames.ARGEO_REMOTE_ROLES).getValues();
-                       if (remoteRoles.length != roles.size())
-                               writeRoles = true;
-                       else
-                               for (int i = 0; i < remoteRoles.length; i++)
-                                       if (!remoteRoles[i].getString().equals(roles.get(i)))
-                                               writeRoles = true;
-               } else
-                       writeRoles = true;
-
-               if (writeRoles) {
-                       userHome.getSession().getWorkspace().getVersionManager()
-                                       .checkout(userHome.getPath());
-                       String[] roleIds = roles.toArray(new String[roles.size()]);
-                       userHome.setProperty(ArgeoNames.ARGEO_REMOTE_ROLES, roleIds);
-                       JcrUtils.updateLastModified(userHome);
-                       userHome.getSession().save();
-                       userHome.getSession().getWorkspace().getVersionManager()
-                                       .checkin(userHome.getPath());
-                       if (log.isDebugEnabled())
-                               log.debug("Wrote remote roles " + roles + " for "
-                                               + userHome.getProperty(ArgeoNames.ARGEO_USER_ID));
-               }
-
-       }
-
-       public void setHomeBasePath(String homeBasePath) {
-               this.homeBasePath = homeBasePath;
-       }
-
-}
diff --git a/org.argeo.security.core/src/main/java/org/argeo/security/jcr/rememberme/JcrPersistentTokenRepository.java b/org.argeo.security.core/src/main/java/org/argeo/security/jcr/rememberme/JcrPersistentTokenRepository.java
deleted file mode 100644 (file)
index 37dc986..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.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/org.argeo.security.core/src/main/java/org/argeo/security/log4j/SecureLogger.java b/org.argeo.security.core/src/main/java/org/argeo/security/log4j/SecureLogger.java
deleted file mode 100644 (file)
index 1da9857..0000000
+++ /dev/null
@@ -1,360 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.security.log4j;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Map;
-import java.util.Properties;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
-
-import org.apache.log4j.AppenderSkeleton;
-import org.apache.log4j.Level;
-import org.apache.log4j.LogManager;
-import org.apache.log4j.Logger;
-import org.apache.log4j.PropertyConfigurator;
-import org.apache.log4j.spi.LoggingEvent;
-import org.argeo.ArgeoException;
-import org.argeo.ArgeoLogListener;
-import org.argeo.ArgeoLogger;
-import org.argeo.security.SecurityUtils;
-
-/** Not meant to be used directly in standard log4j config */
-public class SecureLogger implements ArgeoLogger {
-
-       private Boolean disabled = false;
-
-       private String level = null;
-
-       private Level log4jLevel = null;
-       // private Layout layout;
-
-       private Properties configuration;
-
-       private AppenderImpl appender;
-
-       private final List<ArgeoLogListener> everythingListeners = Collections
-                       .synchronizedList(new ArrayList<ArgeoLogListener>());
-       private final List<ArgeoLogListener> allUsersListeners = Collections
-                       .synchronizedList(new ArrayList<ArgeoLogListener>());
-       private final Map<String, List<ArgeoLogListener>> userListeners = Collections
-                       .synchronizedMap(new HashMap<String, List<ArgeoLogListener>>());
-
-       private BlockingQueue<LogEvent> events;
-       private LogDispatcherThread logDispatcherThread = new LogDispatcherThread();
-
-       private Integer maxLastEventsCount = 10 * 1000;
-
-       /** Marker to prevent stack overflow */
-       private ThreadLocal<Boolean> dispatching = new ThreadLocal<Boolean>() {
-
-               @Override
-               protected Boolean initialValue() {
-                       return false;
-               }
-       };
-
-       public void init() {
-               try {
-                       events = new LinkedBlockingQueue<LogEvent>();
-
-                       // if (layout != null)
-                       // setLayout(layout);
-                       // else
-                       // setLayout(new PatternLayout(pattern));
-                       appender = new AppenderImpl();
-                       reloadConfiguration();
-                       Logger.getRootLogger().addAppender(appender);
-
-                       logDispatcherThread = new LogDispatcherThread();
-                       logDispatcherThread.start();
-               } catch (Exception e) {
-                       throw new ArgeoException("Cannot initialize log4j");
-               }
-       }
-
-       public void destroy() throws Exception {
-               Logger.getRootLogger().removeAppender(appender);
-               allUsersListeners.clear();
-               for (List<ArgeoLogListener> lst : userListeners.values())
-                       lst.clear();
-               userListeners.clear();
-
-               events.clear();
-               events = null;
-               logDispatcherThread.interrupt();
-       }
-
-       // public void setLayout(Layout layout) {
-       // this.layout = layout;
-       // }
-
-       public synchronized void register(ArgeoLogListener listener,
-                       Integer numberOfPreviousEvents) {
-               String username = SecurityUtils.getCurrentThreadUsername();
-               if (username == null)
-                       throw new ArgeoException(
-                                       "Only authenticated users can register a log listener");
-
-               if (!userListeners.containsKey(username)) {
-                       List<ArgeoLogListener> lst = Collections
-                                       .synchronizedList(new ArrayList<ArgeoLogListener>());
-                       userListeners.put(username, lst);
-               }
-               userListeners.get(username).add(listener);
-               List<LogEvent> lastEvents = logDispatcherThread.getLastEvents(username,
-                               numberOfPreviousEvents);
-               for (LogEvent evt : lastEvents)
-                       dispatchEvent(listener, evt);
-       }
-
-       public synchronized void registerForAll(ArgeoLogListener listener,
-                       Integer numberOfPreviousEvents, boolean everything) {
-               if (everything)
-                       everythingListeners.add(listener);
-               else
-                       allUsersListeners.add(listener);
-               List<LogEvent> lastEvents = logDispatcherThread.getLastEvents(null,
-                               numberOfPreviousEvents);
-               for (LogEvent evt : lastEvents)
-                       if (everything || evt.getUsername() != null)
-                               dispatchEvent(listener, evt);
-       }
-
-       public synchronized void unregister(ArgeoLogListener listener) {
-               String username = SecurityUtils.getCurrentThreadUsername();
-               if (!userListeners.containsKey(username))
-                       throw new ArgeoException("No user listeners " + listener
-                                       + " registered for user " + username);
-               if (!userListeners.get(username).contains(listener))
-                       throw new ArgeoException("No user listeners " + listener
-                                       + " registered for user " + username);
-               userListeners.get(username).remove(listener);
-               if (userListeners.get(username).isEmpty())
-                       userListeners.remove(username);
-
-       }
-
-       public synchronized void unregisterForAll(ArgeoLogListener listener) {
-               everythingListeners.remove(listener);
-               allUsersListeners.remove(listener);
-       }
-
-       /** For development purpose, since using regular logging is not easy here */
-       static void stdOut(Object obj) {
-               System.out.println(obj);
-       }
-
-       // public void setPattern(String pattern) {
-       // this.pattern = pattern;
-       // }
-
-       public void setDisabled(Boolean disabled) {
-               this.disabled = disabled;
-       }
-
-       public void setLevel(String level) {
-               this.level = level;
-       }
-
-       public void setConfiguration(Properties configuration) {
-               this.configuration = configuration;
-       }
-
-       public void updateConfiguration(Properties configuration) {
-               setConfiguration(configuration);
-               reloadConfiguration();
-       }
-
-       public Properties getConfiguration() {
-               return configuration;
-       }
-
-       /** Reloads configuration (if the configuration {@link Properties} is set) */
-       protected void reloadConfiguration() {
-               if (configuration != null) {
-                       LogManager.resetConfiguration();
-                       PropertyConfigurator.configure(configuration);
-               }
-       }
-
-       protected synchronized void processLoggingEvent(LogEvent event) {
-               if (disabled)
-                       return;
-
-               if (dispatching.get())
-                       return;
-
-               if (level != null && !level.trim().equals("")) {
-                       if (log4jLevel == null || !log4jLevel.toString().equals(level))
-                               try {
-                                       log4jLevel = Level.toLevel(level);
-                               } catch (Exception e) {
-                                       System.err
-                                                       .println("Log4j level could not be set for level '"
-                                                                       + level + "', resetting it to null.");
-                                       e.printStackTrace();
-                                       level = null;
-                               }
-
-                       if (log4jLevel != null
-                                       && !event.getLoggingEvent().getLevel()
-                                                       .isGreaterOrEqual(log4jLevel)) {
-                               return;
-                       }
-               }
-
-               try {
-                       // admin listeners
-                       Iterator<ArgeoLogListener> everythingIt = everythingListeners
-                                       .iterator();
-                       while (everythingIt.hasNext())
-                               dispatchEvent(everythingIt.next(), event);
-
-                       if (event.getUsername() != null) {
-                               Iterator<ArgeoLogListener> allUsersIt = allUsersListeners
-                                               .iterator();
-                               while (allUsersIt.hasNext())
-                                       dispatchEvent(allUsersIt.next(), event);
-
-                               if (userListeners.containsKey(event.getUsername())) {
-                                       Iterator<ArgeoLogListener> userIt = userListeners.get(
-                                                       event.getUsername()).iterator();
-                                       while (userIt.hasNext())
-                                               dispatchEvent(userIt.next(), event);
-                               }
-                       }
-               } catch (Exception e) {
-                       stdOut("Cannot process logging event");
-                       e.printStackTrace();
-               }
-       }
-
-       protected void dispatchEvent(ArgeoLogListener logListener, LogEvent evt) {
-               LoggingEvent event = evt.getLoggingEvent();
-               logListener.appendLog(evt.getUsername(), event.getTimeStamp(), event
-                               .getLevel().toString(), event.getLoggerName(), event
-                               .getThreadName(), event.getMessage(), event
-                               .getThrowableStrRep());
-       }
-
-       private class AppenderImpl extends AppenderSkeleton {
-               public boolean requiresLayout() {
-                       return false;
-               }
-
-               public void close() {
-               }
-
-               @Override
-               protected void append(LoggingEvent event) {
-                       if (events != null) {
-                               try {
-                                       String username = SecurityUtils.getCurrentThreadUsername();
-                                       events.put(new LogEvent(username, event));
-                               } catch (InterruptedException e) {
-                                       // silent
-                               }
-                       }
-               }
-
-       }
-
-       private class LogDispatcherThread extends Thread {
-               /** encapsulated in order to simplify concurrency management */
-               private LinkedList<LogEvent> lastEvents = new LinkedList<LogEvent>();
-
-               public LogDispatcherThread() {
-                       super("Argeo Logging Dispatcher Thread");
-               }
-
-               public void run() {
-                       while (events != null) {
-                               try {
-                                       LogEvent loggingEvent = events.take();
-                                       processLoggingEvent(loggingEvent);
-                                       addLastEvent(loggingEvent);
-                               } catch (InterruptedException e) {
-                                       if (events == null)
-                                               return;
-                               }
-                       }
-               }
-
-               protected synchronized void addLastEvent(LogEvent loggingEvent) {
-                       if (lastEvents.size() >= maxLastEventsCount)
-                               lastEvents.poll();
-                       lastEvents.add(loggingEvent);
-               }
-
-               public synchronized List<LogEvent> getLastEvents(String username,
-                               Integer maxCount) {
-                       LinkedList<LogEvent> evts = new LinkedList<LogEvent>();
-                       ListIterator<LogEvent> it = lastEvents.listIterator(lastEvents
-                                       .size());
-                       int count = 0;
-                       while (it.hasPrevious() && (count < maxCount)) {
-                               LogEvent evt = it.previous();
-                               if (username == null || username.equals(evt.getUsername())) {
-                                       evts.push(evt);
-                                       count++;
-                               }
-                       }
-                       return evts;
-               }
-       }
-
-       private class LogEvent {
-               private final String username;
-               private final LoggingEvent loggingEvent;
-
-               public LogEvent(String username, LoggingEvent loggingEvent) {
-                       super();
-                       this.username = username;
-                       this.loggingEvent = loggingEvent;
-               }
-
-               @Override
-               public int hashCode() {
-                       return loggingEvent.hashCode();
-               }
-
-               @Override
-               public boolean equals(Object obj) {
-                       return loggingEvent.equals(obj);
-               }
-
-               @Override
-               public String toString() {
-                       return username + "@ " + loggingEvent.toString();
-               }
-
-               public String getUsername() {
-                       return username;
-               }
-
-               public LoggingEvent getLoggingEvent() {
-                       return loggingEvent;
-               }
-
-       }
-}
diff --git a/org.argeo.security.core/src/org/argeo/security/NodeAuthenticationToken.java b/org.argeo.security.core/src/org/argeo/security/NodeAuthenticationToken.java
new file mode 100644 (file)
index 0000000..1870675
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.security;
+
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
+
+/** Credentials required for the authentication to a node. */
+public class NodeAuthenticationToken extends
+               UsernamePasswordAuthenticationToken {
+       private static final long serialVersionUID = 1955222132884795213L;
+       private final String url;
+
+       /** Non authenticated local constructor */
+       public NodeAuthenticationToken(Object principal, Object credentials) {
+               super(principal, credentials);
+               this.url = null;
+       }
+
+       /** Non authenticated remote constructor */
+       public NodeAuthenticationToken(Object principal, Object credentials,
+                       String url) {
+               super(principal, credentials);
+               this.url = url;
+       }
+
+       /** Authenticated constructor */
+       public NodeAuthenticationToken(NodeAuthenticationToken sat,
+                       GrantedAuthority[] authorities) {
+               super(sat.getPrincipal(), sat.getCredentials(), authorities);
+               this.url = sat.getUrl();
+       }
+
+       public String getUrl() {
+               return url;
+       }
+
+       public Boolean isRemote() {
+               return url != null;
+       }
+}
diff --git a/org.argeo.security.core/src/org/argeo/security/OsAuthenticationToken.java b/org.argeo.security.core/src/org/argeo/security/OsAuthenticationToken.java
new file mode 100644 (file)
index 0000000..b3727b2
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.security;
+
+import java.security.AccessController;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+import javax.security.auth.Subject;
+
+import org.argeo.ArgeoException;
+import org.argeo.OperatingSystem;
+import org.springframework.security.Authentication;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.GrantedAuthorityImpl;
+import org.springframework.security.userdetails.UserDetails;
+
+/** Abstracts principals provided by com.sun.security.auth.module login modules. */
+public class OsAuthenticationToken implements Authentication {
+       private static final long serialVersionUID = -7544626794250917244L;
+
+       final Class<? extends Principal> osUserPrincipalClass;
+       final Class<? extends Principal> osUserIdPrincipalClass;
+       final Class<? extends Principal> osGroupIdPrincipalClass;
+
+       private List<GrantedAuthority> grantedAuthorities;
+
+       private UserDetails details;
+
+       /** Request */
+       public OsAuthenticationToken(GrantedAuthority[] grantedAuthorities) {
+               this.grantedAuthorities = grantedAuthorities != null ? Arrays
+                               .asList(grantedAuthorities) : null;
+               ClassLoader cl = getClass().getClassLoader();
+               switch (OperatingSystem.os) {
+               case OperatingSystem.WINDOWS:
+                       osUserPrincipalClass = getPrincipalClass(cl,
+                                       "com.sun.security.auth.NTUserPrincipal");
+                       osUserIdPrincipalClass = getPrincipalClass(cl,
+                                       "com.sun.security.auth.NTSidUserPrincipal");
+                       osGroupIdPrincipalClass = getPrincipalClass(cl,
+                                       "com.sun.security.auth.NTSidGroupPrincipal");
+                       break;
+               case OperatingSystem.NIX:
+                       osUserPrincipalClass = getPrincipalClass(cl,
+                                       "com.sun.security.auth.UnixPrincipal");
+                       osUserIdPrincipalClass = getPrincipalClass(cl,
+                                       "com.sun.security.auth.UnixNumericUserPrincipal");
+                       osGroupIdPrincipalClass = getPrincipalClass(cl,
+                                       "com.sun.security.auth.UnixNumericGroupPrincipal");
+                       break;
+               case OperatingSystem.SOLARIS:
+                       osUserPrincipalClass = getPrincipalClass(cl,
+                                       "com.sun.security.auth.SolarisPrincipal");
+                       osUserIdPrincipalClass = getPrincipalClass(cl,
+                                       "com.sun.security.auth.SolarisNumericUserPrincipal");
+                       osGroupIdPrincipalClass = getPrincipalClass(cl,
+                                       "com.sun.security.auth.SolarisNumericGroupPrincipal");
+                       break;
+
+               default:
+                       throw new ArgeoException("Unsupported operating system "
+                                       + OperatingSystem.os);
+               }
+
+       }
+
+       /** Authenticated */
+       public OsAuthenticationToken() {
+               this(null);
+       }
+
+       /** @return the name, or null if not yet logged */
+       public String getName() {
+               Subject subject = Subject.getSubject(AccessController.getContext());
+               if (subject == null)
+                       return null;
+               return getUser().getName();
+       }
+
+       /**
+        * Should not be called during authentication since group IDs are not yet
+        * available {@link Subject} has been set
+        */
+       public GrantedAuthority[] getAuthorities() {
+               // grantedAuthorities should not be null at this stage
+               List<GrantedAuthority> gas = new ArrayList<GrantedAuthority>(
+                               grantedAuthorities);
+               for (Principal groupPrincipal : getGroupsIds()) {
+                       gas.add(new GrantedAuthorityImpl("OSGROUP_"
+                                       + groupPrincipal.getName()));
+               }
+               return gas.toArray(new GrantedAuthority[gas.size()]);
+       }
+
+       public UserDetails getDetails() {
+               return details;
+       }
+
+       public void setDetails(UserDetails details) {
+               this.details = details;
+       }
+
+       public boolean isAuthenticated() {
+               return grantedAuthorities != null;
+       }
+
+       public void setAuthenticated(boolean isAuthenticated)
+                       throws IllegalArgumentException {
+               if (grantedAuthorities != null)
+                       grantedAuthorities.clear();
+               grantedAuthorities = null;
+       }
+
+       @SuppressWarnings("unchecked")
+       protected static Class<? extends Principal> getPrincipalClass(
+                       ClassLoader cl, String className) {
+               try {
+                       return (Class<? extends Principal>) cl.loadClass(className);
+               } catch (ClassNotFoundException e) {
+                       throw new ArgeoException("Cannot load principal class", e);
+               }
+       }
+
+       public Object getPrincipal() {
+               return getUser();
+       }
+
+       public Principal getUser() {
+               Subject subject = getSubject();
+               Set<? extends Principal> userPrincipals = subject
+                               .getPrincipals(osUserPrincipalClass);
+               if (userPrincipals == null || userPrincipals.size() == 0)
+                       throw new ArgeoException("No OS principal");
+               if (userPrincipals.size() > 1)
+                       throw new ArgeoException("More than one OS principal");
+               Principal user = userPrincipals.iterator().next();
+               return user;
+       }
+
+       public Principal getUserId() {
+               Subject subject = getSubject();
+               Set<? extends Principal> userIdsPrincipals = subject
+                               .getPrincipals(osUserIdPrincipalClass);
+               if (userIdsPrincipals == null || userIdsPrincipals.size() == 0)
+                       throw new ArgeoException("No user id principal");
+               if (userIdsPrincipals.size() > 1)
+                       throw new ArgeoException("More than one user id principal");
+               Principal userId = userIdsPrincipals.iterator().next();
+               return userId;
+       }
+
+       public Set<? extends Principal> getGroupsIds() {
+               Subject subject = getSubject();
+               return (Set<? extends Principal>) subject
+                               .getPrincipals(osGroupIdPrincipalClass);
+       }
+
+       /** @return the subject always non null */
+       protected Subject getSubject() {
+               Subject subject = Subject.getSubject(AccessController.getContext());
+               if (subject == null)
+                       throw new ArgeoException("No subject in JAAS context");
+               return subject;
+       }
+
+       public Object getCredentials() {
+               return "";
+       }
+
+}
diff --git a/org.argeo.security.core/src/org/argeo/security/SecurityUtils.java b/org.argeo.security.core/src/org/argeo/security/SecurityUtils.java
new file mode 100644 (file)
index 0000000..e5b8ae7
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.security;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.springframework.security.Authentication;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.context.SecurityContext;
+import org.springframework.security.context.SecurityContextHolder;
+import org.springframework.security.providers.anonymous.AnonymousAuthenticationToken;
+
+/** Static utilities */
+public class SecurityUtils {
+
+       private SecurityUtils() {
+       }
+
+       /** Whether the current thread has the admin role */
+       public static boolean hasCurrentThreadAuthority(String authority) {
+               SecurityContext securityContext = SecurityContextHolder.getContext();
+               if (securityContext != null) {
+                       Authentication authentication = securityContext.getAuthentication();
+                       if (authentication != null) {
+                               for (GrantedAuthority ga : authentication.getAuthorities())
+                                       if (ga.getAuthority().equals(authority))
+                                               return true;
+                       }
+               }
+               return false;
+       }
+
+       /**
+        * @return the authenticated username or null if not authenticated /
+        *         anonymous
+        */
+       public static String getCurrentThreadUsername() {
+               SecurityContext securityContext = SecurityContextHolder.getContext();
+               if (securityContext != null) {
+                       Authentication authentication = securityContext.getAuthentication();
+                       if (authentication != null) {
+                               if (authentication instanceof AnonymousAuthenticationToken) {
+                                       return null;
+                               }
+                               return authentication.getName();
+                       }
+               }
+               return null;
+       }
+
+       /**
+        * Returns the display name of the user details (by calling toString() on
+        * it)
+        */
+       public static String getUserDetailsDisplayName() {
+               SecurityContext securityContext = SecurityContextHolder.getContext();
+               if (securityContext != null) {
+                       Authentication authentication = securityContext.getAuthentication();
+                       if (authentication != null) {
+                               if (authentication instanceof AnonymousAuthenticationToken) {
+                                       return null;
+                               }
+                               Object details = authentication.getDetails();
+                               if (details != null)
+                                       return details.toString();
+                               return authentication.getName();
+                       }
+               }
+               return null;
+       }
+
+       /**
+        * Converts an array of Spring Security {@link GrantedAuthority} to a
+        * read-only list of strings, for portability and integration
+        */
+       public static List<String> authoritiesToStringList(
+                       GrantedAuthority[] authorities) {
+               List<String> lst = new ArrayList<String>();
+               for (GrantedAuthority ga : authorities)
+                       lst.add(ga.getAuthority());
+               return Collections.unmodifiableList(lst);
+       }
+}
diff --git a/org.argeo.security.core/src/org/argeo/security/SystemAuthentication.java b/org.argeo.security.core/src/org/argeo/security/SystemAuthentication.java
new file mode 100644 (file)
index 0000000..2722c1f
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.security;
+
+/**
+ * Marks a system authentication, that is which did not require a login process.
+ */
+public interface SystemAuthentication {
+
+}
diff --git a/org.argeo.security.core/src/org/argeo/security/SystemExecutionService.java b/org.argeo.security.core/src/org/argeo/security/SystemExecutionService.java
new file mode 100644 (file)
index 0000000..075a6c3
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.security;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Future;
+
+/**
+ * Allows to execute code authenticated as a system user (that is not a real
+ * person). The {@link Executor} interface is not used directly in order to
+ * allow future extension of this interface and to simplify its publication
+ * (e.g. as an OSGi service) and interception.
+ */
+public interface SystemExecutionService extends Executor {
+       /**
+        * Executes this {@link Runnable} within a system authenticated context.
+        * Implementations should make sure that this method is properly secured via
+        * Java permissions since it could access everything without credentials.
+        */
+       public void execute(Runnable runnable);
+
+       /**
+        * Executes this {@link Callable} within a system authenticated context.
+        * Implementations should make sure that this method is properly secured via
+        * Java permissions since it could access everything without credentials.
+        */
+       public <T> Future<T> submit(Callable<T> task);
+}
diff --git a/org.argeo.security.core/src/org/argeo/security/UserAdminService.java b/org.argeo.security.core/src/org/argeo/security/UserAdminService.java
new file mode 100644 (file)
index 0000000..0a84cf6
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.security;
+
+import java.util.Set;
+
+import org.springframework.security.userdetails.UserDetailsManager;
+
+/** Enrich {@link UserDetailsManager} in order to provide roles semantics. */
+public interface UserAdminService extends UserDetailsManager {
+       /**
+        * Usernames must match this regexp pattern ({@value #USERNAME_PATTERN}).
+        * Thanks to <a href=
+        * "http://www.mkyong.com/regular-expressions/how-to-validate-username-with-regular-expression/"
+        * >this tip</a> (modified to add upper-case, add '@')
+        */
+       //public final static String USERNAME_PATTERN = "^[a-zA-Z0-9_-@]{3,64}$";
+
+       /**
+        * Email addresses must match this regexp pattern ({@value #EMAIL_PATTERN}.
+        * Thanks to <a href=
+        * "http://www.mkyong.com/regular-expressions/how-to-validate-email-address-with-regular-expression/"
+        * >this tip</a>.
+        */
+       public final static String EMAIL_PATTERN = "^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";
+
+       /*
+        * USERS
+        */
+       /** List all users. */
+       public Set<String> listUsers();
+
+       /** List users having this role (except the super user). */
+       public Set<String> listUsersInRole(String role);
+
+       /** Synchronize with the underlying DAO. */
+       public void synchronize();
+
+       /*
+        * ROLES
+        */
+       public void newRole(String role);
+
+       public Set<String> listEditableRoles();
+
+       public void deleteRole(String role);
+}
diff --git a/org.argeo.security.core/src/org/argeo/security/core/AbstractSystemExecution.java b/org.argeo.security.core/src/org/argeo/security/core/AbstractSystemExecution.java
new file mode 100644 (file)
index 0000000..b84f3de
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.security.core;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.argeo.security.SystemAuthentication;
+import org.springframework.security.Authentication;
+import org.springframework.security.AuthenticationManager;
+import org.springframework.security.context.SecurityContext;
+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 being
+//             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>() {
+               protected Boolean initialValue() {
+                       return false;
+               }
+       };
+
+       /**
+        * Authenticate the calling thread to the underlying
+        * {@link AuthenticationManager}
+        */
+       protected void authenticateAsSystem() {
+               if (authenticatedBySelf.get())
+                       return;
+               SecurityContext securityContext = SecurityContextHolder.getContext();
+               Authentication currentAuth = securityContext.getAuthentication();
+               if (currentAuth != null) {
+                       if (!(currentAuth instanceof SystemAuthentication))
+                               throw new ArgeoException(
+                                               "System execution on an already authenticated thread: "
+                                                               + currentAuth + ", THREAD="
+                                                               + Thread.currentThread().getId());
+                       return;
+               }
+               // Subject subject = Subject.getSubject(AccessController.getContext());
+               // if (subject != null
+               // && !subject.getPrincipals(Authentication.class).isEmpty())
+               // throw new ArgeoException(
+               // "There is already an authenticated subject: " + subject);
+
+               String key = systemAuthenticationKey != null ? systemAuthenticationKey
+                               : System.getProperty(
+                                               InternalAuthentication.SYSTEM_KEY_PROPERTY,
+                                               InternalAuthentication.SYSTEM_KEY_DEFAULT);
+               if (key == null)
+                       throw new ArgeoException("No system key defined");
+               Authentication auth = authenticationManager
+                               .authenticate(new InternalAuthentication(key));
+               securityContext.setAuthentication(auth);
+               authenticatedBySelf.set(true);
+               if (log.isTraceEnabled())
+                       log.trace("System authenticated");
+       }
+
+       // /** Removes the authentication from the calling thread. */
+       // protected void deauthenticateAsSystem() {
+       // // remove the authentication
+       // // SecurityContext securityContext = SecurityContextHolder.getContext();
+       // // securityContext.setAuthentication(null);
+       // // authenticatedBySelf.set(false);
+       // if (log.isTraceEnabled()) {
+       // log.trace("System deauthenticated");
+       // // Thread.dumpStack();
+       // }
+       // }
+
+       /**
+        * Whether the current thread was authenticated by this component or a
+        * parent thread.
+        */
+       protected Boolean isAuthenticatedBySelf() {
+               return authenticatedBySelf.get();
+       }
+
+       public void setAuthenticationManager(
+                       AuthenticationManager authenticationManager) {
+               this.authenticationManager = authenticationManager;
+       }
+
+       public void setSystemAuthenticationKey(String systemAuthenticationKey) {
+               this.systemAuthenticationKey = systemAuthenticationKey;
+       }
+
+}
diff --git a/org.argeo.security.core/src/org/argeo/security/core/AsyncSystemTaskExecutor.java b/org.argeo.security.core/src/org/argeo/security/core/AsyncSystemTaskExecutor.java
new file mode 100644 (file)
index 0000000..0e400c8
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.security.core;
+
+import org.argeo.security.SystemExecutionService;
+import org.springframework.core.task.SimpleAsyncTaskExecutor;
+
+/**
+ * Asynchronous Spring TaskExecutor (for use in JMS for example) wrapping a
+ * {@link SystemExecutionService}.
+ */
+public class AsyncSystemTaskExecutor extends SimpleAsyncTaskExecutor {
+       private static final long serialVersionUID = -8035527542087963068L;
+
+       private SystemExecutionService systemExecutionService;
+
+       public AsyncSystemTaskExecutor() {
+               super();
+       }
+
+       public AsyncSystemTaskExecutor(String threadNamePrefix) {
+               super(threadNamePrefix);
+       }
+
+       @Override
+       public Thread createThread(final Runnable runnable) {
+               Runnable systemExecutionRunnable = new Runnable() {
+
+                       public void run() {
+                               systemExecutionService.execute(runnable);
+
+                       }
+               };
+               return super.createThread(systemExecutionRunnable);
+       }
+
+       public void setSystemExecutionService(
+                       SystemExecutionService systemExecutionService) {
+               this.systemExecutionService = systemExecutionService;
+       }
+
+}
diff --git a/org.argeo.security.core/src/org/argeo/security/core/AuthenticatedApplicationContextInitialization.java b/org.argeo.security.core/src/org/argeo/security/core/AuthenticatedApplicationContextInitialization.java
new file mode 100644 (file)
index 0000000..97dd6ca
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.security.core;
+
+import java.beans.PropertyDescriptor;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.PropertyValues;
+import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ContextRefreshedEvent;
+
+/**
+ * Executes with a system authentication the instantiation and initialization
+ * methods of the application context where it has been defined.
+ */
+public class AuthenticatedApplicationContextInitialization extends
+               AbstractSystemExecution implements InstantiationAwareBeanPostProcessor,
+               ApplicationListener {
+       // private Log log = LogFactory
+       // .getLog(AuthenticatedApplicationContextInitialization.class);
+       /** If non empty, restricts to these beans */
+       private List<String> beanNames = new ArrayList<String>();
+
+       @SuppressWarnings("rawtypes")
+       public Object postProcessBeforeInstantiation(Class beanClass,
+                       String beanName) throws BeansException {
+               // we authenticate when any bean is instantiated
+               // we will deauthenticate only when the application context has been
+               // refreshed in order to be able to deal with factory beans has well
+               if (!isAuthenticatedBySelf()) {
+                       if (beanNames.size() == 0)
+                               authenticateAsSystem();
+                       else if (beanNames.contains(beanName))
+                               authenticateAsSystem();
+               }
+               return null;
+       }
+
+       public boolean postProcessAfterInstantiation(Object bean, String beanName)
+                       throws BeansException {
+               return true;
+       }
+
+       public PropertyValues postProcessPropertyValues(PropertyValues pvs,
+                       PropertyDescriptor[] pds, Object bean, String beanName)
+                       throws BeansException {
+               return pvs;
+       }
+
+       public Object postProcessBeforeInitialization(Object bean, String beanName)
+                       throws BeansException {
+               // authenticateAsSystem();
+               return bean;
+       }
+
+       public Object postProcessAfterInitialization(Object bean, String beanName)
+                       throws BeansException {
+               // NOTE: in case there was an exception in on the initialization method
+               // we expect the underlying thread to die and thus the system
+               // authentication to be lost. We have currently no way to catch the
+               // exception and perform the deauthentication by ourselves.
+               // deauthenticateAsSystem();
+               return bean;
+       }
+
+       public void onApplicationEvent(ApplicationEvent event) {
+               if (event instanceof ContextRefreshedEvent) {
+                       // make sure that we have deauthenticated after the application
+                       // context was initialized/refreshed
+                       // deauthenticateAsSystem();
+               }
+       }
+
+       public void setBeanNames(List<String> beanNames) {
+               this.beanNames = beanNames;
+       }
+
+}
diff --git a/org.argeo.security.core/src/org/argeo/security/core/AuthenticationProvidersRegister.java b/org.argeo.security.core/src/org/argeo/security/core/AuthenticationProvidersRegister.java
new file mode 100644 (file)
index 0000000..317815e
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.security.core;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.factory.InitializingBean;
+
+/**
+ * Maintains a list of authentication providers injected in to a provider
+ * manager, in order to avoid issues with OSGi services and use packages.
+ */
+public class AuthenticationProvidersRegister implements InitializingBean {
+       private Log log = LogFactory.getLog(AuthenticationProvidersRegister.class);
+
+       private List<Object> providers = new ArrayList<Object>();
+       private List<Object> defaultProviders = new ArrayList<Object>();
+
+       public void register(Object authenticationProvider,
+                       Map<String, String> parameters) {
+               providers.add(authenticationProvider);
+               if (log.isTraceEnabled())
+                       log.trace("Registered authentication provider " + parameters);
+       }
+
+       public void unregister(Object authenticationProvider,
+                       Map<String, String> parameters) {
+               providers.remove(authenticationProvider);
+               if (log.isTraceEnabled())
+                       log.trace("Unregistered authentication provider " + parameters);
+       }
+
+       public List<Object> getProviders() {
+               return providers;
+       }
+
+       public void setDefaultProviders(
+                       List<Object> defaultProviders) {
+               this.defaultProviders = defaultProviders;
+       }
+
+       public void afterPropertiesSet() throws Exception {
+               providers.addAll(defaultProviders);
+       }
+
+}
diff --git a/org.argeo.security.core/src/org/argeo/security/core/ConsoleCallbackHandler.java b/org.argeo.security.core/src/org/argeo/security/core/ConsoleCallbackHandler.java
new file mode 100644 (file)
index 0000000..faa81b0
--- /dev/null
@@ -0,0 +1,70 @@
+package org.argeo.security.core;
+
+import java.io.Console;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Locale;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.NameCallback;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.TextOutputCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+import org.argeo.ArgeoException;
+import org.argeo.util.LocaleCallback;
+
+/** Callback handler to be used with a command line UI. */
+public class ConsoleCallbackHandler implements CallbackHandler {
+
+       @Override
+       public void handle(Callback[] callbacks) throws IOException,
+                       UnsupportedCallbackException {
+               Console console = System.console();
+               if (console == null)
+                       throw new ArgeoException("No console available");
+
+               PrintWriter writer = console.writer();
+               for (int i = 0; i < callbacks.length; i++) {
+                       if (callbacks[i] instanceof TextOutputCallback) {
+                               TextOutputCallback callback = (TextOutputCallback) callbacks[i];
+                               writer.write(callback.getMessage());
+                       } else if (callbacks[i] instanceof NameCallback) {
+                               NameCallback callback = (NameCallback) callbacks[i];
+                               writer.write(callback.getPrompt());
+                               if (callback.getDefaultName() != null)
+                                       writer.write(" (" + callback.getDefaultName() + ")");
+                               writer.write(" : ");
+                               String answer = console.readLine();
+                               if (callback.getDefaultName() != null
+                                               && answer.trim().equals(""))
+                                       callback.setName(callback.getDefaultName());
+                               else
+                                       callback.setName(answer);
+                       } else if (callbacks[i] instanceof PasswordCallback) {
+                               PasswordCallback callback = (PasswordCallback) callbacks[i];
+                               writer.write(callback.getPrompt());
+                               char[] answer = console.readPassword();
+                               callback.setPassword(answer);
+                               Arrays.fill(answer, ' ');
+                       } else if (callbacks[i] instanceof LocaleCallback) {
+                               LocaleCallback callback = (LocaleCallback) callbacks[i];
+                               writer.write(callback.getPrompt());
+                               writer.write("\n");
+                               for (int j = 0; j < callback.getAvailableLocales().size(); j++) {
+                                       Locale locale = callback.getAvailableLocales().get(j);
+                                       writer.print(j + " : " + locale.getDisplayName() + "\n");
+                               }
+                               writer.write("(" + callback.getDefaultIndex() + ") : ");
+                               String answer = console.readLine();
+                               if (answer.trim().equals(""))
+                                       callback.setSelectedIndex(callback.getDefaultIndex());
+                               else
+                                       callback.setSelectedIndex(new Integer(answer.trim()));
+                       }
+               }
+       }
+
+}
diff --git a/org.argeo.security.core/src/org/argeo/security/core/InternalAuthentication.java b/org.argeo.security.core/src/org/argeo/security/core/InternalAuthentication.java
new file mode 100644 (file)
index 0000000..267ddd3
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.security.core;
+
+import org.argeo.security.SystemAuthentication;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.GrantedAuthorityImpl;
+import org.springframework.security.adapters.PrincipalSpringSecurityUserToken;
+
+/** A token base on a system key used to request a system authentication. */
+public class InternalAuthentication extends PrincipalSpringSecurityUserToken
+               implements SystemAuthentication {
+       private static final long serialVersionUID = -6783376375615949315L;
+       /** 'admin' for consistency with JCR */
+       public final static String DEFAULT_SYSTEM_USERNAME = "admin";
+       public final static String DEFAULT_SYSTEM_ROLE = "ROLE_SYSTEM";
+       public final static String SYSTEM_KEY_PROPERTY = "argeo.security.systemKey";
+       public final static String SYSTEM_KEY_DEFAULT = "argeo";
+
+       public InternalAuthentication(String key, String systemUsername,
+                       String systemRole) {
+               super(
+                               key,
+                               systemUsername,
+                               key,
+                               new GrantedAuthority[] { new GrantedAuthorityImpl(systemRole) },
+                               systemUsername);
+       }
+
+       public InternalAuthentication(String key) {
+               this(key, DEFAULT_SYSTEM_USERNAME, DEFAULT_SYSTEM_ROLE);
+       }
+
+}
diff --git a/org.argeo.security.core/src/org/argeo/security/core/KeyBasedSystemExecutionService.java b/org.argeo.security.core/src/org/argeo/security/core/KeyBasedSystemExecutionService.java
new file mode 100644 (file)
index 0000000..6c85df1
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.security.core;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+
+import org.argeo.ArgeoException;
+import org.argeo.security.SystemExecutionService;
+
+/**
+ * Implementation of a {@link SystemExecutionService} using a key-based
+ * {@link InternalAuthentication}
+ */
+public class KeyBasedSystemExecutionService extends AbstractSystemExecution
+               implements SystemExecutionService {
+       public void execute(Runnable runnable) {
+               try {
+                       wrapWithSystemAuthentication(Executors.callable(runnable)).call();
+               } catch (RuntimeException e) {
+                       throw e;
+               } catch (Exception e) {
+                       throw new ArgeoException(
+                                       "Exception when running system authenticated task", e);
+               }
+       }
+
+       public <T> Future<T> submit(Callable<T> task) {
+               FutureTask<T> future = new FutureTask<T>(
+                               wrapWithSystemAuthentication(task));
+               future.run();
+               return future;
+       }
+
+       protected <T> Callable<T> wrapWithSystemAuthentication(
+                       final Callable<T> runnable) {
+               return new Callable<T>() {
+
+                       public T call() throws Exception {
+                               authenticateAsSystem();
+                               try {
+                                       return runnable.call();
+                               } finally {
+//                                     deauthenticateAsSystem();
+                               }
+                       }
+               };
+       }
+}
diff --git a/org.argeo.security.core/src/org/argeo/security/core/MatchingAuthenticationProvider.java b/org.argeo.security.core/src/org/argeo/security/core/MatchingAuthenticationProvider.java
new file mode 100644 (file)
index 0000000..0471151
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.security.core;
+
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import org.springframework.core.io.Resource;
+import org.springframework.security.AuthenticationException;
+import org.springframework.security.BadCredentialsException;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.GrantedAuthorityImpl;
+import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
+import org.springframework.security.providers.dao.AbstractUserDetailsAuthenticationProvider;
+import org.springframework.security.userdetails.User;
+import org.springframework.security.userdetails.UserDetails;
+
+/** @deprecated */
+@Deprecated
+public class MatchingAuthenticationProvider extends
+               AbstractUserDetailsAuthenticationProvider {
+
+       private Resource mapping;
+       private Properties properties;
+
+       private List<String> defaultRoles = new ArrayList<String>();
+
+       @Override
+       protected void doAfterPropertiesSet() throws Exception {
+               properties = new Properties();
+               InputStream propIn = mapping.getInputStream();
+               try {
+                       properties.load(propIn);
+               } finally {
+                       propIn.close();
+               }
+       }
+
+       @Override
+       protected void additionalAuthenticationChecks(UserDetails userDetails,
+                       UsernamePasswordAuthenticationToken authentication)
+                       throws AuthenticationException {
+               if (!userDetails.getPassword().equals(authentication.getCredentials()))
+                       throw new BadCredentialsException(
+                                       "Invalid credentails provided by "
+                                                       + authentication.getName());
+       }
+
+       @Override
+       protected UserDetails retrieveUser(String username,
+                       UsernamePasswordAuthenticationToken authentication)
+                       throws AuthenticationException {
+               String value = properties.getProperty(username);
+               if (value == null)
+                       throw new BadCredentialsException("User " + username
+                                       + " is not registered");
+               List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
+               for (String role : defaultRoles)
+                       grantedAuthorities.add(new GrantedAuthorityImpl(role));
+               return new User(
+                               username,
+                               value,
+                               true,
+                               true,
+                               true,
+                               true,
+                               grantedAuthorities
+                                               .toArray(new GrantedAuthority[grantedAuthorities.size()]));
+       }
+
+       public void setMapping(Resource mapping) {
+               this.mapping = mapping;
+       }
+
+       public void setDefaultRoles(List<String> defaultRoles) {
+               this.defaultRoles = defaultRoles;
+       }
+
+}
diff --git a/org.argeo.security.core/src/org/argeo/security/core/OsAuthenticationProvider.java b/org.argeo.security.core/src/org/argeo/security/core/OsAuthenticationProvider.java
new file mode 100644 (file)
index 0000000..0e29ecd
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.security.core;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.argeo.security.OsAuthenticationToken;
+import org.springframework.security.Authentication;
+import org.springframework.security.AuthenticationException;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.GrantedAuthorityImpl;
+import org.springframework.security.providers.AuthenticationProvider;
+
+/**
+ * Validates an OS authentication. The id is that it will always be
+ * authenticated since we are always runnign within an OS, but the fact that the
+ * {@link Authentication} works properly depends on the proper OS login module
+ * having been called as well. TODO make it more configurable (base roles, is
+ * admin)
+ */
+public class OsAuthenticationProvider implements AuthenticationProvider {
+       final static String osUserRole = "ROLE_OS_USER";
+       final static String userRole = "ROLE_USER";
+       final static String adminRole = "ROLE_ADMIN";
+
+       final static Boolean isAdmin = true;
+
+       public Authentication authenticate(Authentication authentication)
+                       throws AuthenticationException {
+               return new OsAuthenticationToken(getBaseAuthorities());
+       }
+
+       public static GrantedAuthority[] getBaseAuthorities() {
+               List<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
+               auths.add(new GrantedAuthorityImpl(osUserRole));
+               auths.add(new GrantedAuthorityImpl(userRole));
+               if (isAdmin)
+                       auths.add(new GrantedAuthorityImpl(adminRole));
+               return auths.toArray(new GrantedAuthority[auths.size()]);
+       }
+
+       @SuppressWarnings("rawtypes")
+       public boolean supports(Class authentication) {
+               return OsAuthenticationToken.class.isAssignableFrom(authentication);
+       }
+
+}
diff --git a/org.argeo.security.core/src/org/argeo/security/core/OsgiModuleLabel.java b/org.argeo.security.core/src/org/argeo/security/core/OsgiModuleLabel.java
new file mode 100644 (file)
index 0000000..45c9e16
--- /dev/null
@@ -0,0 +1,41 @@
+package org.argeo.security.core;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+
+/**
+ * Logs the name and version of an OSGi bundle based on its
+ * {@link BundleContext}.
+ */
+public class OsgiModuleLabel {
+       private final static Log log = LogFactory.getLog(OsgiModuleLabel.class);
+
+       private Bundle bundle;
+
+       public OsgiModuleLabel() {
+       }
+
+       /** Sets without logging. */
+       public OsgiModuleLabel(Bundle bundle) {
+               this.bundle = bundle;
+       }
+
+       /**
+        * Retrieved bundle from a bundle context and logs it. Typically to be set
+        * as a Spring bean.
+        */
+       public void setBundleContext(BundleContext bundleContext) {
+               this.bundle = bundleContext.getBundle();
+               log.info(msg());
+       }
+
+       public String msg() {
+               String name = bundle.getHeaders().get(Constants.BUNDLE_NAME).toString();
+               String symbolicName = bundle.getSymbolicName();
+               String version = bundle.getVersion().toString();
+               return name + " v" + version + " (" + symbolicName + ")";
+       }
+}
diff --git a/org.argeo.security.core/src/org/argeo/security/core/SimpleRoleRegistration.java b/org.argeo.security.core/src/org/argeo/security/core/SimpleRoleRegistration.java
new file mode 100644 (file)
index 0000000..aa8a5f0
--- /dev/null
@@ -0,0 +1,57 @@
+package org.argeo.security.core;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.security.UserAdminService;
+
+/**
+ * Register one or many roles via a user admin service. Does nothing if the role
+ * is already registered.
+ */
+public class SimpleRoleRegistration implements Runnable {
+       private final static Log log = LogFactory
+                       .getLog(SimpleRoleRegistration.class);
+
+       private String role;
+       private List<String> roles = new ArrayList<String>();
+       private UserAdminService userAdminService;
+
+       @Override
+       public void run() {
+               Set<String> existingRoles = userAdminService.listEditableRoles();
+               if (role != null && !existingRoles.contains(role))
+                       newRole(role);
+               for (String r : roles) {
+                       if (!existingRoles.contains(r))
+                               newRole(r);
+               }
+       }
+
+       protected void newRole(String r) {
+               userAdminService.newRole(r);
+               log.info("Added role " + r + " required by application.");
+       }
+
+       public void register(UserAdminService userAdminService, Map<?, ?> properties) {
+               this.userAdminService = userAdminService;
+               run();
+       }
+
+       public void setRole(String role) {
+               this.role = role;
+       }
+
+       public void setRoles(List<String> roles) {
+               this.roles = roles;
+       }
+
+       public void setUserAdminService(UserAdminService userAdminService) {
+               this.userAdminService = userAdminService;
+       }
+
+}
diff --git a/org.argeo.security.core/src/org/argeo/security/crypto/AbstractKeyring.java b/org.argeo.security.core/src/org/argeo/security/crypto/AbstractKeyring.java
new file mode 100644 (file)
index 0000000..daa1ebd
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.security.crypto;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.security.AccessController;
+import java.security.MessageDigest;
+import java.security.Provider;
+import java.security.Security;
+import java.util.Arrays;
+import java.util.Iterator;
+
+import javax.crypto.SecretKey;
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.TextOutputCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.auth.login.LoginContext;
+import javax.security.auth.login.LoginException;
+
+import org.argeo.ArgeoException;
+import org.argeo.StreamUtils;
+import org.argeo.util.security.Keyring;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+/** username / password based keyring. TODO internationalize */
+public abstract class AbstractKeyring implements Keyring, CryptoKeyring {
+       static {
+               Security.addProvider(new BouncyCastleProvider());
+       }
+
+       public final static String DEFAULT_KEYRING_LOGIN_CONTEXT = "KEYRING";
+
+       private String loginContextName = DEFAULT_KEYRING_LOGIN_CONTEXT;
+       private CallbackHandler defaultCallbackHandler;
+
+       private String charset = "UTF-8";
+
+       /**
+        * Default provider is bouncy castle, in order to have consistent behaviour
+        * across implementations
+        */
+       private String securityProviderName = "BC";
+
+       /**
+        * Whether the keyring has already been created in the past with a master
+        * password
+        */
+       protected abstract Boolean isSetup();
+
+       /**
+        * Setup the keyring persistently, {@link #isSetup()} must return true
+        * afterwards
+        */
+       protected abstract void setup(char[] password);
+
+       /** Populates the key spec callback */
+       protected abstract void handleKeySpecCallback(PBEKeySpecCallback pbeCallback);
+
+       protected abstract void encrypt(String path, InputStream unencrypted);
+
+       protected abstract InputStream decrypt(String path);
+
+       /** Triggers lazy initialization */
+       protected SecretKey getSecretKey() {
+               Subject subject = Subject.getSubject(AccessController.getContext());
+               // we assume only one secrete key is available
+               Iterator<SecretKey> iterator = subject.getPrivateCredentials(
+                               SecretKey.class).iterator();
+               if (!iterator.hasNext()) {// not initialized
+                       CallbackHandler callbackHandler = new KeyringCallbackHandler();
+                       try {
+                               LoginContext loginContext = new LoginContext(loginContextName,
+                                               subject, callbackHandler);
+                               loginContext.login();
+                               // FIXME will login even if password is wrong
+                               iterator = subject.getPrivateCredentials(SecretKey.class)
+                                               .iterator();
+                               return iterator.next();
+                       } catch (LoginException e) {
+                               throw new ArgeoException("Keyring login failed", e);
+                       }
+
+               } else {
+                       SecretKey secretKey = iterator.next();
+                       if (iterator.hasNext())
+                               throw new ArgeoException(
+                                               "More than one secret key in private credentials");
+                       return secretKey;
+               }
+       }
+
+       public InputStream getAsStream(String path) {
+               return decrypt(path);
+       }
+
+       public void set(String path, InputStream in) {
+               encrypt(path, in);
+       }
+
+       public char[] getAsChars(String path) {
+               InputStream in = getAsStream(path);
+               CharArrayWriter writer = null;
+               Reader reader = null;
+               try {
+                       writer = new CharArrayWriter();
+                       reader = new InputStreamReader(in, charset);
+                       StreamUtils.copy(reader, writer);
+                       return writer.toCharArray();
+               } catch (IOException e) {
+                       throw new ArgeoException("Cannot decrypt to char array", e);
+               } finally {
+                       StreamUtils.closeQuietly(reader);
+                       StreamUtils.closeQuietly(in);
+                       StreamUtils.closeQuietly(writer);
+               }
+       }
+
+       public void set(String path, char[] arr) {
+               ByteArrayOutputStream out = new ByteArrayOutputStream();
+               ByteArrayInputStream in = null;
+               Writer writer = null;
+               try {
+                       writer = new OutputStreamWriter(out, charset);
+                       writer.write(arr);
+                       writer.flush();
+                       in = new ByteArrayInputStream(out.toByteArray());
+                       set(path, in);
+               } catch (IOException e) {
+                       throw new ArgeoException("Cannot encrypt to char array", e);
+               } finally {
+                       StreamUtils.closeQuietly(writer);
+                       StreamUtils.closeQuietly(out);
+                       StreamUtils.closeQuietly(in);
+               }
+       }
+
+       protected Provider getSecurityProvider() {
+               return Security.getProvider(securityProviderName);
+       }
+
+       public void setLoginContextName(String loginContextName) {
+               this.loginContextName = loginContextName;
+       }
+
+       public void setDefaultCallbackHandler(CallbackHandler defaultCallbackHandler) {
+               this.defaultCallbackHandler = defaultCallbackHandler;
+       }
+
+       public void setCharset(String charset) {
+               this.charset = charset;
+       }
+
+       public void setSecurityProviderName(String securityProviderName) {
+               this.securityProviderName = securityProviderName;
+       }
+
+       @Deprecated
+       protected static byte[] hash(char[] password, byte[] salt,
+                       Integer iterationCount) {
+               ByteArrayOutputStream out = null;
+               OutputStreamWriter writer = null;
+               try {
+                       out = new ByteArrayOutputStream();
+                       writer = new OutputStreamWriter(out, "UTF-8");
+                       writer.write(password);
+                       MessageDigest pwDigest = MessageDigest.getInstance("SHA-256");
+                       pwDigest.reset();
+                       pwDigest.update(salt);
+                       byte[] btPass = pwDigest.digest(out.toByteArray());
+                       for (int i = 0; i < iterationCount; i++) {
+                               pwDigest.reset();
+                               btPass = pwDigest.digest(btPass);
+                       }
+                       return btPass;
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot hash", e);
+               } finally {
+                       StreamUtils.closeQuietly(out);
+                       StreamUtils.closeQuietly(writer);
+               }
+
+       }
+
+       /**
+        * Convenience method using the underlying callback to ask for a password
+        * (typically used when the password is not saved in the keyring)
+        */
+       protected char[] ask() {
+               PasswordCallback passwordCb = new PasswordCallback("Password", false);
+               Callback[] dialogCbs = new Callback[] { passwordCb };
+               try {
+                       defaultCallbackHandler.handle(dialogCbs);
+                       char[] password = passwordCb.getPassword();
+                       return password;
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot ask for a password", e);
+               }
+
+       }
+
+       class KeyringCallbackHandler implements CallbackHandler {
+               public void handle(Callback[] callbacks) throws IOException,
+                               UnsupportedCallbackException {
+                       // checks
+                       if (callbacks.length != 2)
+                               throw new IllegalArgumentException(
+                                               "Keyring required 2 and only 2 callbacks: {PasswordCallback,PBEKeySpecCallback}");
+                       if (!(callbacks[0] instanceof PasswordCallback))
+                               throw new UnsupportedCallbackException(callbacks[0]);
+                       if (!(callbacks[1] instanceof PBEKeySpecCallback))
+                               throw new UnsupportedCallbackException(callbacks[0]);
+
+                       PasswordCallback passwordCb = (PasswordCallback) callbacks[0];
+                       PBEKeySpecCallback pbeCb = (PBEKeySpecCallback) callbacks[1];
+
+                       if (isSetup()) {
+                               Callback[] dialogCbs = new Callback[] { passwordCb };
+                               defaultCallbackHandler.handle(dialogCbs);
+                       } else {// setup keyring
+                               TextOutputCallback textCb1 = new TextOutputCallback(
+                                               TextOutputCallback.INFORMATION,
+                                               "Enter a master password which will protect your private data");
+                               TextOutputCallback textCb2 = new TextOutputCallback(
+                                               TextOutputCallback.INFORMATION,
+                                               "(for example your credentials to third-party services)");
+                               TextOutputCallback textCb3 = new TextOutputCallback(
+                                               TextOutputCallback.INFORMATION,
+                                               "Don't forget this password since the data cannot be read without it");
+                               PasswordCallback confirmPasswordCb = new PasswordCallback(
+                                               "Confirm password", false);
+                               // first try
+                               Callback[] dialogCbs = new Callback[] { textCb1, textCb2,
+                                               textCb3, passwordCb, confirmPasswordCb };
+                               defaultCallbackHandler.handle(dialogCbs);
+
+                               // if passwords different, retry (except if cancelled)
+                               while (passwordCb.getPassword() != null
+                                               && !Arrays.equals(passwordCb.getPassword(),
+                                                               confirmPasswordCb.getPassword())) {
+                                       TextOutputCallback textCb = new TextOutputCallback(
+                                                       TextOutputCallback.ERROR,
+                                                       "The passwords do not match");
+                                       dialogCbs = new Callback[] { textCb, passwordCb,
+                                                       confirmPasswordCb };
+                                       defaultCallbackHandler.handle(dialogCbs);
+                               }
+
+                               if (passwordCb.getPassword() != null) {// not cancelled
+                                       setup(passwordCb.getPassword());
+                               }
+                       }
+
+                       if (passwordCb.getPassword() != null)
+                               handleKeySpecCallback(pbeCb);
+               }
+
+       }
+}
diff --git a/org.argeo.security.core/src/org/argeo/security/crypto/CryptoKeyring.java b/org.argeo.security.core/src/org/argeo/security/crypto/CryptoKeyring.java
new file mode 100644 (file)
index 0000000..d25eccd
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.security.crypto;
+
+import org.argeo.util.security.Keyring;
+
+/**
+ * Advanced keyring based on cryptography that can easily be centralized and
+ * coordinated with {@link KeyringLoginModule} (since they ar ein the same
+ * package)
+ */
+public interface CryptoKeyring extends Keyring {
+
+}
diff --git a/org.argeo.security.core/src/org/argeo/security/crypto/KeyringLoginModule.java b/org.argeo.security.core/src/org/argeo/security/crypto/KeyringLoginModule.java
new file mode 100644 (file)
index 0000000..34b7d40
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.security.crypto;
+
+import java.security.AccessController;
+import java.util.Map;
+import java.util.Set;
+
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.SecretKeySpec;
+import javax.security.auth.Subject;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.login.LoginException;
+import javax.security.auth.spi.LoginModule;
+
+/** Adds a secret key to the private credentials */
+public class KeyringLoginModule implements LoginModule {
+       private Subject subject;
+       private CallbackHandler callbackHandler;
+       private SecretKey secretKey;
+
+       public void initialize(Subject subject, CallbackHandler callbackHandler,
+                       Map<String, ?> sharedState, Map<String, ?> options) {
+               this.subject = subject;
+               if (subject == null) {
+                       subject = Subject.getSubject(AccessController.getContext());
+               }
+               this.callbackHandler = callbackHandler;
+       }
+
+       public boolean login() throws LoginException {
+               Set<SecretKey> pbes = subject.getPrivateCredentials(SecretKey.class);
+               if (pbes.size() > 0)
+                       return true;
+               PasswordCallback pc = new PasswordCallback("Master password", false);
+               PBEKeySpecCallback pbeCb = new PBEKeySpecCallback();
+               Callback[] callbacks = { pc, pbeCb };
+               try {
+                       callbackHandler.handle(callbacks);
+                       char[] password = pc.getPassword();
+
+                       SecretKeyFactory keyFac = SecretKeyFactory.getInstance(pbeCb
+                                       .getSecretKeyFactory());
+                       PBEKeySpec keySpec;
+                       if (pbeCb.getKeyLength() != null)
+                               keySpec = new PBEKeySpec(password, pbeCb.getSalt(),
+                                               pbeCb.getIterationCount(), pbeCb.getKeyLength());
+                       else
+                               keySpec = new PBEKeySpec(password, pbeCb.getSalt(),
+                                               pbeCb.getIterationCount());
+
+                       String secKeyEncryption = pbeCb.getSecretKeyEncryption();
+                       if (secKeyEncryption != null) {
+                               SecretKey tmp = keyFac.generateSecret(keySpec);
+                               secretKey = new SecretKeySpec(tmp.getEncoded(),
+                                               secKeyEncryption);
+                       } else {
+                               secretKey = keyFac.generateSecret(keySpec);
+                       }
+               } catch (Exception e) {
+                       LoginException le = new LoginException("Cannot login keyring");
+                       le.initCause(e);
+                       throw le;
+               }
+               return true;
+       }
+
+       public boolean commit() throws LoginException {
+               if (secretKey != null)
+                       subject.getPrivateCredentials().add(secretKey);
+               return true;
+       }
+
+       public boolean abort() throws LoginException {
+               return true;
+       }
+
+       public boolean logout() throws LoginException {
+               Set<PasswordBasedEncryption> pbes = subject
+                               .getPrivateCredentials(PasswordBasedEncryption.class);
+               pbes.clear();
+               return true;
+       }
+
+}
diff --git a/org.argeo.security.core/src/org/argeo/security/crypto/PBEKeySpecCallback.java b/org.argeo.security.core/src/org/argeo/security/crypto/PBEKeySpecCallback.java
new file mode 100644 (file)
index 0000000..e964366
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.security.crypto;
+
+import javax.crypto.spec.PBEKeySpec;
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.PasswordCallback;
+
+/**
+ * All information required to set up a {@link PBEKeySpec} bar the password
+ * itself (use a {@link PasswordCallback})
+ */
+public class PBEKeySpecCallback implements Callback {
+       private String secretKeyFactory;
+       private byte[] salt;
+       private Integer iterationCount;
+       /** Can be null for some algorithms */
+       private Integer keyLength;
+       /** Can be null, will trigger secret key encryption if not */
+       private String secretKeyEncryption;
+
+       private String encryptedPasswordHashCipher;
+       private byte[] encryptedPasswordHash;
+
+       public void set(String secretKeyFactory, byte[] salt,
+                       Integer iterationCount, Integer keyLength,
+                       String secretKeyEncryption) {
+               this.secretKeyFactory = secretKeyFactory;
+               this.salt = salt;
+               this.iterationCount = iterationCount;
+               this.keyLength = keyLength;
+               this.secretKeyEncryption = secretKeyEncryption;
+//             this.encryptedPasswordHashCipher = encryptedPasswordHashCipher;
+//             this.encryptedPasswordHash = encryptedPasswordHash;
+       }
+
+       public String getSecretKeyFactory() {
+               return secretKeyFactory;
+       }
+
+       public byte[] getSalt() {
+               return salt;
+       }
+
+       public Integer getIterationCount() {
+               return iterationCount;
+       }
+
+       public Integer getKeyLength() {
+               return keyLength;
+       }
+
+       public String getSecretKeyEncryption() {
+               return secretKeyEncryption;
+       }
+
+       public String getEncryptedPasswordHashCipher() {
+               return encryptedPasswordHashCipher;
+       }
+
+       public byte[] getEncryptedPasswordHash() {
+               return encryptedPasswordHash;
+       }
+
+}
diff --git a/org.argeo.security.core/src/org/argeo/security/crypto/PasswordBasedEncryption.java b/org.argeo.security.core/src/org/argeo/security/crypto/PasswordBasedEncryption.java
new file mode 100644 (file)
index 0000000..aec25ac
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.security.crypto;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.Security;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import javax.crypto.CipherOutputStream;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.PBEKeySpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.argeo.StreamUtils;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+/** Simple password based encryption / decryption */
+public class PasswordBasedEncryption {
+       private final static Log log = LogFactory
+                       .getLog(PasswordBasedEncryption.class);
+
+       static {
+               Security.addProvider(new BouncyCastleProvider());
+       }
+
+       public final static Integer DEFAULT_ITERATION_COUNT = 1024;
+       /** Stronger with 256, but causes problem with Oracle JVM */
+       public final static Integer DEFAULT_SECRETE_KEY_LENGTH = 256;
+       public final static Integer DEFAULT_SECRETE_KEY_LENGTH_RESTRICTED = 128;
+       public final static String DEFAULT_SECRETE_KEY_FACTORY = "PBKDF2WithHmacSHA1";
+       public final static String DEFAULT_SECRETE_KEY_ENCRYPTION = "AES";
+       public final static String DEFAULT_CIPHER_NAME = "AES/CBC/PKCS5Padding";
+       public final static String DEFAULT_CHARSET = "UTF-8";
+
+       private Integer iterationCount = DEFAULT_ITERATION_COUNT;
+       private Integer secreteKeyLength = DEFAULT_SECRETE_KEY_LENGTH;
+       private String secreteKeyFactoryName = DEFAULT_SECRETE_KEY_FACTORY;
+       private String secreteKeyEncryption = DEFAULT_SECRETE_KEY_ENCRYPTION;
+       private String cipherName = DEFAULT_CIPHER_NAME;
+
+       private static byte[] DEFAULT_SALT_8 = { (byte) 0xA9, (byte) 0x9B,
+                       (byte) 0xC8, (byte) 0x32, (byte) 0x56, (byte) 0x35, (byte) 0xE3,
+                       (byte) 0x03 };
+       private static byte[] DEFAULT_IV_16 = { (byte) 0xA9, (byte) 0x9B,
+                       (byte) 0xC8, (byte) 0x32, (byte) 0x56, (byte) 0x35, (byte) 0xE3,
+                       (byte) 0x03, (byte) 0xA9, (byte) 0x9B, (byte) 0xC8, (byte) 0x32,
+                       (byte) 0x56, (byte) 0x35, (byte) 0xE3, (byte) 0x03 };
+
+       private Key key;
+       private Cipher ecipher;
+       private Cipher dcipher;
+
+       /**
+        * Default provider is bouncy castle, in order to have consistent behaviour
+        * across implementations
+        */
+       private String securityProviderName = "BC";
+
+       /**
+        * 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 {
+                       initKeyAndCiphers(password, passwordSalt, initializationVector);
+               } catch (InvalidKeyException e) {
+                       Integer previousSecreteKeyLength = secreteKeyLength;
+                       secreteKeyLength = DEFAULT_SECRETE_KEY_LENGTH_RESTRICTED;
+                       log.warn("'" + e.getMessage() + "', will use " + secreteKeyLength
+                                       + " secrete key length instead of "
+                                       + previousSecreteKeyLength);
+                       try {
+                               initKeyAndCiphers(password, passwordSalt, initializationVector);
+                       } catch (Exception e1) {
+                               throw new ArgeoException(
+                                               "Cannot get secret key (with restricted length)", e1);
+                       }
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot get secret key", e);
+               }
+       }
+
+       protected void initKeyAndCiphers(char[] password, byte[] passwordSalt,
+                       byte[] initializationVector) throws GeneralSecurityException {
+               byte[] salt = new byte[8];
+               System.arraycopy(passwordSalt, 0, salt, 0, salt.length);
+               // for (int i = 0; i < password.length && i < salt.length; i++)
+               // salt[i] = (byte) password[i];
+               byte[] iv = new byte[16];
+               System.arraycopy(initializationVector, 0, iv, 0, iv.length);
+
+               SecretKeyFactory keyFac = SecretKeyFactory
+                               .getInstance(getSecretKeyFactoryName());
+               PBEKeySpec keySpec = new PBEKeySpec(password, salt,
+                               getIterationCount(), getKeyLength());
+               String secKeyEncryption = getSecretKeyEncryption();
+               if (secKeyEncryption != null) {
+                       SecretKey tmp = keyFac.generateSecret(keySpec);
+                       key = new SecretKeySpec(tmp.getEncoded(), getSecretKeyEncryption());
+               } else {
+                       key = keyFac.generateSecret(keySpec);
+               }
+               ecipher = Cipher.getInstance(getCipherName(), securityProviderName);
+               ecipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
+               dcipher = Cipher.getInstance(getCipherName());
+               dcipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
+       }
+
+       public void encrypt(InputStream decryptedIn, OutputStream encryptedOut)
+                       throws IOException {
+               try {
+                       CipherOutputStream out = new CipherOutputStream(encryptedOut,
+                                       ecipher);
+                       StreamUtils.copy(decryptedIn, out);
+                       StreamUtils.closeQuietly(out);
+               } catch (IOException e) {
+                       throw e;
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot encrypt", e);
+               } finally {
+                       StreamUtils.closeQuietly(decryptedIn);
+               }
+       }
+
+       public void decrypt(InputStream encryptedIn, OutputStream decryptedOut)
+                       throws IOException {
+               try {
+                       CipherInputStream decryptedIn = new CipherInputStream(encryptedIn,
+                                       dcipher);
+                       StreamUtils.copy(decryptedIn, decryptedOut);
+               } catch (IOException e) {
+                       throw e;
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot decrypt", e);
+               } finally {
+                       StreamUtils.closeQuietly(encryptedIn);
+               }
+       }
+
+       public byte[] encryptString(String str) {
+               ByteArrayOutputStream out = null;
+               ByteArrayInputStream in = null;
+               try {
+                       out = new ByteArrayOutputStream();
+                       in = new ByteArrayInputStream(str.getBytes(DEFAULT_CHARSET));
+                       encrypt(in, out);
+                       return out.toByteArray();
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot encrypt", e);
+               } finally {
+                       StreamUtils.closeQuietly(out);
+               }
+       }
+
+       /** Closes the input stream */
+       public String decryptAsString(InputStream in) {
+               ByteArrayOutputStream out = null;
+               try {
+                       out = new ByteArrayOutputStream();
+                       decrypt(in, out);
+                       return new String(out.toByteArray(), DEFAULT_CHARSET);
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot decrypt", e);
+               } finally {
+                       StreamUtils.closeQuietly(out);
+               }
+       }
+
+       protected Key getKey() {
+               return key;
+       }
+
+       protected Cipher getEcipher() {
+               return ecipher;
+       }
+
+       protected Cipher getDcipher() {
+               return dcipher;
+       }
+
+       protected Integer getIterationCount() {
+               return iterationCount;
+       }
+
+       protected Integer getKeyLength() {
+               return secreteKeyLength;
+       }
+
+       protected String getSecretKeyFactoryName() {
+               return secreteKeyFactoryName;
+       }
+
+       protected String getSecretKeyEncryption() {
+               return secreteKeyEncryption;
+       }
+
+       protected String getCipherName() {
+               return cipherName;
+       }
+
+       public void setIterationCount(Integer iterationCount) {
+               this.iterationCount = iterationCount;
+       }
+
+       public void setSecreteKeyLength(Integer keyLength) {
+               this.secreteKeyLength = keyLength;
+       }
+
+       public void setSecreteKeyFactoryName(String secreteKeyFactoryName) {
+               this.secreteKeyFactoryName = secreteKeyFactoryName;
+       }
+
+       public void setSecreteKeyEncryption(String secreteKeyEncryption) {
+               this.secreteKeyEncryption = secreteKeyEncryption;
+       }
+
+       public void setCipherName(String cipherName) {
+               this.cipherName = cipherName;
+       }
+
+       public void setSecurityProviderName(String securityProviderName) {
+               this.securityProviderName = securityProviderName;
+       }
+}
diff --git a/org.argeo.security.core/src/org/argeo/security/jcr/JcrKeyring.java b/org.argeo.security.core/src/org/argeo/security/jcr/JcrKeyring.java
new file mode 100644 (file)
index 0000000..1b9f244
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.security.jcr;
+
+import java.io.ByteArrayInputStream;
+import java.io.CharArrayReader;
+import java.io.InputStream;
+import java.io.Reader;
+import java.security.SecureRandom;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.jcr.Binary;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.commons.io.IOUtils;
+import org.argeo.ArgeoException;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.ArgeoTypes;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.UserJcrUtils;
+import org.argeo.security.crypto.AbstractKeyring;
+import org.argeo.security.crypto.PBEKeySpecCallback;
+
+/** JCR based implementation of a keyring */
+public class JcrKeyring extends AbstractKeyring implements ArgeoNames {
+       /**
+        * Stronger with 256, but causes problem with Oracle JVM, force 128 in this
+        * case
+        */
+       public final static Long DEFAULT_SECRETE_KEY_LENGTH = 256l;
+       public final static String DEFAULT_SECRETE_KEY_FACTORY = "PBKDF2WithHmacSHA1";
+       public final static String DEFAULT_SECRETE_KEY_ENCRYPTION = "AES";
+       public final static String DEFAULT_CIPHER_NAME = "AES/CBC/PKCS5Padding";
+
+       private Integer iterationCountFactor = 200;
+       private Long secreteKeyLength = DEFAULT_SECRETE_KEY_LENGTH;
+       private String secreteKeyFactoryName = DEFAULT_SECRETE_KEY_FACTORY;
+       private String secreteKeyEncryption = DEFAULT_SECRETE_KEY_ENCRYPTION;
+       private String cipherName = DEFAULT_CIPHER_NAME;
+
+       private Session session;
+
+       /**
+        * When setup is called the session has not yet been saved and we don't want
+        * to save it since there maybe other data which would be inconsistent. So
+        * we keep a reference to this node which will then be used (an reset to
+        * null) when handling the PBE callback. We keep one per thread in case
+        * multiple users are accessing the same instance of a keyring.
+        */
+       private ThreadLocal<Node> notYetSavedKeyring = new ThreadLocal<Node>() {
+
+               @Override
+               protected Node initialValue() {
+                       return null;
+               }
+       };
+
+       @Override
+       protected Boolean isSetup() {
+               try {
+                       if (notYetSavedKeyring.get() != null)
+                               return true;
+
+                       Node userHome = UserJcrUtils.getUserHome(session);
+                       return userHome.hasNode(ARGEO_KEYRING);
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot check whether keyring is setup", e);
+               }
+       }
+
+       @Override
+       protected void setup(char[] password) {
+               Binary binary = null;
+               InputStream in = null;
+               try {
+                       Node userHome = UserJcrUtils.getUserHome(session);
+                       if (userHome.hasNode(ARGEO_KEYRING))
+                               throw new ArgeoException("Keyring already setup");
+                       Node keyring = userHome.addNode(ARGEO_KEYRING);
+                       keyring.addMixin(ArgeoTypes.ARGEO_PBE_SPEC);
+
+                       // deterministic salt and iteration count based on username
+                       String username = session.getUserID();
+                       byte[] salt = new byte[8];
+                       byte[] usernameBytes = username.getBytes();
+                       for (int i = 0; i < salt.length; i++) {
+                               if (i < usernameBytes.length)
+                                       salt[i] = usernameBytes[i];
+                               else
+                                       salt[i] = 0;
+                       }
+                       in = new ByteArrayInputStream(salt);
+                       binary = session.getValueFactory().createBinary(in);
+                       keyring.setProperty(ARGEO_SALT, binary);
+
+                       Integer iterationCount = username.length() * iterationCountFactor;
+                       keyring.setProperty(ARGEO_ITERATION_COUNT, iterationCount);
+
+                       // default algo
+                       // TODO check if algo and key length are available, use DES if not
+                       keyring.setProperty(ARGEO_SECRET_KEY_FACTORY, secreteKeyFactoryName);
+                       keyring.setProperty(ARGEO_KEY_LENGTH, secreteKeyLength);
+                       keyring.setProperty(ARGEO_SECRET_KEY_ENCRYPTION,
+                                       secreteKeyEncryption);
+                       keyring.setProperty(ARGEO_CIPHER, cipherName);
+
+                       // encrypted password hash
+                       // IOUtils.closeQuietly(in);
+                       // JcrUtils.closeQuietly(binary);
+                       // byte[] btPass = hash(password, salt, iterationCount);
+                       // in = new ByteArrayInputStream(btPass);
+                       // binary = session.getValueFactory().createBinary(in);
+                       // keyring.setProperty(ARGEO_PASSWORD, binary);
+
+                       notYetSavedKeyring.set(keyring);
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot setup keyring", e);
+               } finally {
+                       JcrUtils.closeQuietly(binary);
+                       IOUtils.closeQuietly(in);
+                       // JcrUtils.discardQuietly(session);
+               }
+       }
+
+       @Override
+       protected void handleKeySpecCallback(PBEKeySpecCallback pbeCallback) {
+               try {
+                       Node userHome = UserJcrUtils.getUserHome(session);
+                       Node keyring;
+                       if (userHome.hasNode(ARGEO_KEYRING))
+                               keyring = userHome.getNode(ARGEO_KEYRING);
+                       else if (notYetSavedKeyring.get() != null)
+                               keyring = notYetSavedKeyring.get();
+                       else
+                               throw new ArgeoException("Keyring not setup");
+
+                       pbeCallback.set(keyring.getProperty(ARGEO_SECRET_KEY_FACTORY)
+                                       .getString(), JcrUtils.getBinaryAsBytes(keyring
+                                       .getProperty(ARGEO_SALT)),
+                                       (int) keyring.getProperty(ARGEO_ITERATION_COUNT).getLong(),
+                                       (int) keyring.getProperty(ARGEO_KEY_LENGTH).getLong(),
+                                       keyring.getProperty(ARGEO_SECRET_KEY_ENCRYPTION)
+                                                       .getString());
+
+                       if (notYetSavedKeyring.get() != null)
+                               notYetSavedKeyring.remove();
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot handle key spec callback", e);
+               }
+       }
+
+       /** The parent node must already exist at this path. */
+       @Override
+       protected synchronized void encrypt(String path, InputStream unencrypted) {
+               // should be called first for lazy initialization
+               SecretKey secretKey = getSecretKey();
+
+               Binary binary = null;
+               InputStream in = null;
+               try {
+                       Cipher cipher = createCipher();
+                       Node node;
+                       if (!session.nodeExists(path)) {
+                               String parentPath = JcrUtils.parentPath(path);
+                               if (!session.nodeExists(parentPath))
+                                       throw new ArgeoException("No parent node of " + path);
+                               Node parentNode = session.getNode(parentPath);
+                               node = parentNode.addNode(JcrUtils.nodeNameFromPath(path));
+                       } else {
+                               node = session.getNode(path);
+                       }
+                       node.addMixin(ArgeoTypes.ARGEO_ENCRYPTED);
+                       SecureRandom random = new SecureRandom();
+                       byte[] iv = new byte[16];
+                       random.nextBytes(iv);
+                       cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));
+                       JcrUtils.setBinaryAsBytes(node, ARGEO_IV, iv);
+
+                       in = new CipherInputStream(unencrypted, cipher);
+                       binary = session.getValueFactory().createBinary(in);
+                       node.setProperty(Property.JCR_DATA, binary);
+                       session.save();
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot encrypt", e);
+               } finally {
+                       IOUtils.closeQuietly(unencrypted);
+                       IOUtils.closeQuietly(in);
+                       JcrUtils.closeQuietly(binary);
+               }
+       }
+
+       @Override
+       protected synchronized InputStream decrypt(String path) {
+               Binary binary = null;
+               InputStream encrypted = null;
+               Reader reader = null;
+               try {
+                       if (!session.nodeExists(path)) {
+                               char[] password = ask();
+                               reader = new CharArrayReader(password);
+                               return new ByteArrayInputStream(IOUtils.toByteArray(reader));
+                       } else {
+                               // should be called first for lazy initialisation
+                               SecretKey secretKey = getSecretKey();
+
+                               Cipher cipher = createCipher();
+
+                               Node node = session.getNode(path);
+                               if (node.hasProperty(ARGEO_IV)) {
+                                       byte[] iv = JcrUtils.getBinaryAsBytes(node
+                                                       .getProperty(ARGEO_IV));
+                                       cipher.init(Cipher.DECRYPT_MODE, secretKey,
+                                                       new IvParameterSpec(iv));
+                               } else {
+                                       cipher.init(Cipher.DECRYPT_MODE, secretKey);
+                               }
+
+                               binary = node.getProperty(Property.JCR_DATA).getBinary();
+                               encrypted = binary.getStream();
+                               return new CipherInputStream(encrypted, cipher);
+                       }
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot decrypt", e);
+               } finally {
+                       IOUtils.closeQuietly(encrypted);
+                       IOUtils.closeQuietly(reader);
+                       JcrUtils.closeQuietly(binary);
+               }
+       }
+
+       protected Cipher createCipher() {
+               try {
+                       Node userHome = UserJcrUtils.getUserHome(session);
+                       if (!userHome.hasNode(ARGEO_KEYRING))
+                               throw new ArgeoException("Keyring not setup");
+                       Node keyring = userHome.getNode(ARGEO_KEYRING);
+                       Cipher cipher = Cipher.getInstance(keyring
+                                       .getProperty(ARGEO_CIPHER).getString(),
+                                       getSecurityProvider());
+                       return cipher;
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot get cipher", e);
+               }
+       }
+
+       public synchronized void changePassword(char[] oldPassword,
+                       char[] newPassword) {
+               // TODO decrypt with old pw / encrypt with new pw all argeo:encrypted
+       }
+
+       public synchronized void setSession(Session session) {
+               this.session = session;
+       }
+
+       public void setIterationCountFactor(Integer iterationCountFactor) {
+               this.iterationCountFactor = iterationCountFactor;
+       }
+
+       public void setSecreteKeyLength(Long keyLength) {
+               this.secreteKeyLength = keyLength;
+       }
+
+       public void setSecreteKeyFactoryName(String secreteKeyFactoryName) {
+               this.secreteKeyFactoryName = secreteKeyFactoryName;
+       }
+
+       public void setSecreteKeyEncryption(String secreteKeyEncryption) {
+               this.secreteKeyEncryption = secreteKeyEncryption;
+       }
+
+       public void setCipherName(String cipherName) {
+               this.cipherName = cipherName;
+       }
+
+}
\ No newline at end of file
diff --git a/org.argeo.security.core/src/org/argeo/security/jcr/JcrSecurityModel.java b/org.argeo.security.core/src/org/argeo/security/jcr/JcrSecurityModel.java
new file mode 100644 (file)
index 0000000..e9ab89c
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.security.jcr;
+
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.Session;
+
+/**
+ * Manages data expected by the Argeo security model, such as user home and
+ * profile.
+ */
+public interface JcrSecurityModel {
+       /**
+        * To be called before user details are loaded. Make sure than any logged in
+        * user has a home directory with full access and a profile with information
+        * about him (read access)
+        * 
+        * @return the user profile (whose parent is the user home), never null
+        */
+       public Node sync(Session session, String username, List<String> roles);
+}
diff --git a/org.argeo.security.core/src/org/argeo/security/jcr/JcrUserDetails.java b/org.argeo.security.core/src/org/argeo/security/jcr/JcrUserDetails.java
new file mode 100644 (file)
index 0000000..2f7b97b
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.security.jcr;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.UserJcrUtils;
+import org.springframework.security.BadCredentialsException;
+import org.springframework.security.DisabledException;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.GrantedAuthorityImpl;
+import org.springframework.security.LockedException;
+import org.springframework.security.userdetails.User;
+
+/** User details based on a user profile node. */
+public class JcrUserDetails extends User implements ArgeoNames {
+       private static final long serialVersionUID = -8142764995842559646L;
+       private final String homePath;
+       private final String securityWorkspace;
+
+       /** Human readable user name */
+       private String displayName;
+
+       protected JcrUserDetails(String securityWorkspace, String homePath,
+                       String username, String password, boolean enabled,
+                       boolean accountNonExpired, boolean credentialsNonExpired,
+                       boolean accountNonLocked, GrantedAuthority[] authorities)
+                       throws IllegalArgumentException {
+               super(username, password, enabled, accountNonExpired,
+                               credentialsNonExpired, accountNonLocked, authorities);
+               this.homePath = homePath;
+               this.securityWorkspace = securityWorkspace;
+       }
+
+       public JcrUserDetails(Node userProfile, String password,
+                       GrantedAuthority[] authorities) throws RepositoryException {
+               super(
+                               userProfile.getProperty(ARGEO_USER_ID).getString(),
+                               password,
+                               userProfile.getProperty(ARGEO_ENABLED).getBoolean(),
+                               userProfile.getProperty(ARGEO_ACCOUNT_NON_EXPIRED).getBoolean(),
+                               userProfile.getProperty(ARGEO_CREDENTIALS_NON_EXPIRED)
+                                               .getBoolean(), userProfile.getProperty(
+                                               ARGEO_ACCOUNT_NON_LOCKED).getBoolean(), authorities);
+               // human readable name
+               if (userProfile.hasProperty(Property.JCR_TITLE)) {
+                       displayName = userProfile.getProperty(Property.JCR_TITLE)
+                                       .getString();
+                       if (displayName.trim().equals(""))
+                               displayName = null;
+               }
+               if (displayName == null)
+                       displayName = userProfile.getProperty(ARGEO_USER_ID).getString();
+               // home is defined as the parent of the profile
+               homePath = userProfile.getParent().getPath();
+               securityWorkspace = userProfile.getSession().getWorkspace().getName();
+       }
+
+       /**
+        * Convenience constructor
+        * 
+        * @param session
+        *            the security session
+        * @param username
+        *            the username
+        * @param password
+        *            the password, can be null
+        * @param authorities
+        *            the granted authorities
+        */
+       public JcrUserDetails(Session session, String username, String password,
+                       GrantedAuthority[] authorities) throws RepositoryException {
+               this(UserJcrUtils.getUserProfile(session, username),
+                               password != null ? password : "", authorities);
+       }
+
+       /**
+        * Check the account status in JCR, throwing the exceptions expected by
+        * Spring security if needed.
+        */
+       public static void checkAccountStatus(Node userProfile) {
+               try {
+                       if (!userProfile.getProperty(ARGEO_ENABLED).getBoolean())
+                               throw new DisabledException(userProfile.getPath()
+                                               + " is disabled");
+                       if (!userProfile.getProperty(ARGEO_ACCOUNT_NON_LOCKED).getBoolean())
+                               throw new LockedException(userProfile.getPath() + " is locked");
+               } catch (RepositoryException e) {
+                       throw new BadCredentialsException("Cannot check account status", e);
+               }
+       }
+
+       /** Clone immutable with new roles */
+       public JcrUserDetails cloneWithNewRoles(List<String> roles) {
+               List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
+               for (String role : roles) {
+                       authorities.add(new GrantedAuthorityImpl(role));
+               }
+               return new JcrUserDetails(securityWorkspace, homePath, getUsername(),
+                               getPassword(), isEnabled(), isAccountNonExpired(),
+                               isAccountNonExpired(), isAccountNonLocked(),
+                               authorities.toArray(new GrantedAuthority[authorities.size()]));
+       }
+
+       /** Clone immutable with new password */
+       public JcrUserDetails cloneWithNewPassword(String password) {
+               return new JcrUserDetails(securityWorkspace, homePath, getUsername(),
+                               password, isEnabled(), isAccountNonExpired(),
+                               isAccountNonExpired(), isAccountNonLocked(), getAuthorities());
+       }
+
+       public String getHomePath() {
+               return homePath;
+       }
+
+       /** Not yet API */
+       public String getSecurityWorkspace() {
+               return securityWorkspace;
+       }
+
+       /** The human readable name of this user */
+       public String getDisplayName() {
+               return displayName;
+       }
+
+       @Override
+       public String toString() {
+               return getDisplayName();
+       }
+
+}
diff --git a/org.argeo.security.core/src/org/argeo/security/jcr/OsJcrAuthenticationProvider.java b/org.argeo.security.core/src/org/argeo/security/jcr/OsJcrAuthenticationProvider.java
new file mode 100644 (file)
index 0000000..aa95e32
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.security.jcr;
+
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.argeo.ArgeoException;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.security.OsAuthenticationToken;
+import org.argeo.security.SecurityUtils;
+import org.argeo.security.core.OsAuthenticationProvider;
+import org.springframework.security.Authentication;
+import org.springframework.security.AuthenticationException;
+import org.springframework.security.BadCredentialsException;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
+import org.springframework.security.userdetails.UserDetails;
+
+/** Relies on OS to authenticate and additionally setup JCR */
+public class OsJcrAuthenticationProvider extends OsAuthenticationProvider {
+       private Repository repository;
+       private Session nodeSession;
+
+       private UserDetails userDetails;
+       private JcrSecurityModel jcrSecurityModel = new SimpleJcrSecurityModel();
+
+       private final static String JVM_OSUSER = System.getProperty("user.name");
+
+       public void init() {
+               try {
+                       nodeSession = repository.login();
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot initialize", e);
+               }
+       }
+
+       public void destroy() {
+               JcrUtils.logoutQuietly(nodeSession);
+       }
+
+       public Authentication authenticate(Authentication authentication)
+                       throws AuthenticationException {
+               if (authentication instanceof UsernamePasswordAuthenticationToken) {
+                       // deal with remote access to internal server
+                       // FIXME very primitive and unsecure at this sSession adminSession
+                       // =tage
+                       // consider using the keyring for username / password authentication
+                       // or certificate
+                       UsernamePasswordAuthenticationToken upat = (UsernamePasswordAuthenticationToken) authentication;
+                       if (!upat.getPrincipal().toString().equals(JVM_OSUSER))
+                               throw new BadCredentialsException("Wrong credentials");
+                       UsernamePasswordAuthenticationToken authen = new UsernamePasswordAuthenticationToken(
+                                       authentication.getPrincipal(),
+                                       authentication.getCredentials(), getBaseAuthorities());
+                       authen.setDetails(userDetails);
+                       return authen;
+               } else if (authentication instanceof OsAuthenticationToken) {
+                       OsAuthenticationToken authen = (OsAuthenticationToken) super
+                                       .authenticate(authentication);
+                       try {
+                               // WARNING: at this stage we assume that the java properties
+                               // will have the same value
+                               GrantedAuthority[] authorities = getBaseAuthorities();
+                               String username = JVM_OSUSER;
+                               Node userProfile = jcrSecurityModel.sync(nodeSession, username,
+                                               SecurityUtils.authoritiesToStringList(authorities));
+                               JcrUserDetails.checkAccountStatus(userProfile);
+
+                               userDetails = new JcrUserDetails(userProfile, authen
+                                               .getCredentials().toString(), authorities);
+                               authen.setDetails(userDetails);
+                               return authen;
+                       } catch (RepositoryException e) {
+                               JcrUtils.discardQuietly(nodeSession);
+                               throw new ArgeoException(
+                                               "Unexpected exception when synchronizing OS and JCR security ",
+                                               e);
+                       }
+               } else {
+                       throw new ArgeoException("Unsupported authentication "
+                                       + authentication.getClass());
+               }
+       }
+
+       public void setRepository(Repository repository) {
+               this.repository = repository;
+       }
+
+       public void setJcrSecurityModel(JcrSecurityModel jcrSecurityModel) {
+               this.jcrSecurityModel = jcrSecurityModel;
+       }
+
+       @SuppressWarnings("rawtypes")
+       public boolean supports(Class authentication) {
+               return OsAuthenticationToken.class.isAssignableFrom(authentication)
+                               || UsernamePasswordAuthenticationToken.class
+                                               .isAssignableFrom(authentication);
+       }
+}
\ No newline at end of file
diff --git a/org.argeo.security.core/src/org/argeo/security/jcr/OsJcrUserAdminService.java b/org.argeo.security.core/src/org/argeo/security/jcr/OsJcrUserAdminService.java
new file mode 100644 (file)
index 0000000..c25bdb8
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.security.jcr;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.argeo.ArgeoException;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.UserJcrUtils;
+import org.argeo.security.UserAdminService;
+import org.springframework.dao.DataAccessException;
+import org.springframework.security.userdetails.User;
+import org.springframework.security.userdetails.UserDetails;
+import org.springframework.security.userdetails.UsernameNotFoundException;
+
+/**
+ * Dummy user service to be used when running as a single OS user (typically
+ * desktop). TODO integrate with JCR user / groups
+ */
+public class OsJcrUserAdminService implements UserAdminService {
+       private Repository repository;
+
+       /** In memory roles provided by applications. */
+       private List<String> roles = new ArrayList<String>();
+
+       // private Session adminSession;
+
+       public void init() {
+               // try {
+               // adminSession = repository.login();
+               // } catch (RepositoryException e) {
+               // throw new ArgeoException("Cannot initialize", e);
+               // }
+       }
+
+       public void destroy() {
+               // JcrUtils.logoutQuietly(adminSession);
+       }
+
+       /** <b>Unsupported</b> */
+       public void createUser(UserDetails user) {
+               throw new UnsupportedOperationException();
+       }
+
+       /** Does nothing */
+       public void updateUser(UserDetails user) {
+
+       }
+
+       /** <b>Unsupported</b> */
+       public void deleteUser(String username) {
+               throw new UnsupportedOperationException();
+       }
+
+       /** <b>Unsupported</b> */
+       public void changePassword(String oldPassword, String newPassword) {
+               throw new UnsupportedOperationException();
+       }
+
+       public boolean userExists(String username) {
+               if (getSPropertyUsername().equals(username))
+                       return true;
+               else
+                       return false;
+       }
+
+       public UserDetails loadUserByUsername(String username)
+                       throws UsernameNotFoundException, DataAccessException {
+               if (getSPropertyUsername().equals(username)) {
+                       UserDetails userDetails;
+                       if (repository != null) {
+                               Session adminSession = null;
+                               try {
+                                       adminSession = repository.login();
+                                       Node userProfile = UserJcrUtils.getUserProfile(
+                                                       adminSession, username);
+                                       userDetails = new JcrUserDetails(userProfile, "",
+                                                       OsJcrAuthenticationProvider.getBaseAuthorities());
+                               } catch (RepositoryException e) {
+                                       throw new ArgeoException(
+                                                       "Cannot retrieve user profile for " + username, e);
+                               } finally {
+                                       JcrUtils.logoutQuietly(adminSession);
+                               }
+                       } else {
+                               userDetails = new User(username, "", true, true, true, true,
+                                               OsJcrAuthenticationProvider.getBaseAuthorities());
+                       }
+                       return userDetails;
+               } else {
+                       throw new UnsupportedOperationException();
+               }
+       }
+
+       protected final String getSPropertyUsername() {
+               return System.getProperty("user.name");
+       }
+
+       public Set<String> listUsers() {
+               Set<String> set = new HashSet<String>();
+               set.add(getSPropertyUsername());
+               return set;
+       }
+
+       public Set<String> listUsersInRole(String role) {
+               Set<String> set = new HashSet<String>();
+               set.add(getSPropertyUsername());
+               return set;
+       }
+
+       /** Does nothing */
+       public void synchronize() {
+       }
+
+       /** <b>Unsupported</b> */
+       public void newRole(String role) {
+               roles.add(role);
+       }
+
+       public Set<String> listEditableRoles() {
+               return new HashSet<String>(roles);
+       }
+
+       /** <b>Unsupported</b> */
+       public void deleteRole(String role) {
+               roles.remove(role);
+       }
+
+       public void setRepository(Repository repository) {
+               this.repository = repository;
+       }
+}
diff --git a/org.argeo.security.core/src/org/argeo/security/jcr/RemoteJcrAuthenticationProvider.java b/org.argeo.security.core/src/org/argeo/security/jcr/RemoteJcrAuthenticationProvider.java
new file mode 100644 (file)
index 0000000..87208b2
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.security.jcr;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.RepositoryFactory;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+import javax.jcr.Value;
+
+import org.argeo.ArgeoException;
+import org.argeo.jcr.ArgeoJcrConstants;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.UserJcrUtils;
+import org.argeo.security.NodeAuthenticationToken;
+import org.osgi.framework.BundleContext;
+import org.springframework.security.Authentication;
+import org.springframework.security.AuthenticationException;
+import org.springframework.security.BadCredentialsException;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.GrantedAuthorityImpl;
+import org.springframework.security.providers.AuthenticationProvider;
+
+/** Connects to a JCR repository and delegates authentication to it. */
+public class RemoteJcrAuthenticationProvider implements AuthenticationProvider,
+               ArgeoNames {
+       private RepositoryFactory repositoryFactory;
+       private BundleContext bundleContext;
+
+       public final static String ROLE_REMOTE = "ROLE_REMOTE";
+
+       public Authentication authenticate(Authentication authentication)
+                       throws AuthenticationException {
+               NodeAuthenticationToken siteAuth = (NodeAuthenticationToken) authentication;
+               String url = siteAuth.getUrl();
+               if (url == null)// TODO? login on own node
+                       throw new ArgeoException("No url set in " + siteAuth);
+               Session session;
+
+               Node userProfile;
+               try {
+                       SimpleCredentials sp = new SimpleCredentials(siteAuth.getName(),
+                                       siteAuth.getCredentials().toString().toCharArray());
+                       // get repository
+                       Repository repository = new RemoteJcrRepositoryWrapper(
+                                       repositoryFactory, url, sp);
+                       if (bundleContext != null) {
+                               Dictionary<String, String> serviceProperties = new Hashtable<String, String>();
+                               serviceProperties.put(ArgeoJcrConstants.JCR_REPOSITORY_ALIAS,
+                                               ArgeoJcrConstants.ALIAS_NODE);
+                               serviceProperties
+                                               .put(ArgeoJcrConstants.JCR_REPOSITORY_URI, url);
+                               bundleContext.registerService(Repository.class.getName(),
+                                               repository, serviceProperties);
+                       }
+                       // Repository repository = ArgeoJcrUtils.getRepositoryByUri(
+                       // repositoryFactory, url);
+                       // if (repository == null)
+                       // throw new ArgeoException("Cannot connect to " + url);
+
+                       session = repository.login(sp, null);
+
+                       userProfile = UserJcrUtils.getUserProfile(session, sp.getUserID());
+                       JcrUserDetails.checkAccountStatus(userProfile);
+
+                       // Node userHome = UserJcrUtils.getUserHome(session);
+                       // if (userHome == null ||
+                       // !userHome.hasNode(ArgeoNames.ARGEO_PROFILE))
+                       // throw new ArgeoException("No profile for user "
+                       // + siteAuth.getName() + " in security workspace "
+                       // + siteAuth.getSecurityWorkspace() + " of "
+                       // + siteAuth.getUrl());
+                       // userProfile = userHome.getNode(ArgeoNames.ARGEO_PROFILE);
+               } catch (RepositoryException e) {
+                       throw new BadCredentialsException(
+                                       "Cannot authenticate " + siteAuth, e);
+               }
+
+               try {
+                       // Node userHome = UserJcrUtils.getUserHome(session);
+                       // retrieve remote roles
+                       List<GrantedAuthority> authoritiesList = new ArrayList<GrantedAuthority>();
+                       if (userProfile != null
+                                       && userProfile.hasProperty(ArgeoNames.ARGEO_REMOTE_ROLES)) {
+                               Value[] roles = userProfile.getProperty(
+                                               ArgeoNames.ARGEO_REMOTE_ROLES).getValues();
+                               for (int i = 0; i < roles.length; i++)
+                                       authoritiesList.add(new GrantedAuthorityImpl(roles[i]
+                                                       .getString()));
+                       }
+                       authoritiesList.add(new GrantedAuthorityImpl(ROLE_REMOTE));
+
+                       // create authenticated objects
+                       GrantedAuthority[] authorities = authoritiesList
+                                       .toArray(new GrantedAuthority[authoritiesList.size()]);
+                       JcrUserDetails userDetails = new JcrUserDetails(userProfile,
+                                       siteAuth.getCredentials().toString(), authorities);
+                       NodeAuthenticationToken authenticated = new NodeAuthenticationToken(
+                                       siteAuth, authorities);
+                       authenticated.setDetails(userDetails);
+                       return authenticated;
+               } catch (RepositoryException e) {
+                       throw new ArgeoException(
+                                       "Unexpected exception when authenticating to " + url, e);
+               }
+       }
+
+       @SuppressWarnings("rawtypes")
+       public boolean supports(Class authentication) {
+               return NodeAuthenticationToken.class.isAssignableFrom(authentication);
+       }
+
+       public void setRepositoryFactory(RepositoryFactory repositoryFactory) {
+               this.repositoryFactory = repositoryFactory;
+       }
+
+       public void setBundleContext(BundleContext bundleContext) {
+               this.bundleContext = bundleContext;
+       }
+
+}
diff --git a/org.argeo.security.core/src/org/argeo/security/jcr/RemoteJcrRepositoryWrapper.java b/org.argeo.security.core/src/org/argeo/security/jcr/RemoteJcrRepositoryWrapper.java
new file mode 100644 (file)
index 0000000..f0ad3a3
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.security.jcr;
+
+import javax.jcr.Credentials;
+import javax.jcr.LoginException;
+import javax.jcr.NoSuchWorkspaceException;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.RepositoryFactory;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.argeo.jcr.ArgeoJcrUtils;
+import org.argeo.jcr.JcrRepositoryWrapper;
+import org.argeo.security.NodeAuthenticationToken;
+import org.argeo.security.SystemAuthentication;
+import org.springframework.security.Authentication;
+import org.springframework.security.context.SecurityContextHolder;
+import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
+
+/**
+ * Wrapper around a remote Jackrabbit repository which allows to simplify
+ * configuration and intercept some actions. It exposes itself as a
+ * {@link Repository}.
+ */
+public class RemoteJcrRepositoryWrapper extends JcrRepositoryWrapper {
+       private final static Log log = LogFactory
+                       .getLog(RemoteJcrRepositoryWrapper.class);
+
+       private String uri = null;
+
+       private RepositoryFactory repositoryFactory;
+
+       // remote
+       private Credentials remoteSystemCredentials = null;
+
+       /**
+        * Empty constructor, {@link #init()} should be called after properties have
+        * been set
+        */
+       public RemoteJcrRepositoryWrapper() {
+       }
+
+       /**
+        * Embedded constructor, calling the {@link #init()} method.
+        * 
+        * @param alias
+        *            if not null the repository will be published under this alias
+        */
+       public RemoteJcrRepositoryWrapper(RepositoryFactory repositoryFactory,
+                       String uri, Credentials remoteSystemCredentials) {
+               this.repositoryFactory = repositoryFactory;
+               this.uri = uri;
+               this.remoteSystemCredentials = remoteSystemCredentials;
+               init();
+       }
+
+       public void init() {
+               Repository repository = createJackrabbitRepository();
+               setRepository(repository);
+       }
+
+       /** Actually creates the new repository. */
+       protected Repository createJackrabbitRepository() {
+               long begin = System.currentTimeMillis();
+               try {
+                       if (uri == null || uri.trim().equals(""))
+                               throw new ArgeoException("Remote URI not set");
+
+                       Repository repository = ArgeoJcrUtils.getRepositoryByUri(
+                                       repositoryFactory, uri);
+                       if (repository == null)
+                               throw new ArgeoException("Remote JCR repository " + uri
+                                               + " not found");
+                       double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
+                       log.info("Created remote JCR repository in " + duration
+                                       + " s from URI " + uri);
+                       // we assume that the data model of the remote repository has
+                       // been properly initialized
+                       return repository;
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot create remote JCR repository "
+                                       + uri, e);
+               }
+       }
+
+       /** Shutdown the repository */
+       public void destroy() throws Exception {
+               super.destroy();
+       }
+
+       /** Central login method */
+       public Session login(Credentials credentials, String workspaceName)
+                       throws LoginException, NoSuchWorkspaceException,
+                       RepositoryException {
+
+               // retrieve credentials for remote
+               if (credentials == null) {
+                       Authentication authentication = SecurityContextHolder.getContext()
+                                       .getAuthentication();
+                       if (authentication != null) {
+                               if (authentication instanceof UsernamePasswordAuthenticationToken) {
+                                       UsernamePasswordAuthenticationToken upat = (UsernamePasswordAuthenticationToken) authentication;
+                                       credentials = new SimpleCredentials(upat.getName(), upat
+                                                       .getCredentials().toString().toCharArray());
+                               } else if ((authentication instanceof SystemAuthentication)
+                                               || (authentication instanceof NodeAuthenticationToken)) {
+                                       credentials = remoteSystemCredentials;
+                               }
+                       }
+               }
+
+               return super.login(credentials, workspaceName);
+       }
+
+       public void setUri(String uri) {
+               this.uri = uri;
+       }
+
+       public void setRepositoryFactory(RepositoryFactory repositoryFactory) {
+               this.repositoryFactory = repositoryFactory;
+       }
+
+       public void setRemoteSystemCredentials(Credentials remoteSystemCredentials) {
+               this.remoteSystemCredentials = remoteSystemCredentials;
+       }
+
+}
diff --git a/org.argeo.security.core/src/org/argeo/security/jcr/SecureThreadBoundSession.java b/org.argeo.security.core/src/org/argeo/security/jcr/SecureThreadBoundSession.java
new file mode 100644 (file)
index 0000000..b398774
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.security.jcr;
+
+import javax.jcr.Session;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.jcr.spring.ThreadBoundSession;
+import org.springframework.security.Authentication;
+import org.springframework.security.context.SecurityContextHolder;
+
+/**
+ * Thread bounded JCR session factory which checks authentication and is
+ * autoconfigured in Spring.
+ */
+public class SecureThreadBoundSession extends ThreadBoundSession {
+       private final static Log log = LogFactory
+                       .getLog(SecureThreadBoundSession.class);
+
+       @Override
+       protected Session preCall(Session session) {
+               Authentication authentication = SecurityContextHolder.getContext()
+                               .getAuthentication();
+               if (authentication != null) {
+                       String userID = session.getUserID();
+                       String currentUserName = authentication.getName();
+                       if (currentUserName != null) {
+                               if (!userID.equals(currentUserName)) {
+                                       log.warn("Current session has user ID " + userID
+                                                       + " while logged is user is " + currentUserName
+                                                       + "(authentication=" + authentication + ")"
+                                                       + ". Re-login.");
+                                       // TODO throw an exception
+                                       return login();
+                               }
+                       }
+               }
+               return super.preCall(session);
+       }
+
+}
diff --git a/org.argeo.security.core/src/org/argeo/security/jcr/SimpleJcrSecurityModel.java b/org.argeo.security.core/src/org/argeo/security/jcr/SimpleJcrSecurityModel.java
new file mode 100644 (file)
index 0000000..fc01587
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.security.jcr;
+
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.jcr.security.Privilege;
+import javax.jcr.version.VersionManager;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.argeo.jcr.ArgeoJcrConstants;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.ArgeoTypes;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.jcr.UserJcrUtils;
+
+/**
+ * Manages data expected by the Argeo security model, such as user home and
+ * profile.
+ */
+public class SimpleJcrSecurityModel implements JcrSecurityModel {
+       private final static Log log = LogFactory
+                       .getLog(SimpleJcrSecurityModel.class);
+       // ArgeoNames not implemented as interface in order to ease derivation by
+       // Jackrabbit bundles
+
+       /** The home base path. */
+       private String homeBasePath = "/home";
+
+       public synchronized Node sync(Session session, String username,
+                       List<String> roles) {
+               // TODO check user name validity (e.g. should not start by ROLE_)
+
+               try {
+                       Node userHome = UserJcrUtils.getUserHome(session, username);
+                       if (userHome == null) {
+                               String homePath = generateUserPath(homeBasePath, username);
+                               userHome = JcrUtils.mkdirs(session, homePath);
+                               // userHome = JcrUtils.mkfolders(session, homePath);
+                               userHome.addMixin(ArgeoTypes.ARGEO_USER_HOME);
+                               userHome.setProperty(ArgeoNames.ARGEO_USER_ID, username);
+                               session.save();
+
+                               JcrUtils.clearAccessControList(session, homePath, username);
+                               JcrUtils.addPrivilege(session, homePath, username,
+                                               Privilege.JCR_ALL);
+                       } else {
+                               // for backward compatibility with pre 1.0 security model
+                               if (userHome.hasNode(ArgeoNames.ARGEO_PROFILE)) {
+                                       userHome.getNode(ArgeoNames.ARGEO_PROFILE).remove();
+                                       userHome.getSession().save();
+                               }
+                       }
+
+                       // Remote roles
+                       if (roles != null) {
+                               // writeRemoteRoles(userHome, roles);
+                       }
+
+                       Node userProfile = UserJcrUtils.getUserProfile(session, username);
+                       if (userProfile == null) {
+                               String personPath = generateUserPath(
+                                               ArgeoJcrConstants.PEOPLE_BASE_PATH, username);
+                               Node personBase = JcrUtils.mkdirs(session, personPath);
+                               userProfile = personBase.addNode(ArgeoNames.ARGEO_PROFILE);
+                               userProfile.addMixin(ArgeoTypes.ARGEO_USER_PROFILE);
+                               userProfile.setProperty(ArgeoNames.ARGEO_USER_ID, username);
+                               userProfile.setProperty(ArgeoNames.ARGEO_ENABLED, true);
+                               userProfile.setProperty(ArgeoNames.ARGEO_ACCOUNT_NON_EXPIRED,
+                                               true);
+                               userProfile.setProperty(ArgeoNames.ARGEO_ACCOUNT_NON_LOCKED,
+                                               true);
+                               userProfile.setProperty(
+                                               ArgeoNames.ARGEO_CREDENTIALS_NON_EXPIRED, true);
+                               session.save();
+
+                               JcrUtils.clearAccessControList(session, userProfile.getPath(),
+                                               username);
+                               JcrUtils.addPrivilege(session, userProfile.getPath(), username,
+                                               Privilege.JCR_READ);
+
+                               VersionManager versionManager = session.getWorkspace()
+                                               .getVersionManager();
+                               if (versionManager.isCheckedOut(userProfile.getPath()))
+                                       versionManager.checkin(userProfile.getPath());
+
+                       }
+
+                       // Remote roles
+                       if (roles != null) {
+                               writeRemoteRoles(userProfile, roles);
+                       }
+                       return userProfile;
+               } catch (RepositoryException e) {
+                       JcrUtils.discardQuietly(session);
+                       throw new ArgeoException("Cannot sync node security model for "
+                                       + username, e);
+               }
+       }
+
+       /** Generate path for a new user home */
+       protected String generateUserPath(String base, String username) {
+               int atIndex = username.indexOf('@');
+               if (atIndex > 0) {
+                       String domain = username.substring(0, atIndex);
+                       String name = username.substring(atIndex + 1);
+                       return base + '/' + JcrUtils.firstCharsToPath(domain, 2) + '/'
+                                       + domain + '/' + JcrUtils.firstCharsToPath(name, 2) + '/'
+                                       + name;
+               } else if (atIndex == 0 || atIndex == (username.length() - 1)) {
+                       throw new ArgeoException("Unsupported username " + username);
+               } else {
+                       return base + '/' + JcrUtils.firstCharsToPath(username, 2) + '/'
+                                       + username;
+               }
+       }
+
+       /** Write remote roles used by remote access in the home directory */
+       protected void writeRemoteRoles(Node userHome, List<String> roles)
+                       throws RepositoryException {
+               boolean writeRoles = false;
+               if (userHome.hasProperty(ArgeoNames.ARGEO_REMOTE_ROLES)) {
+                       Value[] remoteRoles = userHome.getProperty(
+                                       ArgeoNames.ARGEO_REMOTE_ROLES).getValues();
+                       if (remoteRoles.length != roles.size())
+                               writeRoles = true;
+                       else
+                               for (int i = 0; i < remoteRoles.length; i++)
+                                       if (!remoteRoles[i].getString().equals(roles.get(i)))
+                                               writeRoles = true;
+               } else
+                       writeRoles = true;
+
+               if (writeRoles) {
+                       userHome.getSession().getWorkspace().getVersionManager()
+                                       .checkout(userHome.getPath());
+                       String[] roleIds = roles.toArray(new String[roles.size()]);
+                       userHome.setProperty(ArgeoNames.ARGEO_REMOTE_ROLES, roleIds);
+                       JcrUtils.updateLastModified(userHome);
+                       userHome.getSession().save();
+                       userHome.getSession().getWorkspace().getVersionManager()
+                                       .checkin(userHome.getPath());
+                       if (log.isDebugEnabled())
+                               log.debug("Wrote remote roles " + roles + " for "
+                                               + userHome.getProperty(ArgeoNames.ARGEO_USER_ID));
+               }
+
+       }
+
+       public void setHomeBasePath(String homeBasePath) {
+               this.homeBasePath = homeBasePath;
+       }
+
+}
diff --git a/org.argeo.security.core/src/org/argeo/security/jcr/rememberme/JcrPersistentTokenRepository.java b/org.argeo.security.core/src/org/argeo/security/jcr/rememberme/JcrPersistentTokenRepository.java
new file mode 100644 (file)
index 0000000..37dc986
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.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/org.argeo.security.core/src/org/argeo/security/log4j/SecureLogger.java b/org.argeo.security.core/src/org/argeo/security/log4j/SecureLogger.java
new file mode 100644 (file)
index 0000000..1da9857
--- /dev/null
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2007-2012 Argeo GmbH
+ *
+ * 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.security.log4j;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.PropertyConfigurator;
+import org.apache.log4j.spi.LoggingEvent;
+import org.argeo.ArgeoException;
+import org.argeo.ArgeoLogListener;
+import org.argeo.ArgeoLogger;
+import org.argeo.security.SecurityUtils;
+
+/** Not meant to be used directly in standard log4j config */
+public class SecureLogger implements ArgeoLogger {
+
+       private Boolean disabled = false;
+
+       private String level = null;
+
+       private Level log4jLevel = null;
+       // private Layout layout;
+
+       private Properties configuration;
+
+       private AppenderImpl appender;
+
+       private final List<ArgeoLogListener> everythingListeners = Collections
+                       .synchronizedList(new ArrayList<ArgeoLogListener>());
+       private final List<ArgeoLogListener> allUsersListeners = Collections
+                       .synchronizedList(new ArrayList<ArgeoLogListener>());
+       private final Map<String, List<ArgeoLogListener>> userListeners = Collections
+                       .synchronizedMap(new HashMap<String, List<ArgeoLogListener>>());
+
+       private BlockingQueue<LogEvent> events;
+       private LogDispatcherThread logDispatcherThread = new LogDispatcherThread();
+
+       private Integer maxLastEventsCount = 10 * 1000;
+
+       /** Marker to prevent stack overflow */
+       private ThreadLocal<Boolean> dispatching = new ThreadLocal<Boolean>() {
+
+               @Override
+               protected Boolean initialValue() {
+                       return false;
+               }
+       };
+
+       public void init() {
+               try {
+                       events = new LinkedBlockingQueue<LogEvent>();
+
+                       // if (layout != null)
+                       // setLayout(layout);
+                       // else
+                       // setLayout(new PatternLayout(pattern));
+                       appender = new AppenderImpl();
+                       reloadConfiguration();
+                       Logger.getRootLogger().addAppender(appender);
+
+                       logDispatcherThread = new LogDispatcherThread();
+                       logDispatcherThread.start();
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot initialize log4j");
+               }
+       }
+
+       public void destroy() throws Exception {
+               Logger.getRootLogger().removeAppender(appender);
+               allUsersListeners.clear();
+               for (List<ArgeoLogListener> lst : userListeners.values())
+                       lst.clear();
+               userListeners.clear();
+
+               events.clear();
+               events = null;
+               logDispatcherThread.interrupt();
+       }
+
+       // public void setLayout(Layout layout) {
+       // this.layout = layout;
+       // }
+
+       public synchronized void register(ArgeoLogListener listener,
+                       Integer numberOfPreviousEvents) {
+               String username = SecurityUtils.getCurrentThreadUsername();
+               if (username == null)
+                       throw new ArgeoException(
+                                       "Only authenticated users can register a log listener");
+
+               if (!userListeners.containsKey(username)) {
+                       List<ArgeoLogListener> lst = Collections
+                                       .synchronizedList(new ArrayList<ArgeoLogListener>());
+                       userListeners.put(username, lst);
+               }
+               userListeners.get(username).add(listener);
+               List<LogEvent> lastEvents = logDispatcherThread.getLastEvents(username,
+                               numberOfPreviousEvents);
+               for (LogEvent evt : lastEvents)
+                       dispatchEvent(listener, evt);
+       }
+
+       public synchronized void registerForAll(ArgeoLogListener listener,
+                       Integer numberOfPreviousEvents, boolean everything) {
+               if (everything)
+                       everythingListeners.add(listener);
+               else
+                       allUsersListeners.add(listener);
+               List<LogEvent> lastEvents = logDispatcherThread.getLastEvents(null,
+                               numberOfPreviousEvents);
+               for (LogEvent evt : lastEvents)
+                       if (everything || evt.getUsername() != null)
+                               dispatchEvent(listener, evt);
+       }
+
+       public synchronized void unregister(ArgeoLogListener listener) {
+               String username = SecurityUtils.getCurrentThreadUsername();
+               if (!userListeners.containsKey(username))
+                       throw new ArgeoException("No user listeners " + listener
+                                       + " registered for user " + username);
+               if (!userListeners.get(username).contains(listener))
+                       throw new ArgeoException("No user listeners " + listener
+                                       + " registered for user " + username);
+               userListeners.get(username).remove(listener);
+               if (userListeners.get(username).isEmpty())
+                       userListeners.remove(username);
+
+       }
+
+       public synchronized void unregisterForAll(ArgeoLogListener listener) {
+               everythingListeners.remove(listener);
+               allUsersListeners.remove(listener);
+       }
+
+       /** For development purpose, since using regular logging is not easy here */
+       static void stdOut(Object obj) {
+               System.out.println(obj);
+       }
+
+       // public void setPattern(String pattern) {
+       // this.pattern = pattern;
+       // }
+
+       public void setDisabled(Boolean disabled) {
+               this.disabled = disabled;
+       }
+
+       public void setLevel(String level) {
+               this.level = level;
+       }
+
+       public void setConfiguration(Properties configuration) {
+               this.configuration = configuration;
+       }
+
+       public void updateConfiguration(Properties configuration) {
+               setConfiguration(configuration);
+               reloadConfiguration();
+       }
+
+       public Properties getConfiguration() {
+               return configuration;
+       }
+
+       /** Reloads configuration (if the configuration {@link Properties} is set) */
+       protected void reloadConfiguration() {
+               if (configuration != null) {
+                       LogManager.resetConfiguration();
+                       PropertyConfigurator.configure(configuration);
+               }
+       }
+
+       protected synchronized void processLoggingEvent(LogEvent event) {
+               if (disabled)
+                       return;
+
+               if (dispatching.get())
+                       return;
+
+               if (level != null && !level.trim().equals("")) {
+                       if (log4jLevel == null || !log4jLevel.toString().equals(level))
+                               try {
+                                       log4jLevel = Level.toLevel(level);
+                               } catch (Exception e) {
+                                       System.err
+                                                       .println("Log4j level could not be set for level '"
+                                                                       + level + "', resetting it to null.");
+                                       e.printStackTrace();
+                                       level = null;
+                               }
+
+                       if (log4jLevel != null
+                                       && !event.getLoggingEvent().getLevel()
+                                                       .isGreaterOrEqual(log4jLevel)) {
+                               return;
+                       }
+               }
+
+               try {
+                       // admin listeners
+                       Iterator<ArgeoLogListener> everythingIt = everythingListeners
+                                       .iterator();
+                       while (everythingIt.hasNext())
+                               dispatchEvent(everythingIt.next(), event);
+
+                       if (event.getUsername() != null) {
+                               Iterator<ArgeoLogListener> allUsersIt = allUsersListeners
+                                               .iterator();
+                               while (allUsersIt.hasNext())
+                                       dispatchEvent(allUsersIt.next(), event);
+
+                               if (userListeners.containsKey(event.getUsername())) {
+                                       Iterator<ArgeoLogListener> userIt = userListeners.get(
+                                                       event.getUsername()).iterator();
+                                       while (userIt.hasNext())
+                                               dispatchEvent(userIt.next(), event);
+                               }
+                       }
+               } catch (Exception e) {
+                       stdOut("Cannot process logging event");
+                       e.printStackTrace();
+               }
+       }
+
+       protected void dispatchEvent(ArgeoLogListener logListener, LogEvent evt) {
+               LoggingEvent event = evt.getLoggingEvent();
+               logListener.appendLog(evt.getUsername(), event.getTimeStamp(), event
+                               .getLevel().toString(), event.getLoggerName(), event
+                               .getThreadName(), event.getMessage(), event
+                               .getThrowableStrRep());
+       }
+
+       private class AppenderImpl extends AppenderSkeleton {
+               public boolean requiresLayout() {
+                       return false;
+               }
+
+               public void close() {
+               }
+
+               @Override
+               protected void append(LoggingEvent event) {
+                       if (events != null) {
+                               try {
+                                       String username = SecurityUtils.getCurrentThreadUsername();
+                                       events.put(new LogEvent(username, event));
+                               } catch (InterruptedException e) {
+                                       // silent
+                               }
+                       }
+               }
+
+       }
+
+       private class LogDispatcherThread extends Thread {
+               /** encapsulated in order to simplify concurrency management */
+               private LinkedList<LogEvent> lastEvents = new LinkedList<LogEvent>();
+
+               public LogDispatcherThread() {
+                       super("Argeo Logging Dispatcher Thread");
+               }
+
+               public void run() {
+                       while (events != null) {
+                               try {
+                                       LogEvent loggingEvent = events.take();
+                                       processLoggingEvent(loggingEvent);
+                                       addLastEvent(loggingEvent);
+                               } catch (InterruptedException e) {
+                                       if (events == null)
+                                               return;
+                               }
+                       }
+               }
+
+               protected synchronized void addLastEvent(LogEvent loggingEvent) {
+                       if (lastEvents.size() >= maxLastEventsCount)
+                               lastEvents.poll();
+                       lastEvents.add(loggingEvent);
+               }
+
+               public synchronized List<LogEvent> getLastEvents(String username,
+                               Integer maxCount) {
+                       LinkedList<LogEvent> evts = new LinkedList<LogEvent>();
+                       ListIterator<LogEvent> it = lastEvents.listIterator(lastEvents
+                                       .size());
+                       int count = 0;
+                       while (it.hasPrevious() && (count < maxCount)) {
+                               LogEvent evt = it.previous();
+                               if (username == null || username.equals(evt.getUsername())) {
+                                       evts.push(evt);
+                                       count++;
+                               }
+                       }
+                       return evts;
+               }
+       }
+
+       private class LogEvent {
+               private final String username;
+               private final LoggingEvent loggingEvent;
+
+               public LogEvent(String username, LoggingEvent loggingEvent) {
+                       super();
+                       this.username = username;
+                       this.loggingEvent = loggingEvent;
+               }
+
+               @Override
+               public int hashCode() {
+                       return loggingEvent.hashCode();
+               }
+
+               @Override
+               public boolean equals(Object obj) {
+                       return loggingEvent.equals(obj);
+               }
+
+               @Override
+               public String toString() {
+                       return username + "@ " + loggingEvent.toString();
+               }
+
+               public String getUsername() {
+                       return username;
+               }
+
+               public LoggingEvent getLoggingEvent() {
+                       return loggingEvent;
+               }
+
+       }
+}
diff --git a/org.argeo.security.core/src/test/java/org/argeo/security/PasswordSandbox.java b/org.argeo.security.core/src/test/java/org/argeo/security/PasswordSandbox.java
deleted file mode 100644 (file)
index d8a0846..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.security;
-
-import org.apache.commons.codec.DecoderException;
-import org.apache.commons.codec.binary.Base64;
-import org.apache.commons.codec.binary.Hex;
-import org.springframework.security.providers.ldap.authenticator.LdapShaPasswordEncoder;
-
-public class PasswordSandbox {
-       public static void main(String[] args) {
-               try {
-                       // Tested password
-                       String pwdPlain = "demo";
-
-                       // Check Java generated values
-                       LdapShaPasswordEncoder lspe = new LdapShaPasswordEncoder();
-                       String pwdLdapShaBase64 = lspe.encodePassword(pwdPlain, null);
-                       System.out.println("pwdLdapShaBase64:\t\t" + pwdLdapShaBase64);
-
-                       String pwdShaBase64 = pwdLdapShaBase64.substring("{SHA}".length());
-                       System.out.println("pwdShaBase64:\t\t\t" + pwdShaBase64);
-
-                       byte[] pwdShaArray = Base64.decodeBase64(pwdShaBase64.getBytes());
-                       String pwdShaHex = new String(Hex.encodeHex(pwdShaArray));
-                       System.out.println("pwdShaHex:\t\t\t" + pwdShaHex);
-
-                       // Check that we can use JavaScript generated values in Hex
-                       String jsShaHex = "89e495e7941cf9e40e6980d14a16bf023ccd4c91";
-                       System.out.println("jsShaHex:\t\t\t" + pwdShaHex);
-                       System.out.println("pwdShaHex==jsShaHex:\t\t"
-                                       + (pwdShaHex.equals(jsShaHex)));
-
-                       byte[] jsShaArray = Hex.decodeHex(jsShaHex.toCharArray());
-                       String jsShaBase64 = new String(Base64.encodeBase64(jsShaArray));
-                       System.out.println("jsShaBase64:\t\t\t" + jsShaBase64);
-                       System.out.println("pwdShaBase64==jsShaBase64:\t"
-                                       + (pwdShaBase64.equals(jsShaBase64)));
-               } catch (DecoderException e) {
-                       e.printStackTrace();
-               }
-
-       }
-
-}
\ No newline at end of file
diff --git a/org.argeo.security.core/src/test/java/org/argeo/security/crypto/PasswordBasedEncryptionTest.java b/org.argeo.security.core/src/test/java/org/argeo/security/crypto/PasswordBasedEncryptionTest.java
deleted file mode 100644 (file)
index 6973f57..0000000
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Copyright (C) 2007-2012 Argeo GmbH
- *
- * 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.security.crypto;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-
-import javax.crypto.Cipher;
-import javax.crypto.CipherInputStream;
-import javax.crypto.CipherOutputStream;
-import javax.crypto.SecretKey;
-import javax.crypto.SecretKeyFactory;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.PBEKeySpec;
-import javax.crypto.spec.PBEParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-import javax.xml.bind.DatatypeConverter;
-
-import junit.framework.TestCase;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.StreamUtils;
-import org.argeo.security.crypto.PasswordBasedEncryption;
-
-public class PasswordBasedEncryptionTest extends TestCase {
-       private final static Log log = LogFactory
-                       .getLog(PasswordBasedEncryptionTest.class);
-
-       public void testEncryptDecrypt() {
-               final String password = "test long password since they are safer";
-               PasswordBasedEncryption pbeEnc = new PasswordBasedEncryption(
-                               password.toCharArray());
-               String message = "Hello World!";
-               log.info("Password:\t'" + password + "'");
-               log.info("Message:\t'" + message + "'");
-               byte[] encrypted = pbeEnc.encryptString(message);
-               log.info("Encrypted:\t'"
-                               + DatatypeConverter.printBase64Binary(encrypted) + "'");
-               PasswordBasedEncryption pbeDec = new PasswordBasedEncryption(
-                               password.toCharArray());
-               InputStream in = null;
-               in = new ByteArrayInputStream(encrypted);
-               String decrypted = pbeDec.decryptAsString(in);
-               log.info("Decrypted:\t'" + decrypted + "'");
-               StreamUtils.closeQuietly(in);
-               assertEquals(message, decrypted);
-       }
-
-       public void testPBEWithMD5AndDES() throws Exception {
-               String password = "test";
-               String message = "Hello World!";
-
-               byte[] salt = { (byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c,
-                               (byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99 };
-
-               int count = 1024;
-
-               String cipherAlgorithm = "PBEWithMD5AndDES";
-               String secretKeyAlgorithm = "PBEWithMD5AndDES";
-               SecretKeyFactory keyFac = SecretKeyFactory
-                               .getInstance(secretKeyAlgorithm);
-               PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray());
-               PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, count);
-               SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);
-               Cipher ecipher = Cipher.getInstance(cipherAlgorithm);
-               ecipher.init(Cipher.ENCRYPT_MODE, pbeKey, pbeParamSpec);
-               Cipher dcipher = Cipher.getInstance(cipherAlgorithm);
-               dcipher.init(Cipher.DECRYPT_MODE, pbeKey, pbeParamSpec);
-
-               byte[] encrypted = ecipher.doFinal(message.getBytes());
-               byte[] decrypted = dcipher.doFinal(encrypted);
-               assertEquals(message, new String(decrypted));
-
-       }
-
-       public void testPBEWithSHA1AndAES() throws Exception {
-               String password = "test";
-               String message = "Hello World!";
-
-               byte[] salt = { (byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c,
-                               (byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99 };
-               byte[] iv = { (byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c,
-                               (byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99,
-                               (byte) 0xc7, (byte) 0x73, (byte) 0x21, (byte) 0x8c,
-                               (byte) 0x7e, (byte) 0xc8, (byte) 0xee, (byte) 0x99 };
-
-               int count = 1024;
-               // int keyLength = 256;
-               int keyLength = 128;
-
-               String cipherAlgorithm = "AES/CBC/PKCS5Padding";
-               String secretKeyAlgorithm = "PBKDF2WithHmacSHA1";
-               SecretKeyFactory keyFac = SecretKeyFactory
-                               .getInstance(secretKeyAlgorithm);
-               PBEKeySpec pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt,
-                               count, keyLength);
-               SecretKey tmp = keyFac.generateSecret(pbeKeySpec);
-               SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");
-               Cipher ecipher = Cipher.getInstance(cipherAlgorithm);
-               ecipher.init(Cipher.ENCRYPT_MODE, secret, new IvParameterSpec(iv));
-
-               // decrypt
-               keyFac = SecretKeyFactory.getInstance(secretKeyAlgorithm);
-               pbeKeySpec = new PBEKeySpec(password.toCharArray(), salt, count,
-                               keyLength);
-               tmp = keyFac.generateSecret(pbeKeySpec);
-               secret = new SecretKeySpec(tmp.getEncoded(), "AES");
-               // AlgorithmParameters params = ecipher.getParameters();
-               // byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
-               Cipher dcipher = Cipher.getInstance(cipherAlgorithm);
-               dcipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(iv));
-
-               byte[] encrypted = ecipher.doFinal(message.getBytes());
-               byte[] decrypted = dcipher.doFinal(encrypted);
-               assertEquals(message, new String(decrypted));
-
-               ByteArrayOutputStream out = new ByteArrayOutputStream();
-               CipherOutputStream cipherOut = new CipherOutputStream(out, ecipher);
-               cipherOut.write(message.getBytes());
-               StreamUtils.closeQuietly(cipherOut);
-               byte[] enc = out.toByteArray();
-
-               ByteArrayInputStream in = new ByteArrayInputStream(enc);
-               CipherInputStream cipherIn = new CipherInputStream(in, dcipher);
-               ByteArrayOutputStream dec = new ByteArrayOutputStream();
-               StreamUtils.copy(cipherIn, dec);
-               assertEquals(message, new String(dec.toByteArray()));
-       }
-}
diff --git a/org.argeo.security.core/src/test/resources/log4j.properties b/org.argeo.security.core/src/test/resources/log4j.properties
deleted file mode 100644 (file)
index b33daa9..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-log4j.rootLogger=WARN, console
-
-## Levels
-log4j.logger.org.argeo=DEBUG
-
-log4j.logger.org.hibernate=WARN
-
-log4j.logger.org.springframework=WARN
-#log4j.logger.org.springframework.web=DEBUG
-#log4j.logger.org.springframework.jms=WARN
-#log4j.logger.org.springframework.security=WARN
-
-log4j.logger.org.apache.activemq=WARN
-log4j.logger.org.apache.activemq.transport=WARN
-log4j.logger.org.apache.activemq.ActiveMQMessageConsumer=INFO
-log4j.logger.org.apache.activemq.ActiveMQMessageProducer=INFO
-
-log4j.logger.org.apache.catalina=INFO
-log4j.logger.org.apache.coyote=INFO
-log4j.logger.org.apache.tomcat=INFO
-
-## Appenders
-# console is set to be a ConsoleAppender.
-log4j.appender.console=org.apache.log4j.ConsoleAppender
-
-# console uses PatternLayout.
-log4j.appender.console.layout=org.apache.log4j.PatternLayout
-log4j.appender.console.layout.ConversionPattern= %-5p %d{ISO8601} %m - %c%n
diff --git a/org.argeo.security.core/src/test/resources/org/argeo/security/json/gandalf2.json b/org.argeo.security.core/src/test/resources/org/argeo/security/json/gandalf2.json
deleted file mode 100644 (file)
index 7391822..0000000
+++ /dev/null
@@ -1 +0,0 @@
-{"roles":["ROLE_ADMIN","ROLE_USER"],"userNatures":[{"description":"Superuser","email":"admin@localhost","firstName":"Gandalf","lastName":"User","type":"org.argeo.security.nature.SimpleUserNature"},{"mobile":null,"telephoneNumber":null,"type":"org.argeo.security.nature.CoworkerNature"}],"username":"gandalf2","enabled":true,"password":"{SHA}ieSV55Qc+eQOaYDRSha/AjzNTJE=","authorities":[{"authority":"ROLE_ADMIN"},{"authority":"ROLE_USER"}],"accountNonExpired":true,"accountNonLocked":true,"credentialsNonExpired":true}
\ No newline at end of file