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