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.configuration.CmsSystemConfiguration;
031import org.opencms.db.CmsCacheSettings;
032import org.opencms.db.CmsDbContext;
033import org.opencms.db.CmsDriverManager;
034import org.opencms.db.CmsSecurityManager;
035import org.opencms.db.I_CmsCacheKey;
036import org.opencms.file.CmsProject;
037import org.opencms.file.CmsResource;
038import org.opencms.file.CmsResourceFilter;
039import org.opencms.file.CmsUser;
040import org.opencms.file.types.CmsResourceTypeJsp;
041import org.opencms.lock.CmsLock;
042import org.opencms.main.CmsException;
043import org.opencms.main.CmsInitException;
044import org.opencms.main.CmsLog;
045import org.opencms.main.OpenCms;
046
047import java.util.Iterator;
048
049import org.apache.commons.logging.Log;
050
051/**
052 * Generic base driver interface.<p>
053 *
054 * @since 7.0.2
055 */
056public class CmsDefaultPermissionHandler implements I_CmsPermissionHandler {
057
058    /** The log object for this class. */
059    private static final Log LOG = CmsLog.getLog(CmsDefaultPermissionHandler.class);
060
061    /** Driver Manager instance. */
062    protected CmsDriverManager m_driverManager;
063
064    /** Security Manager instance. */
065    protected CmsSecurityManager m_securityManager;
066
067    /** The class used for cache key generation. */
068    private I_CmsCacheKey m_keyGenerator;
069
070    /**
071     * @see org.opencms.security.I_CmsPermissionHandler#hasPermissions(org.opencms.db.CmsDbContext, org.opencms.file.CmsResource, org.opencms.security.CmsPermissionSet, boolean, org.opencms.file.CmsResourceFilter)
072     */
073    public CmsPermissionCheckResult hasPermissions(
074        CmsDbContext dbc,
075        CmsResource resource,
076        CmsPermissionSet requiredPermissions,
077        boolean checkLock,
078        CmsResourceFilter filter) throws CmsException {
079
080        // check if the resource is valid according to the current filter
081        // if not, throw a CmsResourceNotFoundException
082        if (!filter.isValid(dbc.getRequestContext(), resource)) {
083            return I_CmsPermissionHandler.PERM_FILTERED;
084        }
085
086        // checking the filter is less cost intensive then checking the cache,
087        // this is why basic filter results are not cached
088        String cacheKey = m_keyGenerator.getCacheKeyForUserPermissions(
089            filter.requireVisible() && checkLock
090            ? "11"
091            : (!filter.requireVisible() && checkLock ? "01" : (filter.requireVisible() && !checkLock ? "10" : "00")),
092            dbc,
093            resource,
094            requiredPermissions);
095        CmsPermissionCheckResult cacheResult = OpenCms.getMemoryMonitor().getCachedPermission(cacheKey);
096        if (cacheResult != null) {
097            return cacheResult;
098        }
099
100        int denied = 0;
101
102        // if this is the online project, write is rejected
103        if (dbc.currentProject().isOnlineProject()) {
104            denied |= CmsPermissionSet.PERMISSION_WRITE;
105        }
106
107        // check if the current user is admin
108        boolean canIgnorePermissions = m_securityManager.hasRoleForResource(
109            dbc,
110            dbc.currentUser(),
111            CmsRole.VFS_MANAGER,
112            resource);
113
114        // check lock status
115        boolean writeRequired = requiredPermissions.requiresWritePermission()
116            || requiredPermissions.requiresControlPermission();
117
118        // if the resource type is jsp
119        // write is only allowed for administrators
120        if (writeRequired && !canIgnorePermissions && (CmsResourceTypeJsp.isJsp(resource))) {
121            if (!m_securityManager.hasRoleForResource(dbc, dbc.currentUser(), CmsRole.VFS_MANAGER, resource)) {
122                denied |= CmsPermissionSet.PERMISSION_WRITE;
123                denied |= CmsPermissionSet.PERMISSION_CONTROL;
124            }
125        }
126
127        if (writeRequired && checkLock) {
128            // check lock state only if required
129            CmsLock lock = m_driverManager.getLock(dbc, resource);
130            // if the resource is not locked by the current user, write and control
131            // access must cause a permission error that must not be cached
132            if (lock.isUnlocked() || !lock.isLockableBy(dbc.currentUser())) {
133                return I_CmsPermissionHandler.PERM_NOTLOCKED;
134            }
135        }
136
137        CmsPermissionSetCustom permissions;
138        if (canIgnorePermissions) {
139            // if the current user is administrator, anything is allowed
140            permissions = new CmsPermissionSetCustom(~0);
141        } else {
142            // otherwise, get the permissions from the access control list
143            permissions = m_driverManager.getPermissions(dbc, resource, dbc.currentUser());
144        }
145
146        // revoke the denied permissions
147        permissions.denyPermissions(denied);
148
149        if ((permissions.getPermissions() & CmsPermissionSet.PERMISSION_VIEW) == 0) {
150            // resource "invisible" flag is set for this user
151            if (!canIgnorePermissions && filter.requireVisible()) {
152                // filter requires visible permission - extend required permission set
153                requiredPermissions = new CmsPermissionSet(
154                    requiredPermissions.getAllowedPermissions() | CmsPermissionSet.PERMISSION_VIEW,
155                    requiredPermissions.getDeniedPermissions());
156            } else {
157                // view permissions can be ignored by filter
158                permissions.setPermissions(
159                    // modify permissions so that view is allowed
160                    permissions.getAllowedPermissions() | CmsPermissionSet.PERMISSION_VIEW,
161                    permissions.getDeniedPermissions() & ~CmsPermissionSet.PERMISSION_VIEW);
162            }
163        }
164
165        if (requiredPermissions.requiresDirectPublishPermission()) {
166            // direct publish permission is required
167            if ((permissions.getPermissions() & CmsPermissionSet.PERMISSION_DIRECT_PUBLISH) == 0) {
168                // but the user has no direct publish permission, so check if the user has the project manager role
169                boolean canIgnorePublishPermission = m_securityManager.hasRoleForResource(
170                    dbc,
171                    dbc.currentUser(),
172                    CmsRole.PROJECT_MANAGER,
173                    resource);
174                // if not, check the manageable projects
175                if (!canIgnorePublishPermission) {
176                    CmsUser user = dbc.currentUser();
177                    Iterator<CmsProject> itProjects = m_driverManager.getAllManageableProjects(
178                        dbc,
179                        m_driverManager.readOrganizationalUnit(dbc, user.getOuFqn()),
180                        true).iterator();
181                    while (itProjects.hasNext()) {
182                        CmsProject project = itProjects.next();
183                        if (CmsProject.isInsideProject(m_driverManager.readProjectResources(dbc, project), resource)) {
184                            canIgnorePublishPermission = true;
185                            break;
186                        }
187                    }
188                }
189
190                if (canIgnorePublishPermission) {
191                    // direct publish permission can be ignored
192                    permissions.setPermissions(
193                        // modify permissions so that direct publish is allowed
194                        permissions.getAllowedPermissions() | CmsPermissionSet.PERMISSION_DIRECT_PUBLISH,
195                        permissions.getDeniedPermissions() & ~CmsPermissionSet.PERMISSION_DIRECT_PUBLISH);
196                }
197            }
198        }
199
200        CmsPermissionCheckResult result;
201        if ((requiredPermissions.getPermissions()
202            & (permissions.getPermissions())) == requiredPermissions.getPermissions()) {
203            result = I_CmsPermissionHandler.PERM_ALLOWED;
204        } else {
205            result = I_CmsPermissionHandler.PERM_DENIED;
206            if (LOG.isDebugEnabled()) {
207                LOG.debug(
208                    Messages.get().getBundle().key(
209                        Messages.LOG_NO_PERMISSION_RESOURCE_USER_4,
210                        new Object[] {
211                            dbc.getRequestContext().removeSiteRoot(resource.getRootPath()),
212                            dbc.currentUser().getName(),
213                            requiredPermissions.getPermissionString(),
214                            permissions.getPermissionString()}));
215            }
216        }
217        if (dbc.getProjectId().isNullUUID()) {
218            OpenCms.getMemoryMonitor().cachePermission(cacheKey, result);
219        }
220
221        return result;
222    }
223
224    /**
225     * @see org.opencms.security.I_CmsPermissionHandler#init(org.opencms.db.CmsDriverManager, CmsSystemConfiguration)
226     */
227    public void init(CmsDriverManager driverManager, CmsSystemConfiguration systemConfiguration) {
228
229        m_driverManager = driverManager;
230        m_securityManager = driverManager.getSecurityManager();
231
232        CmsCacheSettings settings = systemConfiguration.getCacheSettings();
233
234        String className = settings.getCacheKeyGenerator();
235        try {
236            // initialize the key generator
237            m_keyGenerator = (I_CmsCacheKey)Class.forName(className).newInstance();
238        } catch (Exception e) {
239            throw new CmsInitException(
240                org.opencms.main.Messages.get().container(
241                    org.opencms.main.Messages.ERR_CRITICAL_CLASS_CREATION_1,
242                    className),
243                e);
244        }
245    }
246}