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, org.opencms.security.I_CmsPermissionHandler.LockCheck, org.opencms.file.CmsResourceFilter)
072     */
073    public CmsPermissionCheckResult hasPermissions(
074        CmsDbContext dbc,
075        CmsResource resource,
076        CmsPermissionSet requiredPermissions,
077        LockCheck checkLock,
078        CmsResourceFilter filter)
079    throws CmsException {
080
081        // check if the resource is valid according to the current filter
082        // if not, throw a CmsResourceNotFoundException
083        if (!filter.isValid(dbc.getRequestContext(), resource)) {
084            return I_CmsPermissionHandler.PERM_FILTERED;
085        }
086
087        // checking the filter is less cost intensive then checking the cache,
088        // this is why basic filter results are not cached
089        String requireVisibleStr = filter.requireVisible() ? "1" : "0";
090        String lockCheckStr = checkLock.getCode();
091        String keyPrefix = requireVisibleStr + lockCheckStr;
092
093        String cacheKey = m_keyGenerator.getCacheKeyForUserPermissions(keyPrefix, dbc, resource, requiredPermissions);
094        CmsPermissionCheckResult cacheResult = OpenCms.getMemoryMonitor().getCachedPermission(cacheKey);
095        if (cacheResult != null) {
096            return cacheResult;
097        }
098
099        int denied = 0;
100
101        // if this is the online project, write is rejected
102        if (dbc.currentProject().isOnlineProject()) {
103            denied |= CmsPermissionSet.PERMISSION_WRITE;
104        }
105
106        // check if the current user is admin
107        boolean canIgnorePermissions = m_securityManager.hasRoleForResource(
108            dbc,
109            dbc.currentUser(),
110            CmsRole.VFS_MANAGER,
111            resource);
112
113        // check lock status
114        boolean writeRequired = requiredPermissions.requiresWritePermission()
115            || requiredPermissions.requiresControlPermission();
116
117        // if the resource type is jsp
118        // write is only allowed for administrators
119        if (writeRequired && !canIgnorePermissions && (CmsResourceTypeJsp.isJsp(resource))) {
120            if (!m_securityManager.hasRoleForResource(dbc, dbc.currentUser(), CmsRole.VFS_MANAGER, resource)) {
121                denied |= CmsPermissionSet.PERMISSION_WRITE;
122                denied |= CmsPermissionSet.PERMISSION_CONTROL;
123            }
124        }
125
126        if (writeRequired && (checkLock != LockCheck.no)) {
127            // check lock state only if required
128            CmsLock lock = m_driverManager.getLock(dbc, resource);
129            // if the resource is not locked by the current user, write and control
130            // access must cause a permission error that must not be cached
131            if (lock.isUnlocked() || !lock.isLockableBy(dbc.currentUser())) {
132                return I_CmsPermissionHandler.PERM_NOTLOCKED;
133            }
134            // if we have a shallow lock, but need a non-shallow one, return NOTLOCKED
135            if (lock.getType().isShallow() && (checkLock != LockCheck.shallowOnly)) {
136                return I_CmsPermissionHandler.PERM_NOTLOCKED;
137            }
138        }
139
140        CmsPermissionSetCustom permissions;
141        if (canIgnorePermissions) {
142            // if the current user is administrator, anything is allowed
143            permissions = new CmsPermissionSetCustom(~0);
144        } else {
145            // otherwise, get the permissions from the access control list
146            permissions = m_driverManager.getPermissions(dbc, resource, dbc.currentUser());
147        }
148
149        // revoke the denied permissions
150        permissions.denyPermissions(denied);
151
152        if ((permissions.getPermissions() & CmsPermissionSet.PERMISSION_VIEW) == 0) {
153            // resource "invisible" flag is set for this user
154            if (!canIgnorePermissions && filter.requireVisible()) {
155                // filter requires visible permission - extend required permission set
156                requiredPermissions = new CmsPermissionSet(
157                    requiredPermissions.getAllowedPermissions() | CmsPermissionSet.PERMISSION_VIEW,
158                    requiredPermissions.getDeniedPermissions());
159            } else {
160                // view permissions can be ignored by filter
161                permissions.setPermissions(
162                    // modify permissions so that view is allowed
163                    permissions.getAllowedPermissions() | CmsPermissionSet.PERMISSION_VIEW,
164                    permissions.getDeniedPermissions() & ~CmsPermissionSet.PERMISSION_VIEW);
165            }
166        }
167
168        if (requiredPermissions.requiresDirectPublishPermission()) {
169            // direct publish permission is required
170            if ((permissions.getPermissions() & CmsPermissionSet.PERMISSION_DIRECT_PUBLISH) == 0) {
171                // but the user has no direct publish permission, so check if the user has the project manager role
172                boolean canIgnorePublishPermission = m_securityManager.hasRoleForResource(
173                    dbc,
174                    dbc.currentUser(),
175                    CmsRole.PROJECT_MANAGER,
176                    resource);
177                // if not, check the manageable projects
178                if (!canIgnorePublishPermission) {
179                    CmsUser user = dbc.currentUser();
180                    Iterator<CmsProject> itProjects = m_driverManager.getAllManageableProjects(
181                        dbc,
182                        m_driverManager.readOrganizationalUnit(dbc, user.getOuFqn()),
183                        true).iterator();
184                    while (itProjects.hasNext()) {
185                        CmsProject project = itProjects.next();
186                        if (CmsProject.isInsideProject(m_driverManager.readProjectResources(dbc, project), resource)) {
187                            canIgnorePublishPermission = true;
188                            break;
189                        }
190                    }
191                }
192
193                if (canIgnorePublishPermission) {
194                    // direct publish permission can be ignored
195                    permissions.setPermissions(
196                        // modify permissions so that direct publish is allowed
197                        permissions.getAllowedPermissions() | CmsPermissionSet.PERMISSION_DIRECT_PUBLISH,
198                        permissions.getDeniedPermissions() & ~CmsPermissionSet.PERMISSION_DIRECT_PUBLISH);
199                }
200            }
201        }
202
203        CmsPermissionCheckResult result;
204        if ((requiredPermissions.getPermissions()
205            & (permissions.getPermissions())) == requiredPermissions.getPermissions()) {
206            result = I_CmsPermissionHandler.PERM_ALLOWED;
207        } else {
208            result = I_CmsPermissionHandler.PERM_DENIED;
209            if (LOG.isDebugEnabled()) {
210                LOG.debug(
211                    Messages.get().getBundle().key(
212                        Messages.LOG_NO_PERMISSION_RESOURCE_USER_4,
213                        new Object[] {
214                            dbc.getRequestContext().removeSiteRoot(resource.getRootPath()),
215                            dbc.currentUser().getName(),
216                            requiredPermissions.getPermissionString(),
217                            permissions.getPermissionString()}));
218            }
219        }
220        if (dbc.getProjectId().isNullUUID() && permissions.isCacheable()) {
221            OpenCms.getMemoryMonitor().cachePermission(cacheKey, result);
222        }
223        if (!permissions.isCacheable()) {
224            // if this method is used for checking permissions in resource lists, the resulting resource lists
225            // are not cacheable either if the permission check for this resource isn't
226            boolean[] nocacheArray = (boolean[])dbc.getAttribute(CmsDriverManager.ATTR_PERMISSION_NOCACHE);
227            if (nocacheArray != null) {
228                nocacheArray[0] = true;
229            }
230        }
231
232        return result;
233    }
234
235    /**
236     * @see org.opencms.security.I_CmsPermissionHandler#init(org.opencms.db.CmsDriverManager, CmsSystemConfiguration)
237     */
238    public void init(CmsDriverManager driverManager, CmsSystemConfiguration systemConfiguration) {
239
240        m_driverManager = driverManager;
241        m_securityManager = driverManager.getSecurityManager();
242
243        CmsCacheSettings settings = systemConfiguration.getCacheSettings();
244
245        String className = settings.getCacheKeyGenerator();
246        try {
247            // initialize the key generator
248            m_keyGenerator = (I_CmsCacheKey)Class.forName(className).newInstance();
249        } catch (Exception e) {
250            throw new CmsInitException(
251                org.opencms.main.Messages.get().container(
252                    org.opencms.main.Messages.ERR_CRITICAL_CLASS_CREATION_1,
253                    className),
254                e);
255        }
256    }
257}