1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.cms.security;
35
36 import static info.magnolia.cms.security.SecurityConstants.NODE_GROUPS;
37 import static info.magnolia.cms.security.SecurityConstants.NODE_ROLES;
38 import info.magnolia.cms.core.Content;
39 import info.magnolia.context.MgnlContext;
40 import info.magnolia.repository.RepositoryConstants;
41
42 import java.io.Serializable;
43 import java.util.ArrayList;
44 import java.util.Calendar;
45 import java.util.Collection;
46 import java.util.Collections;
47 import java.util.List;
48 import java.util.Map;
49 import java.util.Set;
50 import java.util.TreeSet;
51
52 import javax.jcr.ItemNotFoundException;
53 import javax.jcr.Node;
54 import javax.jcr.Property;
55 import javax.jcr.PropertyIterator;
56 import javax.jcr.RepositoryException;
57 import javax.jcr.Session;
58
59 import org.apache.commons.codec.binary.Base64;
60 import org.apache.commons.lang.StringUtils;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
63
64
65
66
67
68
69
70
71
72 public class MgnlUser extends AbstractUser implements User, Serializable {
73
74 private static final long serialVersionUID = 222L;
75
76 private static final Logger log = LoggerFactory.getLogger(MgnlUser.class);
77
78 private final Map<String, String> properties;
79 private final Collection<String> groups;
80 private final Collection<String> roles;
81
82 private final String name;
83 private final String language;
84 private final String encodedPassword;
85 private boolean enabled = true;
86 private String path;
87 private String uuid;
88
89 private final String realm;
90
91
92 public MgnlUser(String name, String realm, Collection<String> groups, Collection<String> roles, Map<String, String> properties) {
93 this.name = name;
94 this.roles = Collections.unmodifiableCollection(roles);
95 this.groups = Collections.unmodifiableCollection(groups);
96 this.properties = Collections.unmodifiableMap(properties);
97 this.realm = realm;
98
99
100 language = properties.get(MgnlUserManager.PROPERTY_LANGUAGE);
101 String enbld = properties.get(MgnlUserManager.PROPERTY_ENABLED);
102
103 enabled = enbld == null ? true : Boolean.parseBoolean(properties.get(MgnlUserManager.PROPERTY_ENABLED));
104 encodedPassword = properties.get(MgnlUserManager.PROPERTY_PASSWORD);
105 }
106
107 public MgnlUser(String name, String realm, Collection<String> groups, Collection<String> roles, Map<String, String> properties, String path, String uuid) {
108 this(name,realm,groups,roles,properties);
109 this.path = path;
110 this.uuid = uuid;
111 }
112
113
114
115
116
117
118 @Override
119 public boolean inGroup(String groupName) {
120 log.debug("inGroup({})", groupName);
121 return this.hasAny(groupName, NODE_GROUPS);
122 }
123
124
125
126
127
128 @Override
129 public void removeGroup(String groupName) throws UnsupportedOperationException {
130 log.debug("removeGroup({})", groupName);
131 throw new UnsupportedOperationException("use manager to remove groups!");
132 }
133
134
135
136
137
138 @Override
139 public void addGroup(String groupName) throws UnsupportedOperationException {
140 log.debug("addGroup({})", groupName);
141 throw new UnsupportedOperationException("use manager to add groups!");
142 }
143
144 @Override
145 public boolean isEnabled() {
146 log.debug("isEnabled()");
147 return enabled ;
148 }
149
150
151
152
153 @Override
154 public void setEnabled(boolean enabled) {
155 log.debug("setEnabled({})", enabled);
156 this.enabled = enabled;
157 }
158
159
160
161
162
163
164 @Override
165 public boolean hasRole(String roleName) {
166 return SecuritySupport.Factory.getInstance().getUserManager(getRealm()).hasAny(getName(), roleName, NODE_ROLES);
167 }
168
169 @Override
170 public void removeRole(String roleName) throws UnsupportedOperationException {
171 log.debug("removeRole({})", roleName);
172 throw new UnsupportedOperationException("use manager to remove roles!");
173 }
174
175 @Override
176 public void addRole(String roleName) throws UnsupportedOperationException {
177 log.debug("addRole({})", roleName);
178 throw new UnsupportedOperationException("use manager to add roles!");
179 }
180
181
182 private boolean hasAny(final String name, final String nodeName) {
183 long start = System.currentTimeMillis();
184 try {
185 String sessionName;
186 if (StringUtils.equalsIgnoreCase(nodeName, NODE_ROLES)) {
187 sessionName = RepositoryConstants.USER_ROLES;
188 } else {
189 sessionName = RepositoryConstants.USER_GROUPS;
190 }
191
192
193 final Collection<String> groupsOrRoles = MgnlContext.doInSystemContext(new SilentSessionOp<Collection<String>>(RepositoryConstants.USERS) {
194
195 @Override
196 public Collection<String> doExec(Session session) throws RepositoryException {
197 Node groupsOrRoles = session.getNode(getName()).getNode(nodeName);
198 List<String> list = new ArrayList<String>();
199 for (PropertyIterator props = groupsOrRoles.getProperties(); props.hasNext();) {
200
201 Property property = props.nextProperty();
202 try {
203 list.add(property.getString());
204 } catch (ItemNotFoundException e) {
205 log.debug("Role [{}] does not exist in the ROLES repository", name);
206 } catch (IllegalArgumentException e) {
207 log.debug("{} has invalid value", property.getPath());
208 }
209 }
210 return list;
211 }});
212
213
214 return MgnlContext.doInSystemContext(new JCRSessionOp<Boolean>(sessionName) {
215
216 @Override
217 public Boolean exec(Session session) throws RepositoryException {
218 for (String groupOrRole : groupsOrRoles) {
219
220 try {
221 if (session.getNodeByIdentifier(groupOrRole).getName().equalsIgnoreCase(name)) {
222 return true;
223 }
224 } catch (ItemNotFoundException e) {
225 log.debug("Role [{}] does not exist in the ROLES repository", name);
226 }
227 }
228 return false;
229 }});
230
231 } catch (RepositoryException e) {
232 log.debug(e.getMessage(), e);
233
234 } finally {
235 log.debug("checked {} for {} in {}ms.", new Object[] {name, nodeName, (System.currentTimeMillis() - start)});
236 }
237 return false;
238 }
239
240 public int getFailedLoginAttempts(){
241 return MgnlContext.doInSystemContext(new SilentSessionOp<Integer>(RepositoryConstants.USERS) {
242 @Override
243 public Integer doExec(Session session) throws RepositoryException {
244 Node userNode = session.getNode("/" + getRealm() + "/" + getName());
245 if (!userNode.hasProperty("failedLoginAttempts")){
246 userNode.setProperty("failedLoginAttempts", 0);
247 session.save();
248 }
249 return (int)userNode.getProperty("failedLoginAttempts").getLong();
250 }});
251 }
252
253 public Calendar getReleaseTime(){
254 return MgnlContext.doInSystemContext(new SilentSessionOp<Calendar>(RepositoryConstants.USERS) {
255 @Override
256 public Calendar doExec(Session session) throws RepositoryException {
257 Node userNode = session.getNode("/" + getRealm() + "/" + getName());
258 if (!userNode.hasProperty("releaseTime")){
259 userNode.setProperty("releaseTime", 0);
260 session.save();
261 }
262 return userNode.getProperty("releaseTime").getDate();
263 }});
264 }
265
266 @Override
267 public String getName() {
268 log.debug("getName()=>{}", name);
269 return name;
270 }
271
272 @Override
273 public String getPassword() {
274 return encodedPassword;
275 }
276
277 protected String decodePassword(String encodedPassword) {
278 return new String(Base64.decodeBase64(encodedPassword.getBytes()));
279 }
280
281 @Override
282 public String getLanguage() {
283 log.debug("getLang()=>{}", language);
284 return this.language;
285 }
286
287 @Override
288 public String getProperty(String propertyName) {
289 log.debug("getProperty({})", propertyName);
290 return properties.get(propertyName);
291 }
292
293 @Override
294 public Collection<String> getGroups() {
295 log.debug("getGroups()");
296 return groups;
297 }
298
299 @Override
300 public Collection<String> getAllGroups() {
301
302
303 log.debug("get groups for {}", getName());
304
305 final Set<String> allGroups = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
306 final Collection<String> groups = getGroups();
307
308
309 GroupManager man = SecuritySupport.Factory.getInstance().getGroupManager();
310
311
312 addSubgroups(allGroups, man, groups);
313
314
315 allGroups.addAll(groups);
316
317 return allGroups;
318 }
319
320 @Override
321 public Collection<String> getRoles() {
322 log.debug("getRoles()");
323 return roles;
324 }
325
326 @Override
327 public Collection<String> getAllRoles() {
328
329 log.debug("get roles for {}", getName());
330
331 final Set<String> allRoles = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
332 final Collection<String> roles = getRoles();
333
334
335 allRoles.addAll(roles);
336
337 Collection<String> allGroups = getAllGroups();
338
339
340 GroupManager man = SecuritySupport.Factory.getInstance().getGroupManager();
341
342
343 for (String group : allGroups) {
344 try {
345 allRoles.addAll(man.getGroup(group).getRoles());
346 } catch (AccessDeniedException e) {
347 log.debug("Skipping denied group " + group + " for user " + getName(), e);
348 } catch (UnsupportedOperationException e) {
349 log.debug("Skipping unsupported getGroup() for group " + group + " and user " + getName(), e);
350 }
351 }
352 return allRoles;
353 }
354
355 public String getPath() {
356 return this.path;
357 }
358
359 @Deprecated
360 public void setPath(String path) {
361 this.path = path;
362 }
363
364
365
366
367 private void addSubgroups(final Set<String> allGroups, GroupManager man, Collection<String> groups) {
368 for (String groupName : groups) {
369
370 if (!allGroups.contains(groupName)) {
371 try {
372 Group group = man.getGroup(groupName);
373 if (group == null) {
374 log.error("Failed to resolve group {} for user {}.", groupName, name);
375 continue;
376 }
377 Collection<String> subgroups = group.getGroups();
378
379 addSubgroups(allGroups, man, subgroups);
380 allGroups.addAll(subgroups);
381 } catch (AccessDeniedException e) {
382 log.debug("Skipping denied group " + groupName + " for user " + getName(), e);
383 } catch (UnsupportedOperationException e) {
384 log.debug("Skipping unsupported getGroup() for group " + groupName + " and user " + getName(), e);
385 }
386
387 }
388 }
389 }
390
391 public String getRealm() {
392 return realm;
393 }
394
395
396
397
398
399 @Deprecated
400 public void setLastAccess() {
401 throw new UnsupportedOperationException("Use manager to update user details.");
402 }
403
404
405
406
407
408 @Deprecated
409 public Content getUserNode() {
410 throw new UnsupportedOperationException("Underlying storage node is no longer exposed nor required for custom user stores.");
411 }
412
413
414
415
416 @Override
417 @Deprecated
418 public void setProperty(String propertyName, String value) {
419 throw new UnsupportedOperationException("Use manager to modify properties of the user.");
420 }
421
422 public String getUuid() {
423 return uuid;
424 }
425 }