Big cleanup of the security layers
authorMathieu Baudier <mbaudier@argeo.org>
Wed, 2 Nov 2011 19:29:31 +0000 (19:29 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Wed, 2 Nov 2011 19:29:31 +0000 (19:29 +0000)
git-svn-id: https://svn.argeo.org/commons/trunk@4872 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

58 files changed:
demo/argeo_node_web.properties
security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/jcrsecuritydao-osgi.xml [deleted file]
security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/jcrsecuritydao.xml [deleted file]
security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/security-jcr-osgi.xml [new file with mode: 0644]
security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/security-jcr-services.xml [new file with mode: 0644]
security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/services.xml [deleted file]
security/modules/org.argeo.security.dao.ldap/META-INF/spring/ldap-jcr.xml [deleted file]
security/modules/org.argeo.security.dao.ldap/META-INF/spring/ldap-osgi.xml [deleted file]
security/modules/org.argeo.security.dao.ldap/META-INF/spring/ldap.xml [deleted file]
security/modules/org.argeo.security.dao.ldap/META-INF/spring/security-ldap-jcr.xml [new file with mode: 0644]
security/modules/org.argeo.security.dao.ldap/META-INF/spring/security-ldap-osgi.xml [new file with mode: 0644]
security/modules/org.argeo.security.dao.ldap/META-INF/spring/security-ldap-services.xml [new file with mode: 0644]
security/modules/org.argeo.security.dao.ldap/META-INF/spring/security-ldap.xml [new file with mode: 0644]
security/modules/org.argeo.security.dao.ldap/ldap.properties
security/modules/org.argeo.security.dao.os/META-INF/MANIFEST.MF
security/modules/org.argeo.security.dao.os/META-INF/spring/security-os-osgi.xml
security/modules/org.argeo.security.dao.os/META-INF/spring/security-os.xml
security/modules/org.argeo.security.dao.os/security.properties
security/plugins/org.argeo.security.equinox/src/main/java/org/argeo/security/equinox/SpringLoginModule.java
security/plugins/org.argeo.security.ui.admin/META-INF/spring/common.xml
security/plugins/org.argeo.security.ui.admin/build.properties
security/plugins/org.argeo.security.ui.admin/security-admin.properties [new file with mode: 0644]
security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/editors/DefaultUserMainPage.java
security/plugins/org.argeo.security.ui.admin/src/main/java/org/argeo/security/ui/admin/wizards/NewUserWizard.java
security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/NodeAuthenticationToken.java [new file with mode: 0644]
security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/OsAuthenticationToken.java
security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/SiteAuthenticationToken.java [deleted file]
security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/UserAdminService.java
security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/core/OsAuthenticationProvider.java
security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrAuthenticationProvider.java [deleted file]
security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrAuthenticationToken.java [deleted file]
security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrUserDetails.java
security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/OsJcrAuthenticationProvider.java
security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/RemoteJcrAuthenticationProvider.java [new file with mode: 0644]
security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/SecureThreadBoundSession.java
security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/SystemSession.java [deleted file]
security/runtime/org.argeo.security.jackrabbit/src/main/java/org/argeo/security/jackrabbit/ArgeoSecurityManager.java
security/runtime/org.argeo.security.jackrabbit/src/main/java/org/argeo/security/jackrabbit/providers/JackrabbitAuthenticationProvider.java [deleted file]
security/runtime/org.argeo.security.ldap/.classpath
security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/jcr/JcrLdapSynchronizer.java [new file with mode: 0644]
security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/jcr/JcrUserDetailsContextMapper.java [deleted file]
server/modules/org.argeo.jackrabbit.webapp/WEB-INF/applicationContext.xml
server/modules/org.argeo.jackrabbit.webapp/WEB-INF/osgi.xml
server/modules/org.argeo.node.repo.jackrabbit/META-INF/spring/noderepo-osgi.xml
server/modules/org.argeo.node.repo.jackrabbit/META-INF/spring/noderepo.xml
server/modules/org.argeo.node.repo.jackrabbit/build.properties
server/modules/org.argeo.node.repo.jackrabbit/noderepo.properties
server/modules/org.argeo.node.repo.jackrabbit/pom.xml
server/modules/org.argeo.node.repo.jackrabbit/repository-h2.xml
server/modules/org.argeo.node.repo.jackrabbit/repository-postgresql.xml
server/plugins/org.argeo.jcr.ui.explorer/.classpath
server/plugins/org.argeo.jcr.ui.explorer/src/main/java/org/argeo/jcr/ui/explorer/model/RepositoriesNode.java
server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/JackrabbitContainer.java
server/runtime/org.argeo.server.jackrabbit/src/main/java/org/argeo/jackrabbit/remote/SimpleSessionProvider.java
server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/ArgeoNames.java
server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/JcrUtils.java
server/runtime/org.argeo.server.jcr/src/main/java/org/argeo/jcr/security/JcrKeyring.java
server/runtime/org.argeo.server.jcr/src/main/resources/org/argeo/jcr/argeo.cnd

index bece10ceebce6eab27d9d749e24237c181a88385..c94e740fa92498c5cdb34df30770b7cad769ce40 100644 (file)
@@ -1,6 +1,5 @@
 argeo.osgi.start=\
 org.springframework.osgi.extender,\
-org.argeo.security.services,\
 org.argeo.node.repofactory.jackrabbit,\
 org.argeo.node.repo.jackrabbit,\
 org.argeo.security.dao.ldap,\
@@ -22,4 +21,7 @@ org.argeo.security.ui.initialPerspective=org.argeo.jcr.ui.explorer.perspective
 log4j.configuration=file:../../log4j.properties
 
 # Note default URL to access the webapp
-# http://localhost:7070/org.argeo.rap.webapp/node
\ No newline at end of file
+# http://localhost:7070/org.argeo.rap.webapp/node
+
+# Useful low level debug information
+com.sun.jndi.ldap.connect.pool.debug=true
\ No newline at end of file
diff --git a/security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/jcrsecuritydao-osgi.xml b/security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/jcrsecuritydao-osgi.xml
deleted file mode 100644 (file)
index 5ab9147..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>\r
-<beans:beans xmlns="http://www.springframework.org/schema/osgi"\r
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"\r
-       xmlns:util="http://www.springframework.org/schema/util"\r
-       xsi:schemaLocation="http://www.springframework.org/schema/osgi  \r
-       http://www.springframework.org/schema/osgi/spring-osgi-1.1.xsd\r
-       http://www.springframework.org/schema/beans   \r
-       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd\r
-       http://www.springframework.org/schema/util\r
-       http://www.springframework.org/schema/util/spring-util-2.5.xsd">\r
-\r
-       <!-- REFERENCE -->\r
-       <reference id="repositoryFactory" interface="javax.jcr.RepositoryFactory"\r
-               cardinality="0..1">\r
-               <listener ref="jcrAuthenticationProvider" bind-method="register"\r
-                       unbind-method="unregister" />\r
-       </reference>\r
-\r
-       <!-- SERVICES -->\r
-       <service ref="systemExecutionService" interface="org.argeo.security.SystemExecutionService" />\r
-\r
-       <service ref="authenticationManager"\r
-               interface="org.springframework.security.AuthenticationManager" />\r
-\r
-</beans:beans>
\ No newline at end of file
diff --git a/security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/jcrsecuritydao.xml b/security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/jcrsecuritydao.xml
deleted file mode 100644 (file)
index c09a53b..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<beans xmlns="http://www.springframework.org/schema/beans"
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
-
-       <bean id="jcrAuthenticationProvider"
-               class="org.argeo.security.jackrabbit.providers.JackrabbitAuthenticationProvider" />
-</beans>
\ No newline at end of file
diff --git a/security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/security-jcr-osgi.xml b/security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/security-jcr-osgi.xml
new file mode 100644 (file)
index 0000000..53f66d5
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<beans:beans xmlns="http://www.springframework.org/schema/osgi"\r
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"\r
+       xmlns:util="http://www.springframework.org/schema/util"\r
+       xsi:schemaLocation="http://www.springframework.org/schema/osgi  \r
+       http://www.springframework.org/schema/osgi/spring-osgi-1.1.xsd\r
+       http://www.springframework.org/schema/beans   \r
+       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd\r
+       http://www.springframework.org/schema/util\r
+       http://www.springframework.org/schema/util/spring-util-2.5.xsd">\r
+\r
+       <!-- REFERENCE -->\r
+       <reference id="repositoryFactory" interface="javax.jcr.RepositoryFactory" />\r
+\r
+       <!-- SERVICES -->\r
+       <service ref="systemExecutionService" interface="org.argeo.security.SystemExecutionService" />\r
+\r
+       <service ref="authenticationManager"\r
+               interface="org.springframework.security.AuthenticationManager" />\r
+\r
+</beans:beans>
\ No newline at end of file
diff --git a/security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/security-jcr-services.xml b/security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/security-jcr-services.xml
new file mode 100644 (file)
index 0000000..49ace03
--- /dev/null
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="
+       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
+
+       <bean
+               class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+               <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
+               <property name="locations">
+                       <value>osgibundle:security.properties</value>
+               </property>
+       </bean>
+
+       <bean id="systemExecutionService" class="org.argeo.security.core.KeyBasedSystemExecutionService">
+               <property name="systemAuthenticationKey" value="${argeo.security.systemKey}" />
+               <property name="authenticationManager" ref="authenticationManager" />
+       </bean>
+
+       <bean id="authenticationManager" class="org.springframework.security.providers.ProviderManager">
+               <property name="providers">
+                       <list>
+                               <ref bean="authByAdapterProvider" />
+                               <ref bean="remoteJcrAuthenticationProvider" />
+                       </list>
+               </property>
+       </bean>
+
+       <!-- Authentication providers -->
+       <bean id="remoteJcrAuthenticationProvider" class="org.argeo.security.jcr.RemoteJcrAuthenticationProvider">
+               <property name="repositoryFactory" ref="repositoryFactory" />
+       </bean>
+
+       <bean id="authByAdapterProvider"
+               class="org.springframework.security.adapters.AuthByAdapterProvider">
+               <property name="key" value="${argeo.security.systemKey}" />
+       </bean>
+
+</beans>
\ No newline at end of file
diff --git a/security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/services.xml b/security/modules/org.argeo.security.dao.jackrabbit/META-INF/spring/services.xml
deleted file mode 100644 (file)
index 00bb453..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<beans xmlns="http://www.springframework.org/schema/beans"
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-       xsi:schemaLocation="
-       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
-
-       <bean
-               class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
-               <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
-               <property name="locations">
-                       <value>osgibundle:security.properties</value>
-               </property>
-       </bean>
-
-       <bean id="systemExecutionService" class="org.argeo.security.core.KeyBasedSystemExecutionService">
-               <property name="systemAuthenticationKey" value="${argeo.security.systemKey}" />
-               <property name="authenticationManager" ref="authenticationManager" />
-       </bean>
-
-       <bean id="authenticationManager" class="org.springframework.security.providers.ProviderManager">
-               <property name="providers">
-                       <bean factory-bean="authenticationProvidersRegister"
-                               factory-method="getProviders" />
-               </property>
-       </bean>
-
-       <bean id="authenticationProvidersRegister" class="org.argeo.security.core.AuthenticationProvidersRegister">
-               <property name="defaultProviders">
-                       <list>
-                               <bean class="org.springframework.security.adapters.AuthByAdapterProvider">
-                                       <property name="key" value="${argeo.security.systemKey}" />
-                               </bean>
-                               <ref bean="jcrAuthenticationProvider" />
-                       </list>
-               </property>
-       </bean>
-</beans>
\ No newline at end of file
diff --git a/security/modules/org.argeo.security.dao.ldap/META-INF/spring/ldap-jcr.xml b/security/modules/org.argeo.security.dao.ldap/META-INF/spring/ldap-jcr.xml
deleted file mode 100644 (file)
index d8a78c1..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-<beans xmlns="http://www.springframework.org/schema/beans"
-       xmlns:security="http://www.springframework.org/schema/security"
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
-       xsi:schemaLocation="http://www.springframework.org/schema/beans
-               http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
-               http://www.springframework.org/schema/security
-               http://www.springframework.org/schema/security/spring-security-2.0.4.xsd
-               http://www.springframework.org/schema/util
-               http://www.springframework.org/schema/util/spring-util-2.5.xsd">
-
-       <bean id="jcrUserDetailsContextMapper" class="org.argeo.security.ldap.jcr.JcrUserDetailsContextMapper">
-               <property name="homeBasePath" value="/home" />
-               <property name="usernameAttribute" value="${argeo.ldap.usernameAttribute}" />
-               <property name="passwordAttribute" value="${argeo.ldap.passwordAttribute}" />
-               <property name="userClasses">
-                       <list>
-                               <value>${argeo.ldap.userClass}</value>
-                       </list>
-               </property>
-               <property name="systemExecutor" ref="systemExecutionService" />
-               <property name="passwordEncoder" ref="passwordEncoder" />
-               <property name="session" ref="nodeSession" />
-               <property name="propertyToAttributes">
-                       <map>
-                               <entry value="cn">
-                                       <key>
-                                               <util:constant static-field="javax.jcr.Property.JCR_TITLE" />
-                                       </key>
-                               </entry>
-                               <entry value="description">
-                                       <key>
-                                               <util:constant static-field="javax.jcr.Property.JCR_DESCRIPTION" />
-                                       </key>
-                               </entry>
-                               <entry value="givenName">
-                                       <key>
-                                               <util:constant static-field="org.argeo.jcr.ArgeoNames.ARGEO_FIRST_NAME" />
-                                       </key>
-                               </entry>
-                               <entry value="sn">
-                                       <key>
-                                               <util:constant static-field="org.argeo.jcr.ArgeoNames.ARGEO_LAST_NAME" />
-                                       </key>
-                               </entry>
-                               <entry value="mail">
-                                       <key>
-                                               <util:constant static-field="org.argeo.jcr.ArgeoNames.ARGEO_PRIMARY_EMAIL" />
-                                       </key>
-                               </entry>
-                               <entry value="o">
-                                       <key>
-                                               <util:constant
-                                                       static-field="org.argeo.jcr.ArgeoNames.ARGEO_PRIMARY_ORGANIZATION" />
-                                       </key>
-                               </entry>
-                       </map>
-               </property>
-       </bean>
-
-       <bean id="nodeSession" class="org.argeo.security.jcr.SecureThreadBoundSession">
-               <property name="repositories" ref="repositories" />
-       </bean>
-
-</beans>
diff --git a/security/modules/org.argeo.security.dao.ldap/META-INF/spring/ldap-osgi.xml b/security/modules/org.argeo.security.dao.ldap/META-INF/spring/ldap-osgi.xml
deleted file mode 100644 (file)
index f67cfb4..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>\r
-<beans:beans xmlns="http://www.springframework.org/schema/osgi"\r
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"\r
-       xsi:schemaLocation="http://www.springframework.org/schema/osgi  \r
-       http://www.springframework.org/schema/osgi/spring-osgi-1.1.xsd\r
-       http://www.springframework.org/schema/beans   \r
-       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">\r
-\r
-       <!-- REFERENCES -->\r
-       <list id="repositories" interface="javax.jcr.Repository"\r
-               filter="(argeo.jcr.repository.alias=${argeo.node.repo.alias})"\r
-               cardinality="0..N">\r
-       </list>\r
-\r
-       <reference id="systemExecutionService" interface="org.argeo.security.SystemExecutionService" />\r
-\r
-       <!-- SERVICES -->\r
-       <service ref="ldapAuthenticationProvider"\r
-               interface="org.springframework.security.providers.AuthenticationProvider"\r
-               context-class-loader="service-provider" />\r
-\r
-       <service ref="userDetailsManager"\r
-               interface="org.springframework.security.userdetails.UserDetailsService"\r
-               context-class-loader="service-provider" />\r
-       <service ref="userDetailsManager"\r
-               interface="org.springframework.security.userdetails.UserDetailsManager"\r
-               context-class-loader="service-provider" />\r
-       <service ref="userDetailsManager" interface="org.argeo.security.UserAdminService"\r
-               context-class-loader="service-provider" />\r
-</beans:beans>
\ No newline at end of file
diff --git a/security/modules/org.argeo.security.dao.ldap/META-INF/spring/ldap.xml b/security/modules/org.argeo.security.dao.ldap/META-INF/spring/ldap.xml
deleted file mode 100644 (file)
index 1f2117d..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-<beans xmlns="http://www.springframework.org/schema/beans"
-       xmlns:security="http://www.springframework.org/schema/security"
-       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
-              http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.4.xsd">
-
-       <!-- COMMON -->
-       <bean
-               class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
-               <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
-               <property name="locations">
-                       <value>osgibundle:ldap.properties</value>
-               </property>
-       </bean>
-
-       <bean id="passwordEncoder" class="org.argeo.security.ldap.ArgeoLdapShaPasswordEncoder">
-               <property name="useSalt" value="${argeo.ldap.password.useSalt}" />
-       </bean>
-
-       <bean id="contextSource"
-               class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
-               <constructor-arg
-                       value="${argeo.ldap.protocol}://${argeo.ldap.host}:${argeo.ldap.port}/${argeo.ldap.rootdn}" />
-               <property name="userDn" value="${argeo.ldap.manager.userdn}" />
-               <property name="password" value="${argeo.ldap.manager.password}" />
-       </bean>
-
-       <!-- AUTHENTICATION -->
-       <bean id="ldapAuthenticationProvider"
-               class="org.springframework.security.providers.ldap.LdapAuthenticationProvider">
-               <constructor-arg ref="ldapAuthenticator" />
-               <constructor-arg ref="authoritiesPopulator" />
-               <property name="userDetailsContextMapper" ref="jcrUserDetailsContextMapper" />
-       </bean>
-
-<!--   <bean id="ldapAuthenticator" -->
-<!--           class="org.springframework.security.providers.ldap.authenticator.BindAuthenticator"> -->
-<!--           <constructor-arg ref="contextSource" /> -->
-<!--           <property name="userDnPatterns"> -->
-<!--                   <list> -->
-<!--                           <value><![CDATA[${argeo.ldap.usernameAttribute}={0},${argeo.ldap.userBase}]]></value> -->
-<!--                   </list> -->
-<!--           </property> -->
-<!--   </bean> -->
-
-       <!-- PasswordComparisonAuthenticator doesn't work with SSHA -->
-       <bean id="ldapAuthenticator"
-               class="org.springframework.security.providers.ldap.authenticator.PasswordComparisonAuthenticator">
-               <constructor-arg ref="contextSource" />
-               <property name="userDnPatterns">
-                       <list>
-                               <value><![CDATA[${argeo.ldap.usernameAttribute}={0},${argeo.ldap.userBase}]]></value>
-                       </list>
-               </property>
-               <property name="passwordAttributeName" value="${argeo.ldap.passwordAttribute}" />
-               <property name="passwordEncoder" ref="passwordEncoder" />
-       </bean>
-
-       <!-- USER DETAILS -->
-       <bean id="userAdminDao" class="org.argeo.security.ldap.ArgeoSecurityDaoLdap">
-               <constructor-arg ref="contextSource" />
-               <property name="userBase" value="${argeo.ldap.userBase}" />
-               <property name="usernameAttribute" value="${argeo.ldap.usernameAttribute}" />
-               <property name="groupClasses">
-                       <list>
-                               <value>top</value>
-                               <value>${argeo.ldap.groupClass}</value>
-                       </list>
-               </property>
-               <property name="groupBase" value="${argeo.ldap.groupBase}" />
-               <property name="groupRoleAttribute" value="${argeo.ldap.groupRoleAttribute}" />
-               <property name="groupMemberAttribute" value="${argeo.ldap.groupMemberAttribute}" />
-               <property name="defaultRole" value="${argeo.security.defaultRole}" />
-               <property name="rolePrefix" value="${argeo.security.rolePrefix}" />
-               <property name="usernameMapper" ref="usernameMapper" />
-       </bean>
-
-       <bean id="usernameMapper"
-               class="org.springframework.security.ldap.DefaultLdapUsernameToDnMapper">
-               <constructor-arg value="${argeo.ldap.userBase}" />
-               <constructor-arg value="${argeo.ldap.usernameAttribute}" />
-       </bean>
-
-       <bean id="authoritiesPopulator"
-               class="org.springframework.security.ldap.populator.DefaultLdapAuthoritiesPopulator">
-               <constructor-arg ref="contextSource" />
-               <constructor-arg value="${argeo.ldap.groupBase}" />
-               <property name="groupSearchFilter" value="${argeo.ldap.groupMemberAttribute}={0}" />
-               <property name="defaultRole" value="${argeo.security.defaultRole}" />
-               <property name="rolePrefix" value="${argeo.security.rolePrefix}" />
-       </bean>
-
-       <bean id="userDetailsManager" class="org.argeo.security.ldap.ArgeoLdapUserDetailsManager">
-               <constructor-arg ref="contextSource" />
-               <property name="groupSearchBase" value="${argeo.ldap.groupBase}" />
-               <property name="groupMemberAttributeName" value="${argeo.ldap.groupMemberAttribute}" />
-               <property name="usernameMapper" ref="usernameMapper" />
-               <property name="userDetailsMapper" ref="jcrUserDetailsContextMapper" />
-               <property name="userAdminDao" ref="userAdminDao" />
-               <property name="passwordEncoder" ref="passwordEncoder" />
-               <property name="passwordAttributeName" value="${argeo.ldap.passwordAttribute}" />
-       </bean>
-</beans>
diff --git a/security/modules/org.argeo.security.dao.ldap/META-INF/spring/security-ldap-jcr.xml b/security/modules/org.argeo.security.dao.ldap/META-INF/spring/security-ldap-jcr.xml
new file mode 100644 (file)
index 0000000..376a8e9
--- /dev/null
@@ -0,0 +1,76 @@
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:security="http://www.springframework.org/schema/security"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans
+               http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+               http://www.springframework.org/schema/security
+               http://www.springframework.org/schema/security/spring-security-2.0.4.xsd
+               http://www.springframework.org/schema/util
+               http://www.springframework.org/schema/util/spring-util-2.5.xsd">
+
+       <bean id="argeoDataModel" class="org.argeo.jackrabbit.JackrabbitContainer"
+               init-method="init" destroy-method="destroy">
+               <description><![CDATA[Make sure that Argeo base data model is registered]]></description>
+               <property name="cndFiles">
+                       <list>
+                               <value>/org/argeo/jcr/argeo.cnd</value>
+                       </list>
+               </property>
+               <property name="repository" ref="nodeRepository" />
+       </bean>
+
+       <bean id="jcrLdapSynchronizer" class="org.argeo.security.ldap.jcr.JcrLdapSynchronizer"
+               init-method="init" destroy-method="destroy" depends-on="argeoDataModel">
+               <!-- LDAP -->
+               <property name="usernameAttribute" value="${argeo.ldap.usernameAttribute}" />
+               <property name="passwordAttribute" value="${argeo.ldap.passwordAttribute}" />
+               <property name="userClasses">
+                       <list>
+                               <value>${argeo.ldap.userClass}</value>
+                       </list>
+               </property>
+               <property name="passwordEncoder" ref="passwordEncoder" />
+               <property name="userBase" value="${argeo.ldap.userBase}" />
+               <property name="usernameMapper" ref="usernameMapper" />
+               <property name="ldapTemplate" ref="ldapTemplate" />
+               <property name="rawLdapTemplate" ref="rawLdapTemplate" />
+               <!-- JCR -->
+               <property name="repository" ref="nodeRepository" />
+               <property name="securityWorkspace" value="${argeo.node.repo.securityWorkspace}" />
+               <property name="propertyToAttributes" ref="propertyToAttributes" />
+       </bean>
+
+       <!-- LDAP / JCR mapping -->
+       <util:map id="propertyToAttributes">
+               <entry value="cn">
+                       <key>
+                               <util:constant static-field="javax.jcr.Property.JCR_TITLE" />
+                       </key>
+               </entry>
+               <entry value="description">
+                       <key>
+                               <util:constant static-field="javax.jcr.Property.JCR_DESCRIPTION" />
+                       </key>
+               </entry>
+               <entry value="givenName">
+                       <key>
+                               <util:constant static-field="org.argeo.jcr.ArgeoNames.ARGEO_FIRST_NAME" />
+                       </key>
+               </entry>
+               <entry value="sn">
+                       <key>
+                               <util:constant static-field="org.argeo.jcr.ArgeoNames.ARGEO_LAST_NAME" />
+                       </key>
+               </entry>
+               <entry value="mail">
+                       <key>
+                               <util:constant static-field="org.argeo.jcr.ArgeoNames.ARGEO_PRIMARY_EMAIL" />
+                       </key>
+               </entry>
+               <entry value="o">
+                       <key>
+                               <util:constant static-field="org.argeo.jcr.ArgeoNames.ARGEO_PRIMARY_ORGANIZATION" />
+                       </key>
+               </entry>
+       </util:map>
+</beans>
diff --git a/security/modules/org.argeo.security.dao.ldap/META-INF/spring/security-ldap-osgi.xml b/security/modules/org.argeo.security.dao.ldap/META-INF/spring/security-ldap-osgi.xml
new file mode 100644 (file)
index 0000000..e8a6c0d
--- /dev/null
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<beans:beans xmlns="http://www.springframework.org/schema/osgi"\r
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"\r
+       xsi:schemaLocation="http://www.springframework.org/schema/osgi  \r
+       http://www.springframework.org/schema/osgi/spring-osgi-1.1.xsd\r
+       http://www.springframework.org/schema/beans   \r
+       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">\r
+\r
+       <!-- REFERENCES -->\r
+       <reference id="nodeRepository" interface="javax.jcr.Repository"\r
+               filter="(argeo.jcr.repository.alias=node)" />\r
+\r
+       <!-- SERVICES -->\r
+       <service ref="systemExecutionService" interface="org.argeo.security.SystemExecutionService" />\r
+       <service ref="authenticationManager"\r
+               interface="org.springframework.security.AuthenticationManager"\r
+               context-class-loader="service-provider" />\r
+       <!-- <service ref="ldapAuthenticationProvider" -->\r
+       <!-- interface="org.springframework.security.providers.AuthenticationProvider" -->\r
+       <!-- context-class-loader="service-provider" /> -->\r
+\r
+       <service ref="userDetailsManager"\r
+               interface="org.springframework.security.userdetails.UserDetailsService"\r
+               context-class-loader="service-provider" />\r
+       <service ref="userDetailsManager"\r
+               interface="org.springframework.security.userdetails.UserDetailsManager"\r
+               context-class-loader="service-provider" />\r
+       <service ref="userDetailsManager" interface="org.argeo.security.UserAdminService"\r
+               context-class-loader="service-provider" />\r
+</beans:beans>
\ No newline at end of file
diff --git a/security/modules/org.argeo.security.dao.ldap/META-INF/spring/security-ldap-services.xml b/security/modules/org.argeo.security.dao.ldap/META-INF/spring/security-ldap-services.xml
new file mode 100644 (file)
index 0000000..e085112
--- /dev/null
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="
+       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
+
+       <bean id="systemExecutionService" class="org.argeo.security.core.KeyBasedSystemExecutionService">
+               <property name="authenticationManager" ref="authenticationManager" />
+               <property name="systemAuthenticationKey" value="${argeo.security.systemKey}" />
+       </bean>
+
+       <bean id="authenticationManager" class="org.springframework.security.providers.ProviderManager">
+               <property name="providers">
+                       <list>
+                               <ref bean="anonymousAuthenticationProvider" />
+                               <ref bean="authByAdapterProvider" />
+                               <ref bean="ldapAuthenticationProvider" />
+                       </list>
+               </property>
+       </bean>
+
+       <!-- Authentication provider -->
+       <bean id="authByAdapterProvider"
+               class="org.springframework.security.adapters.AuthByAdapterProvider">
+               <description><![CDATA[System authentication]]></description>
+               <property name="key" value="${argeo.security.systemKey}" />
+       </bean>
+
+       <bean id="anonymousAuthenticationProvider"
+               class="org.springframework.security.providers.anonymous.AnonymousAuthenticationProvider">
+               <description><![CDATA[Anonymous authentication]]></description>
+               <property name="key" value="${argeo.security.systemKey}" />
+       </bean>
+
+       <!-- Internal authentication, used by during the general authentication 
+               initialization himself, in order to prevent the following dependency cycle: 
+               Repository.login() <= AuthenticationManager <= LdapAuthenticationProvider 
+               <= Repository.login() in init() -->
+       <bean id="internalAuthenticationManager" class="org.springframework.security.providers.ProviderManager">
+               <property name="providers">
+                       <list>
+                               <ref bean="authByAdapterProvider" />
+                       </list>
+               </property>
+       </bean>
+
+       <bean
+               class="org.argeo.security.core.AuthenticatedApplicationContextInitialization">
+               <description><![CDATA[Executes initialization with a system authentication]]></description>
+               <property name="authenticationManager" ref="internalAuthenticationManager" />
+       </bean>
+</beans>
\ No newline at end of file
diff --git a/security/modules/org.argeo.security.dao.ldap/META-INF/spring/security-ldap.xml b/security/modules/org.argeo.security.dao.ldap/META-INF/spring/security-ldap.xml
new file mode 100644 (file)
index 0000000..8ce3081
--- /dev/null
@@ -0,0 +1,120 @@
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:security="http://www.springframework.org/schema/security"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
+              http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.4.xsd">
+
+       <!-- COMMON -->
+       <bean
+               class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+               <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
+               <property name="locations">
+                       <value>osgibundle:ldap.properties</value>
+               </property>
+       </bean>
+
+       <!-- AUTHENTICATION -->
+       <bean id="ldapAuthenticationProvider"
+               class="org.springframework.security.providers.ldap.LdapAuthenticationProvider">
+               <constructor-arg ref="ldapAuthenticator" />
+               <constructor-arg ref="authoritiesPopulator" />
+               <property name="userDetailsContextMapper" ref="jcrLdapSynchronizer" />
+       </bean>
+
+       <!-- PasswordComparisonAuthenticator doesn't work with SSHA -->
+       <bean id="ldapAuthenticator"
+               class="org.springframework.security.providers.ldap.authenticator.PasswordComparisonAuthenticator">
+               <constructor-arg ref="contextSource" />
+               <property name="userDnPatterns">
+                       <list>
+                               <value><![CDATA[${argeo.ldap.usernameAttribute}={0},${argeo.ldap.userBase}]]></value>
+                       </list>
+               </property>
+               <property name="passwordAttributeName" value="${argeo.ldap.passwordAttribute}" />
+               <property name="passwordEncoder" ref="passwordEncoder" />
+       </bean>
+
+       <!-- Bind authenticator doesn't work with Apache DS 1.0 -->
+       <!-- <bean id="ldapAuthenticator" -->
+       <!-- class="org.springframework.security.providers.ldap.authenticator.BindAuthenticator"> -->
+       <!-- <constructor-arg ref="contextSource" /> -->
+       <!-- <property name="userDnPatterns"> -->
+       <!-- <list> -->
+       <!-- <value><![CDATA[${argeo.ldap.usernameAttribute}={0},${argeo.ldap.userBase}]]></value> -->
+       <!-- </list> -->
+       <!-- </property> -->
+       <!-- </bean> -->
+
+       <!-- USER DETAILS -->
+       <bean id="userAdminDao" class="org.argeo.security.ldap.ArgeoSecurityDaoLdap">
+               <constructor-arg ref="contextSource" />
+               <property name="userBase" value="${argeo.ldap.userBase}" />
+               <property name="usernameAttribute" value="${argeo.ldap.usernameAttribute}" />
+               <property name="groupClasses">
+                       <list>
+                               <value>top</value>
+                               <value>${argeo.ldap.groupClass}</value>
+                       </list>
+               </property>
+               <property name="groupBase" value="${argeo.ldap.groupBase}" />
+               <property name="groupRoleAttribute" value="${argeo.ldap.groupRoleAttribute}" />
+               <property name="groupMemberAttribute" value="${argeo.ldap.groupMemberAttribute}" />
+               <property name="defaultRole" value="${argeo.security.defaultRole}" />
+               <property name="rolePrefix" value="${argeo.security.rolePrefix}" />
+               <property name="usernameMapper" ref="usernameMapper" />
+       </bean>
+
+       <bean id="usernameMapper"
+               class="org.springframework.security.ldap.DefaultLdapUsernameToDnMapper">
+               <constructor-arg value="${argeo.ldap.userBase}" />
+               <constructor-arg value="${argeo.ldap.usernameAttribute}" />
+       </bean>
+
+       <bean id="authoritiesPopulator"
+               class="org.springframework.security.ldap.populator.DefaultLdapAuthoritiesPopulator">
+               <constructor-arg ref="contextSource" />
+               <constructor-arg value="${argeo.ldap.groupBase}" />
+               <property name="groupSearchFilter" value="${argeo.ldap.groupMemberAttribute}={0}" />
+               <property name="defaultRole" value="${argeo.security.defaultRole}" />
+               <property name="rolePrefix" value="${argeo.security.rolePrefix}" />
+       </bean>
+
+       <bean id="userDetailsManager" class="org.argeo.security.ldap.ArgeoLdapUserDetailsManager">
+               <constructor-arg ref="contextSource" />
+               <property name="groupSearchBase" value="${argeo.ldap.groupBase}" />
+               <property name="groupMemberAttributeName" value="${argeo.ldap.groupMemberAttribute}" />
+               <property name="usernameMapper" ref="usernameMapper" />
+               <property name="userDetailsMapper" ref="jcrLdapSynchronizer" />
+               <property name="userAdminDao" ref="userAdminDao" />
+               <property name="passwordEncoder" ref="passwordEncoder" />
+               <property name="passwordAttributeName" value="${argeo.ldap.passwordAttribute}" />
+       </bean>
+
+       <!-- LDAP LOW LEVEL -->
+       <bean id="contextSource"
+               class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
+               <constructor-arg
+                       value="${argeo.ldap.protocol}://${argeo.ldap.host}:${argeo.ldap.port}/${argeo.ldap.rootdn}" />
+               <property name="userDn" value="${argeo.ldap.manager.userdn}" />
+               <property name="password" value="${argeo.ldap.manager.password}" />
+       </bean>
+
+       <bean id="ldapTemplate" class="org.springframework.ldap.core.LdapTemplate">
+               <constructor-arg ref="contextSource" />
+       </bean>
+
+       <bean id="rawLdapTemplate" class="org.springframework.ldap.core.LdapTemplate">
+               <description><![CDATA[LDAP template returning raw dir contexts, see http://forum.springsource.org/showthread.php?55955-Persistent-search-with-spring-ldap]]></description>
+               <constructor-arg>
+                       <bean parent="contextSource">
+                               <property name="dirObjectFactory">
+                                       <null />
+                               </property>
+                       </bean>
+               </constructor-arg>
+       </bean>
+
+       <bean id="passwordEncoder" class="org.argeo.security.ldap.ArgeoLdapShaPasswordEncoder">
+               <property name="useSalt" value="${argeo.ldap.password.useSalt}" />
+       </bean>
+</beans>
index 5d7833babf4357f9e5c84431758976d5a65d2427..b96150b192f633ec52e199b2e22056f9a659858d 100644 (file)
@@ -1,8 +1,10 @@
-argeo.node.repo.alias=node
+argeo.node.repo.securityWorkspace=security
 
 argeo.security.defaultRole=ROLE_USER
 argeo.security.rolePrefix=ROLE_
 
+argeo.security.systemKey=argeo
+
 argeo.ldap.rootdn=dc=demo,dc=argeo,dc=org
 argeo.ldap.protocol=ldap
 argeo.ldap.host=localhost
index 1a72f408889adbbf2a83ff6e613ff39df3a966f0..7168f03d8d8ac917495e6b08644f2e71ae28c3f5 100644 (file)
@@ -5,13 +5,13 @@ Bundle-Name: Commons Security DAO OS
 Created-By: 1.6.0_20 (Sun Microsystems Inc.)\r
 Bundle-RequiredExecutionEnvironment: J2SE-1.5\r
 Bundle-Vendor: Argeo\r
-Bundle-Version: 0.3.4.SNAPSHOT-r20111029_170433\r
+Bundle-Version: 0.3.4.SNAPSHOT-r20111102_201754\r
 Bundle-ManifestVersion: 2\r
 Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt\r
-Import-Package: javax.jcr,org.argeo.security,org.argeo.security.core,o\r
- rg.argeo.security.jcr,org.springframework.beans.factory.config,org.sp\r
- ringframework.security,org.springframework.security.adapters,org.spri\r
- ngframework.security.providers\r
+Import-Package: javax.jcr,org.argeo.jackrabbit,org.argeo.security,org.\r
+ argeo.security.core,org.argeo.security.jcr,org.springframework.beans.\r
+ factory.config,org.springframework.security,org.springframework.secur\r
ity.adapters,org.springframework.security.providers\r
 Bundle-SymbolicName: org.argeo.security.dao.os\r
 Bundle-DocURL: http://www.argeo.org\r
 \r
index ba6f0bf8712b488e46d2385f522b20c2075782f4..4386b19c2b2b42c9c955d5fae30c52d9ccc2403b 100644 (file)
 \r
        <!-- REFERENCE -->\r
        <reference id="nodeRepository" interface="javax.jcr.Repository"\r
-               filter="(argeo.jcr.repository.alias=${argeo.node.repo.alias})" cardinality="0..1">\r
-               <listener ref="osJcrAuthenticationProvider" bind-method="register"\r
-                       unbind-method="unregister" />\r
-       </reference>\r
-\r
-       <reference id="systemExecutionServiceRef" interface="org.argeo.security.SystemExecutionService"\r
-               cardinality="0..1">\r
-               <listener ref="osJcrAuthenticationProvider" bind-method="register"\r
-                       unbind-method="unregister" />\r
-       </reference>\r
+               filter="(argeo.jcr.repository.alias=node)" />\r
 \r
        <!-- SERVICES -->\r
        <service ref="systemExecutionService" interface="org.argeo.security.SystemExecutionService" />\r
index 180f1fe8d042af2a7e3665b94dd9043838bebf17..b337218835643a34aa75d12abc1af4d0bdcfe9da 100644 (file)
                </property>
        </bean>
 
-       <bean id="osJcrAuthenticationProvider" class="org.argeo.security.jcr.OsJcrAuthenticationProvider">
+       <bean id="argeoDataModel" class="org.argeo.jackrabbit.JackrabbitContainer"
+               init-method="init" destroy-method="destroy">
+               <description><![CDATA[Make sure that Argeo base data model is registered]]></description>
+               <property name="cndFiles">
+                       <list>
+                               <value>/org/argeo/jcr/argeo.cnd</value>
+                       </list>
+               </property>
+               <property name="repository" ref="nodeRepository" />
        </bean>
 
-
        <bean id="systemExecutionService" class="org.argeo.security.core.KeyBasedSystemExecutionService">
-               <property name="systemAuthenticationKey" value="${argeo.security.systemKey}" />
                <property name="authenticationManager" ref="authenticationManager" />
+               <property name="systemAuthenticationKey" value="${argeo.security.systemKey}" />
        </bean>
 
        <bean id="authenticationManager" class="org.springframework.security.providers.ProviderManager">
                <property name="providers">
-                       <bean factory-bean="authenticationProvidersRegister"
-                               factory-method="getProviders" />
+                       <list>
+                               <ref bean="authByAdapterProvider" />
+                               <ref bean="osJcrAuthenticationProvider" />
+                       </list>
                </property>
        </bean>
 
-       <bean id="authenticationProvidersRegister" class="org.argeo.security.core.AuthenticationProvidersRegister">
-               <property name="defaultProviders">
+       <!-- Authentication providers -->
+       <bean id="osJcrAuthenticationProvider" class="org.argeo.security.jcr.OsJcrAuthenticationProvider"
+               init-method="init" destroy-method="destroy">
+               <property name="repository" ref="argeoDataModel" />
+       </bean>
+
+       <bean id="authByAdapterProvider"
+               class="org.springframework.security.adapters.AuthByAdapterProvider">
+               <description><![CDATA[System authentication]]></description>
+               <property name="key" value="${argeo.security.systemKey}" />
+       </bean>
+
+       <!-- Internal authentication, used by the general authentication process 
+               himself, in order to prevent the following dependency cycle: Repository.login() 
+               <= AuthenticationManager <= osJcrAuthenticationProvider <= Repository.login() 
+               in init() -->
+       <bean id="internalAuthenticationManager" class="org.springframework.security.providers.ProviderManager">
+               <property name="providers">
                        <list>
-                               <bean class="org.springframework.security.adapters.AuthByAdapterProvider">
-                                       <property name="key" value="${argeo.security.systemKey}" />
-                               </bean>
-                               <ref bean="osJcrAuthenticationProvider" />
+                               <ref bean="authByAdapterProvider" />
                        </list>
                </property>
        </bean>
 
+       <bean
+               class="org.argeo.security.core.AuthenticatedApplicationContextInitialization">
+               <description><![CDATA[Executes initialization with a system authentication]]></description>
+               <property name="authenticationManager" ref="internalAuthenticationManager" />
+       </bean>
+
 </beans>
\ No newline at end of file
index beebcb5dcead9375a40a9453acc3712e1ae2d4e5..ae77bf04dade8f71fc0708bf19d278ab09514567 100644 (file)
@@ -1,2 +1,2 @@
 argeo.security.systemKey=argeo
-argeo.node.repo.alias=node
+argeo.node.repo.securityWorkspace=security
index dada3440525caf6c95b3e0172c25c33201223713..71ce5715bc937f5d6a4d310d913aa24af8c3da71 100644 (file)
@@ -11,7 +11,7 @@ import javax.security.auth.login.LoginException;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.argeo.security.SiteAuthenticationToken;
+import org.argeo.security.NodeAuthenticationToken;
 import org.springframework.security.Authentication;
 import org.springframework.security.AuthenticationManager;
 import org.springframework.security.BadCredentialsException;
@@ -63,23 +63,30 @@ public class SpringLoginModule extends SecurityContextLoginModule {
                        if (subject.getPublicCredentials() != null)
                                subject.getPublicCredentials().clear();
 
+                       if (callbackHandler == null)
+                               throw new LoginException("No call back handler available");
+
                        // ask for username and password
                        NameCallback nameCallback = new NameCallback("User");
                        PasswordCallback passwordCallback = new PasswordCallback(
                                        "Password", false);
-
-                       NameCallback urlCallback = new NameCallback("Site URL");
-
-                       if (callbackHandler == null)
-                               throw new LoginException("No call back handler available");
+                       final String defaultNodeUrl = "http://localhost:7070/org.argeo.jcr.webapp/remoting/node";
+                       final String defaultSecurityWorkspace = "security";
+                       NameCallback urlCallback = new NameCallback("Site URL",
+                                       defaultNodeUrl);
+                       NameCallback securityWorkspaceCallback = new NameCallback(
+                                       "Security Workspace", defaultSecurityWorkspace);
+
+                       // handle callbacks
                        if (remote)
                                callbackHandler.handle(new Callback[] { nameCallback,
-                                               passwordCallback, urlCallback });
+                                               passwordCallback, urlCallback,
+                                               securityWorkspaceCallback });
                        else
                                callbackHandler.handle(new Callback[] { nameCallback,
                                                passwordCallback });
 
-                       // Set user name and password
+                       // create credentials
                        String username = nameCallback.getName();
                        if (username == null || username.trim().equals(""))
                                return false;
@@ -88,16 +95,15 @@ public class SpringLoginModule extends SecurityContextLoginModule {
                        if (passwordCallback.getPassword() != null)
                                password = String.valueOf(passwordCallback.getPassword());
 
-                       String url = remote ? urlCallback.getName() : null;
-                       if (remote && (url == null || url.trim().equals("")))
-                               // for convenience, may be removed in the future
-                               url = System.getProperty(NODE_REPO_URI);
-
-                       // TODO: set it via system properties
-                       String workspace = null;
-
-                       SiteAuthenticationToken credentials = new SiteAuthenticationToken(
-                                       username, password, url, workspace);
+                       NodeAuthenticationToken credentials;
+                       if (remote) {
+                               String url = urlCallback.getName();
+                               String workspace = securityWorkspaceCallback.getName();
+                               credentials = new NodeAuthenticationToken(username, password,
+                                               url, workspace);
+                       } else {
+                               credentials = new NodeAuthenticationToken(username, password);
+                       }
 
                        Authentication authentication;
                        try {
index 3a6f8141cf82a8544f1c30a6b4e4cd3721e4239c..5b64119f816b88dd3c783122ca7c27ae9e251f31 100644 (file)
@@ -3,7 +3,16 @@
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">\r
 \r
+       <bean\r
+               class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">\r
+               <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />\r
+               <property name="locations">\r
+                       <value>osgibundle:security-admin.properties</value>\r
+               </property>\r
+       </bean>\r
+\r
        <bean id="session" class="org.argeo.security.jcr.SecureThreadBoundSession">\r
+               <property name="workspace" value="${argeo.node.repo.securityWorkspace}" />\r
                <property name="repository" ref="nodeRepository" />\r
        </bean>\r
 </beans>
\ No newline at end of file
index b840a945646338600b24eb135854324c2080206f..c41623e1f989b3a34f37891adb20835a26ed9a49 100644 (file)
@@ -1,4 +1,5 @@
 bin.includes = plugin.xml,\
-               META-INF/
+               META-INF/,\
+               security-admin.properties
 source.. = src/main/java/
 output.. = target/classes/
diff --git a/security/plugins/org.argeo.security.ui.admin/security-admin.properties b/security/plugins/org.argeo.security.ui.admin/security-admin.properties
new file mode 100644 (file)
index 0000000..92cfc63
--- /dev/null
@@ -0,0 +1 @@
+argeo.node.repo.securityWorkspace=security
index 5a20377cabce94977ba7e811a62ef94cfa291bfb..3581354197e4fc205e60ddd154343e45b1dd9d27 100644 (file)
@@ -49,13 +49,6 @@ public class DefaultUserMainPage extends FormPage implements ArgeoNames {
                        form.setText(getProperty(ARGEO_FIRST_NAME) + " "
                                        + getProperty(ARGEO_LAST_NAME));
                        GridLayout mainLayout = new GridLayout(1, true);
-                       // ColumnLayout mainLayout = new ColumnLayout();
-                       // mainLayout.minNumColumns = 1;
-                       // mainLayout.maxNumColumns = 4;
-                       // mainLayout.topMargin = 0;
-                       // mainLayout.bottomMargin = 5;
-                       // mainLayout.leftMargin = mainLayout.rightMargin =
-                       // mainLayout.horizontalSpacing = mainLayout.verticalSpacing = 10;
                        form.getBody().setLayout(mainLayout);
 
                        createGeneralPart(form.getBody());
@@ -78,15 +71,6 @@ public class DefaultUserMainPage extends FormPage implements ArgeoNames {
                body.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
                body.setLayout(layout);
 
-               // add widgets (view)
-               // final Text username;
-               // if (user.getUsername() != null) {
-               // tk.createLabel(body, "Username");
-               // tk.createLabel(body, user.getUsername());
-               // username = null;
-               // } else {
-               // username = createLT(body, "Username", "");
-               // }
                final Text firstName = createLT(body, "First name",
                                getProperty(ARGEO_FIRST_NAME));
                final Text lastName = createLT(body, "Last name",
@@ -99,16 +83,9 @@ public class DefaultUserMainPage extends FormPage implements ArgeoNames {
                // create form part (controller)
                AbstractFormPart part = new SectionPart(section) {
                        public void commit(boolean onSave) {
-                               // if (username != null) {
-                               // ((SimpleArgeoUser) user).setUsername(username.getText());
-                               // username.setEditable(false);
-                               // username.setEnabled(false);
-                               // }
-                               // simpleNature.setFirstName(firstName.getText());
-                               // simpleNature.setLastName(lastName.getText());
-                               // simpleNature.setEmail(email.getText());
-                               // simpleNature.setDescription(description.getText());
                                try {
+                                       userProfile.getSession().getWorkspace().getVersionManager()
+                                                       .checkout(userProfile.getPath());
                                        userProfile.setProperty(ARGEO_FIRST_NAME,
                                                        firstName.getText());
                                        userProfile
@@ -118,6 +95,8 @@ public class DefaultUserMainPage extends FormPage implements ArgeoNames {
                                        userProfile.setProperty(Property.JCR_DESCRIPTION,
                                                        description.getText());
                                        userProfile.getSession().save();
+                                       userProfile.getSession().getWorkspace().getVersionManager()
+                                                       .checkin(userProfile.getPath());
                                        super.commit(onSave);
                                        if (log.isTraceEnabled())
                                                log.trace("General part committed");
index b99570c200dae79345d0b4fbdb7468c86ebc3a3f..aa1351815eb130f5af1e8614a8b8811d9ca3344f 100644 (file)
@@ -6,8 +6,7 @@ import javax.jcr.Session;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.argeo.eclipse.ui.dialogs.Error;
-import org.argeo.jcr.ArgeoNames;
+import org.argeo.eclipse.ui.ErrorFeedback;
 import org.argeo.jcr.JcrUtils;
 import org.argeo.security.UserAdminService;
 import org.argeo.security.jcr.JcrUserDetails;
@@ -17,8 +16,6 @@ import org.springframework.security.GrantedAuthority;
 /** Wizard to create a new user */
 public class NewUserWizard extends Wizard {
        private final static Log log = LogFactory.getLog(NewUserWizard.class);
-
-       private String homeBasePath = "/home";
        private Session session;
        private UserAdminService userAdminService;
 
@@ -43,16 +40,17 @@ public class NewUserWizard extends Wizard {
 
                String username = mainUserInfo.getUsername();
                try {
-                       session.save();
-                       Node userHome = JcrUtils.createUserHome(session, homeBasePath,
-                                       username);
-                       Node userProfile = userHome.getNode(ArgeoNames.ARGEO_PROFILE);
+                       Node userProfile = JcrUtils.createUserProfile(session, username);
+                       session.getWorkspace().getVersionManager()
+                                       .checkout(userProfile.getPath());
                        mainUserInfo.mapToProfileNode(userProfile);
                        String password = mainUserInfo.getPassword();
-                       JcrUserDetails jcrUserDetails = new JcrUserDetails(
-                                       userHome.getPath(), username, password, true, true, true,
-                                       true, new GrantedAuthority[0]);
+                       // TODO add roles
+                       JcrUserDetails jcrUserDetails = new JcrUserDetails(userProfile,
+                                       password, new GrantedAuthority[0]);
                        session.save();
+                       session.getWorkspace().getVersionManager()
+                                       .checkin(userProfile.getPath());
                        userAdminService.createUser(jcrUserDetails);
                        return true;
                } catch (Exception e) {
@@ -68,7 +66,7 @@ public class NewUserWizard extends Wizard {
                                                        + username, e1);
                                }
                        }
-                       Error.show("Cannot create new user " + username, e);
+                       ErrorFeedback.show("Cannot create new user " + username, e);
                        return false;
                }
        }
diff --git a/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/NodeAuthenticationToken.java b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/NodeAuthenticationToken.java
new file mode 100644 (file)
index 0000000..17b1d48
--- /dev/null
@@ -0,0 +1,47 @@
+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;
+       private final String securityWorkspace;
+
+       /** Non authenticated local constructor */
+       public NodeAuthenticationToken(Object principal, Object credentials) {
+               super(principal, credentials);
+               this.url = null;
+               this.securityWorkspace = null;
+       }
+
+       /** Non authenticated remote constructor */
+       public NodeAuthenticationToken(Object principal, Object credentials,
+                       String url, String workspace) {
+               super(principal, credentials);
+               this.url = url;
+               this.securityWorkspace = workspace;
+       }
+
+       /** Authenticated constructor */
+       public NodeAuthenticationToken(NodeAuthenticationToken sat,
+                       GrantedAuthority[] authorities) {
+               super(sat.getPrincipal(), sat.getCredentials(), authorities);
+               this.url = sat.getUrl();
+               this.securityWorkspace = sat.getSecurityWorkspace();
+       }
+
+       public String getUrl() {
+               return url;
+       }
+
+       public String getSecurityWorkspace() {
+               return securityWorkspace;
+       }
+
+       public Boolean isRemote() {
+               return url != null;
+       }
+}
index 9fba6f054aa482fc52dcfee7435a5f50f862d4f6..61ec539c610f08290ae093b423f8eb000ad735c6 100644 (file)
@@ -4,7 +4,6 @@ import java.security.AccessController;
 import java.security.Principal;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 
@@ -76,6 +75,10 @@ public class OsAuthenticationToken implements Authentication {
                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>(
@@ -121,7 +124,7 @@ public class OsAuthenticationToken implements Authentication {
        }
 
        public Principal getUser() {
-               Subject subject = Subject.getSubject(AccessController.getContext());
+               Subject subject = getSubject();
                Set<? extends Principal> userPrincipals = subject
                                .getPrincipals(osUserPrincipalClass);
                if (userPrincipals == null || userPrincipals.size() == 0)
@@ -133,7 +136,7 @@ public class OsAuthenticationToken implements Authentication {
        }
 
        public Principal getUserId() {
-               Subject subject = Subject.getSubject(AccessController.getContext());
+               Subject subject = getSubject();
                Set<? extends Principal> userIdsPrincipals = subject
                                .getPrincipals(osUserIdPrincipalClass);
                if (userIdsPrincipals == null || userIdsPrincipals.size() == 0)
@@ -145,11 +148,19 @@ public class OsAuthenticationToken implements Authentication {
        }
 
        public Set<? extends Principal> getGroupsIds() {
-               Subject subject = Subject.getSubject(AccessController.getContext());
+               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/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/SiteAuthenticationToken.java b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/SiteAuthenticationToken.java
deleted file mode 100644 (file)
index d836b6f..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-package org.argeo.security;
-
-import org.springframework.security.GrantedAuthority;
-import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
-
-public class SiteAuthenticationToken extends
-               UsernamePasswordAuthenticationToken {
-       private static final long serialVersionUID = 1955222132884795213L;
-       private final String url;
-       private final String workspace;
-
-       public SiteAuthenticationToken(Object principal, Object credentials,
-                       String url, String workspace) {
-               super(principal, credentials);
-               this.url = url;
-               this.workspace = workspace;
-       }
-
-       public SiteAuthenticationToken(Object principal, Object credentials,
-                       GrantedAuthority[] authorities, String url, String workspace) {
-               super(principal, credentials, authorities);
-               this.url = url;
-               this.workspace = workspace;
-       }
-
-       public String getUrl() {
-               return url;
-       }
-
-       public String getWorkspace() {
-               return workspace;
-       }
-
-}
index 8844534c3cd6b7408ccadea8e3f9a8f40c62df17..01e7349ccca83ed445d3b92e6e49276a005394eb 100644 (file)
@@ -4,6 +4,7 @@ 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}).
index 524e73f8f7cdf4d440d7eb7b5e0fc89aab492868..3360b1eeae2c2cf5b1fd7bfb908448cc2fc67fe2 100644 (file)
@@ -10,7 +10,12 @@ import org.springframework.security.GrantedAuthority;
 import org.springframework.security.GrantedAuthorityImpl;
 import org.springframework.security.providers.AuthenticationProvider;
 
-/** Validates an OS authentication. */
+/**
+ * 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.
+ */
 public class OsAuthenticationProvider implements AuthenticationProvider {
        private String osUserRole = "ROLE_OS_USER";
        private String userRole = "ROLE_USER";
@@ -20,16 +25,16 @@ public class OsAuthenticationProvider implements AuthenticationProvider {
 
        public Authentication authenticate(Authentication authentication)
                        throws AuthenticationException {
-               if (!(authentication instanceof OsAuthenticationToken))
-                       return null;
+               return new OsAuthenticationToken(getBaseAuthorities());
+       }
 
+       protected 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 new OsAuthenticationToken(
-                               auths.toArray(new GrantedAuthority[auths.size()]));
+               return auths.toArray(new GrantedAuthority[auths.size()]);
        }
 
        @SuppressWarnings("rawtypes")
diff --git a/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrAuthenticationProvider.java b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrAuthenticationProvider.java
deleted file mode 100644 (file)
index c19e709..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-package org.argeo.security.jcr;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.jcr.Credentials;
-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.JcrUtils;
-import org.argeo.security.SiteAuthenticationToken;
-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;
-import org.springframework.security.userdetails.UserDetails;
-
-/** Connects to a JCR repository and delegates authentication to it. */
-public class JcrAuthenticationProvider implements AuthenticationProvider {
-       public final static String ROLE_REMOTE_JCR_AUTHENTICATED = "ROLE_REMOTE_JCR_AUTHENTICATED";
-
-       private RepositoryFactory repositoryFactory;
-
-       public Authentication authenticate(Authentication authentication)
-                       throws AuthenticationException {
-               if (!(authentication instanceof SiteAuthenticationToken))
-                       return null;
-               SiteAuthenticationToken siteAuth = (SiteAuthenticationToken) authentication;
-               String url = siteAuth.getUrl();
-               if (url == null)
-                       return null;
-
-               try {
-                       SimpleCredentials sp = new SimpleCredentials(siteAuth.getName(),
-                                       siteAuth.getCredentials().toString().toCharArray());
-                       // get repository
-                       Repository repository = getRepository(url, sp);
-                       if (repository == null)
-                               return null;
-
-                       String workspace = siteAuth.getWorkspace();
-                       Session session;
-                       if (workspace == null || workspace.trim().equals(""))
-                               session = repository.login(sp);
-                       else
-                               session = repository.login(sp, workspace);
-
-                       Node userHome = JcrUtils.getUserHome(session);
-
-                       // retrieve remote roles
-                       Node userProfile = JcrUtils.getUserProfile(session);
-                       List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
-                       if (userProfile.hasProperty(ArgeoNames.ARGEO_REMOTE_ROLES)) {
-                               Value[] roles = userProfile.getProperty(
-                                               ArgeoNames.ARGEO_REMOTE_ROLES).getValues();
-                               for (int i = 0; i < roles.length; i++)
-                                       authorities.add(new GrantedAuthorityImpl(roles[i]
-                                                       .getString()));
-                       }
-                       JcrAuthenticationToken authen = new JcrAuthenticationToken(
-                                       siteAuth.getPrincipal(),
-                                       siteAuth.getCredentials(),
-                                       authorities.toArray(new GrantedAuthority[authorities.size()]),
-                                       url, userHome);
-                       authen.setDetails(getUserDetails(userHome, authen));
-
-                       return authen;
-               } catch (RepositoryException e) {
-                       throw new ArgeoException(
-                                       "Unexpected exception when authenticating to " + url, e);
-               }
-       }
-
-       protected Repository getRepository(String url, Credentials credentials)
-                       throws RepositoryException {
-               Map<String, String> parameters = new HashMap<String, String>();
-               parameters.put(ArgeoJcrConstants.JCR_REPOSITORY_URI, url);
-               return repositoryFactory.getRepository(parameters);
-       }
-
-       /**
-        * By default, assigns only the role {@value #ROLE_REMOTE_JCR_AUTHENTICATED}
-        * . Should typically be overridden in order to assign more relevant roles.
-        */
-       protected GrantedAuthority[] getGrantedAuthorities(Session session) {
-               return new GrantedAuthority[] { new GrantedAuthorityImpl(
-                               ROLE_REMOTE_JCR_AUTHENTICATED) };
-       }
-
-       /** Builds user details based on the authentication and the user home. */
-       protected UserDetails getUserDetails(Node userHome, Authentication authen) {
-               try {
-                       // TODO: loads enabled, locked, etc. from the home node.
-                       return new JcrUserDetails(userHome.getPath(), authen.getPrincipal()
-                                       .toString(), authen.getCredentials().toString(),
-                                       isEnabled(userHome), true, true, true,
-                                       authen.getAuthorities());
-               } catch (Exception e) {
-                       throw new ArgeoException("Cannot get user details for " + userHome,
-                                       e);
-               }
-       }
-
-       protected Boolean isEnabled(Node userHome) {
-               return true;
-       }
-
-       @SuppressWarnings("rawtypes")
-       public boolean supports(Class authentication) {
-               return SiteAuthenticationToken.class.isAssignableFrom(authentication);
-       }
-
-       public void register(RepositoryFactory repositoryFactory,
-                       Map<String, String> parameters) {
-               this.repositoryFactory = repositoryFactory;
-       }
-
-       public void unregister(RepositoryFactory repositoryFactory,
-                       Map<String, String> parameters) {
-               this.repositoryFactory = null;
-       }
-}
diff --git a/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrAuthenticationToken.java b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/JcrAuthenticationToken.java
deleted file mode 100644 (file)
index 27b7ee8..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-package org.argeo.security.jcr;
-
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-
-import org.argeo.ArgeoException;
-import org.argeo.security.SiteAuthenticationToken;
-import org.springframework.security.GrantedAuthority;
-
-/** An authenticated authentication based on a JCR session. */
-public class JcrAuthenticationToken extends SiteAuthenticationToken {
-       private static final long serialVersionUID = -2736830165315486169L;
-
-       private final transient Session session;
-       private final String userHomePath;
-
-       public JcrAuthenticationToken(Object principal, Object credentials,
-                       GrantedAuthority[] authorities, String url, Node userHome) {
-               super(principal, credentials, authorities, url,
-                               extractWorkspace(userHome));
-               try {
-                       this.session = userHome.getSession();
-                       this.userHomePath = userHome.getPath();
-               } catch (RepositoryException e) {
-                       throw new ArgeoException("Cannot extract path from " + userHome, e);
-               }
-       }
-
-       private static String extractWorkspace(Node userHome) {
-               try {
-                       return userHome.getSession().getWorkspace().getName();
-               } catch (RepositoryException e) {
-                       throw new ArgeoException("Cannot extract workspace from "
-                                       + userHome, e);
-               }
-       }
-
-       /** The path to the authenticated user home node. */
-       public String getUserHomePath() {
-               return userHomePath;
-       }
-
-       /** The session used to create this authentication. */
-       public Session getSession() {
-               return session;
-       }
-
-       @Override
-       public boolean isAuthenticated() {
-               if (session == null || !session.isLive())
-                       setAuthenticated(false);
-               return super.isAuthenticated();
-       }
-
-       @Override
-       public void setAuthenticated(boolean isAuthenticated)
-                       throws IllegalArgumentException {
-               super.setAuthenticated(isAuthenticated);
-               if (!isAuthenticated && session != null)
-                       session.logout();
-       }
-
-}
index 11e463d349a7a7f86cf30d953e0636ef342ea8db..a59eabc0a8dd862083c3bb027bce5524d91b6ce2 100644 (file)
@@ -3,22 +3,63 @@ package org.argeo.security.jcr;
 import java.util.ArrayList;
 import java.util.List;
 
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.argeo.jcr.ArgeoNames;
+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 wrapping a home node. */
-public class JcrUserDetails extends User {
-       private static final long serialVersionUID = -3594542993773402380L;
+/** 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;
 
-       public JcrUserDetails(String homePath, String username, String password,
-                       boolean enabled, boolean accountNonExpired,
-                       boolean credentialsNonExpired, boolean accountNonLocked,
-                       GrantedAuthority[] authorities) throws IllegalArgumentException {
+       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);
+               // home is defined as the parent of the profile
+               homePath = userProfile.getParent().getPath();
+               securityWorkspace = userProfile.getSession().getWorkspace().getName();
+       }
+
+       /**
+        * 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 */
@@ -27,21 +68,25 @@ public class JcrUserDetails extends User {
                for (String role : roles) {
                        authorities.add(new GrantedAuthorityImpl(role));
                }
-               return new JcrUserDetails(homePath, getUsername(), getPassword(),
-                               isEnabled(), isAccountNonExpired(), isAccountNonExpired(),
-                               isAccountNonLocked(),
+               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(homePath, getUsername(), password,
-                               isEnabled(), isAccountNonExpired(), isAccountNonExpired(),
-                               isAccountNonLocked(), getAuthorities());
+               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;
+       }
 }
index 9abac5972a7f584fc1aaca81326d49877e4c41af..e6f90b165c08cc9efc6d498d88d16f68d5bf5e73 100644 (file)
 package org.argeo.security.jcr;
 
-import java.util.Map;
-import java.util.concurrent.Executor;
-
 import javax.jcr.Node;
 import javax.jcr.Repository;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
+import javax.jcr.version.VersionManager;
 
 import org.argeo.ArgeoException;
+import org.argeo.jcr.ArgeoNames;
 import org.argeo.jcr.JcrUtils;
 import org.argeo.security.OsAuthenticationToken;
-import org.argeo.security.SystemExecutionService;
 import org.argeo.security.core.OsAuthenticationProvider;
 import org.springframework.security.Authentication;
 import org.springframework.security.AuthenticationException;
-import org.springframework.security.userdetails.UserDetails;
 
+/** Relies on OS to authenticate and additionaly setup JCR */
 public class OsJcrAuthenticationProvider extends OsAuthenticationProvider {
-       private Executor systemExecutor;
-       private String homeBasePath = "/home";
        private Repository repository;
-       private String workspace = null;
+       private String securityWorkspace = "security";
+       private Session securitySession;
+
+       public void init() {
+               try {
+                       securitySession = repository.login(securityWorkspace);
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot initialize", e);
+               }
+       }
 
-       private Long timeout = 5 * 60 * 1000l;
+       public void destroy() {
+               JcrUtils.logoutQuietly(securitySession);
+       }
 
        public Authentication authenticate(Authentication authentication)
                        throws AuthenticationException {
                final OsAuthenticationToken authen = (OsAuthenticationToken) super
                                .authenticate(authentication);
-               final Repository repository = getRepositoryBlocking();
-               systemExecutor.execute(new Runnable() {
-                       public void run() {
-                               Session session = null;
-                               try {
-                                       session = repository.login(workspace);
-                                       // WARNING: at this stage we assume that the java properties
-                                       // will have the same value
-                                       String userName = System.getProperty("user.name");
-                                       Node userHome = JcrUtils.getUserHome(session, userName);
-                                       if (userHome == null)
-                                               userHome = JcrUtils.createUserHome(session,
-                                                               homeBasePath, userName);
-                                       // authen.setDetails(getUserDetails(userHome, authen));
-                               } catch (RepositoryException e) {
-                                       JcrUtils.discardQuietly(session);
-                                       throw new ArgeoException(
-                                                       "Unexpected exception when synchronizing OS and JCR security ",
-                                                       e);
-                               } finally {
-                                       JcrUtils.logoutQuietly(session);
-                               }
-                       }
-               });
-               return authen;
-       }
-
-       /** Builds user details based on the authentication and the user home. */
-       protected UserDetails getUserDetails(Node userHome, Authentication authen) {
                try {
-                       // TODO: loads enabled, locked, etc. from the home node.
-                       return new JcrUserDetails(userHome.getPath(), authen.getPrincipal()
-                                       .toString(), authen.getCredentials().toString(),
-                                       isEnabled(userHome), true, true, true,
-                                       authen.getAuthorities());
-               } catch (Exception e) {
-                       throw new ArgeoException("Cannot get user details for " + userHome,
+                       // WARNING: at this stage we assume that the java properties
+                       // will have the same value
+                       String username = System.getProperty("user.name");
+                       Node userHome = JcrUtils.createUserHomeIfNeeded(securitySession,
+                                       username);
+                       Node userProfile = userHome.hasNode(ArgeoNames.ARGEO_PROFILE) ? userHome
+                                       .getNode(ArgeoNames.ARGEO_PROFILE) : JcrUtils
+                                       .createUserProfile(securitySession, username);
+                       if (securitySession.hasPendingChanges())
+                               securitySession.save();
+                       VersionManager versionManager = securitySession.getWorkspace()
+                                       .getVersionManager();
+                       if (versionManager.isCheckedOut(userProfile.getPath()))
+                               versionManager.checkin(userProfile.getPath());
+
+                       JcrUserDetails.checkAccountStatus(userProfile);
+                       // user details
+                       JcrUserDetails userDetails = new JcrUserDetails(userProfile, authen
+                                       .getCredentials().toString(), getBaseAuthorities());
+                       authen.setDetails(userDetails);
+               } catch (RepositoryException e) {
+                       JcrUtils.discardQuietly(securitySession);
+                       throw new ArgeoException(
+                                       "Unexpected exception when synchronizing OS and JCR security ",
                                        e);
+               } finally {
+                       JcrUtils.logoutQuietly(securitySession);
                }
+               return authen;
        }
 
-       protected Boolean isEnabled(Node userHome) {
-               return true;
-       }
-
-       protected Repository getRepositoryBlocking() {
-               long begin = System.currentTimeMillis();
-               while (repository == null) {
-                       synchronized (this) {
-                               try {
-                                       wait(500);
-                               } catch (InterruptedException e) {
-                                       // silent
-                               }
-                       }
-                       if (System.currentTimeMillis() - begin > timeout)
-                               throw new ArgeoException("No repository registered after "
-                                               + timeout + " ms");
-               }
-               return repository;
+       public void setSecurityWorkspace(String securityWorkspace) {
+               this.securityWorkspace = securityWorkspace;
        }
 
-       public synchronized void register(Repository repository,
-                       Map<String, String> parameters) {
+       public void setRepository(Repository repository) {
                this.repository = repository;
-               notifyAll();
-       }
-
-       public synchronized void unregister(Repository repository,
-                       Map<String, String> parameters) {
-               this.repository = null;
-               notifyAll();
-       }
-
-       public void register(SystemExecutionService systemExecutor,
-                       Map<String, String> parameters) {
-               this.systemExecutor = systemExecutor;
-       }
-
-       public void unregister(SystemExecutionService systemExecutor,
-                       Map<String, String> parameters) {
-               this.systemExecutor = null;
-       }
-
-       public void setHomeBasePath(String homeBasePath) {
-               this.homeBasePath = homeBasePath;
        }
-
-       public void setWorkspace(String workspace) {
-               this.workspace = workspace;
-       }
-
 }
diff --git a/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/RemoteJcrAuthenticationProvider.java b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/RemoteJcrAuthenticationProvider.java
new file mode 100644 (file)
index 0000000..76c1be2
--- /dev/null
@@ -0,0 +1,108 @@
+package org.argeo.security.jcr;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.jcr.Credentials;
+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.JcrUtils;
+import org.argeo.security.NodeAuthenticationToken;
+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;
+
+       public Authentication authenticate(Authentication authentication)
+                       throws AuthenticationException {
+               NodeAuthenticationToken siteAuth = (NodeAuthenticationToken) authentication;
+               String url = siteAuth.getUrl();
+               if (url == null)
+                       return null;
+               Session session;
+               Node userProfile;
+
+               try {
+                       SimpleCredentials sp = new SimpleCredentials(siteAuth.getName(),
+                                       siteAuth.getCredentials().toString().toCharArray());
+                       // get repository
+                       Repository repository = getRepository(url, sp);
+                       if (repository == null)
+                               return null;
+
+                       String workspace = siteAuth.getSecurityWorkspace();
+                       session = repository.login(sp, workspace);
+                       Node userHome = JcrUtils.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 {
+                       JcrUserDetails.checkAccountStatus(userProfile);
+                       // retrieve remote roles
+                       List<GrantedAuthority> authoritiesList = new ArrayList<GrantedAuthority>();
+                       if (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()));
+                       }
+
+                       // 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);
+               }
+       }
+
+       protected Repository getRepository(String url, Credentials credentials)
+                       throws RepositoryException {
+               Map<String, String> parameters = new HashMap<String, String>();
+               parameters.put(ArgeoJcrConstants.JCR_REPOSITORY_URI, url);
+               return repositoryFactory.getRepository(parameters);
+       }
+
+       @SuppressWarnings("rawtypes")
+       public boolean supports(Class authentication) {
+               return NodeAuthenticationToken.class.isAssignableFrom(authentication);
+       }
+
+       public void setRepositoryFactory(RepositoryFactory repositoryFactory) {
+               this.repositoryFactory = repositoryFactory;
+       }
+
+}
index 1220eb381e931a1c4bb46a233f350a1deeafed2b..44b0fa3457df7c9b3caef483877700f1ae7b494a 100644 (file)
@@ -33,18 +33,6 @@ public class SecureThreadBoundSession extends ThreadBoundSession {
                                        return login();
                                }
                        }
-                       // UserDetails userDetails = (UserDetails)
-                       // authentication.getDetails();
-                       // if (userDetails != null) {
-                       // String currentUserName = userDetails.getUsername();
-                       // if (!userID.equals(currentUserName)) {
-                       // log.warn("Current session has user ID " + userID
-                       // + " while logged is user is " + currentUserName
-                       // + "(authentication=" + authentication + ")"
-                       // + ". Re-login.");
-                       // return login();
-                       // }
-                       // }
                }
                return super.preCall(session);
        }
diff --git a/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/SystemSession.java b/security/runtime/org.argeo.security.core/src/main/java/org/argeo/security/jcr/SystemSession.java
deleted file mode 100644 (file)
index 62b0f91..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-package org.argeo.security.jcr;
-
-import java.util.concurrent.Callable;
-
-import javax.jcr.Session;
-
-import org.argeo.ArgeoException;
-import org.argeo.jcr.spring.ThreadBoundSession;
-import org.argeo.security.SystemExecutionService;
-
-/** Thread bounded JCR session which logins as system authentication. */
-public class SystemSession extends ThreadBoundSession {
-       private SystemExecutionService systemExecutionService;
-
-       @Override
-       protected Session login() {
-               try {
-                       return systemExecutionService.submit(new Callable<Session>() {
-                               public Session call() throws Exception {
-                                       return SystemSession.super.login();
-                               }
-                       }).get();
-               } catch (Exception e) {
-                       throw new ArgeoException(
-                                       "Cannot login to JCR with system authentication", e);
-               }
-       }
-
-       public void setSystemExecutionService(
-                       SystemExecutionService systemExecutionService) {
-               this.systemExecutionService = systemExecutionService;
-       }
-
-}
index 8f2632d0ff0ccf0445c968a86f8b4e771c36c58b..2153e0e94025c7d96ab3daad787df5612a4be228 100644 (file)
@@ -36,8 +36,6 @@ import org.springframework.security.GrantedAuthority;
 
 /** Intermediary class in order to have a consistent naming in config files. */
 public class ArgeoSecurityManager extends DefaultSecurityManager {
-       public final static String HOME_BASE_PATH = "/home";
-
        private Log log = LogFactory.getLog(ArgeoSecurityManager.class);
 
        @Override
@@ -142,8 +140,7 @@ public class ArgeoSecurityManager extends DefaultSecurityManager {
                        try {
                                userHome = JcrUtils.getUserHome(getSystemSession(), userId);
                                if (userHome == null) {
-                                       userHome = JcrUtils.createUserHome(getSystemSession(),
-                                                       HOME_BASE_PATH, userId);
+                                       userHome = JcrUtils.createUserHomeIfNeeded(getSystemSession(), userId);
                                        //log.warn("No home available for user "+userId);
                                        return;
                                }
diff --git a/security/runtime/org.argeo.security.jackrabbit/src/main/java/org/argeo/security/jackrabbit/providers/JackrabbitAuthenticationProvider.java b/security/runtime/org.argeo.security.jackrabbit/src/main/java/org/argeo/security/jackrabbit/providers/JackrabbitAuthenticationProvider.java
deleted file mode 100644 (file)
index ea84a07..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-package org.argeo.security.jackrabbit.providers;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Properties;
-
-import javax.jcr.Credentials;
-import javax.jcr.Node;
-import javax.jcr.Repository;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-
-import org.apache.jackrabbit.api.JackrabbitSession;
-import org.apache.jackrabbit.api.security.user.Group;
-import org.apache.jackrabbit.api.security.user.User;
-import org.apache.jackrabbit.api.security.user.UserManager;
-import org.argeo.ArgeoException;
-import org.argeo.jackrabbit.JackrabbitContainer;
-import org.argeo.jcr.ArgeoJcrConstants;
-import org.argeo.security.jcr.JcrAuthenticationProvider;
-import org.osgi.framework.BundleContext;
-import org.springframework.security.GrantedAuthority;
-import org.springframework.security.GrantedAuthorityImpl;
-
-public class JackrabbitAuthenticationProvider extends JcrAuthenticationProvider {
-       // @Override
-       // protected Repository getRepository(String url, Credentials credentials)
-       // throws RepositoryException {
-       // JackrabbitContainer repository = new JackrabbitContainer();
-       // repository.setUri(url);
-       // repository.setRemoteSystemCredentials(credentials);
-       // repository.init();
-       // if (bundleContext != null) {
-       // // FIXME check if not already a node
-       // Properties properties = new Properties();
-       // properties.put(ArgeoJcrConstants.JCR_REPOSITORY_ALIAS,
-       // ArgeoJcrConstants.ALIAS_NODE);
-       // bundleContext.registerService(Repository.class.getName(),
-       // repository, properties);
-       // }
-       // return repository;
-       // }
-
-       @Override
-       protected GrantedAuthority[] getGrantedAuthorities(Session session) {
-               try {
-                       if (!(session instanceof JackrabbitSession))
-                               return super.getGrantedAuthorities(session);
-
-                       JackrabbitSession jackrabbitSession = (JackrabbitSession) session;
-                       UserManager userManager = jackrabbitSession.getUserManager();
-                       User user = (User) userManager.getAuthorizable(session.getUserID());
-                       List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
-                       for (Iterator<Group> it = user.memberOf(); it.hasNext();)
-                               authorities.add(new GrantedAuthorityImpl(it.next().getID()));
-                       return authorities
-                                       .toArray(new GrantedAuthority[authorities.size()]);
-               } catch (RepositoryException e) {
-                       throw new ArgeoException("Cannot retrieve authorities for "
-                                       + session.getUserID(), e);
-               }
-       }
-
-       @Override
-       protected Boolean isEnabled(Node userHome) {
-               try {
-                       if (!(userHome.getSession() instanceof JackrabbitSession))
-                               return super.isEnabled(userHome);
-
-                       UserManager userManager = ((JackrabbitSession) userHome
-                                       .getSession()).getUserManager();
-                       User user = (User) userManager.getAuthorizable(userHome
-                                       .getSession().getUserID());
-                       return !user.isDisabled();
-               } catch (RepositoryException e) {
-                       throw new ArgeoException("Cannot check whether " + userHome
-                                       + " is enabled", e);
-               }
-       }
-
-}
index 92f19d2ff95b83a87f5210157582c70bb070ed48..8b978d9ede4d96fb47cbd858fc3b6607968c677f 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
+       <classpathentry kind="src" path="src/main/java"/>
        <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
        <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
-       <classpathentry kind="src" path="src/main/java"/>
        <classpathentry kind="output" path="target/classes"/>
 </classpath>
diff --git a/security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/jcr/JcrLdapSynchronizer.java b/security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/jcr/JcrLdapSynchronizer.java
new file mode 100644 (file)
index 0000000..3a644a6
--- /dev/null
@@ -0,0 +1,568 @@
+package org.argeo.security.ldap.jcr;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.SortedSet;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.observation.Event;
+import javax.jcr.observation.EventIterator;
+import javax.jcr.observation.EventListener;
+import javax.jcr.query.Query;
+import javax.naming.Binding;
+import javax.naming.Name;
+import javax.naming.NamingException;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.ModificationItem;
+import javax.naming.directory.SearchControls;
+import javax.naming.event.EventDirContext;
+import javax.naming.event.NamespaceChangeListener;
+import javax.naming.event.NamingEvent;
+import javax.naming.event.NamingExceptionEvent;
+import javax.naming.event.NamingListener;
+import javax.naming.event.ObjectChangeListener;
+import javax.naming.ldap.UnsolicitedNotification;
+import javax.naming.ldap.UnsolicitedNotificationEvent;
+import javax.naming.ldap.UnsolicitedNotificationListener;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.argeo.ArgeoException;
+import org.argeo.jcr.ArgeoNames;
+import org.argeo.jcr.ArgeoTypes;
+import org.argeo.jcr.JcrUtils;
+import org.argeo.security.jcr.JcrUserDetails;
+import org.springframework.ldap.core.ContextExecutor;
+import org.springframework.ldap.core.ContextMapper;
+import org.springframework.ldap.core.DirContextAdapter;
+import org.springframework.ldap.core.DirContextOperations;
+import org.springframework.ldap.core.DistinguishedName;
+import org.springframework.ldap.core.LdapTemplate;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.ldap.LdapUsernameToDnMapper;
+import org.springframework.security.providers.encoding.PasswordEncoder;
+import org.springframework.security.userdetails.UserDetails;
+import org.springframework.security.userdetails.ldap.UserDetailsContextMapper;
+
+/** Guarantees that LDAP and JCR are in line. */
+public class JcrLdapSynchronizer implements UserDetailsContextMapper,
+               ArgeoNames {
+       private final static Log log = LogFactory.getLog(JcrLdapSynchronizer.class);
+
+       // LDAP
+       private LdapTemplate ldapTemplate;
+       /**
+        * LDAP template whose context source has an object factory set to null. see
+        * <a href=
+        * "http://forum.springsource.org/showthread.php?55955-Persistent-search-with-spring-ldap"
+        * >this</a>
+        */
+       private LdapTemplate rawLdapTemplate;
+
+       private String userBase;
+       private String usernameAttribute;
+       private String passwordAttribute;
+       private String[] userClasses;
+
+       private NamingListener ldapUserListener;
+       private SearchControls subTreeSearchControls;
+       private LdapUsernameToDnMapper usernameMapper;
+
+       private PasswordEncoder passwordEncoder;
+       private final Random random;
+
+       // JCR
+       /** Admin session on the security workspace */
+       private Session securitySession;
+       private Repository repository;
+
+       private String securityWorkspace = "security";
+
+       private JcrProfileListener jcrProfileListener;
+
+       // Mapping
+       private Map<String, String> propertyToAttributes = new HashMap<String, String>();
+
+       public JcrLdapSynchronizer() {
+               random = createRandom();
+       }
+
+       public void init() {
+               try {
+                       securitySession = repository.login(securityWorkspace);
+
+                       synchronize();
+
+                       // LDAP
+                       subTreeSearchControls = new SearchControls();
+                       subTreeSearchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
+                       // LDAP listener
+                       ldapUserListener = new LdapUserListener();
+                       rawLdapTemplate.executeReadOnly(new ContextExecutor() {
+                               public Object executeWithContext(DirContext ctx)
+                                               throws NamingException {
+                                       EventDirContext ectx = (EventDirContext) ctx.lookup("");
+                                       ectx.addNamingListener(userBase, "(" + usernameAttribute
+                                                       + "=*)", subTreeSearchControls, ldapUserListener);
+                                       return null;
+                               }
+                       });
+
+                       // JCR
+                       String[] nodeTypes = { ArgeoTypes.ARGEO_USER_PROFILE };
+                       jcrProfileListener = new JcrProfileListener();
+                       // noLocal is used so that we are not notified when we modify JCR
+                       // from LDAP
+                       securitySession
+                                       .getWorkspace()
+                                       .getObservationManager()
+                                       .addEventListener(jcrProfileListener,
+                                                       Event.PROPERTY_CHANGED | Event.NODE_ADDED, "/",
+                                                       true, null, nodeTypes, true);
+               } catch (Exception e) {
+                       JcrUtils.logoutQuietly(securitySession);
+                       throw new ArgeoException("Cannot initialize LDAP/JCR synchronizer",
+                                       e);
+               }
+       }
+
+       public void destroy() {
+               JcrUtils.removeListenerQuietly(securitySession, jcrProfileListener);
+               JcrUtils.logoutQuietly(securitySession);
+               try {
+                       rawLdapTemplate.executeReadOnly(new ContextExecutor() {
+                               public Object executeWithContext(DirContext ctx)
+                                               throws NamingException {
+                                       EventDirContext ectx = (EventDirContext) ctx.lookup("");
+                                       ectx.removeNamingListener(ldapUserListener);
+                                       return null;
+                               }
+                       });
+               } catch (Exception e) {
+                       // silent (LDAP server may have been shutdown already)
+                       if (log.isTraceEnabled())
+                               log.trace("Cannot remove LDAP listener", e);
+               }
+       }
+
+       /*
+        * LDAP TO JCR
+        */
+       /** Full synchronization between LDAP and JCR. LDAP has priority. */
+       protected void synchronize() {
+               try {
+                       Name userBaseName = new DistinguishedName(userBase);
+                       // TODO subtree search?
+                       @SuppressWarnings("unchecked")
+                       List<String> userPaths = (List<String>) ldapTemplate.listBindings(
+                                       userBaseName, new ContextMapper() {
+                                               public Object mapFromContext(Object ctxObj) {
+                                                       return mapLdapToJcr((DirContextAdapter) ctxObj);
+                                               }
+                                       });
+
+                       // disable accounts which are not in LDAP
+                       Query query = securitySession
+                                       .getWorkspace()
+                                       .getQueryManager()
+                                       .createQuery(
+                                                       "select * from [" + ArgeoTypes.ARGEO_USER_PROFILE
+                                                                       + "]", Query.JCR_SQL2);
+                       NodeIterator it = query.execute().getNodes();
+                       while (it.hasNext()) {
+                               Node userProfile = it.nextNode();
+                               String path = userProfile.getPath();
+                               if (!userPaths.contains(path)) {
+                                       userProfile.setProperty(ArgeoNames.ARGEO_ENABLED, false);
+                               }
+                       }
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot synchronized LDAP and JCR", e);
+               }
+       }
+
+       /** Called during authentication in order to retrieve user details */
+       public UserDetails mapUserFromContext(final DirContextOperations ctx,
+                       final String username, GrantedAuthority[] authorities) {
+               if (ctx == null)
+                       throw new ArgeoException("No LDAP information for user " + username);
+               Node userHome = JcrUtils.getUserHome(securitySession, username);
+               if (userHome == null)
+                       throw new ArgeoException("No JCR information for user " + username);
+
+               // password
+               SortedSet<?> passwordAttributes = ctx
+                               .getAttributeSortedStringSet(passwordAttribute);
+               String password;
+               if (passwordAttributes == null || passwordAttributes.size() == 0) {
+                       throw new ArgeoException("No password found for user " + username);
+               } else {
+                       byte[] arr = (byte[]) passwordAttributes.first();
+                       password = new String(arr);
+                       // erase password
+                       Arrays.fill(arr, (byte) 0);
+               }
+
+               try {
+                       return new JcrUserDetails(userHome.getNode(ARGEO_PROFILE),
+                                       password, authorities);
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot retrieve user details for "
+                                       + username, e);
+               }
+       }
+
+       /**
+        * Writes an LDAP context to the JCR user profile.
+        * 
+        * @return path to user profile
+        */
+       protected String mapLdapToJcr(DirContextAdapter ctx) {
+               Session session = securitySession;
+               try {
+                       // process
+                       String username = ctx.getStringAttribute(usernameAttribute);
+                       Node userHome = JcrUtils.createUserHomeIfNeeded(session, username);
+                       Node userProfile; // = userHome.getNode(ARGEO_PROFILE);
+                       if (userHome.hasNode(ARGEO_PROFILE)) {
+                               userProfile = userHome.getNode(ARGEO_PROFILE);
+                       } else {
+                               userProfile = JcrUtils.createUserProfile(securitySession,
+                                               username);
+                               userProfile.getSession().save();
+                               userProfile.getSession().getWorkspace().getVersionManager()
+                                               .checkin(userProfile.getPath());
+                       }
+
+                       Map<String, String> modifications = new HashMap<String, String>();
+                       for (String jcrProperty : propertyToAttributes.keySet())
+                               ldapToJcr(userProfile, jcrProperty, ctx, modifications);
+
+                       // assign default values
+                       // if (!userProfile.hasProperty(Property.JCR_DESCRIPTION)
+                       // && !modifications.containsKey(Property.JCR_DESCRIPTION))
+                       // modifications.put(Property.JCR_DESCRIPTION, "");
+                       // if (!userProfile.hasProperty(Property.JCR_TITLE))
+                       // modifications.put(Property.JCR_TITLE,
+                       // userProfile.getProperty(ARGEO_FIRST_NAME).getString()
+                       // + " "
+                       // + userProfile.getProperty(ARGEO_LAST_NAME)
+                       // .getString());
+                       int modifCount = modifications.size();
+                       if (modifCount > 0) {
+                               session.getWorkspace().getVersionManager()
+                                               .checkout(userProfile.getPath());
+                               for (String prop : modifications.keySet())
+                                       userProfile.setProperty(prop, modifications.get(prop));
+                               JcrUtils.updateLastModified(userProfile);
+                               session.save();
+                               session.getWorkspace().getVersionManager()
+                                               .checkin(userProfile.getPath());
+                               if (log.isDebugEnabled())
+                                       log.debug("Mapped " + modifCount + " LDAP modification"
+                                                       + (modifCount == 1 ? "" : "s") + " from "
+                                                       + ctx.getDn() + " to " + userProfile);
+                       }
+                       return userProfile.getPath();
+               } catch (Exception e) {
+                       JcrUtils.discardQuietly(session);
+                       throw new ArgeoException("Cannot synchronize JCR and LDAP", e);
+               }
+       }
+
+       /** Maps an LDAP property to a JCR property */
+       protected void ldapToJcr(Node userProfile, String jcrProperty,
+                       DirContextOperations ctx, Map<String, String> modifications) {
+               // TODO do we really need DirContextOperations?
+               try {
+                       String ldapAttribute;
+                       if (propertyToAttributes.containsKey(jcrProperty))
+                               ldapAttribute = propertyToAttributes.get(jcrProperty);
+                       else
+                               throw new ArgeoException(
+                                               "No LDAP attribute mapped for JCR proprty "
+                                                               + jcrProperty);
+
+                       String value = ctx.getStringAttribute(ldapAttribute);
+                       // if (value == null && Property.JCR_TITLE.equals(jcrProperty))
+                       // value = "";
+                       // if (value == null &&
+                       // Property.JCR_DESCRIPTION.equals(jcrProperty))
+                       // value = "";
+                       String jcrValue = userProfile.hasProperty(jcrProperty) ? userProfile
+                                       .getProperty(jcrProperty).getString() : null;
+                       if (value != null && jcrValue != null) {
+                               if (!value.equals(jcrValue))
+                                       modifications.put(jcrProperty, value);
+                       } else if (value != null && jcrValue == null) {
+                               modifications.put(jcrProperty, value);
+                       } else if (value == null && jcrValue != null) {
+                               modifications.put(jcrProperty, value);
+                       }
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot map JCR property " + jcrProperty
+                                       + " from LDAP", e);
+               }
+       }
+
+       /*
+        * JCR to LDAP
+        */
+
+       public void mapUserToContext(UserDetails user, final DirContextAdapter ctx) {
+               if (!(user instanceof JcrUserDetails))
+                       throw new ArgeoException("Unsupported user details: "
+                                       + user.getClass());
+
+               ctx.setAttributeValues("objectClass", userClasses);
+               ctx.setAttributeValue(usernameAttribute, user.getUsername());
+               ctx.setAttributeValue(passwordAttribute,
+                               encodePassword(user.getPassword()));
+
+               final JcrUserDetails jcrUserDetails = (JcrUserDetails) user;
+               try {
+                       Node userProfile = securitySession.getNode(
+                                       jcrUserDetails.getHomePath()).getNode(ARGEO_PROFILE);
+                       for (String jcrProperty : propertyToAttributes.keySet()) {
+                               ModificationItem mi = jcrToLdap(jcrProperty, userProfile
+                                               .getProperty(jcrProperty).getString());
+                               ctx.setAttribute(mi.getAttribute());
+                       }
+                       if (log.isTraceEnabled())
+                               log.trace("Mapped " + userProfile + " to " + ctx.getDn());
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot synchronize JCR and LDAP", e);
+               }
+
+       }
+
+       /** Maps a JCR property to an LDAP property */
+       protected ModificationItem jcrToLdap(String jcrProperty, String value) {
+               // TODO do we really need DirContextOperations?
+               try {
+                       String ldapAttribute;
+                       if (propertyToAttributes.containsKey(jcrProperty))
+                               ldapAttribute = propertyToAttributes.get(jcrProperty);
+                       else
+                               return null;
+
+                       // fix issue with empty 'sn' in LDAP
+                       if (ldapAttribute.equals("sn") && (value.trim().equals("")))
+                               return null;
+                       // fix issue with empty 'description' in LDAP
+                       if (ldapAttribute.equals("description") && value.trim().equals(""))
+                               return null;
+                       BasicAttribute attr = new BasicAttribute(
+                                       propertyToAttributes.get(jcrProperty), value);
+                       ModificationItem mi = new ModificationItem(
+                                       DirContext.REPLACE_ATTRIBUTE, attr);
+                       return mi;
+               } catch (Exception e) {
+                       throw new ArgeoException("Cannot map JCR property " + jcrProperty
+                                       + " from LDAP", e);
+               }
+       }
+
+       /*
+        * UTILITIES
+        */
+       protected String encodePassword(String password) {
+               if (!password.startsWith("{")) {
+                       byte[] salt = new byte[16];
+                       random.nextBytes(salt);
+                       return passwordEncoder.encodePassword(password, salt);
+               } else {
+                       return password;
+               }
+       }
+
+       private static Random createRandom() {
+               try {
+                       return SecureRandom.getInstance("SHA1PRNG");
+               } catch (NoSuchAlgorithmException e) {
+                       return new Random(System.currentTimeMillis());
+               }
+       }
+
+       /*
+        * DEPENDENCY INJECTION
+        */
+
+       public void setLdapTemplate(LdapTemplate ldapTemplate) {
+               this.ldapTemplate = ldapTemplate;
+       }
+
+       public void setRawLdapTemplate(LdapTemplate rawLdapTemplate) {
+               this.rawLdapTemplate = rawLdapTemplate;
+       }
+
+       public void setRepository(Repository repository) {
+               this.repository = repository;
+       }
+
+       public void setSecurityWorkspace(String securityWorkspace) {
+               this.securityWorkspace = securityWorkspace;
+       }
+
+       public void setUserBase(String userBase) {
+               this.userBase = userBase;
+       }
+
+       public void setUsernameAttribute(String usernameAttribute) {
+               this.usernameAttribute = usernameAttribute;
+       }
+
+       public void setPropertyToAttributes(Map<String, String> propertyToAttributes) {
+               this.propertyToAttributes = propertyToAttributes;
+       }
+
+       public void setUsernameMapper(LdapUsernameToDnMapper usernameMapper) {
+               this.usernameMapper = usernameMapper;
+       }
+
+       public void setPasswordAttribute(String passwordAttribute) {
+               this.passwordAttribute = passwordAttribute;
+       }
+
+       public void setUserClasses(String[] userClasses) {
+               this.userClasses = userClasses;
+       }
+
+       public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
+               this.passwordEncoder = passwordEncoder;
+       }
+
+       /** Listen to LDAP */
+       class LdapUserListener implements ObjectChangeListener,
+                       NamespaceChangeListener, UnsolicitedNotificationListener {
+
+               public void namingExceptionThrown(NamingExceptionEvent evt) {
+                       evt.getException().printStackTrace();
+               }
+
+               public void objectChanged(NamingEvent evt) {
+                       Binding user = evt.getNewBinding();
+                       // TODO find a way not to be called when JCR is the source of the
+                       // modification
+                       DirContextAdapter ctx = (DirContextAdapter) ldapTemplate
+                                       .lookup(user.getName());
+                       mapLdapToJcr(ctx);
+               }
+
+               public void objectAdded(NamingEvent evt) {
+                       Binding user = evt.getNewBinding();
+                       DirContextAdapter ctx = (DirContextAdapter) ldapTemplate
+                                       .lookup(user.getName());
+                       mapLdapToJcr(ctx);
+               }
+
+               public void objectRemoved(NamingEvent evt) {
+                       if (log.isDebugEnabled())
+                               log.debug(evt);
+               }
+
+               public void objectRenamed(NamingEvent evt) {
+                       if (log.isDebugEnabled())
+                               log.debug(evt);
+               }
+
+               public void notificationReceived(UnsolicitedNotificationEvent evt) {
+                       UnsolicitedNotification notification = evt.getNotification();
+                       NamingException ne = notification.getException();
+                       String msg = "LDAP notification " + "ID=" + notification.getID()
+                                       + ", referrals=" + notification.getReferrals();
+                       if (ne != null) {
+                               if (log.isTraceEnabled())
+                                       log.trace(msg + ", exception= " + ne, ne);
+                               else
+                                       log.warn(msg + ", exception= " + ne);
+                       } else if (log.isDebugEnabled()) {
+                               log.debug("Unsollicited LDAP notification " + msg);
+                       }
+               }
+
+       }
+
+       /** Listen to JCR */
+       class JcrProfileListener implements EventListener {
+
+               public void onEvent(EventIterator events) {
+                       try {
+                               final Map<Name, List<ModificationItem>> modifications = new HashMap<Name, List<ModificationItem>>();
+                               while (events.hasNext()) {
+                                       Event event = events.nextEvent();
+                                       try {
+                                               if (Event.PROPERTY_CHANGED == event.getType()) {
+                                                       Property property = (Property) securitySession
+                                                                       .getItem(event.getPath());
+                                                       String propertyName = property.getName();
+                                                       Node userProfile = property.getParent();
+                                                       String username = userProfile.getProperty(
+                                                                       ARGEO_USER_ID).getString();
+                                                       if (propertyToAttributes.containsKey(propertyName)) {
+                                                               Name name = usernameMapper.buildDn(username);
+                                                               if (!modifications.containsKey(name))
+                                                                       modifications.put(name,
+                                                                                       new ArrayList<ModificationItem>());
+                                                               String value = property.getString();
+                                                               ModificationItem mi = jcrToLdap(propertyName,
+                                                                               value);
+                                                               if (mi != null)
+                                                                       modifications.get(name).add(mi);
+                                                       }
+                                               } else if (Event.NODE_ADDED == event.getType()) {
+                                                       Node userProfile = securitySession.getNode(event
+                                                                       .getPath());
+                                                       String username = userProfile.getProperty(
+                                                                       ARGEO_USER_ID).getString();
+                                                       Name name = usernameMapper.buildDn(username);
+                                                       for (String propertyName : propertyToAttributes
+                                                                       .keySet()) {
+                                                               if (!modifications.containsKey(name))
+                                                                       modifications.put(name,
+                                                                                       new ArrayList<ModificationItem>());
+                                                               String value = userProfile.getProperty(
+                                                                               propertyName).getString();
+                                                               ModificationItem mi = jcrToLdap(propertyName,
+                                                                               value);
+                                                               if (mi != null)
+                                                                       modifications.get(name).add(mi);
+                                                       }
+                                               }
+                                       } catch (RepositoryException e) {
+                                               throw new ArgeoException("Cannot process event "
+                                                               + event, e);
+                                       }
+                               }
+
+                               for (Name name : modifications.keySet()) {
+                                       List<ModificationItem> userModifs = modifications.get(name);
+                                       int modifCount = userModifs.size();
+                                       ldapTemplate.modifyAttributes(name, userModifs
+                                                       .toArray(new ModificationItem[modifCount]));
+                                       if (log.isDebugEnabled())
+                                               log.debug("Mapped " + modifCount + " JCR modification"
+                                                               + (modifCount == 1 ? "" : "s") + " to " + name);
+                               }
+                       } catch (Exception e) {
+                               // if (log.isDebugEnabled())
+                               // e.printStackTrace();
+                               throw new ArgeoException("Cannot process JCR events ("
+                                               + e.getMessage() + ")", e);
+                       }
+               }
+
+       }
+}
diff --git a/security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/jcr/JcrUserDetailsContextMapper.java b/security/runtime/org.argeo.security.ldap/src/main/java/org/argeo/security/ldap/jcr/JcrUserDetailsContextMapper.java
deleted file mode 100644 (file)
index b6657f0..0000000
+++ /dev/null
@@ -1,319 +0,0 @@
-package org.argeo.security.ldap.jcr;
-
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
-import java.util.Arrays;
-import java.util.Calendar;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Random;
-import java.util.SortedSet;
-import java.util.concurrent.Executor;
-
-import javax.jcr.Node;
-import javax.jcr.Property;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.argeo.ArgeoException;
-import org.argeo.jcr.ArgeoNames;
-import org.argeo.jcr.JcrUtils;
-import org.argeo.security.jcr.JcrUserDetails;
-import org.springframework.ldap.core.DirContextAdapter;
-import org.springframework.ldap.core.DirContextOperations;
-import org.springframework.security.BadCredentialsException;
-import org.springframework.security.GrantedAuthority;
-import org.springframework.security.context.SecurityContextHolder;
-import org.springframework.security.providers.encoding.PasswordEncoder;
-import org.springframework.security.userdetails.UserDetails;
-import org.springframework.security.userdetails.ldap.UserDetailsContextMapper;
-
-/**
- * Maps LDAP attributes and JCR properties. This class is meant to be robust,
- * checks of which values should be mandatory should be performed at a higher
- * level.
- */
-public class JcrUserDetailsContextMapper implements UserDetailsContextMapper,
-               ArgeoNames {
-       private final static Log log = LogFactory
-                       .getLog(JcrUserDetailsContextMapper.class);
-
-       private String usernameAttribute;
-       private String passwordAttribute;
-       private String homeBasePath;
-       private String[] userClasses;
-
-       private Map<String, String> propertyToAttributes = new HashMap<String, String>();
-       private Executor systemExecutor;
-       private Session session;
-
-       private PasswordEncoder passwordEncoder;
-       private final Random random;
-
-       /** 0 is always sync */
-       private Long syncLatency = 10 * 60 * 1000l;
-
-       public JcrUserDetailsContextMapper() {
-               random = createRandom();
-       }
-
-       private static Random createRandom() {
-               try {
-                       return SecureRandom.getInstance("SHA1PRNG");
-               } catch (NoSuchAlgorithmException e) {
-                       return new Random(System.currentTimeMillis());
-               }
-       }
-
-       public UserDetails mapUserFromContext(final DirContextOperations ctx,
-                       final String username, GrantedAuthority[] authorities) {
-               if (ctx == null)
-                       throw new ArgeoException("No LDAP information found for user "
-                                       + username);
-
-               final StringBuffer userHomePathT = new StringBuffer("");
-               Runnable action = new Runnable() {
-                       public void run() {
-                               String userHomepath = mapLdapToJcr(username, ctx);
-                               userHomePathT.append(userHomepath);
-                       }
-               };
-
-               if (SecurityContextHolder.getContext().getAuthentication() == null) {
-                       // authentication
-                       try {
-                               systemExecutor.execute(action);
-                       } finally {
-                               JcrUtils.logoutQuietly(session);
-                       }
-               } else {
-                       // authenticated user
-                       action.run();
-               }
-
-               // password
-               SortedSet<?> passwordAttributes = ctx
-                               .getAttributeSortedStringSet(passwordAttribute);
-               String password;
-               if (passwordAttributes == null || passwordAttributes.size() == 0) {
-                       throw new ArgeoException("No password found for user " + username);
-               } else {
-                       byte[] arr = (byte[]) passwordAttributes.first();
-                       password = new String(arr);
-                       // erase password
-                       Arrays.fill(arr, (byte) 0);
-               }
-               JcrUserDetails userDetails = new JcrUserDetails(
-                               userHomePathT.toString(), username, password, true, true, true,
-                               true, authorities);
-               return userDetails;
-       }
-
-       /** @return path to the user home node */
-       protected synchronized String mapLdapToJcr(String username,
-                       DirContextOperations ctx) {
-               String usernameLdap = ctx.getStringAttribute(usernameAttribute);
-               // log.debug("username=" + username + ", usernameLdap=" + usernameLdap);
-               if (!username.equals(usernameLdap)) {
-                       String msg = "Provided username '" + username
-                                       + "' is different from username stored in LDAP '"
-                                       + usernameLdap + "'";
-                       // we log it because the exception may not be displayed
-                       log.error(msg);
-                       throw new BadCredentialsException(msg);
-               }
-
-               try {
-
-                       Node userHome = JcrUtils.getUserHome(session, username);
-                       boolean justCreatedHome = false;
-                       if (userHome == null) {
-                               userHome = JcrUtils.createUserHome(session, homeBasePath,
-                                               username);
-                               justCreatedHome = true;
-                       }
-                       String userHomePath = userHome.getPath();
-                       Node userProfile; // = userHome.getNode(ARGEO_PROFILE);
-                       if (userHome.hasNode(ARGEO_PROFILE)) {
-                               userProfile = userHome.getNode(ARGEO_PROFILE);
-                               if (syncLatency != 0 && !justCreatedHome) {
-                                       Calendar lastModified = userProfile.getProperty(
-                                                       Property.JCR_LAST_MODIFIED).getDate();
-                                       long timeSinceLastUpdate = System.currentTimeMillis()
-                                                       - lastModified.getTimeInMillis();
-                                       if (timeSinceLastUpdate < syncLatency)// skip sync
-                                               return userHomePath;
-                               }
-                       } else {
-                               throw new ArgeoException("We should never reach this point");
-                               // userProfile = userHome.addNode(ARGEO_PROFILE);
-                               // userProfile.addMixin(NodeType.MIX_TITLE);
-                               // userProfile.addMixin(NodeType.MIX_CREATED);
-                               // userProfile.addMixin(NodeType.MIX_LAST_MODIFIED);
-                       }
-
-                       session.getWorkspace().getVersionManager()
-                                       .checkout(userProfile.getPath());
-                       for (String jcrProperty : propertyToAttributes.keySet())
-                               ldapToJcr(userProfile, jcrProperty, ctx);
-
-                       // assign default values
-                       if (!userProfile.hasProperty(Property.JCR_DESCRIPTION))
-                               userProfile.setProperty(Property.JCR_DESCRIPTION, "");
-                       if (!userProfile.hasProperty(Property.JCR_TITLE))
-                               userProfile.setProperty(Property.JCR_TITLE, userProfile
-                                               .getProperty(ARGEO_FIRST_NAME).getString()
-                                               + " "
-                                               + userProfile.getProperty(ARGEO_LAST_NAME).getString());
-                       JcrUtils.updateLastModified(userProfile);
-                       session.save();
-                       session.getWorkspace().getVersionManager()
-                                       .checkin(userProfile.getPath());
-                       if (log.isTraceEnabled())
-                               log.trace("Mapped " + ctx.getDn() + " to " + userProfile);
-                       return userHomePath;
-               } catch (Exception e) {
-                       JcrUtils.discardQuietly(session);
-                       throw new ArgeoException("Cannot synchronize JCR and LDAP", e);
-               }
-       }
-
-       public void mapUserToContext(UserDetails user, final DirContextAdapter ctx) {
-               if (!(user instanceof JcrUserDetails))
-                       throw new ArgeoException("Unsupported user details: "
-                                       + user.getClass());
-
-               ctx.setAttributeValues("objectClass", userClasses);
-               ctx.setAttributeValue(usernameAttribute, user.getUsername());
-               ctx.setAttributeValue(passwordAttribute,
-                               encodePassword(user.getPassword()));
-
-               final JcrUserDetails jcrUserDetails = (JcrUserDetails) user;
-               try {
-                       Node userProfile = session.getNode(jcrUserDetails.getHomePath()
-                                       + '/' + ARGEO_PROFILE);
-                       for (String jcrProperty : propertyToAttributes.keySet())
-                               jcrToLdap(userProfile, jcrProperty, ctx);
-
-                       if (log.isTraceEnabled())
-                               log.trace("Mapped " + userProfile + " to " + ctx.getDn());
-               } catch (RepositoryException e) {
-                       throw new ArgeoException("Cannot synchronize JCR and LDAP", e);
-               }
-       }
-
-       protected String encodePassword(String password) {
-               if (!password.startsWith("{")) {
-                       byte[] salt = new byte[16];
-                       random.nextBytes(salt);
-                       return passwordEncoder.encodePassword(password, salt);
-               } else {
-                       return password;
-               }
-       }
-
-       protected void ldapToJcr(Node userProfile, String jcrProperty,
-                       DirContextOperations ctx) {
-               try {
-                       String ldapAttribute;
-                       if (propertyToAttributes.containsKey(jcrProperty))
-                               ldapAttribute = propertyToAttributes.get(jcrProperty);
-                       else
-                               throw new ArgeoException(
-                                               "No LDAP attribute mapped for JCR proprty "
-                                                               + jcrProperty);
-
-                       String value = ctx.getStringAttribute(ldapAttribute);
-                       String jcrValue = userProfile.hasProperty(jcrProperty) ? userProfile
-                                       .getProperty(jcrProperty).getString() : null;
-                       if (value != null && jcrValue != null) {
-                               if (!value.equals(jcrValue))
-                                       userProfile.setProperty(jcrProperty, value);
-                       } else if (value != null && jcrValue == null) {
-                               userProfile.setProperty(jcrProperty, value);
-                       } else if (value == null && jcrValue != null) {
-                               userProfile.setProperty(jcrProperty, value);
-                       }
-               } catch (Exception e) {
-                       throw new ArgeoException("Cannot map JCR property " + jcrProperty
-                                       + " from LDAP", e);
-               }
-       }
-
-       protected void jcrToLdap(Node userProfile, String jcrProperty,
-                       DirContextOperations ctx) {
-               try {
-                       String ldapAttribute;
-                       if (propertyToAttributes.containsKey(jcrProperty))
-                               ldapAttribute = propertyToAttributes.get(jcrProperty);
-                       else
-                               throw new ArgeoException(
-                                               "No LDAP attribute mapped for JCR proprty "
-                                                               + jcrProperty);
-
-                       // fix issue with empty 'sn' in LDAP
-                       if (ldapAttribute.equals("sn")
-                                       && (!userProfile.hasProperty(jcrProperty) || userProfile
-                                                       .getProperty(jcrProperty).getString().trim()
-                                                       .equals("")))
-                               userProfile.setProperty(jcrProperty, "empty");
-
-                       if (ldapAttribute.equals("description")) {
-                               String value = userProfile.getProperty(jcrProperty).getString();
-                               if (value.trim().equals(""))
-                                       return;
-                       }
-
-                       if (!userProfile.hasProperty(jcrProperty))
-                               return;
-                       String value = userProfile.getProperty(jcrProperty).getString();
-
-                       ctx.setAttributeValue(ldapAttribute, value);
-               } catch (Exception e) {
-                       throw new ArgeoException("Cannot map JCR property " + jcrProperty
-                                       + " from LDAP", e);
-               }
-       }
-
-       public void setPropertyToAttributes(Map<String, String> propertyToAttributes) {
-               this.propertyToAttributes = propertyToAttributes;
-       }
-
-       public void setSystemExecutor(Executor systemExecutor) {
-               this.systemExecutor = systemExecutor;
-       }
-
-       public void setHomeBasePath(String homeBasePath) {
-               this.homeBasePath = homeBasePath;
-       }
-
-       public void setUsernameAttribute(String usernameAttribute) {
-               this.usernameAttribute = usernameAttribute;
-       }
-
-       public void setPasswordAttribute(String passwordAttribute) {
-               this.passwordAttribute = passwordAttribute;
-       }
-
-       public void setUserClasses(String[] userClasses) {
-               this.userClasses = userClasses;
-       }
-
-       public void setPasswordEncoder(PasswordEncoder passwordEncoder) {
-               this.passwordEncoder = passwordEncoder;
-       }
-
-       public void setSession(Session session) {
-               this.session = session;
-       }
-
-       /**
-        * Time in ms during which the LDAP server is not checked. 0 is always sync.
-        */
-       public void setSyncLatency(Long syncLatency) {
-               this.syncLatency = syncLatency;
-       }
-
-}
index 6b6a54077bbcb80c09570a668bf2e4353a081fa4..d56d0d47849245fe817fc404b9642bff19874d57 100644 (file)
@@ -16,7 +16,7 @@
                </property>
        </bean>
 
-       <bean id="repositoryRegister" class="org.argeo.jcr.DefaultRepositoryRegister" />
+<!--   <bean id="repositoryRegister" class="org.argeo.jcr.DefaultRepositoryRegister" /> -->
 
        <bean id="sessionProvider" scope="session" init-method="init"
                destroy-method="dispose" class="org.argeo.jackrabbit.remote.SimpleSessionProvider">
index 08063ab396e1f8f9a65e888f887e8552ed8eb448..99eb2e4c83d0038591c3e270e28e302c28d3e04e 100644 (file)
@@ -8,11 +8,12 @@
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd\r
        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd">\r
 \r
-       <set id="repositories" interface="javax.jcr.Repository"\r
-               cardinality="0..N">\r
-               <listener ref="repositoryRegister" bind-method="register"\r
-                       unbind-method="unregister" />\r
-       </set>\r
+       <!-- <set id="repositories" interface="javax.jcr.Repository" -->\r
+       <!-- cardinality="0..N"> -->\r
+       <!-- <listener ref="repositoryRegister" bind-method="register" -->\r
+       <!-- unbind-method="unregister" /> -->\r
+       <!-- </set> -->\r
+       <reference id="repositoryRegister" interface="org.argeo.jcr.RepositoryRegister" />\r
 \r
        <reference id="authenticationManager"\r
                interface="org.springframework.security.AuthenticationManager" />\r
index f6e210b7c216fa077e3d3665ada31317af309cf3..1c16f257d5738d693dd0f53937e90c6dce788b7b 100644 (file)
@@ -9,19 +9,10 @@
        http://www.springframework.org/schema/util\r
        http://www.springframework.org/schema/util/spring-util-2.5.xsd">\r
 \r
-       <!-- REFERENCES -->\r
-       <reference id="authenticationManager"\r
-               interface="org.springframework.security.AuthenticationManager" />\r
-\r
        <!-- SERVICES -->\r
        <service ref="nodeJcrRepository" interface="javax.jcr.Repository">\r
                <service-properties>\r
-                       <beans:entry value="node">\r
-                               <beans:key>\r
-                                       <util:constant\r
-                                               static-field="org.argeo.jcr.ArgeoJcrConstants.JCR_REPOSITORY_ALIAS" />\r
-                               </beans:key>\r
-                       </beans:entry>\r
+                       <beans:entry key="argeo.jcr.repository.alias" value="node" />\r
                </service-properties>\r
        </service>\r
 </beans:beans>
\ No newline at end of file
index 8e477fe71b71b47da0c47c4f4468c9eee2b2fd00..3d7b30c6c7f6690ec629f84fe150d7dfa9726597 100644 (file)
        </bean>
 
        <bean id="nodeJcrRepository" class="org.argeo.jackrabbit.JackrabbitContainer"
-               init-method="init" destroy-method="dispose">
+               init-method="init" destroy-method="destroy">
                <property name="uri" value="${argeo.node.repo.uri}" />
                <property name="homeDirectory" value="${argeo.node.repo.home}" />
                <property name="configuration" value="${argeo.node.repo.configuration}" />
                <property name="variables" value="osgibundle:/noderepo.properties" />
-               <property name="namespaces">
-                       <map>
-                               <entry key="argeo" value="http://www.argeo.org/ns/argeo" />
-                       </map>
-               </property>
-               <property name="cndFiles">
-                       <list>
-                               <value>classpath:/org/argeo/jcr/argeo.cnd</value>
-                       </list>
-               </property>
+               <!-- For remote testing -->
                <property name="remoteSystemCredentials">
                        <bean class="javax.jcr.SimpleCredentials">
-                               <constructor-arg value="root" />
-                               <constructor-arg value="demo" />
+                               <constructor-arg value="${argeo.node.repo.remoteSystemUser}" />
+                               <constructor-arg value="${argeo.node.repo.remoteSystemPassword}" />
                        </bean>
                </property>
        </bean>
-
-       <!-- Execute initialization with a system authentication -->
-       <bean
-               class="org.argeo.security.core.AuthenticatedApplicationContextInitialization">
-               <property name="authenticationManager" ref="authenticationManager" />
-       </bean>
-
 </beans>
\ No newline at end of file
index 5f22cdd4480f64cd4765eb82177bb9257920cc0b..a275ae63806d780ca04d7dd31688d15c020a1382 100644 (file)
@@ -1 +1,4 @@
-bin.includes = META-INF/
+bin.includes = META-INF/,\
+               noderepo.properties,\
+               repository-h2.xml,\
+               repository-postgresql.xml
index 046e1790a154a999d2cc0455859da86cba54ccd8..f50029b5d3bad3e6ccac1faadde56e708c403ae7 100644 (file)
@@ -1,5 +1,6 @@
 # Workspace used by the node session
-argeo.node.repo.workspace=dev
+argeo.node.repo.defaultWorkspace=main
+#argeo.node.repo.securityWorkspace=security
 
 # Repository base directory
 argeo.node.repo.home=${osgi.instance.area}/node
@@ -13,6 +14,9 @@ argeo.node.repo.dbpassword=
 ## Remote
 # Remote repository URI (overrides other configurations if not empty)
 argeo.node.repo.uri=
+# may change in the near future:
+argeo.node.repo.remoteSystemUser=root
+argeo.node.repo.remoteSystemPassword=demo
 
 # ADVANCED
 argeo.node.repo.maxPoolSize=10
\ No newline at end of file
index 32840cd1933a5e508cc8b0e8c86c5b025190d09f..2563d2f9acbfe6fc5c8f54c45ed5479b0c8f960d 100644 (file)
                                                <Import-Package>
                                                        *,
                                                        com.mysql.jdbc;version="[5.0.0,6.0.0)";resolution:=optional,
+                                                       org.h2;version="[1.0.0,2.0.0)";resolution:=optional,
+                                                       org.postgresql;version="[8.0.0,9.0.0)";resolution:=optional,
                                                        javax.jcr;version="[2.0.0,3.0.0)",
                                                        org.apache.jackrabbit.core;version="[2.0.0,3.0.0)",
                                                        org.apache.jackrabbit.core.config;version="[2.0.0,3.0.0)",
                                                        org.apache.xalan.processor,
                                                        org.argeo.jackrabbit,
                                                        org.argeo.jcr,
-                                                       org.argeo.security,
-                                                       org.argeo.security.core,
-                                                       org.h2;version="[1.0.0,2.0.0)";resolution:=optional,
-                                                       org.postgresql;version="[8.0.0,9.0.0)";resolution:=optional,
                                                        org.springframework.beans.factory.config,
-                                                       org.springframework.security;version="2.0.6.RELEASE"
                                                </Import-Package>
                                        </instructions>
                                </configuration>
index 9e7886d69c9f609aa2cb4e321aedcad49068d03a..2d3343ec3cbacc967977df0fd59dfede4af9ed6a 100644 (file)
@@ -30,7 +30,7 @@
 
        <!-- Workspace templates -->
        <Workspaces rootPath="${rep.home}/workspaces"
-               defaultWorkspace="dev" />
+               defaultWorkspace="${argeo.node.repo.defaultWorkspace}" />
        <Workspace name="${wsp.name}">
                <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
                        <param name="dataSourceName" value="dataSource" />
index 4c4f4fb41a22a00bd3019c23c5c5a97841c4dead..4b2fe53fc446f492de919acbb39d73fa3be07e85 100644 (file)
@@ -27,7 +27,7 @@
 
        <!-- Workspace templates -->
        <Workspaces rootPath="${rep.home}/workspaces"
-               defaultWorkspace="dev" />
+               defaultWorkspace="${argeo.node.repo.defaultWorkspace}" />
        <Workspace name="${wsp.name}">
                <FileSystem class="org.apache.jackrabbit.core.fs.db.DbFileSystem">
                        <param name="dataSourceName" value="dataSource" />
index d3d5c80958677f1f8de5b580c289b3baf7c5452c..0a607ba1718eb2af79f9423643e3e347427d3a3f 100644 (file)
@@ -1,8 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
-       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
-       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
        <classpathentry kind="src" path="src/main/java"/>
        <classpathentry kind="src" path="src/main/resources"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+       <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
        <classpathentry kind="output" path="target/classes"/>
 </classpath>
index a645809a100051e67a0324039bbdf9eaacab3dec..6c5273aca58c1f8a488b586e29888dab8d274e8e 100644 (file)
@@ -76,7 +76,7 @@ public class RepositoriesNode extends TreeParent implements ArgeoNames {
                        throws RepositoryException {
                Session userSession = jcrKeyring.getSession();
                Node userHome = JcrUtils.getUserHome(userSession);
-               if (userHome.hasNode(ARGEO_REMOTE)) {
+               if (userHome != null && userHome.hasNode(ARGEO_REMOTE)) {
                        NodeIterator it = userHome.getNode(ARGEO_REMOTE).getNodes();
                        while (it.hasNext()) {
                                Node remoteNode = it.nextNode();
index c2e0034ff183f0ca5b623ba7d992eb16f5148535..10c7af1d05e002359ce4abf758b3ebb7d9c08fa0 100644 (file)
 
 package org.argeo.jackrabbit;
 
-import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
+import java.net.URL;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -51,7 +51,6 @@ import org.apache.jackrabbit.api.JackrabbitRepository;
 import org.apache.jackrabbit.commons.NamespaceHelper;
 import org.apache.jackrabbit.commons.cnd.CndImporter;
 import org.apache.jackrabbit.core.RepositoryImpl;
-import org.apache.jackrabbit.core.TransientRepository;
 import org.apache.jackrabbit.core.config.RepositoryConfig;
 import org.apache.jackrabbit.core.config.RepositoryConfigurationParser;
 import org.apache.jackrabbit.jcr2dav.Jcr2davRepositoryFactory;
@@ -59,9 +58,7 @@ import org.argeo.ArgeoException;
 import org.argeo.jcr.ArgeoNames;
 import org.argeo.jcr.JcrUtils;
 import org.argeo.security.SystemAuthentication;
-import org.springframework.context.ResourceLoaderAware;
 import org.springframework.core.io.Resource;
-import org.springframework.core.io.ResourceLoader;
 import org.springframework.security.Authentication;
 import org.springframework.security.context.SecurityContextHolder;
 import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
@@ -72,22 +69,24 @@ import org.xml.sax.InputSource;
  * Wrapper around a Jackrabbit repository which allows to configure it in Spring
  * and expose it as a {@link Repository}.
  */
-public class JackrabbitContainer implements Repository, ResourceLoaderAware {
+public class JackrabbitContainer implements Repository {
        private Log log = LogFactory.getLog(JackrabbitContainer.class);
 
+       // remote
+       private String uri = null;
+       private Credentials remoteSystemCredentials = null;
+
+       // local
        private Resource configuration;
+       private RepositoryConfig repositoryConfig;
        private File homeDirectory;
        private Resource variables;
-
        private Boolean inMemory = false;
 
        // wrapped repository
        private Repository repository;
-       private RepositoryConfig repositoryConfig;
-
-       // CND
-       private ResourceLoader resourceLoader;
 
+       // data model
        /** Node type definitions in CND format */
        private List<String> cndFiles = new ArrayList<String>();
 
@@ -101,10 +100,6 @@ public class JackrabbitContainer implements Repository, ResourceLoaderAware {
 
        private Executor systemExecutor;
 
-       // remote
-       private String uri = null;
-       private Credentials remoteSystemCredentials = null;
-
        /**
         * Empty constructor, {@link #init()} should be called after properties have
         * been set
@@ -122,10 +117,11 @@ public class JackrabbitContainer implements Repository, ResourceLoaderAware {
                init();
        }
 
+       /** Initializes */
        public void init() {
                if (repository != null) {
                        // we are just wrapping another repository
-                       importNodeTypeDefinitions();
+                       prepareDataModel();
                        return;
                }
 
@@ -135,15 +131,15 @@ public class JackrabbitContainer implements Repository, ResourceLoaderAware {
 
                // apply new CND files after migration
                if (cndFiles != null && cndFiles.size() > 0)
-                       importNodeTypeDefinitions();
+                       prepareDataModel();
        }
 
-       /** Actually creates a new repository. */
+       /** Actually creates the new repository. */
        protected void createJackrabbitRepository() {
                long begin = System.currentTimeMillis();
+               InputStream configurationIn = null;
                try {
-                       // remote repository
-                       if (uri != null && !uri.trim().equals("")) {
+                       if (uri != null && !uri.trim().equals("")) {// remote
                                Map<String, String> params = new HashMap<String, String>();
                                params.put(
                                                org.apache.jackrabbit.commons.JcrUtils.REPOSITORY_URI,
@@ -155,53 +151,52 @@ public class JackrabbitContainer implements Repository, ResourceLoaderAware {
                                                        + " not found");
                                log.info("Initialized Jackrabbit repository " + repository
                                                + " from URI " + uri);
-                               // do not perform further initialization since we assume that
-                               // the
-                               // remote repository has been properly configured
-                               return;
-                       } else {
+                               // we assume that the remote repository has been properly
+                               // configured
+                       } else {// local
                                // reset uri to null in order to optimize isRemote()
                                uri = null;
-                       }
 
-                       // local repository
-                       if (inMemory && getHomeDirectory().exists()) {
-                               FileUtils.deleteDirectory(getHomeDirectory());
-                               log.warn("Deleted Jackrabbit home directory "
-                                               + getHomeDirectory());
-                       }
+                               // temporary
+                               if (inMemory && getHomeDirectory().exists()) {
+                                       FileUtils.deleteDirectory(getHomeDirectory());
+                                       log.warn("Deleted Jackrabbit home directory "
+                                                       + getHomeDirectory());
+                               }
 
-                       Properties vars = getConfigurationProperties();
-                       InputStream in = configuration.getInputStream();
-                       try {
+                               // process configuration file
+                               Properties vars = getConfigurationProperties();
+                               configurationIn = configuration.getInputStream();
                                vars.put(
                                                RepositoryConfigurationParser.REPOSITORY_HOME_VARIABLE,
                                                getHomeDirectory().getCanonicalPath());
-                               repositoryConfig = RepositoryConfig.create(new InputSource(in),
-                                               vars);
-                       } catch (Exception e) {
-                               throw new RuntimeException("Cannot read configuration", e);
-                       } finally {
-                               IOUtils.closeQuietly(in);
-                       }
+                               repositoryConfig = RepositoryConfig.create(new InputSource(
+                                               configurationIn), vars);
 
-                       if (inMemory)
-                               repository = new TransientRepository(repositoryConfig);
-                       else
+                               //
+                               // Actual repository creation
+                               //
                                repository = RepositoryImpl.create(repositoryConfig);
 
-                       double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
-                       log.info("Initialized Jackrabbit repository in " + duration
-                                       + " s, home: " + getHomeDirectory() + ", config: "
-                                       + configuration);
+                               double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
+                               log.info("Initialized Jackrabbit repository in " + duration
+                                               + " s, home: " + getHomeDirectory() + ", config: "
+                                               + configuration);
+                       }
                } catch (Exception e) {
                        throw new ArgeoException("Cannot create Jackrabbit repository "
                                        + getHomeDirectory(), e);
+               } finally {
+                       IOUtils.closeQuietly(configurationIn);
                }
        }
 
        /** Executes migrations, if needed. */
        protected void migrate() {
+               // Remote migration not supported
+               if (isRemote())
+                       return;
+
                // No migration to perform
                if (dataModelMigrations.size() == 0)
                        return;
@@ -273,7 +268,7 @@ public class JackrabbitContainer implements Repository, ResourceLoaderAware {
                                                                        + File.separator + "jackrabbit-"
                                                                        + UUID.randomUUID());
                                        homeDirectory.mkdirs();
-                                       // will it work if directory is not empty?
+                                       // will it work if directory is not empty??
                                        homeDirectory.deleteOnExit();
                                }
                        }
@@ -285,47 +280,39 @@ public class JackrabbitContainer implements Repository, ResourceLoaderAware {
                }
        }
 
-       public void dispose() throws Exception {
-               long begin = System.currentTimeMillis();
-               if (repository != null) {
-                       if (repository instanceof JackrabbitRepository)
-                               ((JackrabbitRepository) repository).shutdown();
-                       else if (repository instanceof RepositoryImpl)
-                               ((RepositoryImpl) repository).shutdown();
-                       else if (repository instanceof TransientRepository)
-                               ((TransientRepository) repository).shutdown();
-               }
-
-               if (inMemory)
-                       if (getHomeDirectory().exists()) {
-                               FileUtils.deleteDirectory(getHomeDirectory());
-                               if (log.isDebugEnabled())
-                                       log.debug("Deleted Jackrabbit home directory "
-                                                       + getHomeDirectory());
-                       }
-
-               double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
-               if (uri != null && !uri.trim().equals(""))
-                       log.info("Destroyed Jackrabbit repository with uri " + uri);
-               else
+       /** Shutdown the repository */
+       public void destroy() throws Exception {
+               if (repository != null && repository instanceof RepositoryImpl) {
+                       long begin = System.currentTimeMillis();
+                       ((RepositoryImpl) repository).shutdown();
+                       if (inMemory)
+                               if (getHomeDirectory().exists()) {
+                                       FileUtils.deleteDirectory(getHomeDirectory());
+                                       if (log.isDebugEnabled())
+                                               log.debug("Deleted Jackrabbit home directory "
+                                                               + getHomeDirectory());
+                               }
+                       double duration = ((double) (System.currentTimeMillis() - begin)) / 1000;
                        log.info("Destroyed Jackrabbit repository in " + duration
                                        + " s, home: " + getHomeDirectory() + ", config "
                                        + configuration);
+               }
        }
 
        /**
-        * @deprecated explicitly declare {@link #dispose()} as destroy-method
+        * @deprecated explicitly declare {@link #destroy()} as destroy-method
         *             instead.
         */
-       public void destroy() throws Exception {
-               log.error("## Declare destroy-method=\"dispose\". in the Jackrabbit container bean");
+       public void dispose() throws Exception {
+               log.error("## Declare destroy-method=\"destroy\". in the Jackrabbit container bean");
+               destroy();
        }
 
-       /** @deprecated explicitly declare {@link #init()} as init-method instead. */
-       public void afterPropertiesSet() throws Exception {
-               log.error("## Declare init-method=\"init\". in the Jackrabbit container bean");
-       }
+       /*
+        * UTILITIES
+        */
 
+       /** Generates the properties to use in the configuration. */
        protected Properties getConfigurationProperties() {
                InputStream propsIn = null;
                Properties vars;
@@ -360,38 +347,56 @@ public class JackrabbitContainer implements Repository, ResourceLoaderAware {
        }
 
        /**
-        * Import declared node type definitions, trying to update them if they have
-        * changed. In case of failures an error will be logged but no exception
-        * will be thrown.
+        * Import declared node type definitions and register namespaces. Tries to
+        * update the node definitions if they have changed. In case of failures an
+        * error will be logged but no exception will be thrown.
         */
-       protected void importNodeTypeDefinitions() {
+       protected void prepareDataModel() {
                // importing node def on remote si currently not supported
                if (isRemote())
                        return;
 
                Runnable action = new Runnable() {
                        public void run() {
-                               Reader reader = null;
                                Session session = null;
                                try {
                                        session = login();
-                                       // processNewSession(session);
-                                       // Load cnds as resources
+                                       // register namespaces
+                                       if (namespaces.size() > 0) {
+                                               NamespaceHelper namespaceHelper = new NamespaceHelper(
+                                                               session);
+                                               namespaceHelper.registerNamespaces(namespaces);
+                                       }
+                                       // load CND files from classpath or as URL
                                        for (String resUrl : cndFiles) {
-                                               Resource res = resourceLoader.getResource(resUrl);
-                                               byte[] arr = IOUtils.toByteArray(res.getInputStream());
-                                               reader = new InputStreamReader(
-                                                               new ByteArrayInputStream(arr));
-                                               CndImporter.registerNodeTypes(reader, session, true);
+                                               boolean classpath;
+                                               if (resUrl.startsWith("classpath:")) {
+                                                       resUrl = resUrl.substring("classpath:".length());
+                                                       classpath = true;
+                                               } else if (resUrl.indexOf(':') < 0) {
+                                                       classpath = true;
+                                               } else {
+                                                       classpath = false;
+                                               }
+
+                                               URL url = classpath ? getClass().getClassLoader()
+                                                               .getResource(resUrl) : new URL(resUrl);
+
+                                               Reader reader = null;
+                                               try {
+                                                       reader = new InputStreamReader(url.openStream());
+                                                       CndImporter
+                                                                       .registerNodeTypes(reader, session, true);
+                                               } finally {
+                                                       IOUtils.closeQuietly(reader);
+                                               }
                                        }
-                                       session.save();
                                } catch (Exception e) {
                                        log.error(
                                                        "Cannot import node type definitions " + cndFiles,
                                                        e);
                                        JcrUtils.discardQuietly(session);
                                } finally {
-                                       IOUtils.closeQuietly(reader);
                                        JcrUtils.logoutQuietly(session);
                                }
                        }
@@ -403,7 +408,10 @@ public class JackrabbitContainer implements Repository, ResourceLoaderAware {
                        action.run();
        }
 
-       // JCR REPOSITORY (delegated)
+       /*
+        * DELEGATED JCR REPOSITORY METHODS
+        */
+
        public String getDescriptor(String key) {
                return getRepository().getDescriptor(key);
        }
@@ -460,6 +468,10 @@ public class JackrabbitContainer implements Repository, ResourceLoaderAware {
                return login(null, workspaceName);
        }
 
+       /** Called after a session has been created, does nothing by default. */
+       protected void processNewSession(Session session) {
+       }
+
        public Boolean isRemote() {
                return uri != null;
        }
@@ -475,15 +487,6 @@ public class JackrabbitContainer implements Repository, ResourceLoaderAware {
                return repository;
        }
 
-       protected synchronized void processNewSession(Session session) {
-               try {
-                       NamespaceHelper namespaceHelper = new NamespaceHelper(session);
-                       namespaceHelper.registerNamespaces(namespaces);
-               } catch (Exception e) {
-                       throw new ArgeoException("Cannot process new session", e);
-               }
-       }
-
        /**
         * Logs in to the default workspace, creates the required workspace, logs
         * out, logs in to the required workspace.
@@ -498,10 +501,6 @@ public class JackrabbitContainer implements Repository, ResourceLoaderAware {
                return getRepository().login(credentials, workspaceName);
        }
 
-       public void setResourceLoader(ResourceLoader resourceLoader) {
-               this.resourceLoader = resourceLoader;
-       }
-
        public boolean isStandardDescriptor(String key) {
                return getRepository().isStandardDescriptor(key);
        }
@@ -518,7 +517,10 @@ public class JackrabbitContainer implements Repository, ResourceLoaderAware {
                return getRepository().getDescriptorValues(key);
        }
 
-       // BEANS METHODS
+       /*
+        * FIELDS ACCESS
+        */
+
        public void setHomeDirectory(File homeDirectory) {
                this.homeDirectory = homeDirectory;
        }
@@ -563,5 +565,4 @@ public class JackrabbitContainer implements Repository, ResourceLoaderAware {
                        Set<JackrabbitDataModelMigration> dataModelMigrations) {
                this.dataModelMigrations = dataModelMigrations;
        }
-
 }
index 8e8b1d945ce22f2c7a3883d74cfc0f6673150b57..74b96e9edd45436c0a7cb4869d8fe36884319619 100644 (file)
@@ -42,6 +42,8 @@ public class SimpleSessionProvider implements SessionProvider, Serializable {
 
        private Boolean openSessionInView = true;
 
+       private String securityWorkspace = "security";
+
        public Session getSession(HttpServletRequest request, Repository rep,
                        String workspace) throws LoginException, ServletException,
                        RepositoryException {
@@ -84,6 +86,9 @@ public class SimpleSessionProvider implements SessionProvider, Serializable {
 
        protected void writeRemoteRoles(JackrabbitSession session)
                        throws RepositoryException {
+               if (!session.getWorkspace().getName().equals(securityWorkspace))
+                       return;
+
                // retrieve roles
                String userId = session.getUserID();
                UserManager userManager = session.getUserManager();
@@ -98,7 +103,8 @@ public class SimpleSessionProvider implements SessionProvider, Serializable {
                                userGroupIds.add(it.next().getID());
 
                // write roles if needed
-               Node userProfile = JcrUtils.getUserProfile(session);
+               Node userProfile = JcrUtils.getUserHome(session).getNode(
+                               ArgeoNames.ARGEO_PROFILE);
                boolean writeRoles = false;
                if (userProfile.hasProperty(ArgeoNames.ARGEO_REMOTE_ROLES)) {
                        Value[] roles = userProfile.getProperty(
index 7f060ef6845bf177a26c26f5f2898078cc75519b..9f19fef857460ae68edb652c10f2ca0f7e960f94 100644 (file)
@@ -16,6 +16,14 @@ public interface ArgeoNames {
 
        // user profile
        public final static String ARGEO_PROFILE = "argeo:profile";
+
+       // spring security
+       public final static String ARGEO_ENABLED = "argeo:enabled";
+       public final static String ARGEO_ACCOUNT_NON_EXPIRED = "argeo:accountNonExpired";
+       public final static String ARGEO_ACCOUNT_NON_LOCKED = "argeo:accountNonLocked";
+       public final static String ARGEO_CREDENTIALS_NON_EXPIRED = "argeo:credentialsNonExpired";
+
+       // personal details
        public final static String ARGEO_FIRST_NAME = "argeo:firstName";
        public final static String ARGEO_LAST_NAME = "argeo:lastName";
        public final static String ARGEO_PRIMARY_EMAIL = "argeo:primaryEmail";
index facd475cafd1e789c1689e7f88e317ff1ec1d749..66c6a9388f1f6c1df93e79de91beb6e5a117e832 100644 (file)
@@ -51,11 +51,6 @@ import javax.jcr.nodetype.NodeType;
 import javax.jcr.observation.EventListener;
 import javax.jcr.query.Query;
 import javax.jcr.query.QueryResult;
-import javax.jcr.query.qom.Constraint;
-import javax.jcr.query.qom.DynamicOperand;
-import javax.jcr.query.qom.QueryObjectModelFactory;
-import javax.jcr.query.qom.Selector;
-import javax.jcr.query.qom.StaticOperand;
 
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.logging.Log;
@@ -925,34 +920,30 @@ public class JcrUtils implements ArgeoJcrConstants {
                }
        }
 
-       /** Returns the home node of the session user or null if none was found. */
-       public static Node getUserHome(Session session) {
-               String userID = session.getUserID();
-               return getUserHome(session, userID);
-       }
-
-       /**
-        * Returns user home has path, embedding exceptions. Contrary to
-        * {@link #getUserHome(Session)}, it never returns null but throws and
-        * exception if not found.
-        */
-       public static String getUserHomePath(Session session) {
-               String userID = session.getUserID();
+       /** Removes a listener without throwing exception */
+       public static void removeListenerQuietly(Session session,
+                       EventListener listener) {
+               if (session == null || !session.isLive())
+                       return;
                try {
-                       Node userHome = getUserHome(session, userID);
-                       if (userHome != null)
-                               return userHome.getPath();
-                       else
-                               throw new ArgeoException("No home registered for " + userID);
+                       session.getWorkspace().getObservationManager()
+                                       .removeEventListener(listener);
                } catch (RepositoryException e) {
-                       throw new ArgeoException("Cannot find user home path", e);
+                       // silent
                }
        }
 
-       /** Get the profile of the user attached to this session. */
-       public static Node getUserProfile(Session session) {
+       /** Returns the home node of the session user or null if none was found. */
+       public static Node getUserHome(Session session) {
                String userID = session.getUserID();
-               return getUserProfile(session, userID);
+               return getUserHome(session, userID);
+       }
+
+       /** User home path is NOT configurable */
+       public static String getUserHomePath(String username) {
+               String homeBasePath = "/home";
+               return homeBasePath + '/' + firstCharsToPath(username, 2) + '/'
+                               + username;
        }
 
        /**
@@ -967,102 +958,190 @@ public class JcrUtils implements ArgeoJcrConstants {
         */
        public static Node getUserHome(Session session, String username) {
                try {
-                       QueryObjectModelFactory qomf = session.getWorkspace()
-                                       .getQueryManager().getQOMFactory();
-
-                       // query the user home for this user id
-                       Selector userHomeSel = qomf.selector(ArgeoTypes.ARGEO_USER_HOME,
-                                       "userHome");
-                       DynamicOperand userIdDop = qomf.propertyValue("userHome",
-                                       ArgeoNames.ARGEO_USER_ID);
-                       StaticOperand userIdSop = qomf.literal(session.getValueFactory()
-                                       .createValue(username));
-                       Constraint constraint = qomf.comparison(userIdDop,
-                                       QueryObjectModelFactory.JCR_OPERATOR_EQUAL_TO, userIdSop);
-                       Query query = qomf.createQuery(userHomeSel, constraint, null, null);
-                       Node userHome = JcrUtils.querySingleNode(query);
-                       return userHome;
+                       String homePath = getUserHomePath(username);
+                       return session.itemExists(homePath) ? session.getNode(homePath)
+                                       : null;
+                       // kept for example of QOM queries
+                       // QueryObjectModelFactory qomf = session.getWorkspace()
+                       // .getQueryManager().getQOMFactory();
+                       // Selector userHomeSel = qomf.selector(ArgeoTypes.ARGEO_USER_HOME,
+                       // "userHome");
+                       // DynamicOperand userIdDop = qomf.propertyValue("userHome",
+                       // ArgeoNames.ARGEO_USER_ID);
+                       // StaticOperand userIdSop = qomf.literal(session.getValueFactory()
+                       // .createValue(username));
+                       // Constraint constraint = qomf.comparison(userIdDop,
+                       // QueryObjectModelFactory.JCR_OPERATOR_EQUAL_TO, userIdSop);
+                       // Query query = qomf.createQuery(userHomeSel, constraint, null,
+                       // null);
+                       // Node userHome = JcrUtils.querySingleNode(query);
                } catch (RepositoryException e) {
                        throw new ArgeoException("Cannot find home for user " + username, e);
                }
        }
 
-       public static Node getUserProfile(Session session, String username) {
+       /**
+        * Creates an Argeo user home, does nothing if it already exists. Session is
+        * NOT saved.
+        */
+       public static Node createUserHomeIfNeeded(Session session, String username) {
+               try {
+                       String homePath = getUserHomePath(username);
+                       if (session.itemExists(homePath))
+                               return session.getNode(homePath);
+                       else {
+                               Node userHome = JcrUtils.mkdirs(session, homePath);
+                               userHome.addMixin(ArgeoTypes.ARGEO_USER_HOME);
+                               userHome.setProperty(ArgeoNames.ARGEO_USER_ID, username);
+                               return userHome;
+                       }
+               } catch (RepositoryException e) {
+                       discardQuietly(session);
+                       throw new ArgeoException("Cannot create home for " + username
+                                       + " in workspace " + session.getWorkspace().getName(), e);
+               }
+       }
+
+       /**
+        * Creates a user profile in the home of this user. Creates the home if
+        * needed, but throw an exception if a profile already exists. The session
+        * is not saved and the node is in a checkedOut state (that is, it requires
+        * a subsequent checkin after saving the session).
+        */
+       public static Node createUserProfile(Session session, String username) {
                try {
-                       QueryObjectModelFactory qomf = session.getWorkspace()
-                                       .getQueryManager().getQOMFactory();
-                       Selector sel = qomf.selector(ArgeoTypes.ARGEO_USER_PROFILE,
-                                       "userProfile");
-                       DynamicOperand userIdDop = qomf.propertyValue("userProfile",
-                                       ArgeoNames.ARGEO_USER_ID);
-                       StaticOperand userIdSop = qomf.literal(session.getValueFactory()
-                                       .createValue(username));
-                       Constraint constraint = qomf.comparison(userIdDop,
-                                       QueryObjectModelFactory.JCR_OPERATOR_EQUAL_TO, userIdSop);
-                       Query query = qomf.createQuery(sel, constraint, null, null);
-                       Node userProfile = JcrUtils.querySingleNode(query);
+                       Node userHome = createUserHomeIfNeeded(session, username);
+                       if (userHome.hasNode(ArgeoNames.ARGEO_PROFILE))
+                               throw new ArgeoException(
+                                               "There is already a user profile under " + userHome);
+                       Node userProfile = userHome.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);
                        return userProfile;
                } catch (RepositoryException e) {
-                       throw new ArgeoException(
-                                       "Cannot find profile for user " + username, e);
+                       discardQuietly(session);
+                       throw new ArgeoException("Cannot create home for " + username
+                                       + " in workspace " + session.getWorkspace().getName(), e);
                }
        }
 
        /** Creates an Argeo user home. */
-       public static Node createUserHome(Session session, String homeBasePath,
-                       String username) {
+       // public static Node createUserHome(Session session, String homeBasePath,
+       // String username) {
+       // try {
+       // if (session == null)
+       // throw new ArgeoException("Session is null");
+       // if (session.hasPendingChanges())
+       // throw new ArgeoException(
+       // "Session has pending changes, save them first");
+       //
+       // String homePath = getUserHomePath(username);
+       //
+       // if (session.itemExists(homePath)) {
+       // try {
+       // throw new ArgeoException(
+       // "Trying to create a user home that already exists");
+       // } catch (Exception e) {
+       // // we use this workaround to be sure to get the stack trace
+       // // to identify the sink of the bug.
+       // log.warn("trying to create an already existing userHome at path:"
+       // + homePath + ". Stack trace : ");
+       // e.printStackTrace();
+       // }
+       // }
+       //
+       // Node userHome = JcrUtils.mkdirs(session, homePath);
+       // Node userProfile;
+       // if (userHome.hasNode(ArgeoNames.ARGEO_PROFILE)) {
+       // log.warn("userProfile node already exists for userHome path: "
+       // + homePath + ". We do not add a new one");
+       // } else {
+       // userProfile = userHome.addNode(ArgeoNames.ARGEO_PROFILE);
+       // userProfile.addMixin(ArgeoTypes.ARGEO_USER_PROFILE);
+       // // session.getWorkspace().getVersionManager()
+       // // .checkout(userProfile.getPath());
+       // userProfile.setProperty(ArgeoNames.ARGEO_USER_ID, username);
+       // session.save();
+       // session.getWorkspace().getVersionManager()
+       // .checkin(userProfile.getPath());
+       // // we need to save the profile before adding the user home type
+       // }
+       // userHome.addMixin(ArgeoTypes.ARGEO_USER_HOME);
+       // // see
+       // //
+       // http://jackrabbit.510166.n4.nabble.com/Jackrabbit-2-0-beta-6-Problem-adding-a-Mixin-type-with-mandatory-properties-after-setting-propertiesn-td1290332.html
+       // userHome.setProperty(ArgeoNames.ARGEO_USER_ID, username);
+       // session.save();
+       // return userHome;
+       // } catch (RepositoryException e) {
+       // discardQuietly(session);
+       // throw new ArgeoException("Cannot create home node for user "
+       // + username, e);
+       // }
+       // }
+
+       /**
+        * Returns user home has path, embedding exceptions. Contrary to
+        * {@link #getUserHome(Session)}, it never returns null but throws and
+        * exception if not found.
+        * 
+        * @deprecated use getUserHome() instead, throwing an exception if it
+        *             returns null
+        */
+       @Deprecated
+       public static String getUserHomePath(Session session) {
+               String userID = session.getUserID();
                try {
-                       if (session == null)
-                               throw new ArgeoException("Session is null");
-                       if (session.hasPendingChanges())
-                               throw new ArgeoException(
-                                               "Session has pending changes, save them first");
-
-                       String homePath = homeBasePath + '/'
-                                       + firstCharsToPath(username, 2) + '/' + username;
-
-                       if (session.itemExists(homePath)) {
-                               try {
-                                       throw new ArgeoException(
-                                                       "Trying to create a user home that already exists");
-                               } catch (Exception e) {
-                                       // we use this workaround to be sure to get the stack trace
-                                       // to identify the sink of the bug.
-                                       log.warn("trying to create an already existing userHome at path:"
-                                                       + homePath + ". Stack trace : ");
-                                       e.printStackTrace();
-                               }
-                       }
+                       String homePath = getUserHomePath(userID);
+                       if (session.itemExists(homePath))
+                               return homePath;
+                       else
+                               throw new ArgeoException("No home registered for " + userID);
+               } catch (RepositoryException e) {
+                       throw new ArgeoException("Cannot find user home path", e);
+               }
+       }
 
-                       Node userHome = JcrUtils.mkdirs(session, homePath);
-                       Node userProfile;
-                       if (userHome.hasNode(ArgeoNames.ARGEO_PROFILE)) {
-                               log.warn("userProfile node already exists for userHome path: "
-                                               + homePath + ". We do not add a new one");
-                       } else {
-                               userProfile = userHome.addNode(ArgeoNames.ARGEO_PROFILE);
-                               userProfile.addMixin(ArgeoTypes.ARGEO_USER_PROFILE);
-                               // session.getWorkspace().getVersionManager()
-                               // .checkout(userProfile.getPath());
-                               userProfile.setProperty(ArgeoNames.ARGEO_USER_ID, username);
-                               session.save();
-                               session.getWorkspace().getVersionManager()
-                                               .checkin(userProfile.getPath());
-                               // we need to save the profile before adding the user home type
-                       }
-                       userHome.addMixin(ArgeoTypes.ARGEO_USER_HOME);
-                       // see
-                       // http://jackrabbit.510166.n4.nabble.com/Jackrabbit-2-0-beta-6-Problem-adding-a-Mixin-type-with-mandatory-properties-after-setting-propertiesn-td1290332.html
-                       userHome.setProperty(ArgeoNames.ARGEO_USER_ID, username);
-                       session.save();
-                       return userHome;
+       /**
+        * @return null if not found *
+        * @deprecated will soon be removed. Call instead
+        *             getUserHome().getNode(ARGEO_PROFILE) on the security
+        *             workspace.
+        */
+       @Deprecated
+       public static Node getUserProfile(Session session, String username) {
+               try {
+                       Node userHome = getUserHome(session, username);
+                       if (userHome == null)
+                               return null;
+                       if (userHome.hasNode(ArgeoNames.ARGEO_PROFILE))
+                               return userHome.getNode(ArgeoNames.ARGEO_PROFILE);
+                       else
+                               return null;
                } catch (RepositoryException e) {
-                       discardQuietly(session);
-                       throw new ArgeoException("Cannot create home node for user "
-                                       + username, e);
+                       throw new ArgeoException(
+                                       "Cannot find profile for user " + username, e);
                }
        }
 
+       /**
+        * Get the profile of the user attached to this session.
+        * 
+        * @deprecated will soon be removed. Call instead
+        *             getUserHome().getNode(ARGEO_PROFILE) on the security
+        *             workspace.
+        */
+       @Deprecated
+       public static Node getUserProfile(Session session) {
+               String userID = session.getUserID();
+               return getUserProfile(session, userID);
+       }
+
        /**
         * Quietly unregisters an {@link EventListener} from the udnerlying
         * workspace of this node.
index 7383b39ad301e20f2ab5488747228c67d6b9ccf1..2a323f8b7ad1d9c35189e4c0a393960b7f7ecdbb 100644 (file)
@@ -1,15 +1,11 @@
 package org.argeo.jcr.security;
 
 import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
-import java.io.OutputStream;
-import java.security.AlgorithmParameters;
 import java.security.SecureRandom;
 
 import javax.crypto.Cipher;
 import javax.crypto.CipherInputStream;
-import javax.crypto.CipherOutputStream;
 import javax.crypto.SecretKey;
 import javax.crypto.spec.IvParameterSpec;
 import javax.jcr.Binary;
index dbf9a927fbdca5750049c9df7db971beb5ba652d..2bd38ca5a6afa7d51c2a7547e4b45b29fcb3f14f 100644 (file)
@@ -21,6 +21,10 @@ mixin
 [argeo:userProfile] > mix:created, mix:lastModified, mix:title, mix:versionable
 mixin
 - argeo:userID (STRING) m
+- argeo:enabled (BOOLEAN)
+- argeo:accountNonExpired (BOOLEAN)
+- argeo:accountNonLocked (BOOLEAN)
+- argeo:credentialsNonExpired (BOOLEAN)
 - argeo:remoteRoles (STRING) *
 
 [argeo:preferenceNode] > mix:lastModified, mix:versionable