1 <?php 2 /* Copyright (c) 2012, Geert Bergman (geert@scrivo.nl) 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. Neither the name of "Scrivo" nor the names of its contributors may be 14 * used to endorse or promote products derived from this software without 15 * specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 * 29 * $Id: AccessController.php 866 2013-08-25 16:22:35Z geert $ 30 */ 31 32 /** 33 * Implementation of the \Scrivo\AccessController class. 34 */ 35 36 namespace Scrivo; 37 38 /** 39 * Class that deals with the user permissions on objects. 40 * 41 * The AccessController class implements the fuctionality to determine 42 * user access level to objects like pages and assets. 43 * 44 * Access rights are expressed as bit flags. There are three different access 45 * right defined: 46 * 47 * * AccessController::READ_ACCESS: first bit set (=1) 48 * * AccessController::WRITE_ACCESS: second bit set (=2) 49 * * AccessController::PUBLISH_ACCESS: third bit set (=4) 50 * 51 * It is important to note that Scrivo uses access levels internally. This 52 * means that although there are different permissions (READ_ACCESS, 53 * WRITE_ACCESS, etc.) defined they are not used indivudally: 54 * WRITE_ACCESS always implies READ_ACCESS permission (WRITE_ACCESS 55 * always means READ_ACCESS+WRITE_ACCESS, PUBLISH_ACCESS always means 56 * READ_ACCESS+WRITE_ACCESS+PUBLISH_ACCESS). 57 * 58 * This means that when you retrieve a permission you are guaranteed to 59 * retrieve one of the values (0, 1, 3 or 7). Using bit flags might suggest 60 * otherwise, but note that other combinations are not possible. 61 * 62 * This also means that you can use either bitwise operations or comparison 63 * when checking a permission: 64 * 65 * if (AccessController::getPermission($aContext, $anObjectId) >= 66 * AccessController::ACCESS_LEVEL_READ) { ... } 67 * 68 * is equivalent to: 69 * 70 * if (AccessController::getPermission($aContext, $anObjectId) & 71 * AccessController::ACCESS_LEVEL_READ) { ... } 72 * 73 * Note that AccessController::checkPermission(...) is probably easier 74 * to use. 75 * 76 * Another feature is that Scrivo users are differentiated into three 77 * catagories which limits the range of atainable permissions: 78 * 79 * * Members (Users::STATUS_MEMBER), these users represent the group of users 80 * that visit the actual site: Their users access levels can only be one 81 * of NO_ACCESS or READ_ACCESS. 82 * * Editors (Users::STATUS_EDITOR), the users that login in to do editing 83 * work Scrivo: These users have access level can range from READ_ACCESS to 84 * PUBLISH_ACCESS. 85 * * Super users (Users::STATUS_ADMIN), users that can access everything. These 86 * users always have PUBLISH_ACCESS. 87 * 88 * In other words members can read what they are allowed to but never write, 89 * editors can write (and possibly publish) what they are allowed to and always 90 * read and admins can do everthing. 91 * 92 * For a description of Scrivo user see the Scrivo::User class and to see 93 * how access rights are granted to users see the Scrivo::Role class. 94 */ 95 class AccessController { 96 97 /** 98 * Bit flag that indicates that the user has read access. 99 */ 100 const READ_ACCESS = 1; 101 102 /** 103 * Bit flag that indicates that the user has write access. 104 */ 105 const WRITE_ACCESS = 2; 106 107 /** 108 * Bit flag that indicates that the user has publiser rights. 109 */ 110 const PUBLISH_ACCESS = 4; 111 112 /** 113 * Check the permission of a user on an object (page or asset). 114 * 115 * Note that a valid user and object id are assumed. Invalid user ids 116 * will raise an exception but invalid object ids are accepted and 117 * the given permission will then be the checked against the minimum 118 * access permission for the given user. 119 * 120 * @param Context $context A connection to a Scrivo database. 121 * @param int $perm The permission to test (READ_ACCESS || WRITE_ACCESS 122 * || PUBLISH_ACCESS) 123 * @param int $objectId A valid object id of a page or asset. 124 * 125 * @return boolean True if the user has the specified permission on the 126 * object. 127 */ 128 public static function checkPermission( 129 Context $context, $perm, $objectId=null) { 130 \Scrivo\ArgumentCheck::assertArgs(func_get_args(), array( 131 null, 132 array(\Scrivo\ArgumentCheck::TYPE_INTEGER, array(0,1,2,3,4,5,6,7)), 133 array(\Scrivo\ArgumentCheck::TYPE_INTEGER) 134 ), 2); 135 136 return self::getPermission($context, $objectId) >= $perm; 137 } 138 139 /** 140 * Get the permission of a user on an object (page or asset). 141 * 142 * Note that a valid user and object id are assumed. Invalid user ids 143 * will raise an exception but invalid object ids are accepted and 144 * assigned the minimum access permission for the given user. 145 * 146 * @param Context $context A connection to a Scrivo database. 147 * @param int $objectId A valid object id of a page or asset. 148 * 149 * @return int The user's permission on the object (A bitwise combination 150 * of READ_ACCESS, WRITE_ACCESS and PUBLISH_ACCESS). 151 */ 152 public static function getPermission(Context $context, $objectId=null) { 153 \Scrivo\ArgumentCheck::assertArgs(func_get_args(), array( 154 null, 155 array(\Scrivo\ArgumentCheck::TYPE_INTEGER) 156 ), 1); 157 try { 158 159 // Status (admin/editor/member) of given user. 160 $us = $context->principal->status; 161 162 // Determine minimum access permission for given user. 163 $p = 0; 164 165 if ($us == User::STATUS_EDITOR) { 166 $p = self::READ_ACCESS; 167 } 168 if ($us == User::STATUS_ADMIN) { 169 $p = self::READ_ACCESS | self::WRITE_ACCESS 170 | self::PUBLISH_ACCESS; 171 } 172 173 // For admin users we're finished, more work for others. 174 if ($us != User::STATUS_ADMIN) { 175 176 // Check if there are object roles match the user roles. 177 $sth = $context->connection->prepare( 178 "SELECT MAX(is_publisher) 179 FROM user_role UR, object_role DR, role R 180 WHERE UR.instance_id = :instId AND DR.instance_id = :instId 181 AND R.instance_id = :instId AND R.type = :type 182 AND R.role_id = UR.role_id AND R.role_id = DR.role_id 183 AND UR.user_id = :userId AND DR.page_id = :objectId"); 184 185 $context->connection->bindInstance($sth); 186 $sth->bindValue(":userId", \Scrivo\User::patchId( 187 $context->principal->id), \PDO::PARAM_INT); 188 $sth->bindValue(":type", $us == User::STATUS_MEMBER ? 189 Role::PUBLIC_ROLE : Role::EDITOR_ROLE, \PDO::PARAM_INT); 190 $sth->bindValue(":objectId", $objectId, \PDO::PARAM_INT); 191 192 $sth->execute(); 193 194 // Add additional permissions depending on the values retrieved 195 // from the query. 196 if (null !== ($publ = $sth->fetchColumn(0))) { 197 if ($us == User::STATUS_EDITOR) { 198 $p |= self::WRITE_ACCESS; 199 if ($publ) { 200 $p |= self::PUBLISH_ACCESS; 201 } 202 } else if ($us = User::STATUS_MEMBER) { 203 $p |= self::READ_ACCESS; 204 } 205 } 206 } 207 208 return $p; 209 210 } catch(\PDOException $e) { 211 throw new ResourceException($e); 212 } 213 } 214 215 /** 216 * Get the permissions on a series of objects for a given user. 217 * 218 * @deprecated Access to pages and assets should be checked through role 219 * mapping. 220 * 221 * @param Context $context A connection to a Scrivo database. 222 * @param string[] $queryParts An array that contains SQL fragments to 223 * do the proper select statments for the given case. 224 * @param int $parentId An optional parent id to use in the selection of 225 * the objects. 226 * 227 * @return int[] Array in which the keys are the object ids and the 228 * values the user's permissions (A bitwise combination of READ_ACCESS, 229 * WRITE_ACCESS and PUBLISH_ACCESS) on the objects. 230 */ 231 private static function getPermissionsOnObjects( 232 Context $context, $queryParts, $parentId=-1) { 233 try { 234 // Array for the return values. 235 $ret = array(); 236 237 // Prepare the parent clause to us in queries. 238 $pc = $parentId != -1 ? $queryParts["parentClause"] : ""; 239 240 // Status (admin/editor/member) of given user. 241 $us = $context->principal->status; 242 243 // Determine minimum access permission for given user. 244 $p = 0; 245 if ($us == User::STATUS_EDITOR) { 246 $p = self::READ_ACCESS; 247 } 248 if ($us == User::STATUS_ADMIN) { 249 $p = self::READ_ACCESS | self::WRITE_ACCESS 250 | self::PUBLISH_ACCESS; 251 } 252 253 // Get the object ids from the database. 254 $sth = $context->connection->prepare( 255 $queryParts["listIds"] . $pc); 256 257 $context->connection->bindInstance($sth); 258 if ($parentId != -1) { 259 $sth->bindValue(":parentId", $parentId, \PDO::PARAM_INT); 260 } 261 262 $sth->execute(); 263 264 // Fill the result array with the object id as keys and the 265 // minimum access permission for the values. 266 while ($id = $sth->fetchColumn()) { 267 $ret[$id] = $p; 268 } 269 270 // For admin users we're finished, more work for others. 271 if ($us != User::STATUS_ADMIN) { 272 273 // Select the object ids of objects with object roles that 274 // match the user roles. 275 $sth = $context->connection->prepare( 276 $queryParts["listIdsForRoles"] . $pc . 277 " GROUP BY id"); 278 279 $context->connection->bindInstance($sth); 280 $sth->bindValue(":userId", \Scrivo\User::patchId( 281 $context->principal->id), \PDO::PARAM_INT); 282 $sth->bindValue(":type", $us == User::STATUS_MEMBER ? 283 Role::PUBLIC_ROLE : Role::EDITOR_ROLE, \PDO::PARAM_INT); 284 if ($parentId != -1) { 285 $sth->bindValue(":parentId", $parentId, \PDO::PARAM_INT); 286 } 287 288 $sth->execute(); 289 290 // Add additional permissions depending on the values retrieved 291 // from the query. 292 while ($rd = $sth->fetch(\PDO::FETCH_ASSOC)) { 293 $p2 = $p ? $ret[$rd["id"]] : 0; 294 if ($us == User::STATUS_EDITOR) { 295 $p2 |= self::WRITE_ACCESS; 296 if ($rd["is_publisher"]) { 297 $p2 |= self::PUBLISH_ACCESS; 298 } 299 } else if ($us = User::STATUS_MEMBER) { 300 $p2 |= self::READ_ACCESS; 301 } 302 $ret[$rd["id"]] = $p2; 303 } 304 } 305 306 return $ret; 307 308 } catch(\PDOException $e) { 309 throw new ResourceException($e); 310 } 311 } 312 313 /** 314 * Get the permissions of a user on a set of pages. 315 * 316 * You can either get the permissions of the user on all pages or retrieve 317 * them for all pages directly underneath (not recursive) the page that is 318 * identified by the optional parent id. 319 * 320 * @deprecated Access to pages and assets should be checked through role 321 * mapping. 322 * 323 * @param Context $context A connection to a Scrivo database. 324 * @param int $parentId An optional parent id to make a subselection of 325 * pages. 326 * 327 * @return int[] Array in which the keys are the object ids and the 328 * values the user's permissions (A bitwise combination of READ_ACCESS, 329 * WRITE_ACCESS and PUBLISH_ACCESS) on the objects. 330 */ 331 public static function getPermissionsOnPages(Context $context, $parentId=-1) { 332 \Scrivo\ArgumentCheck::assertArgs(func_get_args(), array( 333 null, 334 array(\Scrivo\ArgumentCheck::TYPE_INTEGER) 335 ), 1); 336 337 $sql = array( 338 "listIds" => 339 "SELECT DISTINCT page_id id 340 FROM page D WHERE D.instance_id = :instId AND version <= 0", 341 "parentClause" => 342 " AND D.parent_id = :parentId", 343 "listIdsForRoles" => 344 "SELECT DR.page_id id, MAX(is_publisher) is_publisher 345 FROM role R, user_role UR, object_role DR, page D 346 WHERE D.instance_id = :instId AND UR.instance_id = :instId 347 AND DR.instance_id = :instId AND R.instance_id = :instId 348 AND R.role_id = UR.role_id AND R.role_id = DR.role_id 349 AND DR.page_id = D.page_id AND R.type = :type 350 AND UR.user_id = :userId AND D.version <= 0" 351 ); 352 353 return self::getPermissionsOnObjects($context, $sql, $parentId); 354 } 355 356 /** 357 * Get the permissions of a user on a set of assets. 358 * 359 * You can either get the permissions of the user on all assets or retrieve 360 * them for all assets in a folder (not recursive) that is identified by 361 * the optional parent id. 362 * 363 * @deprecated Access to pages and assets should be checked through role 364 * mapping. 365 * 366 * @param Context $context A connection to a Scrivo database. 367 * @param int $parentId An optional parent id to make a subselection of 368 * pages. 369 * 370 * @return int[] Array in which the keys are the object ids and the 371 * values the user's permissions (A bitwise combination of READ_ACCESS, 372 * WRITE_ACCESS and PUBLISH_ACCESS) on the objects. 373 */ 374 public static function getPermissionsOnAssets(Context $context, $parentId=-1) { 375 \Scrivo\ArgumentCheck::assertArgs(func_get_args(), array( 376 null, 377 array(\Scrivo\ArgumentCheck::TYPE_INTEGER) 378 ), 1); 379 380 $sql = array( 381 "listIds" => 382 "SELECT DISTINCT asset_id id 383 FROM asset D WHERE D.instance_id = :instId", 384 "parentClause" => 385 " AND D.parent_id = :parentId", 386 "listIdsForRoles" => 387 "SELECT DR.page_id id, MAX(is_publisher) is_publisher 388 FROM role R, user_role UR, object_role DR, asset D 389 WHERE UR.instance_id = :instId AND DR.instance_id = :instId 390 AND R.instance_id = :instId AND D.instance_id = :instId 391 AND R.role_id = UR.role_id AND R.role_id = DR.role_id 392 AND DR.page_id = D.asset_id AND R.type = :type 393 AND UR.user_id = :userId" 394 ); 395 396 return self::getPermissionsOnObjects($context, $sql, $parentId); 397 } 398 399 } 400 401 ?>
Documentation generated by phpDocumentor 2.0.0a12 and ScrivoDocumentor on August 29, 2013