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