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 instanceof CmsRoleAsPrincipal) {
204            return CmsRole.PRINCIPAL_ROLE;
205        } else if (principal.isGroup()) {
206            return I_CmsPrincipal.PRINCIPAL_GROUP;
207        } else {
208            return I_CmsPrincipal.PRINCIPAL_USER;
209        }
210    }
211
212    /**
213     * Utility function to read a prefixed principal from the OpenCms database using the
214     * provided OpenCms user context.<p>
215     *
216     * The principal must be either prefixed with <code>{@link I_CmsPrincipal#PRINCIPAL_GROUP}.</code> or
217     * <code>{@link I_CmsPrincipal#PRINCIPAL_USER}.</code>.<p>
218     *
219     * @param cms the OpenCms user context to use when reading the principal
220     * @param name the prefixed principal name
221     *
222     * @return the principal read from the OpenCms database
223     *
224     * @throws CmsException in case the principal could not be read
225     */
226    public static I_CmsPrincipal readPrefixedPrincipal(CmsObject cms, String name) throws CmsException {
227
228        if (CmsGroup.hasPrefix(name)) {
229            // this principal is a group
230            return cms.readGroup(CmsGroup.removePrefix(name));
231        } else if (CmsUser.hasPrefix(name)) {
232            // this principal is a user
233            return cms.readUser(CmsUser.removePrefix(name));
234        }
235        // invalid principal name was given
236        throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_INVALID_PRINCIPAL_1, name));
237    }
238
239    /**
240     * Utility function to read a principal by its id from the OpenCms database using the
241     * provided OpenCms user context.<p>
242     *
243     * @param cms the OpenCms user context to use when reading the principal
244     * @param id the id of the principal to read
245     *
246     * @return the principal read from the OpenCms database
247     *
248     * @throws CmsException in case the principal could not be read
249     */
250    public static I_CmsPrincipal readPrincipal(CmsObject cms, CmsUUID id) throws CmsException {
251
252        try {
253            // first try to read the principal as a user
254            return cms.readUser(id);
255        } catch (CmsException exc) {
256            // assume user does not exist
257        }
258        try {
259            // now try to read the principal as a group
260            return cms.readGroup(id);
261        } catch (CmsException exc) {
262            //  assume group does not exist
263        }
264        // invalid principal name was given
265        throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_INVALID_PRINCIPAL_1, id));
266    }
267
268    /**
269     * Utility function to read a principal by its id from the OpenCms database using the
270     * provided OpenCms user context.<p>
271     *
272     * @param cms the OpenCms user context to use when reading the principal
273     * @param name the name of the principal to read
274     *
275     * @return the principal read from the OpenCms database
276     *
277     * @throws CmsException in case the principal could not be read
278     */
279    public static I_CmsPrincipal readPrincipal(CmsObject cms, String name) throws CmsException {
280
281        try {
282            // first try to read the principal as a user
283            return cms.readUser(name);
284        } catch (CmsException exc) {
285            // assume user does not exist
286        }
287        try {
288            // now try to read the principal as a group
289            return cms.readGroup(name);
290        } catch (CmsException exc) {
291            //  assume group does not exist
292        }
293        // invalid principal name was given
294        throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_INVALID_PRINCIPAL_1, name));
295    }
296
297    /**
298     * Utility function to read a principal of the given type from the OpenCms database using the
299     * provided OpenCms user context.<p>
300     *
301     * The type must either be <code>{@link I_CmsPrincipal#PRINCIPAL_GROUP}</code> or
302     * <code>{@link I_CmsPrincipal#PRINCIPAL_USER}</code>.<p>
303     *
304     * @param cms the OpenCms user context to use when reading the principal
305     * @param type the principal type
306     * @param name the principal name
307     *
308     * @return the principal read from the OpenCms database
309     *
310     * @throws CmsException in case the principal could not be read
311     */
312    public static I_CmsPrincipal readPrincipal(CmsObject cms, String type, String name) throws CmsException {
313
314        if (CmsStringUtil.isNotEmpty(type)) {
315            String upperCaseType = type.toUpperCase();
316            if (PRINCIPAL_GROUP.equals(upperCaseType)) {
317                // this principal is a group
318                return cms.readGroup(name);
319            } else if (PRINCIPAL_USER.equals(upperCaseType)) {
320                // this principal is a user
321                return cms.readUser(name);
322            }
323        }
324        // invalid principal type was given
325        throw new CmsDbEntryNotFoundException(
326            Messages.get().container(Messages.ERR_INVALID_PRINCIPAL_TYPE_2, type, name));
327    }
328
329    /**
330     * Utility function to read a principal by its id from the OpenCms database using the
331     * provided OpenCms user context.<p>
332     *
333     * @param cms the OpenCms user context to use when reading the principal
334     * @param id the id of the principal to read
335     *
336     * @return the principal read from the OpenCms database
337     *
338     * @throws CmsException in case the principal could not be read
339     */
340    public static I_CmsPrincipal readPrincipalIncludingHistory(CmsObject cms, CmsUUID id) throws CmsException {
341
342        try {
343            // first try to read the principal as a user
344            return cms.readUser(id);
345        } catch (CmsException exc) {
346            // assume user does not exist
347        }
348        try {
349            // now try to read the principal as a group
350            return cms.readGroup(id);
351        } catch (CmsException exc) {
352            //  assume group does not exist
353        }
354        try {
355            // at the end try to read the principal from the history
356            return cms.readHistoryPrincipal(id);
357        } catch (CmsException exc) {
358            //  assume the principal does not exist at all
359        }
360        // invalid principal name was given
361        throw new CmsDbEntryNotFoundException(Messages.get().container(Messages.ERR_INVALID_PRINCIPAL_1, id));
362    }
363
364    /**
365     * @see java.lang.Comparable#compareTo(java.lang.Object)
366     */
367    public int compareTo(I_CmsPrincipal obj) {
368
369        if ((this == obj) || equals(obj)) {
370            return 0;
371        }
372        return getName().compareTo(obj.getName());
373    }
374
375    /**
376     * @see java.lang.Object#equals(java.lang.Object)
377     */
378    @Override
379    public boolean equals(Object obj) {
380
381        if (obj == this) {
382            return true;
383        }
384        if (obj instanceof I_CmsPrincipal) {
385            if (m_id != null) {
386                return m_id.equals(((I_CmsPrincipal)obj).getId());
387            }
388        }
389        return false;
390    }
391
392    /**
393     * @see org.opencms.security.I_CmsPrincipal#getDescription()
394     */
395    public String getDescription() {
396
397        return m_description;
398    }
399
400    /**
401     * Returns the display name of this principal including the organizational unit.<p>
402     *
403     * @param cms the cms context
404     * @param locale the locale
405     *
406     * @return the display name of this principal including the organizational unit
407     *
408     * @throws CmsException if the organizational unit could not be read
409     */
410    public String getDisplayName(CmsObject cms, Locale locale) throws CmsException {
411
412        return Messages.get().getBundle(locale).key(
413            Messages.GUI_PRINCIPAL_DISPLAY_NAME_2,
414            getSimpleName(),
415            OpenCms.getOrgUnitManager().readOrganizationalUnit(cms, getOuFqn()).getDisplayName(locale));
416    }
417
418    /**
419     * Returns the translated display name of this principal if it is a group and the display name otherwise.<p>
420     *
421     * @param cms the current CMS context
422     * @param locale the locale
423     * @param translation the group name translation to use
424     *
425     * @return the translated display name
426     *
427     * @throws CmsException if something goes wrong
428     */
429    public String getDisplayName(CmsObject cms, Locale locale, I_CmsGroupNameTranslation translation)
430    throws CmsException {
431
432        if (!isGroup() || (translation == null)) {
433            return getDisplayName(cms, locale);
434        }
435        return Messages.get().getBundle(locale).key(
436            Messages.GUI_PRINCIPAL_DISPLAY_NAME_2,
437            translation.translateGroupName(getName(), false),
438            OpenCms.getOrgUnitManager().readOrganizationalUnit(cms, getOuFqn()).getDisplayName(locale));
439    }
440
441    /**
442     * @see org.opencms.security.I_CmsPrincipal#getFlags()
443     */
444    public int getFlags() {
445
446        return m_flags;
447    }
448
449    /**
450     * @see org.opencms.security.I_CmsPrincipal#getId()
451     */
452    public CmsUUID getId() {
453
454        return m_id;
455    }
456
457    /**
458     * Returns the fully qualified name of this principal.<p>
459     *
460     * @return the fully qualified name of this principal
461     *
462     * @see java.security.Principal#getName()
463     */
464    public String getName() {
465
466        return m_name;
467    }
468
469    /**
470     * Returns the fully qualified name of the associated organizational unit.<p>
471     *
472     * @return the fully qualified name of the associated organizational unit
473     */
474    public String getOuFqn() {
475
476        return CmsOrganizationalUnit.getParentFqn(m_name);
477    }
478
479    /**
480     * @see org.opencms.security.I_CmsPrincipal#getPrefixedName()
481     */
482    public String getPrefixedName() {
483
484        if (isUser()) {
485            return getPrefixedUser(getName());
486        } else if (isGroup()) {
487            return getPrefixedGroup(getName());
488        }
489        return getName();
490    }
491
492    /**
493     * Returns the simple name of this organizational unit.
494     *
495     * @return the simple name of this organizational unit.
496     */
497    public String getSimpleName() {
498
499        return CmsOrganizationalUnit.getSimpleName(m_name);
500    }
501
502    /**
503     * @see java.lang.Object#hashCode()
504     */
505    @Override
506    public int hashCode() {
507
508        if (m_id != null) {
509            return m_id.hashCode();
510        }
511        return CmsUUID.getNullUUID().hashCode();
512    }
513
514    /**
515     * @see org.opencms.security.I_CmsPrincipal#isEnabled()
516     */
517    public boolean isEnabled() {
518
519        return (getFlags() & I_CmsPrincipal.FLAG_DISABLED) == 0;
520    }
521
522    /**
523     * @see org.opencms.security.I_CmsPrincipal#isGroup()
524     */
525    public boolean isGroup() {
526
527        return (this instanceof CmsGroup);
528    }
529
530    /**
531     * @see org.opencms.security.I_CmsPrincipal#isUser()
532     */
533    public boolean isUser() {
534
535        return (this instanceof CmsUser);
536    }
537
538    /**
539     * @see org.opencms.security.I_CmsPrincipal#setDescription(java.lang.String)
540     */
541    public void setDescription(String description) {
542
543        m_description = description;
544    }
545
546    /**
547     * @see org.opencms.security.I_CmsPrincipal#setEnabled(boolean)
548     */
549    public void setEnabled(boolean enabled) {
550
551        if (enabled != isEnabled()) {
552            // toggle disabled flag if required
553            setFlags(getFlags() ^ I_CmsPrincipal.FLAG_DISABLED);
554        }
555    }
556
557    /**
558     * @see org.opencms.security.I_CmsPrincipal#setFlags(int)
559     */
560    public void setFlags(int value) {
561
562        m_flags = value;
563    }
564
565    /**
566     * @see org.opencms.security.I_CmsPrincipal#setName(java.lang.String)
567     */
568    public void setName(String name) {
569
570        checkName(CmsOrganizationalUnit.getSimpleName(name));
571        m_name = name;
572    }
573}