001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (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, 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.security;
029
030import org.opencms.db.CmsDbEntryNotFoundException;
031import org.opencms.file.CmsGroup;
032import org.opencms.file.CmsObject;
033import org.opencms.file.CmsUser;
034import org.opencms.main.CmsException;
035import org.opencms.main.OpenCms;
036import org.opencms.util.CmsStringUtil;
037import org.opencms.util.CmsUUID;
038import org.opencms.workplace.I_CmsGroupNameTranslation;
039
040import java.util.Iterator;
041import java.util.List;
042import java.util.Locale;
043
044/**
045 * Common methods shared among user and group principals,
046 * also contains several utility functions to deal with principal instances.<p>
047 *
048 * @since 6.2.0
049 */
050public abstract class CmsPrincipal implements I_CmsPrincipal, Comparable<I_CmsPrincipal> {
051
052    /** The serial version id. */
053    private static final long serialVersionUID = -323281048875786320L;
054
055    /** The description of this principal. */
056    protected String m_description;
057
058    /** The flags of this principal. */
059    protected int m_flags;
060
061    /** The unique id of this principal. */
062    protected CmsUUID m_id;
063
064    /** The fully qualified name of this principal. */
065    protected String m_name;
066
067    /**
068     * Empty constructor for subclassing.<p>
069     */
070    protected CmsPrincipal() {
071
072        // empty constructor for subclassing
073    }
074
075    /**
076     * Filters out all principals that do not have the given flag set,
077     * but leaving principals with flags less than <code>{@link I_CmsPrincipal#FLAG_CORE_LIMIT}</code> untouched.<p>
078     *
079     * The given parameter list is directly modified, so the returned list is the same object as the input list.<p>
080     *
081     * @param principals a list of <code>{@link CmsPrincipal}</code> objects
082     * @param flag the flag for filtering
083     *
084     * @return the filtered principal list
085     */
086    public static List<? extends CmsPrincipal> filterCoreFlag(List<? extends CmsPrincipal> principals, int flag) {
087
088        Iterator<? extends CmsPrincipal> it = principals.iterator();
089        while (it.hasNext()) {
090            CmsPrincipal p = it.next();
091            if ((p.getFlags() > I_CmsPrincipal.FLAG_CORE_LIMIT) && ((p.getFlags() & flag) != flag)) {
092                it.remove();
093            }
094        }
095        return principals;
096    }
097
098    /**
099     * Filters out all groups with flags greater than <code>{@link I_CmsPrincipal#FLAG_CORE_LIMIT}</code>.<p>
100     *
101     * The given parameter list is directly modified, so the returned list is the same object as the input list.<p>
102     *
103     * @param groups a list of <code>{@link CmsGroup}</code> objects
104     *
105     * @return the filtered principal list
106     */
107    public static List<CmsGroup> filterCoreGroups(List<CmsGroup> groups) {
108
109        Iterator<CmsGroup> it = groups.iterator();
110        while (it.hasNext()) {
111            CmsGroup p = it.next();
112            if (p.getFlags() > I_CmsPrincipal.FLAG_CORE_LIMIT) {
113                it.remove();
114            }
115        }
116        return groups;
117    }
118
119    /**
120     * Filters out all users with flags greater than <code>{@link I_CmsPrincipal#FLAG_CORE_LIMIT}</code>.<p>
121     *
122     * The given parameter list is directly modified, so the returned list is the same object as the input list.<p>
123     *
124     * @param users a list of <code>{@link CmsUser}</code> objects
125     *
126     * @return the filtered principal list
127     */
128    public static List<CmsUser> filterCoreUsers(List<CmsUser> users) {
129
130        Iterator<CmsUser> it = users.iterator();
131        while (it.hasNext()) {
132            I_CmsPrincipal p = it.next();
133            if (p.getFlags() > I_CmsPrincipal.FLAG_CORE_LIMIT) {
134                it.remove();
135            }
136        }
137        return users;
138    }
139
140    /**
141     * Filters out all principals that do not have the given flag set.<p>
142     *
143     * The given parameter list is directly modified, so the returned list is the same object as the input list.<p>
144     *
145     * @param principals the list of <code>{@link CmsPrincipal}</code> objects
146     * @param flag the flag for filtering
147     *
148     * @return the filtered principal list
149     */
150    public static List<? extends CmsPrincipal> filterFlag(List<? extends CmsPrincipal> principals, int flag) {
151
152        Iterator<? extends CmsPrincipal> it = principals.iterator();
153        while (it.hasNext()) {
154            CmsPrincipal p = it.next();
155            if ((p.getFlags() & flag) != flag) {
156                it.remove();
157            }
158        }
159        return principals;
160    }
161
162    /**
163     * Returns the provided group name prefixed with <code>{@link I_CmsPrincipal#PRINCIPAL_GROUP}.</code>.<p>
164     *
165     * @param name the name to add the prefix to
166     * @return the provided group name prefixed with <code>{@link I_CmsPrincipal#PRINCIPAL_GROUP}.</code>
167     */
168    public static String getPrefixedGroup(String name) {
169
170        StringBuffer result = new StringBuffer(name.length() + 10);
171        result.append(I_CmsPrincipal.PRINCIPAL_GROUP);
172        result.append('.');
173        result.append(name);
174        return result.toString();
175    }
176
177    /**
178     * Returns the provided user name prefixed with <code>{@link I_CmsPrincipal#PRINCIPAL_USER}.</code>.<p>
179     *
180     * @param name the name to add the prefix to
181     * @return the provided user name prefixed with <code>{@link I_CmsPrincipal#PRINCIPAL_USER}.</code>
182     */
183    public static String getPrefixedUser(String name) {
184
185        StringBuffer result = new StringBuffer(name.length() + 10);
186        result.append(I_CmsPrincipal.PRINCIPAL_USER);
187        result.append('.');
188        result.append(name);
189        return result.toString();
190    }
191
192    /**
193     * Gets the type of a principal.<p>
194     *
195     * @param principal the principal
196     * @return the principal type
197     */
198    public static String getType(I_CmsPrincipal principal) {
199
200        if (principal == null) {
201            return null;
202        }
203        if (principal.isGroup()) {
204            return I_CmsPrincipal.PRINCIPAL_GROUP;
205        } else {
206            return I_CmsPrincipal.PRINCIPAL_USER;
207        }
208    }
209
210    /**
211     * Utility function to read a prefixed principal from the OpenCms database using the
212     * provided OpenCms user context.<p>
213     *
214     * The principal must be either prefixed with <code>{@link I_CmsPrincipal#PRINCIPAL_GROUP}.</code> or
215     * <code>{@link I_CmsPrincipal#PRINCIPAL_USER}.</code>.<p>
216     *
217     * @param cms the OpenCms user context to use when reading the principal
218     * @param name the prefixed principal name
219     *
220     * @return the principal read from the OpenCms database
221     *
222     * @throws CmsException in case the principal could not be read
223     */
224    public static I_CmsPrincipal readPrefixedPrincipal(CmsObject cms, String name) throws CmsException {
225
226        if (CmsGroup.hasPrefix(name)) {
227            // this principal is a group
228            return cms.readGroup(CmsGroup.removePrefix(name));
229        } else if (CmsUser.hasPrefix(name)) {
230            // this principal is a user
231            return cms.readUser(CmsUser.removePrefix(name));
232        }
233        // invalid principal name was given
234        throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_INVALID_PRINCIPAL_1, name));
235    }
236
237    /**
238     * Utility function to read a principal by its id from the OpenCms database using the
239     * provided OpenCms user context.<p>
240     *
241     * @param cms the OpenCms user context to use when reading the principal
242     * @param id the id of the principal to read
243     *
244     * @return the principal read from the OpenCms database
245     *
246     * @throws CmsException in case the principal could not be read
247     */
248    public static I_CmsPrincipal readPrincipal(CmsObject cms, CmsUUID id) throws CmsException {
249
250        try {
251            // first try to read the principal as a user
252            return cms.readUser(id);
253        } catch (CmsException exc) {
254            // assume user does not exist
255        }
256        try {
257            // now try to read the principal as a group
258            return cms.readGroup(id);
259        } catch (CmsException exc) {
260            //  assume group does not exist
261        }
262        // invalid principal name was given
263        throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_INVALID_PRINCIPAL_1, id));
264    }
265
266    /**
267     * Utility function to read a principal by its id from the OpenCms database using the
268     * provided OpenCms user context.<p>
269     *
270     * @param cms the OpenCms user context to use when reading the principal
271     * @param name the name of the principal to read
272     *
273     * @return the principal read from the OpenCms database
274     *
275     * @throws CmsException in case the principal could not be read
276     */
277    public static I_CmsPrincipal readPrincipal(CmsObject cms, String name) throws CmsException {
278
279        try {
280            // first try to read the principal as a user
281            return cms.readUser(name);
282        } catch (CmsException exc) {
283            // assume user does not exist
284        }
285        try {
286            // now try to read the principal as a group
287            return cms.readGroup(name);
288        } catch (CmsException exc) {
289            //  assume group does not exist
290        }
291        // invalid principal name was given
292        throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_INVALID_PRINCIPAL_1, name));
293    }
294
295    /**
296     * Utility function to read a principal of the given type from the OpenCms database using the
297     * provided OpenCms user context.<p>
298     *
299     * The type must either be <code>{@link I_CmsPrincipal#PRINCIPAL_GROUP}</code> or
300     * <code>{@link I_CmsPrincipal#PRINCIPAL_USER}</code>.<p>
301     *
302     * @param cms the OpenCms user context to use when reading the principal
303     * @param type the principal type
304     * @param name the principal name
305     *
306     * @return the principal read from the OpenCms database
307     *
308     * @throws CmsException in case the principal could not be read
309     */
310    public static I_CmsPrincipal readPrincipal(CmsObject cms, String type, String name) throws CmsException {
311
312        if (CmsStringUtil.isNotEmpty(type)) {
313            String upperCaseType = type.toUpperCase();
314            if (PRINCIPAL_GROUP.equals(upperCaseType)) {
315                // this principal is a group
316                return cms.readGroup(name);
317            } else if (PRINCIPAL_USER.equals(upperCaseType)) {
318                // this principal is a user
319                return cms.readUser(name);
320            }
321        }
322        // invalid principal type was given
323        throw new CmsDbEntryNotFoundException(
324            Messages.get().container(Messages.ERR_INVALID_PRINCIPAL_TYPE_2, type, name));
325    }
326
327    /**
328     * Utility function to read a principal by its id from the OpenCms database using the
329     * provided OpenCms user context.<p>
330     *
331     * @param cms the OpenCms user context to use when reading the principal
332     * @param id the id of the principal to read
333     *
334     * @return the principal read from the OpenCms database
335     *
336     * @throws CmsException in case the principal could not be read
337     */
338    public static I_CmsPrincipal readPrincipalIncludingHistory(CmsObject cms, CmsUUID id) throws CmsException {
339
340        try {
341            // first try to read the principal as a user
342            return cms.readUser(id);
343        } catch (CmsException exc) {
344            // assume user does not exist
345        }
346        try {
347            // now try to read the principal as a group
348            return cms.readGroup(id);
349        } catch (CmsException exc) {
350            //  assume group does not exist
351        }
352        try {
353            // at the end try to read the principal from the history
354            return cms.readHistoryPrincipal(id);
355        } catch (CmsException exc) {
356            //  assume the principal does not exist at all
357        }
358        // invalid principal name was given
359        throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_INVALID_PRINCIPAL_1, id));
360    }
361
362    /**
363     * @see java.lang.Comparable#compareTo(java.lang.Object)
364     */
365    public int compareTo(I_CmsPrincipal obj) {
366
367        if ((this == obj) || equals(obj)) {
368            return 0;
369        }
370        return getName().compareTo(obj.getName());
371    }
372
373    /**
374     * @see java.lang.Object#equals(java.lang.Object)
375     */
376    @Override
377    public boolean equals(Object obj) {
378
379        if (obj == this) {
380            return true;
381        }
382        if (obj instanceof I_CmsPrincipal) {
383            if (m_id != null) {
384                return m_id.equals(((I_CmsPrincipal)obj).getId());
385            }
386        }
387        return false;
388    }
389
390    /**
391     * @see org.opencms.security.I_CmsPrincipal#getDescription()
392     */
393    public String getDescription() {
394
395        return m_description;
396    }
397
398    /**
399     * Returns the display name of this principal including the organizational unit.<p>
400     *
401     * @param cms the cms context
402     * @param locale the locale
403     *
404     * @return the display name of this principal including the organizational unit
405     *
406     * @throws CmsException if the organizational unit could not be read
407     */
408    public String getDisplayName(CmsObject cms, Locale locale) throws CmsException {
409
410        return Messages.get().getBundle(locale).key(
411            Messages.GUI_PRINCIPAL_DISPLAY_NAME_2,
412            getSimpleName(),
413            OpenCms.getOrgUnitManager().readOrganizationalUnit(cms, getOuFqn()).getDisplayName(locale));
414    }
415
416    /**
417     * Returns the translated display name of this principal if it is a group and the display name otherwise.<p>
418     *
419     * @param cms the current CMS context
420     * @param locale the locale
421     * @param translation the group name translation to use
422     *
423     * @return the translated display name
424     *
425     * @throws CmsException if something goes wrong
426     */
427    public String getDisplayName(CmsObject cms, Locale locale, I_CmsGroupNameTranslation translation)
428    throws CmsException {
429
430        if (!isGroup() || (translation == null)) {
431            return getDisplayName(cms, locale);
432        }
433        return Messages.get().getBundle(locale).key(
434            Messages.GUI_PRINCIPAL_DISPLAY_NAME_2,
435            translation.translateGroupName(getName(), false),
436            OpenCms.getOrgUnitManager().readOrganizationalUnit(cms, getOuFqn()).getDisplayName(locale));
437    }
438
439    /**
440     * @see org.opencms.security.I_CmsPrincipal#getFlags()
441     */
442    public int getFlags() {
443
444        return m_flags;
445    }
446
447    /**
448     * @see org.opencms.security.I_CmsPrincipal#getId()
449     */
450    public CmsUUID getId() {
451
452        return m_id;
453    }
454
455    /**
456     * Returns the fully qualified name of this principal.<p>
457     *
458     * @return the fully qualified name of this principal
459     *
460     * @see java.security.Principal#getName()
461     */
462    public String getName() {
463
464        return m_name;
465    }
466
467    /**
468     * Returns the fully qualified name of the associated organizational unit.<p>
469     *
470     * @return the fully qualified name of the associated organizational unit
471     */
472    public String getOuFqn() {
473
474        return CmsOrganizationalUnit.getParentFqn(m_name);
475    }
476
477    /**
478     * @see org.opencms.security.I_CmsPrincipal#getPrefixedName()
479     */
480    public String getPrefixedName() {
481
482        if (isUser()) {
483            return getPrefixedUser(getName());
484        } else if (isGroup()) {
485            return getPrefixedGroup(getName());
486        }
487        return getName();
488    }
489
490    /**
491     * Returns the simple name of this organizational unit.
492     *
493     * @return the simple name of this organizational unit.
494     */
495    public String getSimpleName() {
496
497        return CmsOrganizationalUnit.getSimpleName(m_name);
498    }
499
500    /**
501     * @see java.lang.Object#hashCode()
502     */
503    @Override
504    public int hashCode() {
505
506        if (m_id != null) {
507            return m_id.hashCode();
508        }
509        return CmsUUID.getNullUUID().hashCode();
510    }
511
512    /**
513     * @see org.opencms.security.I_CmsPrincipal#isEnabled()
514     */
515    public boolean isEnabled() {
516
517        return (getFlags() & I_CmsPrincipal.FLAG_DISABLED) == 0;
518    }
519
520    /**
521     * @see org.opencms.security.I_CmsPrincipal#isGroup()
522     */
523    public boolean isGroup() {
524
525        return (this instanceof CmsGroup);
526    }
527
528    /**
529     * @see org.opencms.security.I_CmsPrincipal#isUser()
530     */
531    public boolean isUser() {
532
533        return (this instanceof CmsUser);
534    }
535
536    /**
537     * @see org.opencms.security.I_CmsPrincipal#setDescription(java.lang.String)
538     */
539    public void setDescription(String description) {
540
541        m_description = description;
542    }
543
544    /**
545     * @see org.opencms.security.I_CmsPrincipal#setEnabled(boolean)
546     */
547    public void setEnabled(boolean enabled) {
548
549        if (enabled != isEnabled()) {
550            // toggle disabled flag if required
551            setFlags(getFlags() ^ I_CmsPrincipal.FLAG_DISABLED);
552        }
553    }
554
555    /**
556     * @see org.opencms.security.I_CmsPrincipal#setFlags(int)
557     */
558    public void setFlags(int value) {
559
560        m_flags = value;
561    }
562
563    /**
564     * @see org.opencms.security.I_CmsPrincipal#setName(java.lang.String)
565     */
566    public void setName(String name) {
567
568        checkName(CmsOrganizationalUnit.getSimpleName(name));
569        m_name = name;
570    }
571}