1 package org
.argeo
.security
.jackrabbit
;
3 import java
.util
.ArrayList
;
4 import java
.util
.Iterator
;
5 import java
.util
.LinkedHashSet
;
10 import javax
.jcr
.Repository
;
11 import javax
.jcr
.RepositoryException
;
12 import javax
.jcr
.Session
;
13 import javax
.jcr
.SimpleCredentials
;
15 import org
.apache
.jackrabbit
.api
.JackrabbitSession
;
16 import org
.apache
.jackrabbit
.api
.security
.user
.Authorizable
;
17 import org
.apache
.jackrabbit
.api
.security
.user
.Group
;
18 import org
.apache
.jackrabbit
.api
.security
.user
.User
;
19 import org
.apache
.jackrabbit
.api
.security
.user
.UserManager
;
20 import org
.apache
.jackrabbit
.core
.security
.authentication
.CryptedSimpleCredentials
;
21 import org
.argeo
.ArgeoException
;
22 import org
.argeo
.jcr
.JcrUtils
;
23 import org
.argeo
.jcr
.UserJcrUtils
;
24 import org
.argeo
.security
.UserAdminService
;
25 import org
.argeo
.security
.jcr
.JcrSecurityModel
;
26 import org
.argeo
.security
.jcr
.JcrUserDetails
;
27 import org
.springframework
.dao
.DataAccessException
;
28 import org
.springframework
.security
.Authentication
;
29 import org
.springframework
.security
.AuthenticationException
;
30 import org
.springframework
.security
.BadCredentialsException
;
31 import org
.springframework
.security
.GrantedAuthority
;
32 import org
.springframework
.security
.GrantedAuthorityImpl
;
33 import org
.springframework
.security
.context
.SecurityContextHolder
;
34 import org
.springframework
.security
.providers
.AuthenticationProvider
;
35 import org
.springframework
.security
.providers
.UsernamePasswordAuthenticationToken
;
36 import org
.springframework
.security
.userdetails
.UserDetails
;
37 import org
.springframework
.security
.userdetails
.UsernameNotFoundException
;
40 * An implementation of {@link UserAdminService} which closely wraps Jackrabbits
41 * implementation. Roles are implemented with Groups.
43 public class JackrabbitUserAdminService
implements UserAdminService
,
44 AuthenticationProvider
{
45 final static String userRole
= "ROLE_USER";
46 final static String adminRole
= "ROLE_ADMIN";
48 private Repository repository
;
49 private JcrSecurityModel securityModel
;
51 private JackrabbitSession adminSession
= null;
53 private String superUsername
= "root";
54 private String superUserInitialPassword
= "demo";
56 public void init() throws RepositoryException
{
57 Authentication authentication
= SecurityContextHolder
.getContext()
59 authentication
.getName();
60 adminSession
= (JackrabbitSession
) repository
.login();
61 Authorizable adminGroup
= getUserManager().getAuthorizable(adminRole
);
62 if (adminGroup
== null) {
63 adminGroup
= getUserManager().createGroup(adminRole
);
66 Authorizable superUser
= getUserManager()
67 .getAuthorizable(superUsername
);
68 if (superUser
== null) {
69 superUser
= getUserManager().createUser(superUsername
,
70 superUserInitialPassword
);
71 ((Group
) adminGroup
).addMember(superUser
);
72 securityModel
.sync(adminSession
, superUsername
, null);
77 public void destroy() throws RepositoryException
{
78 JcrUtils
.logoutQuietly(adminSession
);
81 private UserManager
getUserManager() throws RepositoryException
{
82 return adminSession
.getUserManager();
86 public void createUser(UserDetails user
) {
88 // FIXME workaround for issue in new user wizard where
89 // security model is hardcoded and it already exists
90 if (getUserManager().getAuthorizable(user
.getUsername()) == null) {
91 getUserManager().createUser(user
.getUsername(),
93 securityModel
.sync(adminSession
, user
.getUsername(), null);
96 } catch (RepositoryException e
) {
97 throw new ArgeoException("Cannot create user " + user
, e
);
102 public void updateUser(UserDetails userDetails
) {
104 User user
= (User
) getUserManager().getAuthorizable(
105 userDetails
.getUsername());
108 char[] newPassword
= userDetails
.getPassword().toCharArray();
109 SimpleCredentials sp
= new SimpleCredentials(
110 userDetails
.getUsername(), newPassword
);
111 CryptedSimpleCredentials credentials
= (CryptedSimpleCredentials
) user
113 if (!credentials
.matches(sp
))
114 user
.changePassword(new String(newPassword
));
116 List
<String
> roles
= new ArrayList
<String
>();
117 for (GrantedAuthority ga
: userDetails
.getAuthorities()) {
118 if (ga
.getAuthority().equals(userRole
))
120 roles
.add(ga
.getAuthority());
123 for (Iterator
<Group
> it
= user
.memberOf(); it
.hasNext();) {
124 Group group
= it
.next();
125 if (roles
.contains(group
.getPrincipal().getName()))
126 roles
.remove(group
.getPrincipal().getName());
128 group
.removeMember(user
);
131 // remaining (new ones)
132 for (String role
: roles
) {
133 Group group
= (Group
) getUserManager().getAuthorizable(role
);
135 throw new ArgeoException("Group " + role
137 + " whereas it was granted to user " + userDetails
);
138 group
.addMember(user
);
140 } catch (Exception e
) {
141 throw new ArgeoException("Cannot update user details", e
);
147 public void deleteUser(String username
) {
149 getUserManager().getAuthorizable(username
).remove();
150 } catch (RepositoryException e
) {
151 throw new ArgeoException("Cannot remove user " + username
, e
);
156 public void changePassword(String oldPassword
, String newPassword
) {
157 Authentication authentication
= SecurityContextHolder
.getContext()
158 .getAuthentication();
160 SimpleCredentials sp
= new SimpleCredentials(
161 authentication
.getName(),
162 ((UserDetails
) authentication
.getDetails()).getPassword()
164 User user
= (User
) getUserManager().getAuthorizable(
165 authentication
.getName());
166 CryptedSimpleCredentials credentials
= (CryptedSimpleCredentials
) user
168 if (credentials
.matches(sp
))
169 user
.changePassword(newPassword
);
171 throw new BadCredentialsException("Bad credentials provided");
172 } catch (Exception e
) {
173 throw new ArgeoException("Cannot change password for user "
174 + authentication
.getName(), e
);
179 public boolean userExists(String username
) {
181 Authorizable authorizable
= getUserManager().getAuthorizable(
183 if (authorizable
!= null && authorizable
instanceof User
)
186 } catch (RepositoryException e
) {
187 throw new ArgeoException("Cannot check whether user " + username
193 public Set
<String
> listUsers() {
194 LinkedHashSet
<String
> res
= new LinkedHashSet
<String
>();
196 Iterator
<Authorizable
> users
= getUserManager().findAuthorizables(
197 "rep:principalName", null, UserManager
.SEARCH_TYPE_USER
);
198 while (users
.hasNext()) {
199 res
.add(users
.next().getPrincipal().getName());
202 } catch (RepositoryException e
) {
203 throw new ArgeoException("Cannot list users", e
);
208 public Set
<String
> listUsersInRole(String role
) {
209 LinkedHashSet
<String
> res
= new LinkedHashSet
<String
>();
211 Group group
= (Group
) getUserManager().getAuthorizable(role
);
212 Iterator
<Authorizable
> users
= group
.getMembers();
214 while (users
.hasNext()) {
215 res
.add(users
.next().getPrincipal().getName());
218 } catch (RepositoryException e
) {
219 throw new ArgeoException("Cannot list users in role " + role
, e
);
224 public void synchronize() {
228 public void newRole(String role
) {
230 getUserManager().createGroup(role
);
231 } catch (RepositoryException e
) {
232 throw new ArgeoException("Cannot create role " + role
, e
);
237 public Set
<String
> listEditableRoles() {
238 LinkedHashSet
<String
> res
= new LinkedHashSet
<String
>();
240 Iterator
<Authorizable
> groups
= getUserManager().findAuthorizables(
241 "rep:principalName", null, UserManager
.SEARCH_TYPE_GROUP
);
242 while (groups
.hasNext()) {
243 res
.add(groups
.next().getPrincipal().getName());
246 } catch (RepositoryException e
) {
247 throw new ArgeoException("Cannot list groups", e
);
252 public void deleteRole(String role
) {
254 getUserManager().getAuthorizable(role
).remove();
255 } catch (RepositoryException e
) {
256 throw new ArgeoException("Cannot remove role " + role
, e
);
261 public UserDetails
loadUserByUsername(String username
)
262 throws UsernameNotFoundException
, DataAccessException
{
264 User user
= (User
) getUserManager().getAuthorizable(username
);
266 throw new UsernameNotFoundException("User " + username
267 + " cannot be found");
268 return loadJcrUserDetails(adminSession
, username
,
269 user
.getCredentials());
270 } catch (RepositoryException e
) {
271 throw new ArgeoException("Cannot load user " + username
, e
);
275 protected JcrUserDetails
loadJcrUserDetails(Session session
,
276 String username
, Object credentials
) throws RepositoryException
{
277 if (username
== null)
278 username
= session
.getUserID();
279 User user
= (User
) getUserManager().getAuthorizable(username
);
280 ArrayList
<GrantedAuthority
> authorities
= new ArrayList
<GrantedAuthority
>();
281 // FIXME make it more generic
282 authorities
.add(new GrantedAuthorityImpl("ROLE_USER"));
283 Iterator
<Group
> groups
= user
.declaredMemberOf();
284 while (groups
.hasNext()) {
285 Group group
= groups
.next();
286 // String role = "ROLE_"
287 // + group.getPrincipal().getName().toUpperCase();
288 String role
= group
.getPrincipal().getName();
289 authorities
.add(new GrantedAuthorityImpl(role
));
292 Node userProfile
= UserJcrUtils
.getUserProfile(session
, username
);
293 JcrUserDetails userDetails
= new JcrUserDetails(userProfile
,
294 credentials
.toString(),
295 authorities
.toArray(new GrantedAuthority
[authorities
.size()]));
299 // AUTHENTICATION PROVIDER
300 public synchronized Authentication
authenticate(
301 Authentication authentication
) throws AuthenticationException
{
302 UsernamePasswordAuthenticationToken siteAuth
= (UsernamePasswordAuthenticationToken
) authentication
;
303 String username
= siteAuth
.getName();
305 SimpleCredentials sp
= new SimpleCredentials(siteAuth
.getName(),
306 siteAuth
.getCredentials().toString().toCharArray());
307 User user
= (User
) getUserManager().getAuthorizable(username
);
308 CryptedSimpleCredentials credentials
= (CryptedSimpleCredentials
) user
310 // String providedPassword = siteAuth.getCredentials().toString();
311 if (!credentials
.matches(sp
)) {
312 throw new BadCredentialsException("Passwords do not match");
314 // session = repository.login(sp, null);
316 Node userProfile
= UserJcrUtils
.getUserProfile(adminSession
,
318 JcrUserDetails
.checkAccountStatus(userProfile
);
319 } catch (Exception e
) {
320 throw new BadCredentialsException(
321 "Cannot authenticate " + siteAuth
, e
);
325 JcrUserDetails userDetails
= loadJcrUserDetails(adminSession
,
326 username
, siteAuth
.getCredentials());
327 UsernamePasswordAuthenticationToken authenticated
= new UsernamePasswordAuthenticationToken(
328 siteAuth
, "", userDetails
.getAuthorities());
329 authenticated
.setDetails(userDetails
);
330 return authenticated
;
331 } catch (RepositoryException e
) {
332 throw new ArgeoException(
333 "Unexpected exception when authenticating " + siteAuth
, e
);
337 @SuppressWarnings("rawtypes")
338 public boolean supports(Class authentication
) {
339 return UsernamePasswordAuthenticationToken
.class
340 .isAssignableFrom(authentication
);
343 public void setRepository(Repository repository
) {
344 this.repository
= repository
;
347 public void setSecurityModel(JcrSecurityModel securityModel
) {
348 this.securityModel
= securityModel
;