1 package org
.argeo
.cms
.internal
.useradmin
.jackrabbit
;
3 import java
.util
.ArrayList
;
4 import java
.util
.Arrays
;
5 import java
.util
.Iterator
;
6 import java
.util
.LinkedHashSet
;
10 import javax
.jcr
.Node
;
11 import javax
.jcr
.Repository
;
12 import javax
.jcr
.RepositoryException
;
13 import javax
.jcr
.Session
;
14 import javax
.jcr
.SimpleCredentials
;
15 import javax
.jcr
.version
.VersionManager
;
17 import org
.apache
.jackrabbit
.api
.JackrabbitSession
;
18 import org
.apache
.jackrabbit
.api
.security
.user
.Authorizable
;
19 import org
.apache
.jackrabbit
.api
.security
.user
.Group
;
20 import org
.apache
.jackrabbit
.api
.security
.user
.User
;
21 import org
.apache
.jackrabbit
.api
.security
.user
.UserManager
;
22 import org
.apache
.jackrabbit
.core
.security
.authentication
.CryptedSimpleCredentials
;
23 import org
.apache
.jackrabbit
.core
.security
.user
.UserAccessControlProvider
;
24 import org
.argeo
.ArgeoException
;
25 import org
.argeo
.cms
.KernelHeader
;
26 import org
.argeo
.cms
.internal
.auth
.GrantedAuthorityPrincipal
;
27 import org
.argeo
.cms
.internal
.auth
.JcrSecurityModel
;
28 import org
.argeo
.jcr
.JcrUtils
;
29 import org
.argeo
.jcr
.UserJcrUtils
;
30 import org
.argeo
.security
.NodeAuthenticationToken
;
31 import org
.argeo
.security
.SecurityUtils
;
32 import org
.argeo
.security
.UserAdminService
;
33 import org
.argeo
.security
.jcr
.JcrUserDetails
;
34 import org
.argeo
.security
.jcr
.NewUserDetails
;
35 import org
.springframework
.dao
.DataAccessException
;
36 import org
.springframework
.security
.authentication
.AuthenticationProvider
;
37 import org
.springframework
.security
.authentication
.BadCredentialsException
;
38 import org
.springframework
.security
.authentication
.UsernamePasswordAuthenticationToken
;
39 import org
.springframework
.security
.core
.Authentication
;
40 import org
.springframework
.security
.core
.AuthenticationException
;
41 import org
.springframework
.security
.core
.GrantedAuthority
;
42 import org
.springframework
.security
.core
.context
.SecurityContextHolder
;
43 import org
.springframework
.security
.core
.userdetails
.UserDetails
;
44 import org
.springframework
.security
.core
.userdetails
.UsernameNotFoundException
;
47 * An implementation of {@link UserAdminService} which closely wraps Jackrabbits
48 * implementation. Roles are implemented with Groups.
50 public class JackrabbitUserAdminService
implements UserAdminService
,
51 AuthenticationProvider
{
52 private final static String JACKR_ADMINISTRATORS
= "administrators";
53 private final static String REP_PRINCIPAL_NAME
= "rep:principalName";
55 private Repository repository
;
56 private JcrSecurityModel securityModel
;
58 private JackrabbitSession adminSession
= null;
60 private String superUserInitialPassword
= "demo";
62 public void init() throws RepositoryException
{
63 Authentication authentication
= SecurityContextHolder
.getContext()
65 authentication
.getName();
66 adminSession
= (JackrabbitSession
) repository
.login();
67 Authorizable adminGroup
= getUserManager().getAuthorizable(
68 KernelHeader
.ROLE_ADMIN
);
69 if (adminGroup
== null) {
70 adminGroup
= getUserManager().createGroup(KernelHeader
.ROLE_ADMIN
);
73 Authorizable superUser
= getUserManager().getAuthorizable(
74 KernelHeader
.USERNAME_ADMIN
);
75 if (superUser
== null) {
76 superUser
= getUserManager().createUser(
77 KernelHeader
.USERNAME_ADMIN
, superUserInitialPassword
);
78 ((Group
) adminGroup
).addMember(superUser
);
79 securityModel
.sync(adminSession
, KernelHeader
.USERNAME_ADMIN
, null);
82 securityModel
.init(adminSession
);
85 public void destroy() throws RepositoryException
{
86 JcrUtils
.logoutQuietly(adminSession
);
89 private UserManager
getUserManager() throws RepositoryException
{
90 return adminSession
.getUserManager();
94 public void createUser(UserDetails user
) {
96 // if (getUserManager().getAuthorizable(user.getUsername()) == null)
98 getUserManager().createUser(user
.getUsername(), user
.getPassword());
99 Node userProfile
= securityModel
.sync(adminSession
,
100 user
.getUsername(), null);
101 if (user
instanceof NewUserDetails
)
102 ((NewUserDetails
) user
).mapToProfileNode(userProfile
);
103 userProfile
.getSession().save();
106 VersionManager versionManager
= userProfile
.getSession()
107 .getWorkspace().getVersionManager();
108 if (versionManager
.isCheckedOut(userProfile
.getPath()))
109 versionManager
.checkin(userProfile
.getPath());
112 } catch (RepositoryException e
) {
113 throw new ArgeoException("Cannot create user " + user
, e
);
118 public void updateUser(UserDetails userDetails
) {
120 User user
= (User
) getUserManager().getAuthorizable(
121 userDetails
.getUsername());
123 throw new ArgeoException("No user " + userDetails
.getUsername());
126 String newPassword
= userDetails
.getPassword();
127 if (!newPassword
.trim().equals("")) {
128 SimpleCredentials sp
= new SimpleCredentials(
129 userDetails
.getUsername(), newPassword
.toCharArray());
130 CryptedSimpleCredentials credentials
= (CryptedSimpleCredentials
) user
132 if (!credentials
.matches(sp
))
133 user
.changePassword(new String(newPassword
));
136 List
<String
> roles
= new ArrayList
<String
>();
137 for (GrantedAuthority ga
: userDetails
.getAuthorities()) {
138 if (ga
.getAuthority().equals(KernelHeader
.ROLE_USER
))
140 roles
.add(ga
.getAuthority());
143 groups
: for (Iterator
<Group
> it
= user
.memberOf(); it
.hasNext();) {
144 Group group
= it
.next();
145 String groupName
= group
.getPrincipal().getName();
146 String role
= groupNameToRole(groupName
);
150 if (roles
.contains(role
))
153 group
.removeMember(user
);
154 if (role
.equals(KernelHeader
.ROLE_ADMIN
)) {
155 Group administratorsGroup
= ((Group
) getUserManager()
156 .getAuthorizable(JACKR_ADMINISTRATORS
));
157 if (administratorsGroup
.isDeclaredMember(user
))
158 administratorsGroup
.removeMember(user
);
163 // remaining (new memberships)
164 for (String role
: roles
) {
165 String groupName
= roleToGroupName(role
);
166 Group group
= (Group
) getUserManager().getAuthorizable(
169 throw new ArgeoException("Group " + role
171 + " whereas it was granted to user " + userDetails
);
172 group
.addMember(user
);
174 // add to Jackrabbit administrators
175 if (role
.equals(KernelHeader
.ROLE_ADMIN
)) {
176 Group administratorsGroup
= (Group
) getUserManager()
177 .getAuthorizable(JACKR_ADMINISTRATORS
);
178 administratorsGroup
.addMember(user
);
182 } catch (Exception e
) {
183 throw new ArgeoException("Cannot update user details", e
);
189 public void deleteUser(String username
) {
191 getUserManager().getAuthorizable(username
).remove();
192 } catch (RepositoryException e
) {
193 throw new ArgeoException("Cannot remove user " + username
, e
);
198 public void changePassword(String oldPassword
, String newPassword
) {
199 Authentication authentication
= SecurityContextHolder
.getContext()
200 .getAuthentication();
201 String username
= authentication
.getName();
203 SimpleCredentials sp
= new SimpleCredentials(username
,
204 oldPassword
.toCharArray());
205 User user
= (User
) getUserManager().getAuthorizable(username
);
206 CryptedSimpleCredentials credentials
= (CryptedSimpleCredentials
) user
208 if (credentials
.matches(sp
))
209 user
.changePassword(newPassword
);
211 throw new BadCredentialsException("Bad credentials provided");
212 } catch (Exception e
) {
213 throw new ArgeoException("Cannot change password for user "
219 public boolean userExists(String username
) {
221 Authorizable authorizable
= getUserManager().getAuthorizable(
223 if (authorizable
!= null && authorizable
instanceof User
)
226 } catch (RepositoryException e
) {
227 throw new ArgeoException("Cannot check whether user " + username
233 public Set
<String
> listUsers() {
234 LinkedHashSet
<String
> res
= new LinkedHashSet
<String
>();
236 Iterator
<Authorizable
> users
= getUserManager().findAuthorizables(
237 "rep:principalName", null, UserManager
.SEARCH_TYPE_USER
);
238 while (users
.hasNext()) {
239 res
.add(users
.next().getPrincipal().getName());
242 } catch (RepositoryException e
) {
243 throw new ArgeoException("Cannot list users", e
);
248 public Set
<String
> listUsersInRole(String role
) {
249 LinkedHashSet
<String
> res
= new LinkedHashSet
<String
>();
251 Group group
= (Group
) getUserManager().getAuthorizable(role
);
252 Iterator
<Authorizable
> users
= group
.getMembers();
254 while (users
.hasNext()) {
255 res
.add(users
.next().getPrincipal().getName());
258 } catch (RepositoryException e
) {
259 throw new ArgeoException("Cannot list users in role " + role
, e
);
264 public void synchronize() {
268 public void newRole(String role
) {
270 getUserManager().createGroup(role
);
271 } catch (RepositoryException e
) {
272 throw new ArgeoException("Cannot create role " + role
, e
);
277 public Set
<String
> listEditableRoles() {
278 LinkedHashSet
<String
> res
= new LinkedHashSet
<String
>();
280 Iterator
<Authorizable
> groups
= getUserManager().findAuthorizables(
281 REP_PRINCIPAL_NAME
, null, UserManager
.SEARCH_TYPE_GROUP
);
282 while (groups
.hasNext()) {
283 Group group
= (Group
) groups
.next();
284 String groupName
= group
.getPrincipal().getName();
285 String role
= groupNameToRole(groupName
);
287 && !role
.equals(KernelHeader
.ROLE_GROUP_ADMIN
)
288 && !(role
.equals(KernelHeader
.ROLE_ADMIN
) && !SecurityUtils
289 .hasCurrentThreadAuthority(KernelHeader
.ROLE_ADMIN
)))
293 } catch (RepositoryException e
) {
294 throw new ArgeoException("Cannot list groups", e
);
299 public void deleteRole(String role
) {
301 getUserManager().getAuthorizable(role
).remove();
302 } catch (RepositoryException e
) {
303 throw new ArgeoException("Cannot remove role " + role
, e
);
307 protected String
roleToGroupName(String role
) {
309 if (role
.equals(KernelHeader
.ROLE_USER_ADMIN
))
310 groupName
= UserAccessControlProvider
.USER_ADMIN_GROUP_NAME
;
311 else if (role
.equals(KernelHeader
.ROLE_GROUP_ADMIN
))
312 groupName
= UserAccessControlProvider
.GROUP_ADMIN_GROUP_NAME
;
318 protected String
groupNameToRole(String groupName
) {
320 if (groupName
.equals(UserAccessControlProvider
.USER_ADMIN_GROUP_NAME
)) {
321 role
= KernelHeader
.ROLE_USER_ADMIN
;
323 .equals(UserAccessControlProvider
.GROUP_ADMIN_GROUP_NAME
)) {
324 role
= KernelHeader
.ROLE_GROUP_ADMIN
;
325 } else if (groupName
.equals(JACKR_ADMINISTRATORS
)) {
334 public UserDetails
loadUserByUsername(String username
)
335 throws UsernameNotFoundException
, DataAccessException
{
337 User user
= (User
) getUserManager().getAuthorizable(username
);
339 throw new UsernameNotFoundException("User " + username
340 + " cannot be found");
341 return loadJcrUserDetails(adminSession
, username
);
342 } catch (RepositoryException e
) {
343 throw new ArgeoException("Cannot load user " + username
, e
);
347 protected JcrUserDetails
loadJcrUserDetails(Session session
, String username
)
348 throws RepositoryException
{
349 if (username
== null)
350 username
= session
.getUserID();
351 User user
= (User
) getUserManager().getAuthorizable(username
);
353 ArrayList
<GrantedAuthorityPrincipal
> authorities
= new ArrayList
<GrantedAuthorityPrincipal
>();
354 authorities
.add(new GrantedAuthorityPrincipal(KernelHeader
.ROLE_USER
));
356 Group adminGroup
= (Group
) getUserManager().getAuthorizable(
357 KernelHeader
.ROLE_ADMIN
);
359 Iterator
<?
extends Authorizable
> groups
;
360 if (username
.equals(KernelHeader
.USERNAME_ADMIN
)
361 || adminGroup
.isDeclaredMember(user
)) {
362 groups
= getUserManager().findAuthorizables(REP_PRINCIPAL_NAME
,
363 null, UserManager
.SEARCH_TYPE_GROUP
);
365 groups
= user
.declaredMemberOf();
368 while (groups
.hasNext()) {
369 Authorizable group
= groups
.next();
370 String groupName
= group
.getPrincipal().getName();
371 String role
= groupNameToRole(groupName
);
373 authorities
.add(new GrantedAuthorityPrincipal(role
));
376 Node userProfile
= UserJcrUtils
.getUserProfile(session
, username
);
377 JcrUserDetails userDetails
= new JcrUserDetails(userProfile
, "",
382 // AUTHENTICATION PROVIDER
383 public synchronized Authentication
authenticate(
384 Authentication authentication
) throws AuthenticationException
{
385 NodeAuthenticationToken siteAuth
= (NodeAuthenticationToken
) authentication
;
386 String username
= siteAuth
.getName();
387 if (!(siteAuth
.getCredentials() instanceof char[]))
388 throw new ArgeoException("Only char array passwords are supported");
389 char[] password
= (char[]) siteAuth
.getCredentials();
391 SimpleCredentials sp
= new SimpleCredentials(siteAuth
.getName(),
393 User user
= (User
) getUserManager().getAuthorizable(username
);
395 throw new BadCredentialsException("Bad credentials");
396 CryptedSimpleCredentials credentials
= (CryptedSimpleCredentials
) user
398 // String providedPassword = siteAuth.getCredentials().toString();
399 if (!credentials
.matches(sp
))
400 throw new BadCredentialsException("Bad credentials");
402 // session = repository.login(sp, null);
404 Node userProfile
= UserJcrUtils
.getUserProfile(adminSession
,
406 JcrUserDetails
.checkAccountStatus(userProfile
);
407 } catch (BadCredentialsException e
) {
409 } catch (Exception e
) {
410 throw new BadCredentialsException(
411 "Cannot authenticate " + siteAuth
, e
);
413 Arrays
.fill(password
, '*');
417 JcrUserDetails userDetails
= loadJcrUserDetails(adminSession
,
419 NodeAuthenticationToken authenticated
= new NodeAuthenticationToken(
420 siteAuth
, userDetails
.getAuthorities());
421 authenticated
.setDetails(userDetails
);
422 return authenticated
;
423 } catch (RepositoryException e
) {
424 throw new ArgeoException(
425 "Unexpected exception when authenticating " + siteAuth
, e
);
429 @SuppressWarnings("rawtypes")
430 public boolean supports(Class authentication
) {
431 return UsernamePasswordAuthenticationToken
.class
432 .isAssignableFrom(authentication
);
435 public void setRepository(Repository repository
) {
436 this.repository
= repository
;
439 public void setSecurityModel(JcrSecurityModel securityModel
) {
440 this.securityModel
= securityModel
;