1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *   http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied.  See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 
20 package org.apache.guacamole.auth.jdbc.user;
21 
22 import com.google.inject.Inject;
23 import com.google.inject.Provider;
24 import java.sql.Date;
25 import java.sql.Time;
26 import java.sql.Timestamp;
27 import java.text.ParseException;
28 import java.util.Arrays;
29 import java.util.Calendar;
30 import java.util.Collection;
31 import java.util.Collections;
32 import java.util.HashSet;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Set;
36 import java.util.TimeZone;
37 import org.apache.guacamole.auth.jdbc.security.PasswordEncryptionService;
38 import org.apache.guacamole.auth.jdbc.security.SaltService;
39 import org.apache.guacamole.GuacamoleException;
40 import org.apache.guacamole.auth.jdbc.base.ModeledPermissions;
41 import org.apache.guacamole.form.BooleanField;
42 import org.apache.guacamole.form.DateField;
43 import org.apache.guacamole.form.EmailField;
44 import org.apache.guacamole.form.Field;
45 import org.apache.guacamole.form.Form;
46 import org.apache.guacamole.form.TextField;
47 import org.apache.guacamole.form.TimeField;
48 import org.apache.guacamole.form.TimeZoneField;
49 import org.apache.guacamole.net.auth.ActivityRecord;
50 import org.apache.guacamole.net.auth.ActivityRecordSet;
51 import org.apache.guacamole.net.auth.Permissions;
52 import org.apache.guacamole.net.auth.RelatedObjectSet;
53 import org.apache.guacamole.net.auth.User;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56 
57 /**
58  * An implementation of the User object which is backed by a database model.
59  */
60 public class ModeledUser extends ModeledPermissions<UserModel> implements User {
61 
62     /**
63      * Logger for this class.
64      */
65     private static final Logger logger = LoggerFactory.getLogger(ModeledUser.class);
66 
67     /**
68      * The name of the attribute which controls whether a user account is
69      * disabled.
70      */
71     public static final String DISABLED_ATTRIBUTE_NAME = "disabled";
72 
73     /**
74      * The name of the attribute which controls whether a user's password is
75      * expired and must be reset upon login.
76      */
77     public static final String EXPIRED_ATTRIBUTE_NAME = "expired";
78 
79     /**
80      * The name of the attribute which controls the time of day after which a
81      * user may login.
82      */
83     public static final String ACCESS_WINDOW_START_ATTRIBUTE_NAME = "access-window-start";
84 
85     /**
86      * The name of the attribute which controls the time of day after which a
87      * user may NOT login.
88      */
89     public static final String ACCESS_WINDOW_END_ATTRIBUTE_NAME = "access-window-end";
90 
91     /**
92      * The name of the attribute which controls the date after which a user's
93      * account is valid.
94      */
95     public static final String VALID_FROM_ATTRIBUTE_NAME = "valid-from";
96 
97     /**
98      * The name of the attribute which controls the date after which a user's
99      * account is no longer valid.
100      */
101     public static final String VALID_UNTIL_ATTRIBUTE_NAME = "valid-until";
102 
103     /**
104      * The name of the attribute which defines the time zone used for all
105      * time and date attributes related to this user.
106      */
107     public static final String TIMEZONE_ATTRIBUTE_NAME = "timezone";
108 
109     /**
110      * All attributes related to user profile information, within a logical
111      * form.
112      */
113     public static final Form PROFILE = new Form("profile", Arrays.<Field>asList(
114         new TextField(User.Attribute.FULL_NAME),
115         new EmailField(User.Attribute.EMAIL_ADDRESS),
116         new TextField(User.Attribute.ORGANIZATION),
117         new TextField(User.Attribute.ORGANIZATIONAL_ROLE)
118     ));
119 
120     /**
121      * All attributes related to restricting user accounts, within a logical
122      * form.
123      */
124     public static final Form ACCOUNT_RESTRICTIONS = new Form("restrictions", Arrays.<Field>asList(
125         new BooleanField(DISABLED_ATTRIBUTE_NAME, "true"),
126         new BooleanField(EXPIRED_ATTRIBUTE_NAME, "true"),
127         new TimeField(ACCESS_WINDOW_START_ATTRIBUTE_NAME),
128         new TimeField(ACCESS_WINDOW_END_ATTRIBUTE_NAME),
129         new DateField(VALID_FROM_ATTRIBUTE_NAME),
130         new DateField(VALID_UNTIL_ATTRIBUTE_NAME),
131         new TimeZoneField(TIMEZONE_ATTRIBUTE_NAME)
132     ));
133 
134     /**
135      * All possible attributes of user objects organized as individual,
136      * logical forms.
137      */
138     public static final Collection<Form> ATTRIBUTES = Collections.unmodifiableCollection(Arrays.asList(
139         PROFILE,
140         ACCOUNT_RESTRICTIONS
141     ));
142 
143     /**
144      * The names of all attributes which are explicitly supported by this
145      * extension's User objects.
146      */
147     public static final Set<String> ATTRIBUTE_NAMES =
148             Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
149                 User.Attribute.FULL_NAME,
150                 User.Attribute.EMAIL_ADDRESS,
151                 User.Attribute.ORGANIZATION,
152                 User.Attribute.ORGANIZATIONAL_ROLE,
153                 DISABLED_ATTRIBUTE_NAME,
154                 EXPIRED_ATTRIBUTE_NAME,
155                 ACCESS_WINDOW_START_ATTRIBUTE_NAME,
156                 ACCESS_WINDOW_END_ATTRIBUTE_NAME,
157                 VALID_FROM_ATTRIBUTE_NAME,
158                 VALID_UNTIL_ATTRIBUTE_NAME,
159                 TIMEZONE_ATTRIBUTE_NAME
160             )));
161 
162     /**
163      * Service for managing users.
164      */
165     @Inject
166     private UserService userService;
167 
168     /**
169      * Service for hashing passwords.
170      */
171     @Inject
172     private PasswordEncryptionService encryptionService;
173 
174     /**
175      * Service for providing secure, random salts.
176      */
177     @Inject
178     private SaltService saltService;
179 
180     /**
181      * Provider for RelatedObjectSets containing the user groups of which this
182      * user is a member.
183      */
184     @Inject
185     private Provider<UserParentUserGroupSet> parentUserGroupSetProvider;
186 
187     /**
188      * Provider for creating user record sets.
189      */
190     @Inject
191     private Provider<UserRecordSet> userRecordSetProvider;
192 
193     /**
194      * Whether attributes which control access restrictions should be exposed
195      * via getAttributes() or allowed to be set via setAttributes().
196      */
197     private boolean exposeRestrictedAttributes = false;
198 
199     /**
200      * Initializes this ModeledUser, associating it with the current
201      * authenticated user and populating it with data from the given user
202      * model.
203      *
204      * @param currentUser
205      *     The user that created or retrieved this object.
206      *
207      * @param model
208      *     The backing model object.
209      *
210      * @param exposeRestrictedAttributes
211      *     Whether attributes which control access restrictions should be
212      *     exposed via getAttributes() or allowed to be set via
213      *     setAttributes().
214      */
init(ModeledAuthenticatedUser currentUser, UserModel model, boolean exposeRestrictedAttributes)215     public void init(ModeledAuthenticatedUser currentUser, UserModel model,
216             boolean exposeRestrictedAttributes) {
217         super.init(currentUser, model);
218         this.exposeRestrictedAttributes = exposeRestrictedAttributes;
219     }
220 
221     /**
222      * The plaintext password previously set by a call to setPassword(), if
223      * any. The password of a user cannot be retrieved once saved into the
224      * database, so this serves to ensure getPassword() returns a reasonable
225      * value if setPassword() is called. If no password has been set, or the
226      * user was retrieved from the database, this will be null.
227      */
228     private String password = null;
229 
230     /**
231      * The data associated with this user's password at the time this user was
232      * queried. If the user is new, this will be null.
233      */
234     private PasswordRecordModel passwordRecord = null;
235 
236     /**
237      * Creates a new, empty ModeledUser.
238      */
ModeledUser()239     public ModeledUser() {
240     }
241 
242     @Override
setModel(UserModel model)243     public void setModel(UserModel model) {
244 
245         super.setModel(model);
246 
247         // Store previous password, if any
248         if (model.getPasswordHash() != null)
249             this.passwordRecord = new PasswordRecordModel(model);
250 
251     }
252 
253     @Override
getPassword()254     public String getPassword() {
255         return password;
256     }
257 
258     @Override
setPassword(String password)259     public void setPassword(String password) {
260 
261         UserModel userModel = getModel();
262 
263         // Store plaintext password internally
264         this.password = password;
265 
266         // If no password provided, set random password
267         if (password == null) {
268             userModel.setPasswordSalt(saltService.generateSalt());
269             userModel.setPasswordHash(saltService.generateSalt());
270         }
271 
272         // Otherwise generate new salt and hash given password using newly-generated salt
273         else {
274             byte[] salt = saltService.generateSalt();
275             byte[] hash = encryptionService.createPasswordHash(password, salt);
276 
277             // Set stored salt and hash
278             userModel.setPasswordSalt(salt);
279             userModel.setPasswordHash(hash);
280         }
281 
282         userModel.setPasswordDate(new Timestamp(System.currentTimeMillis()));
283 
284     }
285 
286     /**
287      * Returns the this user's current password record. If the user is new, this
288      * will be null. Note that this may represent a different password than what
289      * is returned by getPassword(): unlike the other password-related functions
290      * of ModeledUser, the data returned by this function is historical and is
291      * unaffected by calls to setPassword(). It will always return the values
292      * stored in the database at the time this user was queried.
293      *
294      * @return
295      *     The historical data associated with this user's password, or null if
296      *     the user is new.
297      */
getPasswordRecord()298     public PasswordRecordModel getPasswordRecord() {
299         return passwordRecord;
300     }
301 
302     /**
303      * Stores all restricted (privileged) attributes within the given Map,
304      * pulling the values of those attributes from the underlying user model.
305      * If no value is yet defined for an attribute, that attribute will be set
306      * to null.
307      *
308      * @param attributes
309      *     The Map to store all restricted attributes within.
310      */
putRestrictedAttributes(Map<String, String> attributes)311     private void putRestrictedAttributes(Map<String, String> attributes) {
312 
313         // Set disabled attribute
314         attributes.put(DISABLED_ATTRIBUTE_NAME, getModel().isDisabled() ? "true" : null);
315 
316         // Set password expired attribute
317         attributes.put(EXPIRED_ATTRIBUTE_NAME, getModel().isExpired() ? "true" : null);
318 
319         // Set access window start time
320         attributes.put(ACCESS_WINDOW_START_ATTRIBUTE_NAME, TimeField.format(getModel().getAccessWindowStart()));
321 
322         // Set access window end time
323         attributes.put(ACCESS_WINDOW_END_ATTRIBUTE_NAME, TimeField.format(getModel().getAccessWindowEnd()));
324 
325         // Set account validity start date
326         attributes.put(VALID_FROM_ATTRIBUTE_NAME, DateField.format(getModel().getValidFrom()));
327 
328         // Set account validity end date
329         attributes.put(VALID_UNTIL_ATTRIBUTE_NAME, DateField.format(getModel().getValidUntil()));
330 
331         // Set timezone attribute
332         attributes.put(TIMEZONE_ATTRIBUTE_NAME, getModel().getTimeZone());
333 
334     }
335 
336     /**
337      * Stores all unrestricted (unprivileged) attributes within the given Map,
338      * pulling the values of those attributes from the underlying user model.
339      * If no value is yet defined for an attribute, that attribute will be set
340      * to null.
341      *
342      * @param attributes
343      *     The Map to store all unrestricted attributes within.
344      */
putUnrestrictedAttributes(Map<String, String> attributes)345     private void putUnrestrictedAttributes(Map<String, String> attributes) {
346 
347         // Set full name attribute
348         attributes.put(User.Attribute.FULL_NAME, getModel().getFullName());
349 
350         // Set email address attribute
351         attributes.put(User.Attribute.EMAIL_ADDRESS, getModel().getEmailAddress());
352 
353         // Set organization attribute
354         attributes.put(User.Attribute.ORGANIZATION, getModel().getOrganization());
355 
356         // Set role attribute
357         attributes.put(User.Attribute.ORGANIZATIONAL_ROLE, getModel().getOrganizationalRole());
358 
359     }
360 
361     /**
362      * Parses the given string into a corresponding date. The string must
363      * follow the standard format used by date attributes, as defined by
364      * DateField.FORMAT and as would be produced by DateField.format().
365      *
366      * @param dateString
367      *     The date string to parse, which may be null.
368      *
369      * @return
370      *     The date corresponding to the given date string, or null if the
371      *     provided date string was null or blank.
372      *
373      * @throws ParseException
374      *     If the given date string does not conform to the standard format
375      *     used by date attributes.
376      */
parseDate(String dateString)377     private Date parseDate(String dateString)
378     throws ParseException {
379 
380         // Return null if no date provided
381         java.util.Date parsedDate = DateField.parse(dateString);
382         if (parsedDate == null)
383             return null;
384 
385         // Convert to SQL Date
386         return new Date(parsedDate.getTime());
387 
388     }
389 
390     /**
391      * Parses the given string into a corresponding time. The string must
392      * follow the standard format used by time attributes, as defined by
393      * TimeField.FORMAT and as would be produced by TimeField.format().
394      *
395      * @param timeString
396      *     The time string to parse, which may be null.
397      *
398      * @return
399      *     The time corresponding to the given time string, or null if the
400      *     provided time string was null or blank.
401      *
402      * @throws ParseException
403      *     If the given time string does not conform to the standard format
404      *     used by time attributes.
405      */
parseTime(String timeString)406     private Time parseTime(String timeString)
407     throws ParseException {
408 
409         // Return null if no time provided
410         java.util.Date parsedDate = TimeField.parse(timeString);
411         if (parsedDate == null)
412             return null;
413 
414         // Convert to SQL Time
415         return new Time(parsedDate.getTime());
416 
417     }
418 
419     /**
420      * Stores all restricted (privileged) attributes within the underlying user
421      * model, pulling the values of those attributes from the given Map.
422      *
423      * @param attributes
424      *     The Map to pull all restricted attributes from.
425      */
setRestrictedAttributes(Map<String, String> attributes)426     private void setRestrictedAttributes(Map<String, String> attributes) {
427 
428         // Translate disabled attribute
429         getModel().setDisabled("true".equals(attributes.get(DISABLED_ATTRIBUTE_NAME)));
430 
431         // Translate password expired attribute
432         getModel().setExpired("true".equals(attributes.get(EXPIRED_ATTRIBUTE_NAME)));
433 
434         // Translate access window start time
435         try { getModel().setAccessWindowStart(parseTime(attributes.get(ACCESS_WINDOW_START_ATTRIBUTE_NAME))); }
436         catch (ParseException e) {
437             logger.warn("Not setting start time of user access window: {}", e.getMessage());
438             logger.debug("Unable to parse time attribute.", e);
439         }
440 
441         // Translate access window end time
442         try { getModel().setAccessWindowEnd(parseTime(attributes.get(ACCESS_WINDOW_END_ATTRIBUTE_NAME))); }
443         catch (ParseException e) {
444             logger.warn("Not setting end time of user access window: {}", e.getMessage());
445             logger.debug("Unable to parse time attribute.", e);
446         }
447 
448         // Translate account validity start date
449         try { getModel().setValidFrom(parseDate(attributes.get(VALID_FROM_ATTRIBUTE_NAME))); }
450         catch (ParseException e) {
451             logger.warn("Not setting user validity start date: {}", e.getMessage());
452             logger.debug("Unable to parse date attribute.", e);
453         }
454 
455         // Translate account validity end date
456         try { getModel().setValidUntil(parseDate(attributes.get(VALID_UNTIL_ATTRIBUTE_NAME))); }
457         catch (ParseException e) {
458             logger.warn("Not setting user validity end date: {}", e.getMessage());
459             logger.debug("Unable to parse date attribute.", e);
460         }
461 
462         // Translate timezone attribute
463         getModel().setTimeZone(TimeZoneField.parse(attributes.get(TIMEZONE_ATTRIBUTE_NAME)));
464 
465     }
466 
467     /**
468      * Stores all unrestricted (unprivileged) attributes within the underlying
469      * user model, pulling the values of those attributes from the given Map.
470      *
471      * @param attributes
472      *     The Map to pull all unrestricted attributes from.
473      */
setUnrestrictedAttributes(Map<String, String> attributes)474     private void setUnrestrictedAttributes(Map<String, String> attributes) {
475 
476         // Translate full name attribute
477         getModel().setFullName(TextField.parse(attributes.get(User.Attribute.FULL_NAME)));
478 
479         // Translate email address attribute
480         getModel().setEmailAddress(TextField.parse(attributes.get(User.Attribute.EMAIL_ADDRESS)));
481 
482         // Translate organization attribute
483         getModel().setOrganization(TextField.parse(attributes.get(User.Attribute.ORGANIZATION)));
484 
485         // Translate role attribute
486         getModel().setOrganizationalRole(TextField.parse(attributes.get(User.Attribute.ORGANIZATIONAL_ROLE)));
487 
488     }
489 
490     @Override
getSupportedAttributeNames()491     public Set<String> getSupportedAttributeNames() {
492         return ATTRIBUTE_NAMES;
493     }
494 
495     @Override
getAttributes()496     public Map<String, String> getAttributes() {
497 
498         // Include any defined arbitrary attributes
499         Map<String, String> attributes = super.getAttributes();
500 
501         // Always include unrestricted attributes
502         putUnrestrictedAttributes(attributes);
503 
504         // Include restricted attributes only if they should be exposed
505         if (exposeRestrictedAttributes)
506             putRestrictedAttributes(attributes);
507 
508         return attributes;
509     }
510 
511     @Override
setAttributes(Map<String, String> attributes)512     public void setAttributes(Map<String, String> attributes) {
513 
514         // Set arbitrary attributes
515         super.setAttributes(attributes);
516 
517         // Always assign unrestricted attributes
518         setUnrestrictedAttributes(attributes);
519 
520         // Assign restricted attributes only if they are exposed
521         if (exposeRestrictedAttributes)
522             setRestrictedAttributes(attributes);
523 
524     }
525 
526     /**
527      * Returns the time zone associated with this user. This time zone must be
528      * used when interpreting all date/time restrictions related to this user.
529      *
530      * @return
531      *     The time zone associated with this user.
532      */
getTimeZone()533     private TimeZone getTimeZone() {
534 
535         // If no time zone is set, use the default
536         String timeZone = getModel().getTimeZone();
537         if (timeZone == null)
538             return TimeZone.getDefault();
539 
540         // Otherwise parse and return time zone
541         return TimeZone.getTimeZone(timeZone);
542 
543     }
544 
545     /**
546      * Converts a SQL Time to a Calendar, independently of time zone, using the
547      * given Calendar as a base. The time components will be copied to the
548      * given Calendar verbatim, leaving the date and time zone components of
549      * the given Calendar otherwise intact.
550      *
551      * @param base
552      *     The Calendar object to use as a base for the conversion.
553      *
554      * @param time
555      *     The SQL Time object containing the time components to be applied to
556      *     the given Calendar.
557      *
558      * @return
559      *     The given Calendar, now modified to represent the given time.
560      */
asCalendar(Calendar base, Time time)561     private Calendar asCalendar(Calendar base, Time time) {
562 
563         // Get calendar from given SQL time
564         Calendar timeCalendar = Calendar.getInstance();
565         timeCalendar.setTime(time);
566 
567         // Apply given time to base calendar
568         base.set(Calendar.HOUR_OF_DAY, timeCalendar.get(Calendar.HOUR_OF_DAY));
569         base.set(Calendar.MINUTE,      timeCalendar.get(Calendar.MINUTE));
570         base.set(Calendar.SECOND,      timeCalendar.get(Calendar.SECOND));
571         base.set(Calendar.MILLISECOND, timeCalendar.get(Calendar.MILLISECOND));
572 
573         return base;
574 
575     }
576 
577     /**
578      * Returns the time during the current day when this user account can start
579      * being used.
580      *
581      * @return
582      *     The time during the current day when this user account can start
583      *     being used.
584      */
getAccessWindowStart()585     private Calendar getAccessWindowStart() {
586 
587         // Get window start time
588         Time start = getModel().getAccessWindowStart();
589         if (start == null)
590             return null;
591 
592         // Return within defined time zone, current day
593         return asCalendar(Calendar.getInstance(getTimeZone()), start);
594 
595     }
596 
597     /**
598      * Returns the time during the current day when this user account can no
599      * longer be used.
600      *
601      * @return
602      *     The time during the current day when this user account can no longer
603      *     be used.
604      */
getAccessWindowEnd()605     private Calendar getAccessWindowEnd() {
606 
607         // Get window end time
608         Time end = getModel().getAccessWindowEnd();
609         if (end == null)
610             return null;
611 
612         // Return within defined time zone, current day
613         return asCalendar(Calendar.getInstance(getTimeZone()), end);
614 
615     }
616 
617     /**
618      * Returns the date after which this account becomes valid. The time
619      * components of the resulting Calendar object will be set to midnight of
620      * the date in question.
621      *
622      * @return
623      *     The date after which this account becomes valid.
624      */
getValidFrom()625     private Calendar getValidFrom() {
626 
627         // Get valid from date
628         Date validFrom = getModel().getValidFrom();
629         if (validFrom == null)
630             return null;
631 
632         // Convert to midnight within defined time zone
633         Calendar validFromCalendar = Calendar.getInstance(getTimeZone());
634         validFromCalendar.setTime(validFrom);
635         validFromCalendar.set(Calendar.HOUR_OF_DAY, 0);
636         validFromCalendar.set(Calendar.MINUTE,      0);
637         validFromCalendar.set(Calendar.SECOND,      0);
638         validFromCalendar.set(Calendar.MILLISECOND, 0);
639         return validFromCalendar;
640 
641     }
642 
643     /**
644      * Returns the date after which this account becomes invalid. The time
645      * components of the resulting Calendar object will be set to the last
646      * millisecond of the day in question (23:59:59.999).
647      *
648      * @return
649      *     The date after which this account becomes invalid.
650      */
getValidUntil()651     private Calendar getValidUntil() {
652 
653         // Get valid until date
654         Date validUntil = getModel().getValidUntil();
655         if (validUntil == null)
656             return null;
657 
658         // Convert to end-of-day within defined time zone
659         Calendar validUntilCalendar = Calendar.getInstance(getTimeZone());
660         validUntilCalendar.setTime(validUntil);
661         validUntilCalendar.set(Calendar.HOUR_OF_DAY,  23);
662         validUntilCalendar.set(Calendar.MINUTE,       59);
663         validUntilCalendar.set(Calendar.SECOND,       59);
664         validUntilCalendar.set(Calendar.MILLISECOND, 999);
665         return validUntilCalendar;
666 
667     }
668 
669     /**
670      * Given a time when a particular state changes from inactive to active,
671      * and a time when a particular state changes from active to inactive,
672      * determines whether that state is currently active.
673      *
674      * @param activeStart
675      *     The time at which the state changes from inactive to active.
676      *
677      * @param inactiveStart
678      *     The time at which the state changes from active to inactive.
679      *
680      * @return
681      *     true if the state is currently active, false otherwise.
682      */
isActive(Calendar activeStart, Calendar inactiveStart)683     private boolean isActive(Calendar activeStart, Calendar inactiveStart) {
684 
685         // If end occurs before start, convert to equivalent case where start
686         // start is before end
687         if (inactiveStart != null && activeStart != null && inactiveStart.before(activeStart))
688             return !isActive(inactiveStart, activeStart);
689 
690         // Get current time
691         Calendar current = Calendar.getInstance();
692 
693         // State is active iff the current time is between the start and end
694         return !(activeStart != null && current.before(activeStart))
695             && !(inactiveStart != null && current.after(inactiveStart));
696 
697     }
698 
699     /**
700      * Returns whether this user account is currently valid as of today.
701      * Account validity depends on optional date-driven restrictions which
702      * define when an account becomes valid, and when an account ceases being
703      * valid.
704      *
705      * @return
706      *     true if the account is valid as of today, false otherwise.
707      */
isAccountValid()708     public boolean isAccountValid() {
709         return isActive(getValidFrom(), getValidUntil());
710     }
711 
712     /**
713      * Returns whether the current time is within this user's allowed access
714      * window. If the login times for this user are not limited, this will
715      * return true.
716      *
717      * @return
718      *     true if the current time is within this user's allowed access
719      *     window, or if this user has no restrictions on login time, false
720      *     otherwise.
721      */
isAccountAccessible()722     public boolean isAccountAccessible() {
723         return isActive(getAccessWindowStart(), getAccessWindowEnd());
724     }
725 
726     /**
727      * Returns whether this user account has been disabled. The credentials of
728      * disabled user accounts are treated as invalid, effectively disabling
729      * that user's access to data for which they would otherwise have
730      * permission.
731      *
732      * @return
733      *     true if this user account has been disabled, false otherwise.
734      */
isDisabled()735     public boolean isDisabled() {
736         return getModel().isDisabled();
737     }
738 
739     /**
740      * Returns whether this user's password has expired. If a user's password
741      * is expired, it must be immediately changed upon login. A user account
742      * with an expired password cannot be used until the password has been
743      * changed.
744      *
745      * @return
746      *     true if this user's password has expired, false otherwise.
747      */
isExpired()748     public boolean isExpired() {
749         return getModel().isExpired();
750     }
751 
752     @Override
getLastActive()753     public Timestamp getLastActive() {
754         return getModel().getLastActive();
755     }
756 
757     @Override
getUserHistory()758     public ActivityRecordSet<ActivityRecord> getUserHistory()
759             throws GuacamoleException {
760         UserRecordSet userRecordSet = userRecordSetProvider.get();
761         userRecordSet.init(getCurrentUser(), this.getIdentifier());
762         return userRecordSet;
763     }
764 
765     @Override
getUserGroups()766     public RelatedObjectSet getUserGroups() throws GuacamoleException {
767         UserParentUserGroupSet parentUserGroupSet = parentUserGroupSetProvider.get();
768         parentUserGroupSet.init(getCurrentUser(), this);
769         return parentUserGroupSet;
770     }
771 
772     @Override
getEffectivePermissions()773     public Permissions getEffectivePermissions() throws GuacamoleException {
774         return super.getEffective();
775     }
776 
777     /**
778      * Returns true if this user is a skeleton user, lacking a database entity
779      * entry.
780      *
781      * @return
782      *     True if this user is a skeleton user, otherwise false.
783      */
isSkeleton()784     public boolean isSkeleton() {
785         return (getModel().getEntityID() == null);
786     }
787 
788 }
789