1 package org
.argeo
.security
.ldap
.jcr
;
3 import java
.util
.Arrays
;
4 import java
.util
.HashMap
;
6 import java
.util
.concurrent
.Executor
;
9 import javax
.jcr
.Repository
;
10 import javax
.jcr
.RepositoryException
;
11 import javax
.jcr
.RepositoryFactory
;
12 import javax
.jcr
.Session
;
14 import org
.apache
.commons
.logging
.Log
;
15 import org
.apache
.commons
.logging
.LogFactory
;
16 import org
.argeo
.ArgeoException
;
17 import org
.argeo
.jcr
.ArgeoJcrConstants
;
18 import org
.argeo
.jcr
.ArgeoNames
;
19 import org
.argeo
.jcr
.ArgeoTypes
;
20 import org
.argeo
.jcr
.JcrUtils
;
21 import org
.argeo
.security
.jcr
.JcrUserDetails
;
22 import org
.springframework
.ldap
.core
.DirContextAdapter
;
23 import org
.springframework
.ldap
.core
.DirContextOperations
;
24 import org
.springframework
.security
.GrantedAuthority
;
25 import org
.springframework
.security
.userdetails
.UserDetails
;
26 import org
.springframework
.security
.userdetails
.ldap
.UserDetailsContextMapper
;
29 * Maps LDAP attributes and JCR properties. This class is meant to be robust,
30 * checks of which values should be mandatory should be performed at a higher
33 public class JcrUserDetailsContextMapper
implements UserDetailsContextMapper
,
35 private final static Log log
= LogFactory
36 .getLog(JcrUserDetailsContextMapper
.class);
38 private String usernameAttribute
;
39 private String passwordAttribute
;
40 private String homeBasePath
;
41 private String
[] userClasses
;
43 private Map
<String
, String
> propertyToAttributes
= new HashMap
<String
, String
>();
44 private Executor systemExecutor
;
45 private RepositoryFactory repositoryFactory
;
47 public UserDetails
mapUserFromContext(final DirContextOperations ctx
,
48 final String username
, GrantedAuthority
[] authorities
) {
49 if (repositoryFactory
== null)
50 throw new ArgeoException("No JCR repository factory registered");
51 final StringBuffer userHomePathT
= new StringBuffer("");
52 systemExecutor
.execute(new Runnable() {
54 String userHomepath
= mapLdapToJcr(username
, ctx
);
55 userHomePathT
.append(userHomepath
);
60 byte[] arr
= (byte[]) ctx
61 .getAttributeSortedStringSet(passwordAttribute
).first();
62 JcrUserDetails userDetails
= new JcrUserDetails(
63 userHomePathT
.toString(), username
, new String(arr
), true,
64 true, true, true, authorities
);
66 Arrays
.fill(arr
, (byte) 0);
70 /** @return path to the user home node */
71 protected String
mapLdapToJcr(String username
, DirContextOperations ctx
) {
72 Session session
= null;
74 Repository nodeRepo
= JcrUtils
.getRepositoryByAlias(
75 repositoryFactory
, ArgeoJcrConstants
.ALIAS_NODE
);
76 session
= nodeRepo
.login();
77 Node userHome
= JcrUtils
.getUserHome(session
, username
);
79 userHome
= createUserHome(session
, username
);
80 String userHomePath
= userHome
.getPath();
81 Node userProfile
= userHome
.hasNode(ARGEO_USER_PROFILE
) ? userHome
82 .getNode(ARGEO_USER_PROFILE
) : userHome
83 .addNode(ARGEO_USER_PROFILE
);
84 for (String jcrProperty
: propertyToAttributes
.keySet())
85 ldapToJcr(userProfile
, jcrProperty
, ctx
);
87 if (log
.isDebugEnabled())
88 log
.debug("Mapped " + ctx
.getDn() + " to " + userProfile
);
90 } catch (RepositoryException e
) {
91 JcrUtils
.discardQuietly(session
);
92 throw new ArgeoException("Cannot synchronize JCR and LDAP", e
);
98 protected Node
createUserHome(Session session
, String username
) {
100 Node userHome
= JcrUtils
.mkdirs(session
,
101 usernameToHomePath(username
));
102 userHome
.addMixin(ArgeoTypes
.ARGEO_USER_HOME
);
103 userHome
.setProperty(ARGEO_USER_ID
, username
);
105 } catch (RepositoryException e
) {
106 throw new ArgeoException("Cannot create home node for user "
111 protected String
usernameToHomePath(String username
) {
112 return homeBasePath
+ '/' + JcrUtils
.firstCharsToPath(username
, 2)
116 public void mapUserToContext(UserDetails user
, final DirContextAdapter ctx
) {
117 if (!(user
instanceof JcrUserDetails
))
118 throw new ArgeoException("Unsupported user details: "
121 ctx
.setAttributeValues("objectClass", userClasses
);
122 ctx
.setAttributeValue(usernameAttribute
, user
.getUsername());
123 ctx
.setAttributeValue(passwordAttribute
, user
.getPassword());
125 final JcrUserDetails jcrUserDetails
= (JcrUserDetails
) user
;
126 systemExecutor
.execute(new Runnable() {
128 Session session
= null;
130 Repository nodeRepo
= JcrUtils
.getRepositoryByAlias(
131 repositoryFactory
, ArgeoJcrConstants
.ALIAS_NODE
);
132 session
= nodeRepo
.login();
133 Node userProfile
= session
.getNode(jcrUserDetails
134 .getHomePath() + '/' + ARGEO_USER_PROFILE
);
135 for (String jcrProperty
: propertyToAttributes
.keySet())
136 jcrToLdap(userProfile
, jcrProperty
, ctx
);
137 if (log
.isDebugEnabled())
138 log
.debug("Mapped " + userProfile
+ " to "
140 } catch (RepositoryException e
) {
141 throw new ArgeoException("Cannot synchronize JCR and LDAP",
150 protected void ldapToJcr(Node userProfile
, String jcrProperty
,
151 DirContextOperations ctx
) {
153 String ldapAttribute
;
154 if (propertyToAttributes
.containsKey(jcrProperty
))
155 ldapAttribute
= propertyToAttributes
.get(jcrProperty
);
157 throw new ArgeoException(
158 "No LDAP attribute mapped for JCR proprty "
161 String value
= ctx
.getStringAttribute(ldapAttribute
);
164 userProfile
.setProperty(jcrProperty
, value
);
165 } catch (Exception e
) {
166 throw new ArgeoException("Cannot map JCR property " + jcrProperty
171 protected void jcrToLdap(Node userProfile
, String jcrProperty
,
172 DirContextOperations ctx
) {
174 if (!userProfile
.hasProperty(jcrProperty
))
176 String value
= userProfile
.getProperty(jcrProperty
).getString();
178 String ldapAttribute
;
179 if (propertyToAttributes
.containsKey(jcrProperty
))
180 ldapAttribute
= propertyToAttributes
.get(jcrProperty
);
182 throw new ArgeoException(
183 "No LDAP attribute mapped for JCR proprty "
185 ctx
.setAttributeValue(ldapAttribute
, value
);
186 } catch (Exception e
) {
187 throw new ArgeoException("Cannot map JCR property " + jcrProperty
192 public void setPropertyToAttributes(Map
<String
, String
> propertyToAttributes
) {
193 this.propertyToAttributes
= propertyToAttributes
;
196 public void setSystemExecutor(Executor systemExecutor
) {
197 this.systemExecutor
= systemExecutor
;
200 public void setHomeBasePath(String homeBasePath
) {
201 this.homeBasePath
= homeBasePath
;
204 public void register(RepositoryFactory repositoryFactory
,
205 Map
<String
, String
> parameters
) {
206 this.repositoryFactory
= repositoryFactory
;
209 public void unregister(RepositoryFactory repositoryFactory
,
210 Map
<String
, String
> parameters
) {
211 this.repositoryFactory
= null;
214 public void setUsernameAttribute(String usernameAttribute
) {
215 this.usernameAttribute
= usernameAttribute
;
218 public void setPasswordAttribute(String passwordAttribute
) {
219 this.passwordAttribute
= passwordAttribute
;
222 public void setUserClasses(String
[] userClasses
) {
223 this.userClasses
= userClasses
;