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.file.CmsGroup;
031import org.opencms.file.CmsUser;
032import org.opencms.util.CmsUUID;
033
034import java.io.Serializable;
035import java.util.ArrayList;
036import java.util.Collections;
037import java.util.HashMap;
038import java.util.Iterator;
039import java.util.List;
040import java.util.Map;
041import java.util.Set;
042
043/**
044 * An access control list contains the permission sets of all principals for a distinct resource
045 * that are calculated on the permissions defined by various access control entries.<p>
046 *
047 * <p>To each single resource, access control entries of type <code>CmsAccessControlEntry</code> can be assigned.
048 * An access control entry defines the permissions (both allowed and explicitly denied) of a user or group for this resource.</p>
049 *
050 * <p>By calling the method <code>getAccessControlList</code> the list is generated on the resource. It contains the result of
051 * merging both access control entries defined immediately on the resource and inherited along the folder hierarchie in the
052 * OpenCms virtual file system (controlled by flags in the entry).</p>
053 *
054 * <p>To check the permissions of a user on a distinct resource, the method <code>hasPermissions</code> in the driver manager
055 * is called in each operation. This method acts as access guard and matches the required permissions for the operation
056 * against the allowed and denied permissions defined for the user or groups of this user.</p>
057 *
058 * @since 6.0.0
059 */
060public class CmsAccessControlList implements Serializable {
061
062    /** The serial version id. */
063    private static final long serialVersionUID = -8772251229957990081L;
064    /** The principal IDs of users/groups which should have exclusive access to the content outside of its released/expired range. */
065    private Set<CmsUUID> m_exclusiveAccessPrincipals = Collections.emptySet();
066
067    /**
068     * Collected permissions of a principal on this resource .
069     */
070    private Map<CmsUUID, CmsPermissionSetCustom> m_permissions;
071
072    /**
073     * Constructor to create an empty access control list for a given resource.<p>
074     *
075     */
076    public CmsAccessControlList() {
077
078        m_permissions = new HashMap<CmsUUID, CmsPermissionSetCustom>();
079    }
080
081    /**
082     * Adds an access control entry to the access control list.<p>
083     *
084     * @param entry the access control entry to add
085     */
086    public void add(CmsAccessControlEntry entry) {
087
088        CmsPermissionSetCustom p = m_permissions.get(entry.getPrincipal());
089        if (p == null) {
090            p = new CmsPermissionSetCustom();
091            m_permissions.put(entry.getPrincipal(), p);
092        }
093        p.addPermissions(entry.getPermissions());
094    }
095
096    /**
097     * Returns a clone of this Objects instance.<p>
098     *
099     * @return a clone of this instance
100     */
101    @Override
102    public Object clone() {
103
104        CmsAccessControlList acl = new CmsAccessControlList();
105        Iterator<CmsUUID> i = m_permissions.keySet().iterator();
106        while (i.hasNext()) {
107            CmsUUID id = i.next();
108            acl.m_permissions.put(id, (CmsPermissionSetCustom)(m_permissions.get(id)).clone());
109        }
110        return acl;
111    }
112
113    /**
114     * Gets the principal IDs of users/groups which should have exclusive access to the content outside of its released/expired range.
115     *
116     * @return the exclusive access principal IDs
117     */
118    public Set<CmsUUID> getExclusiveAccessPrincipals() {
119
120        return m_exclusiveAccessPrincipals;
121    }
122
123    /**
124     * Returns the permission map of this access control list.<p>
125     *
126     * @return permission map
127     */
128    public Map<CmsUUID, CmsPermissionSetCustom> getPermissionMap() {
129
130        return m_permissions;
131    }
132
133    /**
134     * Calculates the permissions of the given user and his groups from the access control list.<p>
135     *
136     * @param user the user
137     * @param groups the groups of this user
138     * @param roles the roles of this user
139     *
140     * @return the summarized permission set of the user
141     */
142    public CmsPermissionSetCustom getPermissions(CmsUser user, List<CmsGroup> groups, List<CmsRole> roles) {
143
144        CmsPermissionSetCustom sum = new CmsPermissionSetCustom();
145        boolean hasPermissions = false;
146        CmsPermissionSet p = m_permissions.get(user.getId());
147        if (p != null) {
148            sum.addPermissions(p);
149            hasPermissions = true;
150        }
151        if (groups != null) {
152            int size = groups.size();
153            for (int i = 0; i < size; i++) {
154                I_CmsPrincipal principal = groups.get(i);
155                p = m_permissions.get(principal.getId());
156                if (p != null) {
157                    sum.addPermissions(p);
158                    hasPermissions = true;
159                }
160            }
161        }
162        if (roles != null) {
163            int size = roles.size();
164            for (int i = 0; i < size; i++) {
165                CmsRole role = roles.get(i);
166                p = m_permissions.get(role.getId());
167                if (p != null) {
168                    sum.addPermissions(p);
169                    hasPermissions = true;
170                }
171            }
172        }
173        if (!hasPermissions) {
174            // if no applicable entry is found check the 'all others' entry
175            p = m_permissions.get(CmsAccessControlEntry.PRINCIPAL_ALL_OTHERS_ID);
176            if (p != null) {
177                sum.addPermissions(p);
178            }
179        }
180        return sum;
181    }
182
183    /**
184     * Returns the permission set of a principal as stored in the access control list.<p>
185     *
186     * @param principalId the id of the principal (group or user)
187     *
188     * @return the current permissions of this single principal
189     */
190    public CmsPermissionSetCustom getPermissions(CmsUUID principalId) {
191
192        return m_permissions.get(principalId);
193    }
194
195    /**
196     * Calculates the permissions of the given user and his groups from the access control list.<p>
197     * The permissions are returned as permission string in the format {{+|-}{r|w|v|c|i}}*.
198     *
199     * @param user the user
200     * @param groups the groups of this user
201     * @param roles the roles of this user
202     *
203     * @return a string that displays the permissions
204     */
205    public String getPermissionString(CmsUser user, List<CmsGroup> groups, List<CmsRole> roles) {
206
207        return getPermissions(user, groups, roles).getPermissionString();
208    }
209
210    /**
211     * Returns the principals with specific permissions stored in this access control list.<p>
212     *
213     * @return enumeration of principals (each group or user)
214     */
215    public List<CmsUUID> getPrincipals() {
216
217        List<CmsUUID> principals = new ArrayList<CmsUUID>(m_permissions.keySet());
218        Collections.sort(principals, CmsAccessControlEntry.COMPARATOR_PRINCIPALS);
219        return principals;
220    }
221
222    /**
223     * Sets the allowed permissions of a given access control entry as allowed permissions in the access control list.<p>
224     * The denied permissions are left unchanged.
225     *
226     * @param entry the access control entry
227     */
228    public void setAllowedPermissions(CmsAccessControlEntry entry) {
229
230        CmsPermissionSetCustom p = m_permissions.get(entry.getPrincipal());
231        if (p == null) {
232            p = new CmsPermissionSetCustom();
233            m_permissions.put(entry.getPrincipal(), p);
234        }
235        p.setPermissions(entry.getAllowedPermissions(), p.getDeniedPermissions());
236    }
237
238    /**
239     * Sets the denied permissions of a given access control entry as denied permissions in the access control list.<p>
240     * The allowed permissions are left unchanged.
241     *
242     * @param entry the access control entry
243     */
244    public void setDeniedPermissions(CmsAccessControlEntry entry) {
245
246        CmsPermissionSetCustom p = m_permissions.get(entry.getPrincipal());
247        if (p == null) {
248            p = new CmsPermissionSetCustom();
249            m_permissions.put(entry.getPrincipal(), p);
250        }
251        p.setPermissions(p.getAllowedPermissions(), entry.getDeniedPermissions());
252    }
253
254    /**
255     * Sets the exclusive access principal IDs.
256     *
257     * @param exclusiveAccessPrincipals the IDs of the exclusive access principals
258     */
259    public void setExclusiveAccessPrincipals(Set<CmsUUID> exclusiveAccessPrincipals) {
260
261        m_exclusiveAccessPrincipals = Collections.unmodifiableSet(exclusiveAccessPrincipals);
262    }
263
264}