1 package org
.argeo
.security
.jcr
.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
;
16 import org
.apache
.jackrabbit
.api
.JackrabbitSession
;
17 import org
.apache
.jackrabbit
.api
.security
.user
.Authorizable
;
18 import org
.apache
.jackrabbit
.api
.security
.user
.Group
;
19 import org
.apache
.jackrabbit
.api
.security
.user
.User
;
20 import org
.apache
.jackrabbit
.api
.security
.user
.UserManager
;
21 import org
.apache
.jackrabbit
.core
.security
.authentication
.CryptedSimpleCredentials
;
22 import org
.argeo
.ArgeoException
;
23 import org
.argeo
.jcr
.JcrUtils
;
24 import org
.argeo
.jcr
.UserJcrUtils
;
25 import org
.argeo
.security
.NodeAuthenticationToken
;
26 import org
.argeo
.security
.UserAdminService
;
27 import org
.argeo
.security
.jcr
.JcrSecurityModel
;
28 import org
.argeo
.security
.jcr
.JcrUserDetails
;
29 import org
.springframework
.dao
.DataAccessException
;
30 import org
.springframework
.security
.authentication
.AuthenticationProvider
;
31 import org
.springframework
.security
.authentication
.BadCredentialsException
;
32 import org
.springframework
.security
.authentication
.UsernamePasswordAuthenticationToken
;
33 import org
.springframework
.security
.core
.Authentication
;
34 import org
.springframework
.security
.core
.AuthenticationException
;
35 import org
.springframework
.security
.core
.GrantedAuthority
;
36 import org
.springframework
.security
.core
.authority
.SimpleGrantedAuthority
;
37 import org
.springframework
.security
.core
.context
.SecurityContextHolder
;
38 import org
.springframework
.security
.core
.userdetails
.UserDetails
;
39 import org
.springframework
.security
.core
.userdetails
.UsernameNotFoundException
;
42 * An implementation of {@link UserAdminService} which closely wraps Jackrabbits
43 * implementation. Roles are implemented with Groups.
45 public class JackrabbitUserAdminService
implements UserAdminService
,
46 AuthenticationProvider
{
47 final static String userRole
= "ROLE_USER";
48 final static String adminRole
= "ROLE_ADMIN";
50 private Repository repository
;
51 private JcrSecurityModel securityModel
;
53 private JackrabbitSession adminSession
= null;
55 private String superUsername
= "root";
56 private String superUserInitialPassword
= "demo";
58 public void init() throws RepositoryException
{
59 Authentication authentication
= SecurityContextHolder
.getContext()
61 authentication
.getName();
62 adminSession
= (JackrabbitSession
) repository
.login();
63 Authorizable adminGroup
= getUserManager().getAuthorizable(adminRole
);
64 if (adminGroup
== null) {
65 adminGroup
= getUserManager().createGroup(adminRole
);
68 Authorizable superUser
= getUserManager()
69 .getAuthorizable(superUsername
);
70 if (superUser
== null) {
71 superUser
= getUserManager().createUser(superUsername
,
72 superUserInitialPassword
);
73 ((Group
) adminGroup
).addMember(superUser
);
74 securityModel
.sync(adminSession
, superUsername
, null);
79 public void destroy() throws RepositoryException
{
80 JcrUtils
.logoutQuietly(adminSession
);
83 private UserManager
getUserManager() throws RepositoryException
{
84 return adminSession
.getUserManager();
88 public void createUser(UserDetails user
) {
90 // FIXME workaround for issue in new user wizard where
91 // security model is hardcoded and it already exists
92 if (getUserManager().getAuthorizable(user
.getUsername()) == null) {
93 getUserManager().createUser(user
.getUsername(),
95 securityModel
.sync(adminSession
, user
.getUsername(), null);
98 } catch (RepositoryException e
) {
99 throw new ArgeoException("Cannot create user " + user
, e
);
104 public void updateUser(UserDetails userDetails
) {
106 User user
= (User
) getUserManager().getAuthorizable(
107 userDetails
.getUsername());
109 throw new ArgeoException("No user " + userDetails
.getUsername());
112 String newPassword
= userDetails
.getPassword();
113 if (!newPassword
.trim().equals("")) {
114 SimpleCredentials sp
= new SimpleCredentials(
115 userDetails
.getUsername(), newPassword
.toCharArray());
116 CryptedSimpleCredentials credentials
= (CryptedSimpleCredentials
) user
118 if (!credentials
.matches(sp
))
119 user
.changePassword(new String(newPassword
));
122 List
<String
> roles
= new ArrayList
<String
>();
123 for (GrantedAuthority ga
: userDetails
.getAuthorities()) {
124 if (ga
.getAuthority().equals(userRole
))
126 roles
.add(ga
.getAuthority());
129 for (Iterator
<Group
> it
= user
.memberOf(); it
.hasNext();) {
130 Group group
= it
.next();
131 if (roles
.contains(group
.getPrincipal().getName()))
132 roles
.remove(group
.getPrincipal().getName());
134 group
.removeMember(user
);
137 // remaining (new ones)
138 for (String role
: roles
) {
139 Group group
= (Group
) getUserManager().getAuthorizable(role
);
141 throw new ArgeoException("Group " + role
143 + " whereas it was granted to user " + userDetails
);
144 group
.addMember(user
);
146 } catch (Exception e
) {
147 throw new ArgeoException("Cannot update user details", e
);
153 public void deleteUser(String username
) {
155 getUserManager().getAuthorizable(username
).remove();
156 } catch (RepositoryException e
) {
157 throw new ArgeoException("Cannot remove user " + username
, e
);
162 public void changePassword(String oldPassword
, String newPassword
) {
163 Authentication authentication
= SecurityContextHolder
.getContext()
164 .getAuthentication();
165 String username
= authentication
.getName();
167 SimpleCredentials sp
= new SimpleCredentials(username
,
168 oldPassword
.toCharArray());
169 User user
= (User
) getUserManager().getAuthorizable(username
);
170 CryptedSimpleCredentials credentials
= (CryptedSimpleCredentials
) user
172 if (credentials
.matches(sp
))
173 user
.changePassword(newPassword
);
175 throw new BadCredentialsException("Bad credentials provided");
176 } catch (Exception e
) {
177 throw new ArgeoException("Cannot change password for user "
183 public boolean userExists(String username
) {
185 Authorizable authorizable
= getUserManager().getAuthorizable(
187 if (authorizable
!= null && authorizable
instanceof User
)
190 } catch (RepositoryException e
) {
191 throw new ArgeoException("Cannot check whether user " + username
197 public Set
<String
> listUsers() {
198 LinkedHashSet
<String
> res
= new LinkedHashSet
<String
>();
200 Iterator
<Authorizable
> users
= getUserManager().findAuthorizables(
201 "rep:principalName", null, UserManager
.SEARCH_TYPE_USER
);
202 while (users
.hasNext()) {
203 res
.add(users
.next().getPrincipal().getName());
206 } catch (RepositoryException e
) {
207 throw new ArgeoException("Cannot list users", e
);
212 public Set
<String
> listUsersInRole(String role
) {
213 LinkedHashSet
<String
> res
= new LinkedHashSet
<String
>();
215 Group group
= (Group
) getUserManager().getAuthorizable(role
);
216 Iterator
<Authorizable
> users
= group
.getMembers();
218 while (users
.hasNext()) {
219 res
.add(users
.next().getPrincipal().getName());
222 } catch (RepositoryException e
) {
223 throw new ArgeoException("Cannot list users in role " + role
, e
);
228 public void synchronize() {
232 public void newRole(String role
) {
234 getUserManager().createGroup(role
);
235 } catch (RepositoryException e
) {
236 throw new ArgeoException("Cannot create role " + role
, e
);
241 public Set
<String
> listEditableRoles() {
242 LinkedHashSet
<String
> res
= new LinkedHashSet
<String
>();
244 Iterator
<Authorizable
> groups
= getUserManager().findAuthorizables(
245 "rep:principalName", null, UserManager
.SEARCH_TYPE_GROUP
);
246 while (groups
.hasNext()) {
247 res
.add(groups
.next().getPrincipal().getName());
250 } catch (RepositoryException e
) {
251 throw new ArgeoException("Cannot list groups", e
);
256 public void deleteRole(String role
) {
258 getUserManager().getAuthorizable(role
).remove();
259 } catch (RepositoryException e
) {
260 throw new ArgeoException("Cannot remove role " + role
, e
);
265 public UserDetails
loadUserByUsername(String username
)
266 throws UsernameNotFoundException
, DataAccessException
{
268 User user
= (User
) getUserManager().getAuthorizable(username
);
270 throw new UsernameNotFoundException("User " + username
271 + " cannot be found");
272 return loadJcrUserDetails(adminSession
, username
);
273 } catch (RepositoryException e
) {
274 throw new ArgeoException("Cannot load user " + username
, e
);
278 protected JcrUserDetails
loadJcrUserDetails(Session session
, String username
)
279 throws RepositoryException
{
280 if (username
== null)
281 username
= session
.getUserID();
282 User user
= (User
) getUserManager().getAuthorizable(username
);
283 ArrayList
<GrantedAuthority
> authorities
= new ArrayList
<GrantedAuthority
>();
284 // FIXME make it more generic
285 authorities
.add(new SimpleGrantedAuthority("ROLE_USER"));
286 Iterator
<Group
> groups
= user
.declaredMemberOf();
287 while (groups
.hasNext()) {
288 Group group
= groups
.next();
289 // String role = "ROLE_"
290 // + group.getPrincipal().getName().toUpperCase();
291 String role
= group
.getPrincipal().getName();
292 authorities
.add(new SimpleGrantedAuthority(role
));
295 Node userProfile
= UserJcrUtils
.getUserProfile(session
, username
);
296 JcrUserDetails userDetails
= new JcrUserDetails(userProfile
, "",
301 // AUTHENTICATION PROVIDER
302 public synchronized Authentication
authenticate(
303 Authentication authentication
) throws AuthenticationException
{
304 NodeAuthenticationToken siteAuth
= (NodeAuthenticationToken
) authentication
;
305 String username
= siteAuth
.getName();
306 if (!(siteAuth
.getCredentials() instanceof char[]))
307 throw new ArgeoException("Only char array passwords are supported");
308 char[] password
= (char[]) siteAuth
.getCredentials();
310 SimpleCredentials sp
= new SimpleCredentials(siteAuth
.getName(),
312 User user
= (User
) getUserManager().getAuthorizable(username
);
314 throw new BadCredentialsException("Bad credentials");
315 CryptedSimpleCredentials credentials
= (CryptedSimpleCredentials
) user
317 // String providedPassword = siteAuth.getCredentials().toString();
318 if (!credentials
.matches(sp
))
319 throw new BadCredentialsException("Bad credentials");
321 // session = repository.login(sp, null);
323 Node userProfile
= UserJcrUtils
.getUserProfile(adminSession
,
325 JcrUserDetails
.checkAccountStatus(userProfile
);
326 } catch (BadCredentialsException e
) {
328 } catch (Exception e
) {
329 throw new BadCredentialsException(
330 "Cannot authenticate " + siteAuth
, e
);
332 Arrays
.fill(password
, '*');
336 JcrUserDetails userDetails
= loadJcrUserDetails(adminSession
,
338 NodeAuthenticationToken authenticated
= new NodeAuthenticationToken(
339 siteAuth
, userDetails
.getAuthorities());
340 authenticated
.setDetails(userDetails
);
341 return authenticated
;
342 } catch (RepositoryException e
) {
343 throw new ArgeoException(
344 "Unexpected exception when authenticating " + siteAuth
, e
);
348 @SuppressWarnings("rawtypes")
349 public boolean supports(Class authentication
) {
350 return UsernamePasswordAuthenticationToken
.class
351 .isAssignableFrom(authentication
);
354 public void setRepository(Repository repository
) {
355 this.repository
= repository
;
358 public void setSecurityModel(JcrSecurityModel securityModel
) {
359 this.securityModel
= securityModel
;