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