1    /*
2     * Copyright 2006 :torweg free software group
3     * 
4     * This program is free software: you can redistribute it and/or modify
5     * it under the terms of the GNU General Public License as published by
6     * the Free Software Foundation, either version 3 of the License, or
7     * (at your option) any later version.
8     * 
9     * This program is distributed in the hope that it will be useful,
10    * but WITHOUT ANY WARRANTY; without even the implied warranty of
11    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    * GNU General Public License for more details.
13    * 
14    * You should have received a copy of the GNU General Public License
15    * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16    *
17    */
18   package org.torweg.pulse.accesscontrol;
19   
20   import java.security.NoSuchAlgorithmException;
21   import java.util.Date;
22   import java.util.HashMap;
23   import java.util.HashSet;
24   import java.util.Locale;
25   import java.util.Map;
26   import java.util.Set;
27   
28   import javax.persistence.Basic;
29   import javax.persistence.CascadeType;
30   import javax.persistence.Column;
31   import javax.persistence.Entity;
32   import javax.persistence.EnumType;
33   import javax.persistence.Enumerated;
34   import javax.persistence.ManyToMany;
35   import javax.persistence.OneToMany;
36   import javax.persistence.Table;
37   import javax.persistence.Temporal;
38   import javax.persistence.TemporalType;
39   import javax.persistence.Transient;
40   import javax.persistence.UniqueConstraint;
41   import javax.xml.bind.annotation.XmlAccessOrder;
42   import javax.xml.bind.annotation.XmlAccessType;
43   import javax.xml.bind.annotation.XmlAccessorOrder;
44   import javax.xml.bind.annotation.XmlAccessorType;
45   import javax.xml.bind.annotation.XmlElement;
46   import javax.xml.bind.annotation.XmlElementWrapper;
47   import javax.xml.bind.annotation.XmlRootElement;
48   import javax.xml.bind.annotation.XmlTransient;
49   import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
50   
51   import net.sf.json.JSONObject;
52   
53   import org.hibernate.LazyInitializationException;
54   import org.hibernate.Session;
55   import org.hibernate.Transaction;
56   import org.hibernate.annotations.Index;
57   import org.jdom.Element;
58   import org.slf4j.Logger;
59   import org.slf4j.LoggerFactory;
60   import org.torweg.pulse.accesscontrol.attributes.AbstractAttribute;
61   import org.torweg.pulse.accesscontrol.attributes.AbstractValue;
62   import org.torweg.pulse.accesscontrol.attributes.AttributeRegistry;
63   import org.torweg.pulse.accesscontrol.attributes.BundleAttribute;
64   import org.torweg.pulse.configuration.ConfigurationException;
65   import org.torweg.pulse.invocation.lifecycle.Lifecycle;
66   import org.torweg.pulse.service.PulseException;
67   import org.torweg.pulse.service.request.Command;
68   import org.torweg.pulse.site.map.SitemapNode;
69   import org.torweg.pulse.util.INameable;
70   import org.torweg.pulse.util.StringUtils;
71   import org.torweg.pulse.util.time.Duration;
72   import org.torweg.pulse.util.xml.bind.LocaleXmlAdapter;
73   
74   /**
75    * A {@code User} is an entity representing a user in the <em>pulse</em>
76    * container.
77    * 
78    * <p>
79    * Every {@code User} consists of a unique name and a password that are both
80    * used for authentication.
81    * </p>
82    * 
83    * @author Thomas Weber, Christian Schatt, Daniel Dietz
84    * @version $Revision: 2032 $
85    */
86   @Entity
87   @Table(name = "`User`", uniqueConstraints = { @UniqueConstraint(columnNames = "name") })
88   @XmlRootElement(name = "user")
89   @XmlAccessorOrder(XmlAccessOrder.UNDEFINED)
90   @XmlAccessorType(XmlAccessType.FIELD)
91   public class User extends AbstractUserBase implements INameable {
92   
93       /**
94        * The serialVersionUID of the {@code User}.
95        */
96       private static final long serialVersionUID = -5023498623906595440L;
97   
98       /**
99        * the logger.
100       */
101      private static final Logger LOGGER = LoggerFactory.getLogger(User.class);
102  
103      /**
104       * a set of characters which are illegal for the user name.
105       */
106      private static final char[] ILLEGAL_CHARACTERS = ":\\".toCharArray();
107  
108      /**
109       * The email-address of the {@code User}.
110       */
111      @Basic
112      @Column(nullable = false, unique = true)
113      @Index(name = "index_email")
114      @XmlElement(name = "email")
115      private String email = null;
116  
117      /**
118       * The password of the {@code User}.
119       */
120      @Basic
121      @Column(nullable = false)
122      @XmlTransient
123      private String password = null;
124  
125      /**
126       * The superuser-flag of the {@code User}.
127       */
128      @Basic
129      @Column(nullable = false)
130      @XmlElement(name = "superuser")
131      private Boolean superuser = false;
132  
133      /**
134       * The activate-flag of the {@code User}.
135       */
136      // @Basic
137      // @Column(nullable = false)
138      // private Boolean active = true; // NOPMD (see toggleActive())
139      /**
140       * the state of the {@code User}.
141       */
142      @Enumerated(EnumType.STRING)
143      @Column(nullable = false)
144      @XmlElement(name = "state")
145      private State state = State.ACTIVE;
146  
147      /**
148       * The {@code Date} indicating the creation time of the {@code User}.
149       */
150      @Temporal(TemporalType.TIMESTAMP)
151      @Column(nullable = false)
152      @XmlElement(name = "creation-time")
153      private final Date creationTime = new Date();
154  
155      /**
156       * The {@code Date} indicating the last modification time of the
157       * {@code User}.
158       */
159      @Temporal(TemporalType.TIMESTAMP)
160      @Column(nullable = false)
161      @XmlElement(name = "last-modification-time")
162      private Date lastModificationTime = new Date();
163  
164      /**
165       * The {@code Date} indicating the last login time of the {@code User}.
166       */
167      @Temporal(TemporalType.TIMESTAMP)
168      @XmlElement(name = "last-login-time")
169      private Date lastLoginTime = null;
170  
171      /**
172       * The {@code WebdavRole} associated with the {@code User}.
173       */
174      @Basic
175      @XmlElement(name = "webdav-enabled")
176      private Boolean webdavEnabled = Boolean.FALSE;
177  
178      /**
179       * The {@code Group}s associated with the {@code User}.
180       */
181      @ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE,
182              CascadeType.REFRESH })
183      @XmlTransient
184      // getter JAXB-annotated
185      private final Set<Group> groups = new HashSet<Group>();
186  
187      /**
188       * The {@code Permission}s associated with the {@code User}.
189       */
190      @ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE,
191              CascadeType.REFRESH })
192      @XmlTransient
193      // getter JAXB-annotated
194      private final Set<Permission> permissions = new HashSet<Permission>();
195  
196      /**
197       * The {@code Role}s associated with the {@code User}.
198       */
199      @ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE,
200              CascadeType.REFRESH })
201      @XmlTransient
202      // getter JAXB-annotated
203      private final Set<Role> roles = new HashSet<Role>();
204  
205      /**
206       * The {@code Role}s ONLY valid during {@code Session} associated with the
207       * {@code User}.
208       */
209      @Transient
210      @XmlTransient
211      // getter JAXB-annotated -> getRolesJAXB
212      private final Set<Role> sessionRoles = new HashSet<Role>();
213  
214      /**
215       * the values associated with the user.
216       */
217      @OneToMany(targetEntity = AbstractValue.class, mappedBy = "user", cascade = CascadeType.ALL)
218      @XmlTransient
219      // getter JAXB-annotated
220      private final Set<AbstractValue<?>> values = new HashSet<AbstractValue<?>>();
221  
222      /**
223       * The attributes of the {@code User}. TODO: remove when attributes are
224       * finished
225       */
226      @Transient
227      @XmlTransient
228      // ! Not annotated for JAXB at all !
229      private final Set<UserAttribute> attributes = new HashSet<UserAttribute>();
230  
231      /**
232       * the locale used during the sign-up process.
233       */
234      @Basic(optional = true)
235      @XmlElement(name = "sign-up-locale")
236      @XmlJavaTypeAdapter(LocaleXmlAdapter.class)
237      private Locale signUpLocale;
238  
239      /**
240       * used for Hibernate<sup>TM</sup>.
241       */
242      @Deprecated
243      protected User() {
244          super();
245      }
246  
247      /**
248       * The {@code Constructor} that sets name, email-address and password of the
249       * {@code User}.
250       * 
251       * @param newName
252       *            the name of the {@code User}
253       * @param newEmail
254       *            the email-address of the {@code User}
255       * @param newPassword
256       *            the password of the {@code User}
257       */
258      public User(final String newName, final String newEmail,
259              final String newPassword) {
260          super();
261          this.setName(newName);
262          this.setEmail(newEmail);
263          this.setPassword(newPassword);
264      }
265  
266      /**
267       * creates a new {@code User} from a given {@code PreliminaryUser} .
268       * 
269       * @param p
270       *            the preliminary user
271       */
272      public User(final PreliminaryUser p) {
273          super();
274          this.setName(p.getName());
275          this.setEmail(p.getEmail());
276          this.setPassword(p.getPassword());
277          this.setSignUpLocale(p.getSignUpLocale());
278      }
279  
280      /**
281       * Sets the name of the {@code User}.
282       * 
283       * @param newName
284       *            the new name of the {@code User}
285       */
286      @Override
287      public final void setName(final String newName) {
288          if (newName == null) {
289              throw new NullPointerException("User.name must not be set to null.");
290          }
291          for (char c : ILLEGAL_CHARACTERS) {
292              if (newName.indexOf(c) != -1) {
293                  throw new PulseException("Illegal character '" + c
294                          + "' in User.name.");
295              }
296          }
297          super.setName(newName);
298      }
299  
300      /**
301       * Returns the email-address of the {@code User}.
302       * 
303       * @return the email-address of the {@code User}
304       */
305      public final String getEmail() {
306          return this.email;
307      }
308  
309      /**
310       * Sets the email-address of the {@code User}.
311       * 
312       * @param newEmail
313       *            the new email-address of the {@code User}
314       */
315      public final void setEmail(final String newEmail) {
316          if (newEmail == null) {
317              throw new NullPointerException(
318                      "User.email must not be set to null.");
319          }
320          this.email = newEmail;
321      }
322  
323      /**
324       * Checks if a {@code String} pwd is equal to the {@code User}'s password.
325       * 
326       * @param pwd
327       *            the {@code String} used for the equality check
328       * @return true if the {@code String} pwd is equal to the {@code User}'s
329       *         password
330       */
331      public final boolean checkPassword(final String pwd) {
332          try {
333              return this.password.equals(StringUtils.digest16(pwd));
334          } catch (NoSuchAlgorithmException e) {
335              throw new ConfigurationException("Hash algorithm missing: "
336                      + e.getLocalizedMessage(), e);
337          }
338      }
339  
340      /**
341       * returns the digested password.
342       * 
343       * @return the digested password
344       */
345      public final String getPassword() {
346          return this.password;
347      }
348  
349      /**
350       * Sets the password of the {@code User}.
351       * 
352       * @param newPassword
353       *            the new password of the {@code User}
354       */
355      public final void setPassword(final String newPassword) {
356          if (newPassword == null) {
357              throw new NullPointerException(
358                      "User.password must not be set to null.");
359          }
360          try {
361              this.password = StringUtils.digest16(newPassword);
362          } catch (NoSuchAlgorithmException e) {
363              throw new ConfigurationException("Hash algorithm missing: "
364                      + e.getLocalizedMessage(), e);
365          }
366      }
367  
368      /**
369       * Returns the superuser-flag of the {@code User}.
370       * 
371       * @return the superuser-flag of the {@code User}
372       */
373      public final Boolean isSuperuser() {
374          return this.superuser;
375      }
376  
377      /**
378       * Sets the superuser-flag of the {@code User}.
379       * 
380       * @param newFlag
381       *            the new superuser-flag of the {@code User}
382       */
383      public final void setSuperuser(final Boolean newFlag) {
384          if (newFlag == null) {
385              throw new NullPointerException(
386                      "User.superuser must not be set to null.");
387          }
388          this.superuser = newFlag;
389      }
390  
391      /**
392       * Returns the active-flag of the {@code User}.
393       * 
394       * @return the active-flag of the {@code User}
395       */
396      public final Boolean isActive() {
397          return (this.state == State.ACTIVE);
398      }
399  
400      /**
401       * Toggles the active-flag of the {@code User}.
402       */
403      public final void toggleActive() {
404          if (this.state == State.ACTIVE) {
405              this.state = State.INACTIVE;
406          } else if (this.state == State.INACTIVE) {
407              this.state = State.ACTIVE;
408          }
409      }
410  
411      /**
412       * returns whether the {@code User} is expunged.
413       * 
414       * @return {@code true}, if the {@code User} is expunged
415       */
416      public final boolean isExpunged() {
417          return (this.state == State.EXPUNGED);
418      }
419  
420      /**
421       * expunges the {@code User}.
422       * 
423       * @return {@code true}, if and only if the {@code User} could be expunged.
424       *         Otherwise {@code false}.
425       */
426      public final boolean expunge() {
427          if (this.state == State.EXPUNGED) {
428              return false;
429          }
430          this.state = State.EXPUNGED;
431  
432          this.roles.clear();
433          this.groups.clear();
434          this.permissions.clear();
435  
436          this.webdavEnabled = false;
437          this.superuser = false;
438  
439          this.password = "";
440  
441          this.values.clear();
442          this.attributes.clear();
443  
444          return true;
445      }
446  
447      /**
448       * Returns the {@code Date} indicating the creation time of the {@code User}
449       * .
450       * 
451       * @return the {@code Date} indicating the creation time of the {@code User}
452       */
453      private Date getCreationTime() {
454          if (this.creationTime == null) {
455              return null;
456          }
457          return new Date(this.creationTime.getTime());
458      }
459  
460      /**
461       * Returns the {@code Date} indicating the last modification time of the
462       * {@code User}.
463       * 
464       * @return the {@code Date} indicating the last modification time of the
465       *         {@code User}
466       */
467      private Date getLastModificationTime() {
468          if (this.lastModificationTime == null) {
469              return null;
470          }
471          return new Date(this.lastModificationTime.getTime());
472      }
473  
474      /**
475       * Sets the {@code Date} indicating the last modification time of the
476       * {@code User} to the present moment.
477       */
478      public final void setLastModificationTime() {
479          this.lastModificationTime = new Date();
480      }
481  
482      /**
483       * Returns the {@code Date} indicating the last login time of the
484       * {@code User}.
485       * 
486       * @return the {@code Date} indicating the last login time of the
487       *         {@code User}
488       */
489      public Date getLastLoginTime() {
490          if (this.lastLoginTime == null) {
491              return null;
492          }
493          return new Date(this.lastLoginTime.getTime());
494      }
495  
496      /**
497       * Sets the {@code Date} indicating the last login time of the {@code User}
498       * to the present moment.
499       */
500      public final void setLastLoginTime() {
501          this.lastLoginTime = new Date();
502      }
503  
504      /**
505       * Returns a shallow copy of the {@code User}'s {@code Collection} of
506       * {@code Group}s.
507       * 
508       * <p>
509       * Attention: Since the returned {@code Set} is not a reference to the
510       * {@code User}'s internal {@code Collection}, any modifications to its
511       * state will get lost. However, modifications to the state of the
512       * {@code Set}'s elements will be consistent.
513       * </p>
514       * 
515       * <p>
516       * For example: Adding/Deleting a {@code Group} to/from the {@code Set} will
517       * not have any effect, when the {@code User} is saved. On the other hand,
518       * modifications to a {@code Group} in the {@code Set} will be made
519       * persistent, when the {@code Group} is saved.
520       * </p>
521       * 
522       * @return the {@code Group}s associated with the {@code User}
523       */
524      public final Set<Group> getGroups() {
525          return new HashSet<Group>(this.groups);
526      }
527  
528      /**
529       * For JAXB only.
530       * 
531       * @return this.getGroups()
532       */
533      @XmlElementWrapper(name = "groups")
534      @XmlElement(name = "group")
535      @SuppressWarnings("unused")
536      @Deprecated
537      private HashSet<Group> getGroupsJAXB() { // NOPMD
538          try {
539              return new HashSet<Group>(getGroups());
540          } catch (LazyInitializationException e) {
541              LOGGER.debug("ignored: {}", e.getLocalizedMessage());
542              return null;
543          }
544      }
545  
546      /**
547       * Adds a {@code Group} to the {@code User}'s {@code Set} of {@code Group}s.
548       * If it already is part of the {@code Set}, it will not be added.
549       * 
550       * @param grp
551       *            the {@code Group} to be added
552       * @return true if the {@code Set} changed as a result of the call
553       */
554      public final boolean addGroup(final Group grp) {
555          return this.groups.add(grp);
556      }
557  
558      /**
559       * Removes a {@code Group} from the {@code User}'s {@code Set} of
560       * {@code Group}s if it is part of it.
561       * 
562       * @param grp
563       *            the {@code Group} to be removed
564       * @return true if the {@code Set} changed as a result of the call
565       */
566      public final boolean removeGroup(final Group grp) {
567          return this.groups.remove(grp);
568      }
569  
570      /**
571       * Returns a shallow copy of the {@code User}'s {@code Collection} of
572       * {@code Permission}s.
573       * 
574       * <p>
575       * Attention: Since the returned {@code Set} is not a reference to the
576       * {@code User}'s internal {@code Collection}, any modifications to its
577       * state will get lost. However, modifications to the state of the
578       * {@code Set}'s elements will be consistent.
579       * </p>
580       * 
581       * <p>
582       * For example: Adding/Deleting a {@code Permission} to/from the {@code Set}
583       * will not have any effect, when the {@code User} is saved. On the other
584       * hand, modifications to a {@code Permission} in the {@code Set} will be
585       * made persistent, when the {@code Permission} is saved.
586       * </p>
587       * 
588       * @return the {@code Permission}s associated with the {@code User}
589       */
590      public final Set<Permission> getPermissions() {
591          return new HashSet<Permission>(this.permissions);
592      }
593  
594      /**
595       * For JAXB only.
596       * 
597       * @return this.getPermissions()
598       */
599      @XmlElementWrapper(name = "permissions")
600      @XmlElement(name = "permission")
601      @SuppressWarnings("unused")
602      @Deprecated
603      private HashSet<Permission> getPermissionsJAXB() { // NOPMD
604          try {
605              return new HashSet<Permission>(getPermissions());
606          } catch (LazyInitializationException e) {
607              LOGGER.debug("ignored: {}", e.getLocalizedMessage());
608              return null;
609          }
610      }
611  
612      /**
613       * Adds a {@code Permission} to the {@code User}'s {@code Set} of
614       * {@code Permission}s. If it already is part of the {@code Set}, it will
615       * not be added.
616       * 
617       * @param permission
618       *            the {@code Permission} to be added
619       * @return true if the {@code Set} changed as a result of the call
620       */
621      public final boolean addPermission(final Permission permission) {
622          if (permission == null) {
623              return false;
624          }
625          return this.permissions.add(permission);
626      }
627  
628      /**
629       * Removes a {@code Permission} from the {@code User}'s {@code Set} of
630       * {@code Permission}s if it is part of it.
631       * 
632       * @param permission
633       *            the {@code Permission} to be removed
634       * @return true if the {@code Set} changed as a result of the call
635       */
636      public final boolean removePermission(final Permission permission) {
637          if (permission == null) {
638              return false;
639          }
640          return this.permissions.remove(permission);
641      }
642  
643      /**
644       * Returns all {@code Role}s associated with the current {@code User}, which
645       * can be {@code Session}-valid-only {@code Role}s as well as the persisted
646       * {@code Role}s.
647       * 
648       * @return all {@code Role}s associated with the {@code User} which are
649       *         getPersistentRoles() + getSessionRoles()
650       */
651      public final Set<Role> getRoles() {
652          HashSet<Role> allRoles = new HashSet<Role>(getPersistentRoles());
653          if (!this.getSessionRoles().isEmpty()) {
654              allRoles.addAll(getSessionRoles());
655          }
656          return allRoles;
657      }
658  
659      /**
660       * For JAXB only.
661       * 
662       * @return this.getPersistentRoles() & this.getSessionRoles()
663       */
664      @XmlElementWrapper(name = "roles")
665      @XmlElement(name = "role")
666      @SuppressWarnings("unused")
667      @Deprecated
668      private HashSet<Role> getRolesJAXB() { // NOPMD
669          try {
670              return new HashSet<Role>(getRoles());
671          } catch (LazyInitializationException e) {
672              LOGGER.debug("ignored: {}", e.getLocalizedMessage());
673              return null;
674          }
675      }
676  
677      /**
678       * Returns a shallow copy of the {@code User}'s {@code Collection} of
679       * {@code Role}s.
680       * 
681       * <p>
682       * Attention: Since the returned {@code Set} is not a reference to the
683       * {@code User}'s internal {@code Collection}, any modifications to its
684       * state will get lost. However, modifications to the state of the
685       * {@code Set}'s elements will be consistent.
686       * </p>
687       * 
688       * <p>
689       * For example: Adding/Deleting a {@code Role} to/from the {@code Set} will
690       * not have any effect, when the {@code User} is saved. On the other hand,
691       * modifications to a {@code Role} in the {@code Set} will be made
692       * persistent, when the {@code Role} is saved.
693       * </p>
694       * 
695       * @return the {@code Role}s associated with the {@code User}
696       */
697      public final Set<Role> getPersistentRoles() {
698          return new HashSet<Role>(this.roles);
699      }
700  
701      /**
702       * Adds a {@code Role} to the {@code User}'s {@code Set} of {@code Role}s.
703       * If it already is part of the {@code Set}, it will not be added.
704       * 
705       * @param rl
706       *            the {@code Role} to be added
707       * @return true if the {@code Set} changed as a result of the call
708       */
709      public final boolean addPersistentRole(final Role rl) {
710          return this.roles.add(rl);
711      }
712  
713      /**
714       * Removes a {@code Role} from the {@code User}'s {@code Set} of
715       * {@code Role}s if it is part of it.
716       * 
717       * @param rl
718       *            the {@code Role} to be removed
719       * @return true if the {@code Set} changed as a result of the call
720       */
721      public final boolean removePersistentRole(final Role rl) {
722          return this.roles.remove(rl);
723      }
724  
725      /**
726       * @return the {@code Role}s associated with the {@code User} for the
727       *         current {@code Session}
728       */
729      public final Set<Role> getSessionRoles() {
730          return new HashSet<Role>(this.sessionRoles);
731      }
732  
733      /**
734       * Adds a {@code Role} to the {@code User}'s {@code Set} of {@code Session}
735       * -valid-only {@code Role}s. If it already is part of the {@code Set}, it
736       * will not be added.
737       * 
738       * @param rl
739       *            the {@code Role} to be added
740       * @return true if the {@code Set} changed as a result of the call
741       */
742      public final boolean addSessionRole(final Role rl) {
743          return this.sessionRoles.add(rl);
744      }
745  
746      /**
747       * Removes a {@code Role} from the {@code User}'s {@code Set} of
748       * {@code Session}-valid-only {@code Role}s if it is part of it.
749       * 
750       * @param rl
751       *            the {@code Role} to be removed
752       * @return true if the {@code Set} changed as a result of the call
753       */
754      public final boolean removeSessionRole(final Role rl) {
755          return this.sessionRoles.remove(rl);
756      }
757  
758      /**
759       * returns whether the user is enabled for WebDAV.
760       * 
761       * @return whether the user is enabled for WebDAV
762       */
763      public final boolean isWebdavEnabled() {
764          return this.webdavEnabled.booleanValue();
765      }
766  
767      /**
768       * sets whether the user is enabled for WebDAV.
769       * 
770       * @param b
771       *            the value to set
772       */
773      public final void setWebdavEnabled(final boolean b) {
774          this.webdavEnabled = Boolean.valueOf(b);
775      }
776  
777      /**
778       * returns the user's values.
779       * 
780       * @return the attribute's value or empty {@code Set&lt;AbstractValue&gt;}
781       */
782      public final Set<? extends AbstractValue<?>> getAttributeValues() {
783          return this.values;
784      }
785  
786      /**
787       * For JAXB only.
788       * 
789       * @return this.getAttributeValues()
790       */
791      @XmlElementWrapper(name = "attribute-values")
792      @XmlElement(name = "attribute-value")
793      @SuppressWarnings("unused")
794      @Deprecated
795      private HashSet<? extends AbstractValue<?>> getAttributeValuesJAXB() { // NOPMD
796          try {
797              return new HashSet<AbstractValue<?>>(getAttributeValues());
798          } catch (LazyInitializationException e) {
799              LOGGER.debug("ignored: {}", e.getLocalizedMessage());
800              return null;
801          }
802      }
803  
804      /**
805       * returns the user's value for the given attribute, or {@code null}, if the
806       * user does not have a stored value for the given attribute.
807       * 
808       * @param a
809       *            the attribute
810       * @return the attribute's value or {@code null}
811       */
812      public final AbstractValue<?> getAttributeValue(final AbstractAttribute<?> a) {
813          for (AbstractValue<?> v : this.values) {
814              if (v.getAttribute().equals(a)) {
815                  return v;
816              }
817          }
818          return null;
819      }
820  
821      /**
822       * sets the given value for the user.
823       * 
824       * @param v
825       *            the value to set
826       */
827      public final void setAttributeValue(final AbstractValue<?> v) {
828          if ((v.getUser() != null) && (!v.getUser().equals(this))) {
829              throw new IllegalArgumentException("The given value is "
830                      + "already associated with a different user!");
831          }
832          if (v.getUser() == null) {
833              v.setUser(this);
834          }
835          this.values.add(v);
836      }
837  
838      /**
839       * removes the value of a given attribute form the user.
840       * 
841       * @param a
842       *            the attribute
843       * @return either the removed value or null
844       */
845      public final AbstractValue<?> removeAttributeValue(
846              final AbstractAttribute<?> a) {
847          AbstractValue<?> v = getAttributeValue(a);
848          if (v != null) {
849              this.values.remove(v);
850          }
851          return v;
852      }
853  
854      /**
855       * Checks if the associated {@code User} has the necessary
856       * {@code Permission}s for executing a {@code Command}.
857       * 
858       * @param command
859       *            the {@code Command} defining the necessary {@code Permission}s
860       * @return {@code true} if the associated {@code User} has the necessary
861       *         {@code Permission}s
862       */
863      public final boolean hasAllowanceFor(final Command command) {
864          // if the user is a superuser, he is allowed to do anything he likes
865          if (this.isSuperuser()) {
866              return true;
867          }
868  
869          // check against the user's command matchers (collected during init)
870          for (CommandMatcher matcher : getCommandMatchers()) {
871              if (matcher.matchesCommand(command)) {
872                  return true;
873              }
874          }
875          return false;
876      }
877  
878      /**
879       * Checks if the associated {@code User} has the necessary {@code Role}s for
880       * accessing a {@code SitemapNode}.
881       * 
882       * @param node
883       *            the node to check against
884       * @return {@code true}, if either the node is {@code null}, the node has no
885       *         roles or the user does contain at least one of the roles
886       *         attached. Otherwise {@code false}.
887       */
888      public final boolean hasAllowanceFor(final SitemapNode node) {
889          if (node == null) {
890              return true;
891          }
892          /* check duration */
893          Duration duration = node.getDuration();
894          if ((duration != null)
895                  && !duration.contains(System.currentTimeMillis())) {
896              return false;
897          }
898          /* check roles */
899          if (!node.getRoles().isEmpty()) {
900              for (Role r : node.getRoles()) {
901                  /*
902                   * if the current role is ~Everybody or part of the user's roles
903                   */
904                  if ((r.getId().equals(getEveryBodyId()))
905                          || getRoles().contains(r)) {
906                      return true;
907                  }
908              }
909              return false;
910          }
911          return true;
912      }
913  
914      /**
915       * returns the attributes of the {@code User}.
916       * 
917       * @return the attributes of the {@code User}
918       */
919      @Deprecated
920      public final Set<UserAttribute> getAttributes() {
921          return this.attributes;
922      }
923  
924      /**
925       * sets an attribute of the {@code User}.
926       * 
927       * @param att
928       *            the attribute to be set
929       * @return {@code true}, if and only if, the attribute was successfully set
930       */
931      protected final boolean addAttribute(final UserAttribute att) {
932          return this.attributes.add(att);
933      }
934  
935      /**
936       * removes an attribute from the {@code User}.
937       * 
938       * @param att
939       *            the attribute to be removed
940       * @return {@code true}, if and only if, the attribute was successfully
941       *         removed
942       */
943      protected final boolean removeAttribute(final UserAttribute att) {
944          return this.attributes.remove(att);
945      }
946  
947      /**
948       * clears all attributes of the {@code User}.
949       */
950      protected final void clearAttributes() {
951          this.attributes.clear();
952      }
953  
954      /**
955       * Serializes the state of the {@code User} as a JDOM {@code Element}.
956       * 
957       * @return the state of the {@code User} as a JDOM {@code Element}
958       */
959      public final Element deserializeToJDOM() {
960          Element element = deserializeToJDOMShallow();
961          deserializeGroupsPermissionsRoles(element);
962  
963          // adds the attributes to the User
964          deserializeAttributes(element);
965  
966          return element;
967      }
968  
969      /**
970       * returns the sign-up locale.
971       * 
972       * @return the sign-up locale
973       */
974      public final Locale getSignUpLocale() {
975          return signUpLocale;
976      }
977  
978      /**
979       * sets the sign-up locale.
980       * 
981       * @param l
982       *            the locale
983       */
984      public final void setSignUpLocale(final Locale l) {
985          this.signUpLocale = l;
986      }
987  
988      /**
989       * serialises the {@code User} without {@code Group}, {@code Role},
990       * {@code Permission} and {@code Attribute} information.
991       * 
992       * @return the user without group, role, permission and attribute
993       *         information as a JDOM element.
994       */
995      public final Element deserializeToJDOMShallow() {
996          Element element = new Element(getClass().getSimpleName());
997          if (getId() != null) {
998              element.setAttribute("id", getId().toString());
999          }
1000         element.setAttribute("class", getClass().getCanonicalName());
1001         element.setAttribute("name", getName());
1002         element.setAttribute("email", getEmail());
1003         element.setAttribute("superuser", isSuperuser().toString());
1004         element.setAttribute("active", isActive().toString());
1005         element.setAttribute("creationTime", getCreationTime().toString());
1006         element.setAttribute("lastModificationTime", getLastModificationTime()
1007                 .toString());
1008         if (this.getLastLoginTime() != null) {
1009             element.setAttribute("lastLoginTime", getLastLoginTime().toString());
1010         } else {
1011             element.setAttribute("lastLoginTime", "");
1012         }
1013         if (this.signUpLocale != null) {
1014             element.setAttribute("sign-up-locale", this.signUpLocale.toString());
1015         }
1016 
1017         element.setAttribute("webdav-enabled", this.webdavEnabled.toString());
1018         return element;
1019     }
1020 
1021     /**
1022      * adds the attribute-tree and sets the values of the {@code User}.
1023      * 
1024      * @param element
1025      *            the result element
1026      */
1027     private void deserializeAttributes(final Element element) {
1028 
1029         Session s = Lifecycle.getHibernateDataSource().createNewSession();
1030         Transaction tx = s.beginTransaction();
1031         try {
1032 
1033             // build "attribute-id -> value"-map
1034             Map<Long, AbstractValue<?>> valueMap = new HashMap<Long, AbstractValue<?>>();
1035             if (!(this instanceof User.Everybody)) {
1036                 s.refresh(this);
1037             }
1038             for (AbstractValue<?> abv : getAttributeValues()) {
1039                 valueMap.put(abv.getAttribute().getId(), abv);
1040             }
1041 
1042             // retrieve attribute-registry
1043             AttributeRegistry attributeRegistry = (AttributeRegistry) s.get(
1044                     AttributeRegistry.class, 1L);
1045 
1046             // build the users attribute-tree
1047             Set<BundleAttribute> rootnodes = attributeRegistry.getRootNodes();
1048             Element root = new Element("attributes");
1049             for (BundleAttribute b : rootnodes) {
1050                 root.addContent(((AbstractAttribute<?>) b)
1051                         .deserializeToJDOM(valueMap));
1052             }
1053             element.addContent(root);
1054 
1055             tx.commit();
1056         } catch (Exception e) {
1057             tx.rollback();
1058             throw new PulseException("Error: " + e.getLocalizedMessage(), e);
1059         } finally {
1060             s.close();
1061         }
1062 
1063     }
1064 
1065     /**
1066      * deserialises the user's groups, permissions and roles.
1067      * 
1068      * @param element
1069      *            the result element
1070      */
1071     private void deserializeGroupsPermissionsRoles(final Element element) {
1072         try {
1073             for (Group group : getGroups()) {
1074                 element.addContent(group.deserializeToJDOM());
1075             }
1076         } catch (LazyInitializationException exception) {
1077             element.removeChildren(Group.class.getSimpleName());
1078         }
1079         try {
1080             for (Permission permission : getPermissions()) {
1081                 element.addContent(permission.deserializeToJDOM());
1082             }
1083         } catch (LazyInitializationException exception) {
1084             element.removeChildren(Permission.class.getSimpleName());
1085         }
1086         try {
1087             for (AbstractAccessControlObject role : getRoles()) {
1088                 element.addContent(role.deserializeToJDOM());
1089             }
1090         } catch (LazyInitializationException exception) {
1091             element.removeChildren(Role.class.getSimpleName());
1092         }
1093     }
1094 
1095     /**
1096      * Serialises the state of the {@code User} as a {@code JSONObject}.
1097      * 
1098      * @return the state of the {@code User} as a {@code JSONObject}
1099      */
1100     @Override
1101     public final JSONObject toJSON() {
1102 
1103         JSONObject json = super.toJSON();
1104 
1105         json.put("name", getName());
1106         json.put("email", getEmail());
1107 
1108         json.put("superuser", isSuperuser());
1109         json.put("active", isActive());
1110         json.put("creationTime", getCreationTime().toString());
1111         json.put("lastModificationTime", getLastModificationTime().toString());
1112         if (getLastLoginTime() != null) {
1113             json.put("lastLoginTime", getLastLoginTime().toString());
1114         } else {
1115             json.put("lastLoginTime", "");
1116         }
1117         json.put("webdavEnabled", this.webdavEnabled);
1118 
1119         // adds info for ext-tree
1120         json.put("uiProvider", "AccessControlTreeNodeUI");
1121         json.put("leaf", true);
1122         json.put("expandable", false);
1123         json.put("allowChildren", false);
1124         json.put("text", getName());
1125 
1126         return json;
1127     }
1128 
1129     /**
1130      * is a dummy {@code User} used for non-logged-in users to provide them the
1131      * "{@code ~Everybody}" {@code Role} for access checks.
1132      * <p>
1133      * The user will return an empty string as the name and otherwise behave
1134      * like a normal user.
1135      * </p>
1136      * 
1137      * @author Thomas Weber
1138      */
1139     public static class Everybody extends User {
1140 
1141         /**
1142          * serialVersionUID.
1143          */
1144         private static final long serialVersionUID = 5070002854317169181L;
1145 
1146         /**
1147          * initialises the {@code Everybody}.
1148          * 
1149          * @param everybodyRole
1150          *            the "{@code ~Everybody}" {@code Role}
1151          */
1152         public Everybody(final Role everybodyRole) {
1153             super();
1154             setName("");
1155             setEmail("");
1156             addPersistentRole(everybodyRole);
1157         }
1158 
1159     }
1160 
1161     /**
1162      * the state of the {@code User}.
1163      * 
1164      * @author Thomas Weber
1165      */
1166     public enum State {
1167 
1168         /**
1169          * the user is not active.
1170          */
1171         INACTIVE,
1172 
1173         /**
1174          * the user is active.
1175          */
1176         ACTIVE,
1177 
1178         /**
1179          * the user is expunged, but could not be deleted due to foreign key
1180          * constraints.
1181          */
1182         EXPUNGED;
1183 
1184     }
1185 
1186 }
1187