001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software GmbH, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.file;
029
030import org.opencms.db.CmsUserSettings;
031import org.opencms.main.CmsException;
032import org.opencms.main.CmsIllegalArgumentException;
033import org.opencms.main.OpenCms;
034import org.opencms.security.CmsPrincipal;
035import org.opencms.security.CmsSecurityException;
036import org.opencms.security.I_CmsPrincipal;
037import org.opencms.util.CmsMacroResolver;
038import org.opencms.util.CmsStringUtil;
039import org.opencms.util.CmsUUID;
040
041import java.util.Collections;
042import java.util.HashMap;
043import java.util.Locale;
044import java.util.Map;
045
046/**
047 * A user principal in the OpenCms permission system.<p>
048 *
049 * A user in OpenCms is uniquely defined by its user named returned by
050 * <code>{@link #getName()}</code>.<p>
051 *
052 * Basic users in OpenCms are users that can access the OpenCms Workplace.
053 * Moreover, the user must be created by another user with the
054 * <code>{@link org.opencms.security.CmsRole#ACCOUNT_MANAGER}</code> role.
055 * These users are "content managers" that actually have write permissions in
056 * at last some parts of the VFS.<p>
057 *
058 * Another possibility is to have users in a 'Guests' group.
059 * These users do not have access to the OpenCms Workplace.
060 * However, an user in a 'Guests' group can be created by every user, for example
061 * the "Guest" user. The main use case is that these users are used for users of
062 * the website that can generate their own accounts, in a "please register your
063 * account..." scenario.
064 * These user accounts can then be used to build personalized web sites.<p>
065 *
066 * @since 6.0.0
067 *
068 * @see CmsGroup
069 */
070public class CmsUser extends CmsPrincipal implements Cloneable {
071
072    /** Storage for additional user information. */
073    private Map<String, Object> m_additionalInfo;
074
075    /** The creation date. */
076    private long m_dateCreated;
077
078    /**  The email of the user. */
079    private String m_email;
080
081    /** The first name of this user. */
082    private String m_firstname;
083
084    /** Boolean flag whether the last-login time stamp of this user was modified. */
085    private boolean m_isTouched;
086
087    /** The last login date of this user. */
088    private long m_lastlogin;
089
090    /** The last name of this user. */
091    private String m_lastname;
092
093    /** The password of this user. */
094    private String m_password;
095
096    /**
097     * Creates a new, empty OpenCms user principal.<p>
098     *
099     * Mostly intended to be used with the <code>org.opencms.workplace.tools.accounts.A_CmsEditUserDialog</code>.<p>
100     */
101    public CmsUser() {
102
103        this(
104            null,
105            "",
106            "",
107            "",
108            "",
109            "",
110            0,
111            I_CmsPrincipal.FLAG_ENABLED,
112            System.currentTimeMillis(),
113            Collections.singletonMap(CmsUserSettings.ADDITIONAL_INFO_DESCRIPTION, (Object)""));
114    }
115
116    /**
117     * Creates a new OpenCms user principal.<p>
118     *
119     * @param id the unique id of the new user
120     * @param name the fully qualified name of the new user
121     * @param password the password of the user
122     * @param firstname the first name
123     * @param lastname the last name
124     * @param email the email address
125     * @param lastlogin time stamp
126     * @param flags flags
127     * @param dateCreated the creation date
128     * @param additionalInfo user related information
129     */
130    public CmsUser(
131        CmsUUID id,
132        String name,
133        String password,
134        String firstname,
135        String lastname,
136        String email,
137        long lastlogin,
138        int flags,
139        long dateCreated,
140        Map<String, Object> additionalInfo) {
141
142        m_id = id;
143        m_name = name;
144        m_password = password;
145        m_firstname = firstname;
146        m_lastname = lastname;
147        m_email = email;
148        m_lastlogin = lastlogin;
149        m_flags = flags;
150        m_dateCreated = dateCreated;
151        if (additionalInfo != null) {
152            m_additionalInfo = new HashMap<String, Object>(additionalInfo);
153        } else {
154            m_additionalInfo = new HashMap<String, Object>();
155        }
156        if (m_additionalInfo.get(CmsUserSettings.ADDITIONAL_INFO_ADDRESS) == null) {
157            m_additionalInfo.put(CmsUserSettings.ADDITIONAL_INFO_ADDRESS, "");
158        }
159        if (m_additionalInfo.get(CmsUserSettings.ADDITIONAL_INFO_DESCRIPTION) == null) {
160            m_additionalInfo.put(CmsUserSettings.ADDITIONAL_INFO_DESCRIPTION, "");
161        }
162    }
163
164    /**
165     * Validates an email address.<p>
166     *
167     * That means, the parameter should only be composed by digits and standard english letters, points, underscores and exact one "At" symbol.<p>
168     *
169     * @param email the email to validate
170     */
171    public static void checkEmail(String email) {
172
173        OpenCms.getValidationHandler().checkEmail(email);
174    }
175
176    /**
177     * Validates a zip code.<p>
178     *
179     * That means, the parameter should only be composed by digits and standard english letters.<p>
180     *
181     * @param zipcode the zip code to validate
182     */
183    public static void checkZipCode(String zipcode) {
184
185        OpenCms.getValidationHandler().checkZipCode(zipcode);
186    }
187
188    /**
189     * Returns the "full" name of the given user in the format <code>"{firstname} {lastname} ({username})"</code>,
190     * or the empty String <code>""</code> if the user is null.<p>
191     *
192     * @param user the user to get the full name from
193     * @return the "full" name the user
194     *
195     * @see #getFullName()
196     */
197    public static String getFullName(CmsUser user) {
198
199        if (user == null) {
200            return "";
201        } else {
202            return user.getFullName();
203        }
204    }
205
206    /**
207     * Checks if the given String starts with {@link I_CmsPrincipal#PRINCIPAL_USER} followed by a dot.<p>
208     *
209     * <ul>
210     * <li>Works if the given String is <code>null</code>.
211     * <li>Removes white spaces around the String before the check.
212     * <li>Also works with prefixes not being in upper case.
213     * <li>Does not check if the user after the prefix actually exists.
214     * </ul>
215     *
216     * @param principalName the user name to check
217     *
218     * @return <code>true</code> in case the String starts with {@link I_CmsPrincipal#PRINCIPAL_USER} followed by a dot
219     */
220    public static boolean hasPrefix(String principalName) {
221
222        return CmsStringUtil.isNotEmptyOrWhitespaceOnly(principalName)
223            && (principalName.trim().toUpperCase().startsWith(I_CmsPrincipal.PRINCIPAL_USER + "."));
224    }
225
226    /**
227     * Removes the prefix if the given String starts with {@link I_CmsPrincipal#PRINCIPAL_USER} followed by a dot.<p>
228     *
229     * <ul>
230     * <li>Works if the given String is <code>null</code>.
231     * <li>If the given String does not start with {@link I_CmsPrincipal#PRINCIPAL_USER} followed by a dot it is returned unchanged.
232     * <li>Removes white spaces around the user name.
233     * <li>Also works with prefixes not being in upper case.
234     * <li>Does not check if the user after the prefix actually exists.
235     * </ul>
236     *
237     * @param principalName the user name to remove the prefix from
238     *
239     * @return the given String with the prefix {@link I_CmsPrincipal#PRINCIPAL_USER} and the following dot removed
240     */
241    public static String removePrefix(String principalName) {
242
243        String result = principalName;
244        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(principalName)) {
245            if (hasPrefix(principalName)) {
246                result = principalName.trim().substring(I_CmsPrincipal.PRINCIPAL_USER.length() + 1);
247            }
248        }
249        return result;
250    }
251
252    /**
253     * Checks if the provided user name is a valid user name and can be used as an argument value
254     * for {@link #setName(String)}.<p>
255     *
256     * @param name the user name to check
257     *
258     * @throws CmsIllegalArgumentException if the check fails
259     */
260    public void checkName(String name) throws CmsIllegalArgumentException {
261
262        OpenCms.getValidationHandler().checkUserName(name);
263    }
264
265    /**
266     * @see java.lang.Object#clone()
267     */
268    @Override
269    public Object clone() {
270
271        return new CmsUser(
272            m_id,
273            m_name,
274            m_password,
275            m_firstname,
276            m_lastname,
277            m_email,
278            m_lastlogin,
279            m_flags,
280            m_dateCreated,
281            m_additionalInfo);
282    }
283
284    /**
285     * Deletes a value from this users "additional information" storage map.<p>
286     *
287     * @param key the additional user information to delete
288     *
289     * @see #getAdditionalInfo()
290     */
291    public void deleteAdditionalInfo(String key) {
292
293        m_additionalInfo.remove(key);
294    }
295
296    /**
297     * Returns this users complete "additional information" storage map.<p>
298     *
299     * The "additional information" storage map is a simple {@link java.util.Map}
300     * that can be used to store any key / value pairs for the user.
301     * Some information parts of the users address are stored in this map
302     * by default.<p>
303     *
304     * @return this users complete "additional information" storage map
305     */
306    public Map<String, Object> getAdditionalInfo() {
307
308        return m_additionalInfo;
309    }
310
311    /**
312     * Returns a value from this users "additional information" storage map,
313     * or <code>null</code> if no value for the given key is available.<p>
314     *
315     * @param key selects the value to return from the "additional information" storage map
316     *
317     * @return the selected value from this users "additional information" storage map
318     *
319     * @see #getAdditionalInfo()
320     */
321    public Object getAdditionalInfo(String key) {
322
323        return m_additionalInfo.get(key);
324    }
325
326    /**
327     * Returns the address line of this user.<p>
328     *
329     * @return the address line of this user
330     */
331    public String getAddress() {
332
333        return (String)getAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_ADDRESS);
334    }
335
336    /**
337     * Returns the city information of this user.<p>
338     *
339     * This information is stored in the "additional information" storage map
340     * using the key <code>{@link CmsUserSettings#ADDITIONAL_INFO_CITY}</code>.<p>
341     *
342     * @return the city information of this user
343     */
344    public String getCity() {
345
346        return (String)getAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_CITY);
347    }
348
349    /**
350     * Returns the country information of this user.<p>
351     *
352     * This information is stored in the "additional information" storage map
353     * using the key <code>{@link CmsUserSettings#ADDITIONAL_INFO_COUNTRY}</code>.<p>
354     *
355     * @return the country information of this user
356     */
357    public String getCountry() {
358
359        return (String)getAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_COUNTRY);
360    }
361
362    /**
363     * Returns the creation date.<p>
364     *
365     * @return the creation date
366     */
367    public long getDateCreated() {
368
369        return m_dateCreated;
370    }
371
372    /**
373     * @see org.opencms.security.CmsPrincipal#getDescription()
374     */
375    @Override
376    public String getDescription() {
377
378        return (String)getAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_DESCRIPTION);
379    }
380
381    /**
382     * Returns the description of this organizational unit.<p>
383     *
384     * @param locale the locale
385     *
386     * @return the description of this organizational unit
387     */
388    public String getDescription(Locale locale) {
389
390        CmsMacroResolver macroResolver = new CmsMacroResolver();
391        macroResolver.setMessages(org.opencms.db.generic.Messages.get().getBundle(locale));
392        return macroResolver.resolveMacros((String)getAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_DESCRIPTION));
393    }
394
395    /**
396     * @see org.opencms.security.CmsPrincipal#getDisplayName(org.opencms.file.CmsObject, java.util.Locale)
397     */
398    @Override
399    public String getDisplayName(CmsObject cms, Locale locale) throws CmsException {
400
401        if (OpenCms.getOrgUnitManager().getOrganizationalUnits(cms, "", true).size() > 0) {
402            return org.opencms.security.Messages.get().getBundle(locale).key(
403                org.opencms.security.Messages.GUI_PRINCIPAL_DISPLAY_NAME_2,
404                getFullName(),
405                OpenCms.getOrgUnitManager().readOrganizationalUnit(cms, getOuFqn()).getDisplayName(locale));
406        } else {
407            return getFullName();
408        }
409    }
410
411    /**
412     * Returns the email address of this user.<p>
413     *
414     * @return the email address of this user
415     */
416    public String getEmail() {
417
418        return m_email;
419    }
420
421    /**
422     * Returns the first name of this user.<p>
423     *
424     * @return the first name of this user
425     */
426    public String getFirstname() {
427
428        return m_firstname;
429    }
430
431    /**
432     * Returns the "full" name of the this user in the format <code>"{firstname} {lastname} ({username})"</code>.<p>
433     *
434     * @return the "full" name this user
435     */
436    public String getFullName() {
437
438        StringBuffer buf = new StringBuffer();
439        String first = getFirstname();
440        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(first)) {
441            buf.append(first);
442            buf.append(" ");
443        }
444        String last = getLastname();
445        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(last)) {
446            buf.append(last);
447            buf.append(" ");
448        }
449        buf.append("(");
450        buf.append(getSimpleName());
451        buf.append(")");
452        return buf.toString();
453    }
454
455    /**
456     * Returns the institution information of this user.<p>
457     *
458     * This information is stored in the "additional information" storage map
459     * using the key <code>{@link CmsUserSettings#ADDITIONAL_INFO_INSTITUTION}</code>.<p>
460     *
461     * @return the institution information of this user
462     */
463    public String getInstitution() {
464
465        return (String)getAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_INSTITUTION);
466    }
467
468    /**
469     * Returns the time of the last login of this user.<p>
470     *
471     * @return the time of the last login of this user
472     */
473    public long getLastlogin() {
474
475        return m_lastlogin;
476    }
477
478    /**
479     * Returns the last name of this user.<p>
480     *
481     * @return the last name of this user
482     */
483    public String getLastname() {
484
485        return m_lastname;
486    }
487
488    /**
489     * Returns the encrypted user password.<p>
490     *
491     * @return the encrypted user password
492     */
493    public String getPassword() {
494
495        return m_password;
496    }
497
498    /**
499     * Returns the zip code information of this user.<p>
500     *
501     * This information is stored in the "additional information" storage map
502     * using the key <code>{@link CmsUserSettings#ADDITIONAL_INFO_ZIPCODE}</code>.<p>
503     *
504     * @return the zip code information of this user
505     */
506    public String getZipcode() {
507
508        return (String)getAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_ZIPCODE);
509    }
510
511    /**
512     * @see org.opencms.security.I_CmsPrincipal#isGroup()
513     */
514    @Override
515    public boolean isGroup() {
516
517        return false;
518    }
519
520    /**
521     * Checks if this user is the default guest user.<p>
522     *
523     * @return <code>true</code> if this user is the default guest user
524     */
525    public boolean isGuestUser() {
526
527        return OpenCms.getDefaultUsers().isUserGuest(getName());
528    }
529
530    /**
531     * Returns <code>true</code> if this user is not able to manage itself.<p>
532     *
533     * @return <code>true</code> if this user is not able to manage itself
534     */
535    public boolean isManaged() {
536
537        return (getFlags() & I_CmsPrincipal.FLAG_USER_MANAGED) == I_CmsPrincipal.FLAG_USER_MANAGED;
538    }
539
540    /**
541     * Returns <code>true</code> if this user was touched.<p>
542     *
543     * @return boolean true if this user was touched
544     */
545    public boolean isTouched() {
546
547        return m_isTouched;
548    }
549
550    /**
551     * @see org.opencms.security.I_CmsPrincipal#isUser()
552     */
553    @Override
554    public boolean isUser() {
555
556        return true;
557    }
558
559    /**
560     * Checks if the user is marked as webuser.<p>
561     *
562     * @return <code>true</code> if the user is marked as webuser
563     */
564    public boolean isWebuser() {
565
566        return (getFlags() & FLAG_USER_WEBUSER) == FLAG_USER_WEBUSER;
567    }
568
569    /**
570     * Sets this users complete "additional information" storage map to the given value.<p>
571     *
572     * @param additionalInfo the complete "additional information" map to set
573     *
574     * @see #getAdditionalInfo()
575     */
576    public void setAdditionalInfo(Map<String, Object> additionalInfo) {
577
578        m_additionalInfo = additionalInfo;
579    }
580
581    /**
582     * Stores a value in this users "additional information" storage map with the given access key.<p>
583     *
584     * @param key the key to store the value under
585     * @param value the value to store in the users "additional information" storage map
586     *
587     * @see #getAdditionalInfo()
588     */
589    public void setAdditionalInfo(String key, Object value) {
590
591        if (key == null) {
592            throw new CmsIllegalArgumentException(
593                Messages.get().container(Messages.ERR_USER_ADDINFO_KEY_NULL_1, getFullName()));
594        }
595        m_additionalInfo.put(key, value);
596    }
597
598    /**
599     * Sets the address line of this user.<p>
600     *
601     * @param address the address line to set
602     */
603    public void setAddress(String address) {
604
605        setAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_ADDRESS, address);
606    }
607
608    /**
609     * Sets the city information of this user.<p>
610     *
611     * @param city the city information to set
612     */
613    public void setCity(String city) {
614
615        setAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_CITY, city);
616    }
617
618    /**
619     * Sets the country information of this user.<p>
620     *
621     * @param country the city information to set
622     */
623    public void setCountry(String country) {
624
625        setAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_COUNTRY, country);
626    }
627
628    /**
629     * @see org.opencms.security.CmsPrincipal#setDescription(java.lang.String)
630     */
631    @Override
632    public void setDescription(String description) {
633
634        setAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_DESCRIPTION, description);
635    }
636
637    /**
638     * Sets the email address of this user.<p>
639     *
640     * @param email the email address to set
641     */
642    public void setEmail(String email) {
643
644        checkEmail(email);
645        if (email != null) {
646            email = email.trim();
647        }
648        m_email = email;
649    }
650
651    /**
652     * Sets the first name of this user.<p>
653     *
654     * @param firstname the name to set
655     */
656    public void setFirstname(String firstname) {
657
658        OpenCms.getValidationHandler().checkFirstname(firstname);
659        if (firstname != null) {
660            firstname = firstname.trim();
661        }
662        m_firstname = firstname;
663    }
664
665    /**
666     * Sets the institution information of this user.<p>
667     *
668     * @param institution the institution information to set
669     */
670    public void setInstitution(String institution) {
671
672        setAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_INSTITUTION, institution);
673    }
674
675    /**
676     * Sets the last login time stamp of this user.<p>
677     *
678     * @param value the last login time stamp to set
679     */
680    public void setLastlogin(long value) {
681
682        m_isTouched = true;
683        m_lastlogin = value;
684    }
685
686    /**
687     * Sets the last name of this user.<p>
688     *
689     * @param lastname the name to set
690     */
691    public void setLastname(String lastname) {
692
693        OpenCms.getValidationHandler().checkLastname(lastname);
694        if (lastname != null) {
695            lastname = lastname.trim();
696        }
697        m_lastname = lastname;
698    }
699
700    /**
701     * Sets the managed flag for this user to the given value.<p>
702     *
703     * @param value the value to set
704     */
705    public void setManaged(boolean value) {
706
707        if (isManaged() != value) {
708            setFlags(getFlags() ^ I_CmsPrincipal.FLAG_USER_MANAGED);
709        }
710    }
711
712    /**
713     * Sets the password of this user.<p>
714     *
715     * @param value the password to set
716     */
717    public void setPassword(String value) {
718
719        try {
720            OpenCms.getPasswordHandler().validatePassword(value);
721        } catch (CmsSecurityException e) {
722            throw new CmsIllegalArgumentException(e.getMessageContainer());
723        }
724        m_password = value;
725    }
726
727    /**
728     * Sets the zip code information of this user.<p>
729     *
730     * @param zipcode the zip code information to set
731     */
732    public void setZipcode(String zipcode) {
733
734        checkZipCode(zipcode);
735        if (zipcode != null) {
736            zipcode = zipcode.toUpperCase();
737        }
738        setAdditionalInfo(CmsUserSettings.ADDITIONAL_INFO_ZIPCODE, zipcode);
739    }
740
741    /**
742     * @see java.lang.Object#toString()
743     */
744    @Override
745    public String toString() {
746
747        StringBuffer result = new StringBuffer();
748        result.append("[User]");
749        result.append(" name:");
750        result.append(getName());
751        result.append(" id:");
752        result.append(m_id);
753        result.append(" flags:");
754        result.append(getFlags());
755        result.append(" description:");
756        result.append(getDescription());
757        return result.toString();
758    }
759
760    /**
761     * Sets the "touched" status of this user to <code>true</code>.<p>
762     */
763    public void touch() {
764
765        m_isTouched = true;
766    }
767}