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,\
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
+++ /dev/null
-<?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
+++ /dev/null
-<?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
--- /dev/null
+<?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
--- /dev/null
+<?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
+++ /dev/null
-<?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
+++ /dev/null
-<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>
+++ /dev/null
-<?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
+++ /dev/null
-<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>
--- /dev/null
+<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>
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<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>
-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
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
\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
</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
argeo.security.systemKey=argeo
-argeo.node.repo.alias=node
+argeo.node.repo.securityWorkspace=security
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;
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;
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 {
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
bin.includes = plugin.xml,\
- META-INF/
+ META-INF/,\
+ security-admin.properties
source.. = src/main/java/
output.. = target/classes/
--- /dev/null
+argeo.node.repo.securityWorkspace=security
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());
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",
// 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
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");
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;
/** 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;
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) {
+ username, e1);
}
}
- Error.show("Cannot create new user " + username, e);
+ ErrorFeedback.show("Cannot create new user " + username, e);
return false;
}
}
--- /dev/null
+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;
+ }
+}
import java.security.Principal;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
import java.util.Set;
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>(
}
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)
}
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)
}
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 "";
}
+++ /dev/null
-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;
- }
-
-}
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}).
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";
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")
+++ /dev/null
-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;
- }
-}
+++ /dev/null
-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();
- }
-
-}
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 */
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;
+ }
}
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;
- }
-
}
--- /dev/null
+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;
+ }
+
+}
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);
}
+++ /dev/null
-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;
- }
-
-}
/** 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
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;
}
+++ /dev/null
-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);
- }
- }
-
-}
<?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>
--- /dev/null
+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);
+ }
+ }
+
+ }
+}
+++ /dev/null
-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;
- }
-
-}
</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">
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
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
</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
-bin.includes = META-INF/
+bin.includes = META-INF/,\
+ noderepo.properties,\
+ repository-h2.xml,\
+ repository-postgresql.xml
# 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
## 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
<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>
<!-- 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" />
<!-- 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" />
<?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>
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();
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;
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;
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;
* 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>();
private Executor systemExecutor;
- // remote
- private String uri = null;
- private Credentials remoteSystemCredentials = null;
-
/**
* Empty constructor, {@link #init()} should be called after properties have
* been set
init();
}
+ /** Initializes */
public void init() {
if (repository != null) {
// we are just wrapping another repository
- importNodeTypeDefinitions();
+ prepareDataModel();
return;
}
// 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,
+ " 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;
+ 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();
}
}
}
}
- 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;
}
/**
- * 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);
}
}
action.run();
}
- // JCR REPOSITORY (delegated)
+ /*
+ * DELEGATED JCR REPOSITORY METHODS
+ */
+
public String getDescriptor(String key) {
return getRepository().getDescriptor(key);
}
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;
}
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.
return getRepository().login(credentials, workspaceName);
}
- public void setResourceLoader(ResourceLoader resourceLoader) {
- this.resourceLoader = resourceLoader;
- }
-
public boolean isStandardDescriptor(String key) {
return getRepository().isStandardDescriptor(key);
}
return getRepository().getDescriptorValues(key);
}
- // BEANS METHODS
+ /*
+ * FIELDS ACCESS
+ */
+
public void setHomeDirectory(File homeDirectory) {
this.homeDirectory = homeDirectory;
}
Set<JackrabbitDataModelMigration> dataModelMigrations) {
this.dataModelMigrations = dataModelMigrations;
}
-
}
private Boolean openSessionInView = true;
+ private String securityWorkspace = "security";
+
public Session getSession(HttpServletRequest request, Repository rep,
String workspace) throws LoginException, ServletException,
RepositoryException {
protected void writeRemoteRoles(JackrabbitSession session)
throws RepositoryException {
+ if (!session.getWorkspace().getName().equals(securityWorkspace))
+ return;
+
// retrieve roles
String userId = session.getUserID();
UserManager userManager = session.getUserManager();
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(
// 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";
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;
}
}
- /** 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;
}
/**
*/
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.
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;
[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