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.workplace.explorer;
029
030import org.opencms.file.CmsGroup;
031import org.opencms.file.CmsObject;
032import org.opencms.file.CmsResource;
033import org.opencms.file.CmsUser;
034import org.opencms.main.CmsException;
035import org.opencms.main.CmsLog;
036import org.opencms.main.OpenCms;
037import org.opencms.monitor.CmsMemoryMonitor;
038import org.opencms.security.CmsAccessControlEntry;
039import org.opencms.security.CmsAccessControlList;
040import org.opencms.security.CmsPermissionSet;
041import org.opencms.security.CmsPermissionSetCustom;
042import org.opencms.security.CmsRole;
043import org.opencms.security.I_CmsPrincipal;
044import org.opencms.util.CmsUUID;
045
046import java.io.Serializable;
047import java.util.HashMap;
048import java.util.Iterator;
049import java.util.List;
050import java.util.Map;
051
052import org.apache.commons.logging.Log;
053
054/**
055 * Explorer type access object, encapsulates access control entries and lists of a explorer type.<p>
056 *
057 * @since 6.0.0
058 */
059public class CmsExplorerTypeAccess implements Serializable {
060
061    /** Principal key name for the default permission settings. */
062    public static final String PRINCIPAL_DEFAULT = "DEFAULT";
063
064    /** The listener used to flush cached access settings. */
065    protected static CmsExplorerTypeAccessFlushListener flushListener;
066
067    /** The log object for this class. */
068    private static final Log LOG = CmsLog.getLog(CmsExplorerTypeAccess.class);
069
070    /** The serial version id. */
071    private static final long serialVersionUID = 1119068085158682112L;
072
073    static {
074        flushListener = new CmsExplorerTypeAccessFlushListener();
075        flushListener.install();
076    }
077
078    /** The map of configured access control entries. */
079    private Map<String, String> m_accessControl;
080
081    /** The acl based on the map of configured access control entries. */
082    private CmsAccessControlList m_accessControlList;
083
084    /** Cached permissions based on roles. */
085    private transient Map<String, CmsPermissionSetCustom> m_permissionsCache;
086
087    /**
088     * Constructor, creates an empty, CmsExplorerTypeAccess object.<p>
089     */
090    public CmsExplorerTypeAccess() {
091
092        m_accessControl = new HashMap<String, String>();
093        flushListener.add(this);
094    }
095
096    /**
097     * Adds a single access entry to the map of access entries of the explorer type setting.<p>
098     *
099     * This stores the configuration data in a map which is used in the initialize process
100     * to create the access control list.<p>
101     *
102     * @param key the principal of the ace
103     * @param value the permissions for the principal
104     */
105    public void addAccessEntry(String key, String value) {
106
107        m_accessControl.put(key, value);
108        if (LOG.isDebugEnabled()) {
109            LOG.debug(Messages.get().getBundle().key(Messages.LOG_ADD_ACCESS_ENTRY_2, key, value));
110        }
111    }
112
113    /**
114     * Creates the access control list from the temporary map.<p>
115     *
116     * @param resourceType the name of the resource type
117     *
118     * @throws CmsException if something goes wrong
119     */
120    public void createAccessControlList(String resourceType) throws CmsException {
121
122        if (OpenCms.getRunLevel() < OpenCms.RUNLEVEL_2_INITIALIZING) {
123            // we don't need this for simple test cases
124            return;
125        }
126        if (m_permissionsCache == null) {
127            m_permissionsCache = CmsMemoryMonitor.createLRUCacheMap(2048);
128            OpenCms.getMemoryMonitor().register(this.getClass().getName() + "." + resourceType, m_permissionsCache);
129        } else {
130            m_permissionsCache.clear();
131        }
132
133        m_accessControlList = new CmsAccessControlList();
134        Iterator<String> i = m_accessControl.keySet().iterator();
135        while (i.hasNext()) {
136            String key = i.next();
137            if (!PRINCIPAL_DEFAULT.equals(key)) {
138                String value = m_accessControl.get(key);
139                // get the principal name from the principal String
140                String principal = key.substring(key.indexOf('.') + 1, key.length());
141
142                // create an OpenCms user context with "Guest" permissions
143                CmsObject cms = OpenCms.initCmsObject(OpenCms.getDefaultUsers().getUserGuest());
144
145                CmsUUID principalId = null;
146                if (key.startsWith(I_CmsPrincipal.PRINCIPAL_GROUP)) {
147                    // read the group
148                    principal = OpenCms.getImportExportManager().translateGroup(principal);
149                    try {
150                        principalId = cms.readGroup(principal).getId();
151                    } catch (CmsException e) {
152                        if (LOG.isErrorEnabled()) {
153                            LOG.error(e.getLocalizedMessage(), e);
154                        }
155                    }
156                } else if (key.startsWith(I_CmsPrincipal.PRINCIPAL_USER)) {
157                    // read the user
158                    principal = OpenCms.getImportExportManager().translateUser(principal);
159                    try {
160                        principalId = cms.readUser(principal).getId();
161                    } catch (CmsException e) {
162                        if (LOG.isErrorEnabled()) {
163                            LOG.error(e.getLocalizedMessage(), e);
164                        }
165                    }
166                } else {
167                    // read the role with role name
168                    CmsRole role = CmsRole.valueOfRoleName(principal);
169                    if (role == null) {
170                        // try to read the role in the old fashion with group name
171                        role = CmsRole.valueOfGroupName(principal);
172                    }
173                    principalId = role.getId();
174                }
175                if (principalId != null) {
176                    // create a new entry for the principal
177                    CmsAccessControlEntry entry = new CmsAccessControlEntry(null, principalId, value);
178                    m_accessControlList.add(entry);
179                }
180            }
181        }
182    }
183
184    /**
185     * Returns the computed access Control List.<p>
186     *
187     * @return the computed access Control List
188     */
189    public CmsAccessControlList getAccessControlList() {
190
191        return m_accessControlList;
192    }
193
194    /**
195     * Returns the map of access entries of the explorer type setting.<p>
196     *
197     * @return the map of access entries of the explorer type setting
198     */
199    public Map<String, String> getAccessEntries() {
200
201        return m_accessControl;
202    }
203
204    /**
205     * Calculates the permissions for this explorer type settings
206     * for the user in the given OpenCms user context.<p>
207     *
208     * @param cms the OpenCms user context to calculate the permissions for
209     * @param resource the resource to check the permissions for
210     *
211     * @return the permissions for this explorer type settings for the user in the given OpenCms user context
212     */
213    public CmsPermissionSet getPermissions(CmsObject cms, CmsResource resource) {
214
215        String cacheKey = getPermissionsCacheKey(cms, resource);
216        CmsPermissionSetCustom permissions;
217        if (cacheKey != null) {
218            permissions = m_permissionsCache.get(cacheKey);
219            if (permissions != null) {
220                return permissions;
221            }
222        }
223        CmsAccessControlList acl = (CmsAccessControlList)m_accessControlList.clone();
224
225        CmsUser user = cms.getRequestContext().getCurrentUser();
226        List<CmsGroup> groups = null;
227        try {
228            groups = cms.getGroupsOfUser(user.getName(), false);
229        } catch (CmsException e) {
230            // error reading the groups of the current user
231            LOG.error(Messages.get().getBundle().key(Messages.LOG_READ_GROUPS_OF_USER_FAILED_1, user.getName()), e);
232        }
233        List<CmsRole> roles = null;
234        try {
235            roles = OpenCms.getRoleManager().getRolesForResource(cms, user, resource);
236        } catch (CmsException e) {
237            // error reading the roles of the current user
238            LOG.error(Messages.get().getBundle().key(Messages.LOG_READ_GROUPS_OF_USER_FAILED_1, user.getName()), e);
239        }
240        String defaultPermissions = m_accessControl.get(PRINCIPAL_DEFAULT);
241        // add the default permissions to the acl
242        if ((defaultPermissions != null) && !user.isGuestUser()) {
243            boolean found = false;
244            if (acl.getPermissions(user.getId()) != null) {
245                // acl already contains the user, no need for default
246                found = true;
247            }
248            if (!found && (groups != null)) {
249                // look up all groups to see if we need the default
250                Iterator<CmsGroup> itGroups = groups.iterator();
251                while (itGroups.hasNext()) {
252                    CmsGroup group = itGroups.next();
253                    if (acl.getPermissions(group.getId()) != null) {
254                        // acl already contains the group, no need for default
255                        found = true;
256                        break;
257                    }
258                }
259            }
260            if (!found && (roles != null)) {
261                // look up all roles to see if we need the default
262                Iterator<CmsRole> itRoles = roles.iterator();
263                while (itRoles.hasNext()) {
264                    CmsRole role = itRoles.next();
265                    if (acl.getPermissions(role.getId()) != null) {
266                        // acl already contains the group, no need for default
267                        found = true;
268                        break;
269                    }
270                }
271            }
272            if (!found) {
273                // add default access control settings for current user
274                CmsAccessControlEntry entry = new CmsAccessControlEntry(null, user.getId(), defaultPermissions);
275                acl.add(entry);
276            }
277        }
278        permissions = acl.getPermissions(user, groups, roles);
279
280        if (cacheKey != null) {
281            m_permissionsCache.put(cacheKey, permissions);
282        }
283        return permissions;
284    }
285
286    /**
287     * Tests if there are any access information stored.<p>
288     *
289     * @return true or false
290     */
291    public boolean isEmpty() {
292
293        return m_accessControl.isEmpty();
294    }
295
296    /**
297     * Flushes the permission cache.<p>
298     */
299    protected void flushCache() {
300
301        if (m_permissionsCache != null) {
302            m_permissionsCache.clear();
303        }
304    }
305
306    /**
307     * Returns the cache key for the roles and groups of the current user and the given resource.<p>
308     *
309     * In this way, it does not matter if the resource and/or user permissions changes, so we never need to clean the cache.<p>
310     *
311     * And since the cache is a LRU map, old trash entries will be automatically removed.<p>
312     *
313     * @param cms the current cms context
314     * @param resource the resource
315     *
316     * @return the cache key
317     */
318    private String getPermissionsCacheKey(CmsObject cms, CmsResource resource) {
319
320        try {
321            String userName = cms.getRequestContext().getCurrentUser().getName();
322            StringBuffer key = new StringBuffer(256);
323            key.append(resource.getRootPath()).append("_");
324            Iterator<?> itGroups = cms.getGroupsOfUser(userName, true).iterator();
325            while (itGroups.hasNext()) {
326                CmsGroup group = (CmsGroup)itGroups.next();
327                key.append(group.getName()).append("_");
328            }
329            Iterator<?> itRoles = OpenCms.getRoleManager().getRolesOfUser(
330                cms,
331                userName,
332                "",
333                true,
334                true,
335                false).iterator();
336            while (itRoles.hasNext()) {
337                CmsRole role = (CmsRole)itRoles.next();
338                key.append(role.getGroupName()).append("_");
339            }
340            return key.toString();
341        } catch (CmsException e) {
342            return null;
343        }
344    }
345}