]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.security.jackrabbit/src/org/argeo/security/jackrabbit/JackrabbitUserAdminService.java
[maven-release-plugin] prepare for next development iteration
[lgpl/argeo-commons.git] / org.argeo.security.jackrabbit / src / org / argeo / security / jackrabbit / JackrabbitUserAdminService.java
1 package org.argeo.security.jackrabbit;
2
3 import java.util.ArrayList;
4 import java.util.Iterator;
5 import java.util.LinkedHashSet;
6 import java.util.List;
7 import java.util.Set;
8
9 import javax.jcr.Node;
10 import javax.jcr.Repository;
11 import javax.jcr.RepositoryException;
12 import javax.jcr.Session;
13 import javax.jcr.SimpleCredentials;
14
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;
38
39 /**
40 * An implementation of {@link UserAdminService} which closely wraps Jackrabbits
41 * implementation. Roles are implemented with Groups.
42 */
43 public class JackrabbitUserAdminService implements UserAdminService,
44 AuthenticationProvider {
45 final static String userRole = "ROLE_USER";
46 final static String adminRole = "ROLE_ADMIN";
47
48 private Repository repository;
49 private JcrSecurityModel securityModel;
50
51 private JackrabbitSession adminSession = null;
52
53 private String superUsername = "root";
54 private String superUserInitialPassword = "demo";
55
56 public void init() throws RepositoryException {
57 Authentication authentication = SecurityContextHolder.getContext()
58 .getAuthentication();
59 authentication.getName();
60 adminSession = (JackrabbitSession) repository.login();
61 Authorizable adminGroup = getUserManager().getAuthorizable(adminRole);
62 if (adminGroup == null) {
63 adminGroup = getUserManager().createGroup(adminRole);
64 adminSession.save();
65 }
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);
73 adminSession.save();
74 }
75 }
76
77 public void destroy() throws RepositoryException {
78 JcrUtils.logoutQuietly(adminSession);
79 }
80
81 private UserManager getUserManager() throws RepositoryException {
82 return adminSession.getUserManager();
83 }
84
85 @Override
86 public void createUser(UserDetails user) {
87 try {
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(),
92 user.getPassword());
93 securityModel.sync(adminSession, user.getUsername(), null);
94 }
95 updateUser(user);
96 } catch (RepositoryException e) {
97 throw new ArgeoException("Cannot create user " + user, e);
98 }
99 }
100
101 @Override
102 public void updateUser(UserDetails userDetails) {
103 try {
104 User user = (User) getUserManager().getAuthorizable(
105 userDetails.getUsername());
106
107 // new password
108 char[] newPassword = userDetails.getPassword().toCharArray();
109 SimpleCredentials sp = new SimpleCredentials(
110 userDetails.getUsername(), newPassword);
111 CryptedSimpleCredentials credentials = (CryptedSimpleCredentials) user
112 .getCredentials();
113 if (!credentials.matches(sp))
114 user.changePassword(new String(newPassword));
115
116 List<String> roles = new ArrayList<String>();
117 for (GrantedAuthority ga : userDetails.getAuthorities()) {
118 if (ga.getAuthority().equals(userRole))
119 continue;
120 roles.add(ga.getAuthority());
121 }
122
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());
127 else
128 group.removeMember(user);
129 }
130
131 // remaining (new ones)
132 for (String role : roles) {
133 Group group = (Group) getUserManager().getAuthorizable(role);
134 if (group == null)
135 throw new ArgeoException("Group " + role
136 + " does not exist,"
137 + " whereas it was granted to user " + userDetails);
138 group.addMember(user);
139 }
140 } catch (Exception e) {
141 throw new ArgeoException("Cannot update user details", e);
142 }
143
144 }
145
146 @Override
147 public void deleteUser(String username) {
148 try {
149 getUserManager().getAuthorizable(username).remove();
150 } catch (RepositoryException e) {
151 throw new ArgeoException("Cannot remove user " + username, e);
152 }
153 }
154
155 @Override
156 public void changePassword(String oldPassword, String newPassword) {
157 Authentication authentication = SecurityContextHolder.getContext()
158 .getAuthentication();
159 try {
160 SimpleCredentials sp = new SimpleCredentials(
161 authentication.getName(),
162 ((UserDetails) authentication.getDetails()).getPassword()
163 .toCharArray());
164 User user = (User) getUserManager().getAuthorizable(
165 authentication.getName());
166 CryptedSimpleCredentials credentials = (CryptedSimpleCredentials) user
167 .getCredentials();
168 if (credentials.matches(sp))
169 user.changePassword(newPassword);
170 else
171 throw new BadCredentialsException("Bad credentials provided");
172 } catch (Exception e) {
173 throw new ArgeoException("Cannot change password for user "
174 + authentication.getName(), e);
175 }
176 }
177
178 @Override
179 public boolean userExists(String username) {
180 try {
181 Authorizable authorizable = getUserManager().getAuthorizable(
182 username);
183 if (authorizable != null && authorizable instanceof User)
184 return true;
185 return false;
186 } catch (RepositoryException e) {
187 throw new ArgeoException("Cannot check whether user " + username
188 + " exists ", e);
189 }
190 }
191
192 @Override
193 public Set<String> listUsers() {
194 LinkedHashSet<String> res = new LinkedHashSet<String>();
195 try {
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());
200 }
201 return res;
202 } catch (RepositoryException e) {
203 throw new ArgeoException("Cannot list users", e);
204 }
205 }
206
207 @Override
208 public Set<String> listUsersInRole(String role) {
209 LinkedHashSet<String> res = new LinkedHashSet<String>();
210 try {
211 Group group = (Group) getUserManager().getAuthorizable(role);
212 Iterator<Authorizable> users = group.getMembers();
213 // NB: not recursive
214 while (users.hasNext()) {
215 res.add(users.next().getPrincipal().getName());
216 }
217 return res;
218 } catch (RepositoryException e) {
219 throw new ArgeoException("Cannot list users in role " + role, e);
220 }
221 }
222
223 @Override
224 public void synchronize() {
225 }
226
227 @Override
228 public void newRole(String role) {
229 try {
230 getUserManager().createGroup(role);
231 } catch (RepositoryException e) {
232 throw new ArgeoException("Cannot create role " + role, e);
233 }
234 }
235
236 @Override
237 public Set<String> listEditableRoles() {
238 LinkedHashSet<String> res = new LinkedHashSet<String>();
239 try {
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());
244 }
245 return res;
246 } catch (RepositoryException e) {
247 throw new ArgeoException("Cannot list groups", e);
248 }
249 }
250
251 @Override
252 public void deleteRole(String role) {
253 try {
254 getUserManager().getAuthorizable(role).remove();
255 } catch (RepositoryException e) {
256 throw new ArgeoException("Cannot remove role " + role, e);
257 }
258 }
259
260 @Override
261 public UserDetails loadUserByUsername(String username)
262 throws UsernameNotFoundException, DataAccessException {
263 try {
264 User user = (User) getUserManager().getAuthorizable(username);
265 if (user == null)
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);
272 }
273 }
274
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));
290 }
291
292 Node userProfile = UserJcrUtils.getUserProfile(session, username);
293 JcrUserDetails userDetails = new JcrUserDetails(userProfile,
294 credentials.toString(),
295 authorities.toArray(new GrantedAuthority[authorities.size()]));
296 return userDetails;
297 }
298
299 // AUTHENTICATION PROVIDER
300 public synchronized Authentication authenticate(
301 Authentication authentication) throws AuthenticationException {
302 UsernamePasswordAuthenticationToken siteAuth = (UsernamePasswordAuthenticationToken) authentication;
303 String username = siteAuth.getName();
304 try {
305 SimpleCredentials sp = new SimpleCredentials(siteAuth.getName(),
306 siteAuth.getCredentials().toString().toCharArray());
307 User user = (User) getUserManager().getAuthorizable(username);
308 CryptedSimpleCredentials credentials = (CryptedSimpleCredentials) user
309 .getCredentials();
310 // String providedPassword = siteAuth.getCredentials().toString();
311 if (!credentials.matches(sp)) {
312 throw new BadCredentialsException("Passwords do not match");
313 }
314 // session = repository.login(sp, null);
315
316 Node userProfile = UserJcrUtils.getUserProfile(adminSession,
317 username);
318 JcrUserDetails.checkAccountStatus(userProfile);
319 } catch (Exception e) {
320 throw new BadCredentialsException(
321 "Cannot authenticate " + siteAuth, e);
322 }
323
324 try {
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);
334 }
335 }
336
337 @SuppressWarnings("rawtypes")
338 public boolean supports(Class authentication) {
339 return UsernamePasswordAuthenticationToken.class
340 .isAssignableFrom(authentication);
341 }
342
343 public void setRepository(Repository repository) {
344 this.repository = repository;
345 }
346
347 public void setSecurityModel(JcrSecurityModel securityModel) {
348 this.securityModel = securityModel;
349 }
350
351 }