]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.security.core/src/org/argeo/security/jcr/jackrabbit/JackrabbitUserAdminService.java
Move Jackrabbit security model
[lgpl/argeo-commons.git] / org.argeo.security.core / src / org / argeo / security / jcr / jackrabbit / JackrabbitUserAdminService.java
1 package org.argeo.security.jcr.jackrabbit;
2
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.Iterator;
6 import java.util.LinkedHashSet;
7 import java.util.List;
8 import java.util.Set;
9
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
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.argeo.security.login.GrantedAuthorityPrincipal;
30 import org.springframework.dao.DataAccessException;
31 import org.springframework.security.authentication.AuthenticationProvider;
32 import org.springframework.security.authentication.BadCredentialsException;
33 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
34 import org.springframework.security.core.Authentication;
35 import org.springframework.security.core.AuthenticationException;
36 import org.springframework.security.core.GrantedAuthority;
37 import org.springframework.security.core.context.SecurityContextHolder;
38 import org.springframework.security.core.userdetails.UserDetails;
39 import org.springframework.security.core.userdetails.UsernameNotFoundException;
40
41 /**
42 * An implementation of {@link UserAdminService} which closely wraps Jackrabbits
43 * implementation. Roles are implemented with Groups.
44 */
45 public class JackrabbitUserAdminService implements UserAdminService,
46 AuthenticationProvider {
47 final static String userRole = "ROLE_USER";
48 final static String adminRole = "ROLE_ADMIN";
49
50 private Repository repository;
51 private JcrSecurityModel securityModel;
52
53 private JackrabbitSession adminSession = null;
54
55 private String superUsername = "root";
56 private String superUserInitialPassword = "demo";
57
58 public void init() throws RepositoryException {
59 Authentication authentication = SecurityContextHolder.getContext()
60 .getAuthentication();
61 authentication.getName();
62 adminSession = (JackrabbitSession) repository.login();
63 Authorizable adminGroup = getUserManager().getAuthorizable(adminRole);
64 if (adminGroup == null) {
65 adminGroup = getUserManager().createGroup(adminRole);
66 adminSession.save();
67 }
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);
75 adminSession.save();
76 }
77 }
78
79 public void destroy() throws RepositoryException {
80 JcrUtils.logoutQuietly(adminSession);
81 }
82
83 private UserManager getUserManager() throws RepositoryException {
84 return adminSession.getUserManager();
85 }
86
87 @Override
88 public void createUser(UserDetails user) {
89 try {
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(),
94 user.getPassword());
95 securityModel.sync(adminSession, user.getUsername(), null);
96 }
97 updateUser(user);
98 } catch (RepositoryException e) {
99 throw new ArgeoException("Cannot create user " + user, e);
100 }
101 }
102
103 @Override
104 public void updateUser(UserDetails userDetails) {
105 try {
106 User user = (User) getUserManager().getAuthorizable(
107 userDetails.getUsername());
108 if (user == null)
109 throw new ArgeoException("No user " + userDetails.getUsername());
110
111 // new password
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
117 .getCredentials();
118 if (!credentials.matches(sp))
119 user.changePassword(new String(newPassword));
120 }
121
122 List<String> roles = new ArrayList<String>();
123 for (GrantedAuthority ga : userDetails.getAuthorities()) {
124 if (ga.getAuthority().equals(userRole))
125 continue;
126 roles.add(ga.getAuthority());
127 }
128
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());
133 else
134 group.removeMember(user);
135 }
136
137 // remaining (new ones)
138 for (String role : roles) {
139 Group group = (Group) getUserManager().getAuthorizable(role);
140 if (group == null)
141 throw new ArgeoException("Group " + role
142 + " does not exist,"
143 + " whereas it was granted to user " + userDetails);
144 group.addMember(user);
145 }
146 } catch (Exception e) {
147 throw new ArgeoException("Cannot update user details", e);
148 }
149
150 }
151
152 @Override
153 public void deleteUser(String username) {
154 try {
155 getUserManager().getAuthorizable(username).remove();
156 } catch (RepositoryException e) {
157 throw new ArgeoException("Cannot remove user " + username, e);
158 }
159 }
160
161 @Override
162 public void changePassword(String oldPassword, String newPassword) {
163 Authentication authentication = SecurityContextHolder.getContext()
164 .getAuthentication();
165 String username = authentication.getName();
166 try {
167 SimpleCredentials sp = new SimpleCredentials(username,
168 oldPassword.toCharArray());
169 User user = (User) getUserManager().getAuthorizable(username);
170 CryptedSimpleCredentials credentials = (CryptedSimpleCredentials) user
171 .getCredentials();
172 if (credentials.matches(sp))
173 user.changePassword(newPassword);
174 else
175 throw new BadCredentialsException("Bad credentials provided");
176 } catch (Exception e) {
177 throw new ArgeoException("Cannot change password for user "
178 + username, e);
179 }
180 }
181
182 @Override
183 public boolean userExists(String username) {
184 try {
185 Authorizable authorizable = getUserManager().getAuthorizable(
186 username);
187 if (authorizable != null && authorizable instanceof User)
188 return true;
189 return false;
190 } catch (RepositoryException e) {
191 throw new ArgeoException("Cannot check whether user " + username
192 + " exists ", e);
193 }
194 }
195
196 @Override
197 public Set<String> listUsers() {
198 LinkedHashSet<String> res = new LinkedHashSet<String>();
199 try {
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());
204 }
205 return res;
206 } catch (RepositoryException e) {
207 throw new ArgeoException("Cannot list users", e);
208 }
209 }
210
211 @Override
212 public Set<String> listUsersInRole(String role) {
213 LinkedHashSet<String> res = new LinkedHashSet<String>();
214 try {
215 Group group = (Group) getUserManager().getAuthorizable(role);
216 Iterator<Authorizable> users = group.getMembers();
217 // NB: not recursive
218 while (users.hasNext()) {
219 res.add(users.next().getPrincipal().getName());
220 }
221 return res;
222 } catch (RepositoryException e) {
223 throw new ArgeoException("Cannot list users in role " + role, e);
224 }
225 }
226
227 @Override
228 public void synchronize() {
229 }
230
231 @Override
232 public void newRole(String role) {
233 try {
234 getUserManager().createGroup(role);
235 } catch (RepositoryException e) {
236 throw new ArgeoException("Cannot create role " + role, e);
237 }
238 }
239
240 @Override
241 public Set<String> listEditableRoles() {
242 LinkedHashSet<String> res = new LinkedHashSet<String>();
243 try {
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());
248 }
249 return res;
250 } catch (RepositoryException e) {
251 throw new ArgeoException("Cannot list groups", e);
252 }
253 }
254
255 @Override
256 public void deleteRole(String role) {
257 try {
258 getUserManager().getAuthorizable(role).remove();
259 } catch (RepositoryException e) {
260 throw new ArgeoException("Cannot remove role " + role, e);
261 }
262 }
263
264 @Override
265 public UserDetails loadUserByUsername(String username)
266 throws UsernameNotFoundException, DataAccessException {
267 try {
268 User user = (User) getUserManager().getAuthorizable(username);
269 if (user == null)
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);
275 }
276 }
277
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<GrantedAuthorityPrincipal> authorities = new ArrayList<GrantedAuthorityPrincipal>();
284 // FIXME make it more generic
285 authorities.add(new GrantedAuthorityPrincipal("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 GrantedAuthorityPrincipal(role));
293 }
294
295 Node userProfile = UserJcrUtils.getUserProfile(session, username);
296 JcrUserDetails userDetails = new JcrUserDetails(userProfile, "",
297 authorities);
298 return userDetails;
299 }
300
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();
309 try {
310 SimpleCredentials sp = new SimpleCredentials(siteAuth.getName(),
311 password);
312 User user = (User) getUserManager().getAuthorizable(username);
313 if (user == null)
314 throw new BadCredentialsException("Bad credentials");
315 CryptedSimpleCredentials credentials = (CryptedSimpleCredentials) user
316 .getCredentials();
317 // String providedPassword = siteAuth.getCredentials().toString();
318 if (!credentials.matches(sp))
319 throw new BadCredentialsException("Bad credentials");
320
321 // session = repository.login(sp, null);
322
323 Node userProfile = UserJcrUtils.getUserProfile(adminSession,
324 username);
325 JcrUserDetails.checkAccountStatus(userProfile);
326 } catch (BadCredentialsException e) {
327 throw e;
328 } catch (Exception e) {
329 throw new BadCredentialsException(
330 "Cannot authenticate " + siteAuth, e);
331 } finally {
332 Arrays.fill(password, '*');
333 }
334
335 try {
336 JcrUserDetails userDetails = loadJcrUserDetails(adminSession,
337 username);
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);
345 }
346 }
347
348 @SuppressWarnings("rawtypes")
349 public boolean supports(Class authentication) {
350 return UsernamePasswordAuthenticationToken.class
351 .isAssignableFrom(authentication);
352 }
353
354 public void setRepository(Repository repository) {
355 this.repository = repository;
356 }
357
358 public void setSecurityModel(JcrSecurityModel securityModel) {
359 this.securityModel = securityModel;
360 }
361
362 }