001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (https://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: https://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: https://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.CmsModificationContext; 035import org.opencms.db.CmsSecurityManager; 036import org.opencms.db.I_CmsCacheKey; 037import org.opencms.file.CmsProject; 038import org.opencms.file.CmsResource; 039import org.opencms.file.CmsResourceFilter; 040import org.opencms.file.CmsUser; 041import org.opencms.file.types.CmsResourceTypeJsp; 042import org.opencms.lock.CmsLock; 043import org.opencms.main.CmsException; 044import org.opencms.main.CmsInitException; 045import org.opencms.main.CmsLog; 046import org.opencms.main.OpenCms; 047 048import java.util.Iterator; 049 050import org.apache.commons.logging.Log; 051 052/** 053 * Generic base driver interface.<p> 054 * 055 * @since 7.0.2 056 */ 057public class CmsDefaultPermissionHandler implements I_CmsPermissionHandler { 058 059 /** The log object for this class. */ 060 private static final Log LOG = CmsLog.getLog(CmsDefaultPermissionHandler.class); 061 062 /** Driver Manager instance. */ 063 protected CmsDriverManager m_driverManager; 064 065 /** Security Manager instance. */ 066 protected CmsSecurityManager m_securityManager; 067 068 /** The class used for cache key generation. */ 069 private I_CmsCacheKey m_keyGenerator; 070 071 /** 072 * @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) 073 */ 074 public CmsPermissionCheckResult hasPermissions( 075 CmsDbContext dbc, 076 CmsResource resource, 077 CmsPermissionSet requiredPermissions, 078 LockCheck checkLock, 079 CmsResourceFilter filter) 080 throws CmsException { 081 082 // check if the resource is valid according to the current filter 083 // if not, throw a CmsResourceNotFoundException 084 if (!filter.isValid(dbc.getRequestContext(), resource)) { 085 return I_CmsPermissionHandler.PERM_FILTERED; 086 } 087 088 // checking the filter is less cost intensive then checking the cache, 089 // this is why basic filter results are not cached 090 String requireVisibleStr = filter.requireVisible() ? "1" : "0"; 091 String lockCheckStr = checkLock.getCode(); 092 String keyPrefix = requireVisibleStr + lockCheckStr; 093 094 String cacheKey = m_keyGenerator.getCacheKeyForUserPermissions(keyPrefix, dbc, resource, 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 != LockCheck.no)) { 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 // if we have a shallow lock, but need a non-shallow one, return NOTLOCKED 136 if (lock.getType().isShallow() && (checkLock != LockCheck.shallowOnly)) { 137 return I_CmsPermissionHandler.PERM_NOTLOCKED; 138 } 139 } 140 141 CmsPermissionSetCustom permissions; 142 if (canIgnorePermissions) { 143 // if the current user is administrator, anything is allowed 144 permissions = new CmsPermissionSetCustom(~0); 145 } else { 146 // otherwise, get the permissions from the access control list 147 permissions = m_driverManager.getPermissions(dbc, resource, dbc.currentUser()); 148 } 149 150 // revoke the denied permissions 151 permissions.denyPermissions(denied); 152 153 if ((permissions.getPermissions() & CmsPermissionSet.PERMISSION_VIEW) == 0) { 154 // resource "invisible" flag is set for this user 155 if (!canIgnorePermissions && filter.requireVisible()) { 156 // filter requires visible permission - extend required permission set 157 requiredPermissions = new CmsPermissionSet( 158 requiredPermissions.getAllowedPermissions() | CmsPermissionSet.PERMISSION_VIEW, 159 requiredPermissions.getDeniedPermissions()); 160 } else { 161 // view permissions can be ignored by filter 162 permissions.setPermissions( 163 // modify permissions so that view is allowed 164 permissions.getAllowedPermissions() | CmsPermissionSet.PERMISSION_VIEW, 165 permissions.getDeniedPermissions() & ~CmsPermissionSet.PERMISSION_VIEW); 166 } 167 } 168 169 if (requiredPermissions.requiresDirectPublishPermission()) { 170 // direct publish permission is required 171 if ((permissions.getPermissions() & CmsPermissionSet.PERMISSION_DIRECT_PUBLISH) == 0) { 172 // but the user has no direct publish permission, so check if the user has the project manager role 173 boolean canIgnorePublishPermission = CmsModificationContext.isInstantPublishing() 174 || m_securityManager.hasRoleForResource(dbc, dbc.currentUser(), CmsRole.PROJECT_MANAGER, resource); 175 // if not, check the manageable projects 176 if (!canIgnorePublishPermission) { 177 CmsUser user = dbc.currentUser(); 178 Iterator<CmsProject> itProjects = m_driverManager.getAllManageableProjects( 179 dbc, 180 m_driverManager.readOrganizationalUnit(dbc, user.getOuFqn()), 181 true).iterator(); 182 while (itProjects.hasNext()) { 183 CmsProject project = itProjects.next(); 184 if (CmsProject.isInsideProject(m_driverManager.readProjectResources(dbc, project), resource)) { 185 canIgnorePublishPermission = true; 186 break; 187 } 188 } 189 } 190 191 if (canIgnorePublishPermission) { 192 // direct publish permission can be ignored 193 permissions.setPermissions( 194 // modify permissions so that direct publish is allowed 195 permissions.getAllowedPermissions() | CmsPermissionSet.PERMISSION_DIRECT_PUBLISH, 196 permissions.getDeniedPermissions() & ~CmsPermissionSet.PERMISSION_DIRECT_PUBLISH); 197 } 198 } 199 } 200 201 CmsPermissionCheckResult result; 202 if ((requiredPermissions.getPermissions() 203 & (permissions.getPermissions())) == requiredPermissions.getPermissions()) { 204 result = I_CmsPermissionHandler.PERM_ALLOWED; 205 } else { 206 result = I_CmsPermissionHandler.PERM_DENIED; 207 if (LOG.isDebugEnabled()) { 208 LOG.debug( 209 Messages.get().getBundle().key( 210 Messages.LOG_NO_PERMISSION_RESOURCE_USER_4, 211 new Object[] { 212 dbc.getRequestContext().removeSiteRoot(resource.getRootPath()), 213 dbc.currentUser().getName(), 214 requiredPermissions.getPermissionString(), 215 permissions.getPermissionString()})); 216 } 217 } 218 if (dbc.getProjectId().isNullUUID() && permissions.isCacheable()) { 219 OpenCms.getMemoryMonitor().cachePermission(cacheKey, result); 220 } 221 if (!permissions.isCacheable()) { 222 // if this method is used for checking permissions in resource lists, the resulting resource lists 223 // are not cacheable either if the permission check for this resource isn't 224 boolean[] nocacheArray = (boolean[])dbc.getAttribute(CmsDriverManager.ATTR_PERMISSION_NOCACHE); 225 if (nocacheArray != null) { 226 nocacheArray[0] = true; 227 } 228 } 229 230 return result; 231 } 232 233 /** 234 * @see org.opencms.security.I_CmsPermissionHandler#init(org.opencms.db.CmsDriverManager, CmsSystemConfiguration) 235 */ 236 public void init(CmsDriverManager driverManager, CmsSystemConfiguration systemConfiguration) { 237 238 m_driverManager = driverManager; 239 m_securityManager = driverManager.getSecurityManager(); 240 241 CmsCacheSettings settings = systemConfiguration.getCacheSettings(); 242 243 String className = settings.getCacheKeyGenerator(); 244 try { 245 // initialize the key generator 246 m_keyGenerator = (I_CmsCacheKey)Class.forName(className).newInstance(); 247 } catch (Exception e) { 248 throw new CmsInitException( 249 org.opencms.main.Messages.get().container( 250 org.opencms.main.Messages.ERR_CRITICAL_CLASS_CREATION_1, 251 className), 252 e); 253 } 254 } 255}