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.security;
029
030import org.opencms.util.CmsUUID;
031
032import java.util.Comparator;
033import java.util.StringTokenizer;
034
035/**
036 * An access control entry defines the permissions of a user or group for a distinct resource.<p>
037 *
038 * Besides the <code>CmsPermissionSet</code> to define the permissions, the access control entry
039 * contains the UUID of the resource and of the principal (user or group) who has the defined permissions.
040 * Since the principal is identified by its UUID, any other entity may act as principal also.
041 *
042 * <p>Additionally, the entry stores various flags:<br>
043 * <code>ACCESS_FLAGS_DELETED</code> indicates that this entry is deleted<br>
044 * <code>ACCESS_FLAGS_INHERIT</code> indicates that this entry should be inherited<br>
045 * <code>ACCESS_FLAGS_OVERWRITE</code> indicates that this entry overwrites inherited settings<br>
046 * <code>ACCESS_FLAGS_INHERITED</code> indicates that this entry is inherited<br>
047 * <code>ACCESS_FLAGS_USER</code> indicates that the principal is a single user<br>
048 * <code>ACCESS_FLAGS_GROUP</code> indicates that the principal is a group
049 * </p>
050 *
051 * @since 6.0.0
052 */
053public class CmsAccessControlEntry {
054
055    /** Flag to indicate the principal type 'all others'. */
056    public static final int ACCESS_FLAGS_ALLOTHERS = 128;
057
058    /** Flag to indicate the principal type group. */
059    public static final int ACCESS_FLAGS_GROUP = 32;
060
061    /** Flag to indicate that an access control entry should be inherited. */
062    public static final int ACCESS_FLAGS_INHERIT = 2;
063
064    /** Flag to indicate that an access control entry was inherited (read only). */
065    public static final int ACCESS_FLAGS_INHERITED = 8;
066
067    /** Flag to indicate that an access control entry overwrites inherited entries. */
068    public static final int ACCESS_FLAGS_OVERWRITE = 4;
069
070    /** Flag to indicate the principal type 'overwrite all'. */
071    public static final int ACCESS_FLAGS_OVERWRITE_ALL = 256;
072
073    /** Flag to indicate that the principal is responsible for the resource. */
074    public static final int ACCESS_FLAGS_RESPONSIBLE = 64;
075
076    /** Flag to indicate the principal type role. */
077    public static final int ACCESS_FLAGS_ROLE = 512;
078
079    /** Flag to indicate the principal type user. */
080    public static final int ACCESS_FLAGS_USER = 16;
081
082    /**
083     * ACE comparator.<p>
084     *
085     * Sorts the given list of {@link CmsAccessControlEntry} objects.<p>
086     *
087     * The 'overwrite all' ace in first place, the 'all others' ace in second place.<p>
088     */
089    public static final Comparator<CmsAccessControlEntry> COMPARATOR_ACE = new Comparator<CmsAccessControlEntry>() {
090
091        /**
092         * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
093         */
094        public int compare(CmsAccessControlEntry ace1, CmsAccessControlEntry ace2) {
095
096            if (ace1 == ace2) {
097                return 0;
098            }
099            CmsUUID id1 = (ace1).getPrincipal();
100            CmsUUID id2 = (ace2).getPrincipal();
101            return COMPARATOR_PRINCIPALS.compare(id1, id2);
102        }
103    };
104
105    /**
106     * ACE principals comparator.<p>
107     *
108     * Sorts the given list of {@link CmsAccessControlEntry} objects.<p>
109     *
110     * The 'overwrite all' ace in first place, the 'all others' ace in second place.<p>
111     */
112    public static final Comparator<CmsUUID> COMPARATOR_PRINCIPALS = new Comparator<CmsUUID>() {
113
114        /**
115         * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
116         */
117        public int compare(CmsUUID id1, CmsUUID id2) {
118
119            if (id1 == id2) {
120                return 0;
121            }
122            if (id1.equals(id2)) {
123                return 0;
124            } else if (id1.equals(PRINCIPAL_OVERWRITE_ALL_ID)) {
125                return -1;
126            } else if (id1.equals(PRINCIPAL_ALL_OTHERS_ID)) {
127                if (id2.equals(PRINCIPAL_OVERWRITE_ALL_ID)) {
128                    return 1;
129                } else {
130                    return -1;
131                }
132            } else if (id2.equals(PRINCIPAL_ALL_OTHERS_ID)) {
133                if (id1.equals(PRINCIPAL_OVERWRITE_ALL_ID)) {
134                    return -1;
135                } else {
136                    return 1;
137                }
138            } else if (id2.equals(PRINCIPAL_OVERWRITE_ALL_ID)) {
139                return 1;
140            }
141            return id1.compareTo(id2);
142        }
143    };
144
145    /** The used id for ace's that apply to all other principals. */
146    public static final CmsUUID PRINCIPAL_ALL_OTHERS_ID;
147
148    /** The used name for ace's that apply to all other principals. */
149    public static final String PRINCIPAL_ALL_OTHERS_NAME = "ALL_OTHERS";
150
151    /** The used id for ace's that overwrites all inherited permissions. */
152    public static final CmsUUID PRINCIPAL_OVERWRITE_ALL_ID;
153
154    /** The used name for ace's that overwrites all inherited permissions. */
155    public static final String PRINCIPAL_OVERWRITE_ALL_NAME = "OVERWRITE_ALL";
156
157    /** UUID which is used to read all access control entries, should never be written to the database. */
158    public static final CmsUUID PRINCIPAL_READALL_ID;
159
160    /** Flags of this access control entry. */
161    private int m_flags;
162
163    /** The permission set. */
164    private CmsPermissionSetCustom m_permissions;
165
166    /** Id of the principal. */
167    private CmsUUID m_principal;
168
169    /** Id of the resource. */
170    private CmsUUID m_resource;
171
172    /**
173     * Constructor to create a new access control entry for a given resource
174     * based on an existing access control entry.<p>
175     *
176     * @param resource the resource
177     * @param base the base for the created access control entry
178     */
179    public CmsAccessControlEntry(CmsUUID resource, CmsAccessControlEntry base) {
180
181        m_resource = resource;
182        m_principal = base.m_principal;
183        m_permissions = base.m_permissions;
184        m_flags = base.m_flags;
185    }
186
187    /**
188     * Constructor to create a new access control entry on a given resource and a given principal.<p>
189     * Permissions are specified as permission set, flags as bitset.
190     *
191     * @param resource the resource
192     * @param principal the id of a principal (user or group)
193     * @param permissions the set of allowed and denied permissions as permission set
194     * @param flags additional flags of the access control entry
195     */
196    public CmsAccessControlEntry(CmsUUID resource, CmsUUID principal, CmsPermissionSet permissions, int flags) {
197
198        m_resource = resource;
199        m_principal = principal;
200        m_permissions = new CmsPermissionSetCustom(permissions);
201        m_flags = flags;
202    }
203
204    /**
205     * Constructor to create a new access control entry on a given resource and a given principal.<p>
206     * Permissions and flags are specified as bitsets.
207     *
208     * @see CmsPermissionSet
209     *
210     * @param resource the resource
211     * @param principal the id of a principal (user or group)
212     * @param allowed the set of allowed permissions
213     * @param denied set set of explicitly denied permissions
214     * @param flags additional flags of the access control entry
215     */
216    public CmsAccessControlEntry(CmsUUID resource, CmsUUID principal, int allowed, int denied, int flags) {
217
218        m_resource = resource;
219        m_principal = principal;
220        m_permissions = new CmsPermissionSetCustom(allowed, denied);
221        m_flags = flags;
222    }
223
224    /**
225     * Constructor to create a new access control entry on a given resource and a given principal.<p>
226     * Permission and flags are specified as string of the format {{+|-}{r|w|v|c|i}}*
227     *
228     * @param resource the resource
229     * @param principal the id of a principal (user or group)
230     * @param acPermissionString allowed and denied permissions and also flags
231     */
232    public CmsAccessControlEntry(CmsUUID resource, CmsUUID principal, String acPermissionString) {
233
234        m_resource = resource;
235        m_principal = principal;
236        m_flags = 0;
237
238        StringTokenizer tok = new StringTokenizer(acPermissionString, "+-", true);
239        StringBuffer permissionString = new StringBuffer();
240
241        while (tok.hasMoreElements()) {
242            String prefix = tok.nextToken();
243            String suffix = tok.nextToken();
244            switch (suffix.charAt(0)) {
245                case 'I':
246                case 'i':
247                    if (prefix.charAt(0) == '+') {
248                        m_flags |= CmsAccessControlEntry.ACCESS_FLAGS_INHERIT;
249                    }
250                    if (prefix.charAt(0) == '-') {
251                        m_flags &= ~CmsAccessControlEntry.ACCESS_FLAGS_INHERIT;
252                    }
253                    break;
254                case 'O':
255                case 'o':
256                    if (prefix.charAt(0) == '+') {
257                        m_flags |= CmsAccessControlEntry.ACCESS_FLAGS_OVERWRITE;
258                    }
259                    if (prefix.charAt(0) == '-') {
260                        m_flags &= ~CmsAccessControlEntry.ACCESS_FLAGS_OVERWRITE;
261                    }
262                    break;
263                case 'L':
264                case 'l':
265                    if (prefix.charAt(0) == '+') {
266                        m_flags |= CmsAccessControlEntry.ACCESS_FLAGS_RESPONSIBLE;
267                    }
268                    if (prefix.charAt(0) == '-') {
269                        m_flags &= ~CmsAccessControlEntry.ACCESS_FLAGS_RESPONSIBLE;
270                    }
271                    break;
272                default:
273                    permissionString.append(prefix);
274                    permissionString.append(suffix);
275                    break;
276            }
277        }
278
279        m_permissions = new CmsPermissionSetCustom(permissionString.toString());
280    }
281
282    static {
283        PRINCIPAL_ALL_OTHERS_ID = CmsUUID.getConstantUUID(PRINCIPAL_ALL_OTHERS_NAME.toLowerCase());
284        PRINCIPAL_OVERWRITE_ALL_ID = CmsUUID.getConstantUUID(PRINCIPAL_OVERWRITE_ALL_NAME.toLowerCase());
285        PRINCIPAL_READALL_ID = CmsUUID.getConstantUUID("principal-read-all");
286    }
287
288    /**
289     * Sets the explicitly denied permissions in the access control entry.<p>
290     *
291     * @param denied the denied permissions as bitset
292     */
293    public void denyPermissions(int denied) {
294
295        m_permissions.denyPermissions(denied);
296    }
297
298    /**
299     * @see java.lang.Object#equals(java.lang.Object)
300     */
301    @Override
302    public boolean equals(Object obj) {
303
304        if (obj == this) {
305            return true;
306        }
307        if (obj instanceof CmsAccessControlEntry) {
308            CmsAccessControlEntry other = (CmsAccessControlEntry)obj;
309            if (other.m_flags != m_flags) {
310                return false;
311            }
312            if (other.getPermissions().getAllowedPermissions() != getPermissions().getAllowedPermissions()) {
313                return false;
314            }
315            if (other.getPermissions().getDeniedPermissions() != getPermissions().getDeniedPermissions()) {
316                return false;
317            }
318            if (!other.m_resource.equals(m_resource)) {
319                return false;
320            }
321            if (!other.m_principal.equals(m_principal)) {
322                return false;
323            }
324            return true;
325        }
326        return false;
327    }
328
329    /**
330     * Returns the currently allowed permissions as bitset.<p>
331     *
332     * @return the allowed permissions
333     */
334    public int getAllowedPermissions() {
335
336        return m_permissions.getAllowedPermissions();
337    }
338
339    /**
340     * Returns the currently denied permissions as bitset.<p>
341     *
342     * @return the denied permissions
343     */
344    public int getDeniedPermissions() {
345
346        return m_permissions.getDeniedPermissions();
347    }
348
349    /**
350     * Returns the current flags of the access control entry.<p>
351     *
352     * @return bitset with flag values
353     */
354    public int getFlags() {
355
356        return m_flags;
357    }
358
359    /**
360     * Returns the string representation of the "inherit" flag.<p>
361     *
362     * @return string of the format {{+|-}i}*
363     */
364    public String getInheritingString() {
365
366        if (isInheriting()) {
367            return "+i";
368        } else {
369            return "-i";
370        }
371    }
372
373    /**
374     * Returns the current permission set (both allowed and denied permissions).<p>
375     *
376     * @return the set of permissions
377     */
378    public CmsPermissionSet getPermissions() {
379
380        return m_permissions;
381    }
382
383    /**
384     * Returns the principal assigned with this access control entry.<p>
385     *
386     * @return the principal
387     */
388    public CmsUUID getPrincipal() {
389
390        return m_principal;
391    }
392
393    /**
394     * Returns the resource assigned with this access control entry.<p>
395     *
396     * @return the resource
397     */
398    public CmsUUID getResource() {
399
400        return m_resource;
401    }
402
403    /**
404     * Returns the string representation of the "responsible" flag.<p>
405     *
406     * @return string of the format {{+|-}l}*
407     */
408    public String getResponsibleString() {
409
410        if (isResponsible()) {
411            return "+l";
412        } else {
413            return "-l";
414        }
415    }
416
417    /**
418     * Sets the allowed permissions in the access control entry.<p>
419     *
420     * @param allowed the allowed permissions as bitset
421     */
422    public void grantPermissions(int allowed) {
423
424        m_permissions.grantPermissions(allowed);
425    }
426
427    /**
428     * @see java.lang.Object#hashCode()
429     */
430    @Override
431    public int hashCode() {
432
433        if (m_permissions != null) {
434            return m_permissions.hashCode() * m_flags;
435        }
436        return CmsUUID.getNullUUID().hashCode();
437    }
438
439    /**
440     * Checks if the {@link #ACCESS_FLAGS_ALLOTHERS} flag is set.<p>
441     *
442     * @return <code>true</code> if the {@link #ACCESS_FLAGS_ALLOTHERS} flag is set
443     */
444    public boolean isAllOthers() {
445
446        return (m_flags & ACCESS_FLAGS_ALLOTHERS) == ACCESS_FLAGS_ALLOTHERS;
447    }
448
449    /**
450     * Returns if this access control entry has the inherited flag set.<p>
451     * Note: to check if an access control entry is inherited, also the
452     * resource id and the id of the current resource must be different.
453     *
454     * @return true, if the inherited flag is set
455     */
456    public boolean isInherited() {
457
458        return ((m_flags & CmsAccessControlEntry.ACCESS_FLAGS_INHERITED) > 0);
459    }
460
461    /**
462     * Returns if this ace is being inherited to the folder subresources.<p>
463     *
464     * @return  <code>true</code>, if this ace is being inherited to the folder subresources
465     */
466    public boolean isInheriting() {
467
468        return ((m_flags & CmsAccessControlEntry.ACCESS_FLAGS_INHERIT) > 0);
469    }
470
471    /**
472     * Checks if the {@link #ACCESS_FLAGS_OVERWRITE_ALL} flag is set.<p>
473     *
474     * @return <code>true</code> if the {@link #ACCESS_FLAGS_OVERWRITE_ALL} flag is set
475     */
476    public boolean isOverwriteAll() {
477
478        return (m_flags & ACCESS_FLAGS_OVERWRITE_ALL) == ACCESS_FLAGS_OVERWRITE_ALL;
479    }
480
481    /**
482     * Returns if the principal is responsible for the current resource.<p>
483     *
484     * @return  true ,if the principal is responsible for the current resource
485     */
486    public boolean isResponsible() {
487
488        return ((m_flags & CmsAccessControlEntry.ACCESS_FLAGS_RESPONSIBLE) > 0);
489    }
490
491    /**
492     * Resets the given flags in the access control entry.<p>
493     *
494     * @param flags bitset with flag values to reset
495     */
496    public void resetFlags(int flags) {
497
498        m_flags &= ~flags;
499    }
500
501    /**
502     * Sets the given flags in the access control entry.<p>
503     *
504     * @param flags bitset with flag values to set
505     */
506    public void setFlags(int flags) {
507
508        m_flags |= flags;
509    }
510
511    /**
512     * Sets the access flags to identify the given principal type.<p>
513     *
514     * @param principal the principal to set the flags for
515     */
516    public void setFlagsForPrincipal(I_CmsPrincipal principal) {
517
518        setFlags(
519            principal.isGroup() ? CmsAccessControlEntry.ACCESS_FLAGS_GROUP : CmsAccessControlEntry.ACCESS_FLAGS_USER);
520    }
521
522    /**
523     * Sets the allowed and denied permissions of the access control entry.<p>
524     *
525     * @param permissions the set of permissions
526     */
527    public void setPermissions(CmsPermissionSet permissions) {
528
529        m_permissions.setPermissions(permissions);
530    }
531
532    /**
533     * Returns the String representation of this access control entry object.<p>
534     * @see java.lang.Object#toString()
535     */
536    @Override
537    public String toString() {
538
539        return "[Ace:] "
540            + "ResourceId="
541            + m_resource
542            + ", PrincipalId="
543            + m_principal
544            + ", Permissions="
545            + m_permissions.toString()
546            + ", Flags="
547            + m_flags;
548    }
549}