Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
| Total | |
100.00% |
1 / 1 |
|
100.00% |
5 / 5 |
CRAP | |
100.00% |
109 / 109 |
| AccessController | |
100.00% |
1 / 1 |
|
100.00% |
5 / 5 |
28 | |
100.00% |
109 / 109 |
| checkPermission( Context $context, $perm, $objectId=null) | |
100.00% |
1 / 1 |
1 | |
100.00% |
6 / 6 |
|||
| getPermission(Context $context, $objectId=null) | |
100.00% |
1 / 1 |
10 | |
100.00% |
37 / 37 |
|||
| getPermissionsOnObjects( Context $context, $queryParts, $parentId=-1) | |
100.00% |
1 / 1 |
15 | |
100.00% |
50 / 50 |
|||
| getPermissionsOnPages(Context $context, $parentId=-1) | |
100.00% |
1 / 1 |
1 | |
100.00% |
8 / 8 |
|||
| getPermissionsOnAssets(Context $context, $parentId=-1) | |
100.00% |
1 / 1 |
1 | |
100.00% |
8 / 8 |
|||
| <?php | |
| /* Copyright (c) 2012, Geert Bergman (geert@scrivo.nl) | |
| * All rights reserved. | |
| * | |
| * Redistribution and use in source and binary forms, with or without | |
| * modification, are permitted provided that the following conditions are met: | |
| * | |
| * 1. Redistributions of source code must retain the above copyright notice, | |
| * this list of conditions and the following disclaimer. | |
| * 2. Redistributions in binary form must reproduce the above copyright notice, | |
| * this list of conditions and the following disclaimer in the documentation | |
| * and/or other materials provided with the distribution. | |
| * 3. Neither the name of "Scrivo" nor the names of its contributors may be | |
| * used to endorse or promote products derived from this software without | |
| * specific prior written permission. | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
| * POSSIBILITY OF SUCH DAMAGE. | |
| * | |
| * $Id: AccessController.php 866 2013-08-25 16:22:35Z geert $ | |
| */ | |
| /** | |
| * Implementation of the \Scrivo\AccessController class. | |
| */ | |
| namespace Scrivo; | |
| /** | |
| * Class that deals with the user permissions on objects. | |
| * | |
| * The AccessController class implements the fuctionality to determine | |
| * user access level to objects like pages and assets. | |
| * | |
| * Access rights are expressed as bit flags. There are three different access | |
| * right defined: | |
| * | |
| * * AccessController::READ_ACCESS: first bit set (=1) | |
| * * AccessController::WRITE_ACCESS: second bit set (=2) | |
| * * AccessController::PUBLISH_ACCESS: third bit set (=4) | |
| * | |
| * It is important to note that Scrivo uses access levels internally. This | |
| * means that although there are different permissions (READ_ACCESS, | |
| * WRITE_ACCESS, etc.) defined they are not used indivudally: | |
| * WRITE_ACCESS always implies READ_ACCESS permission (WRITE_ACCESS | |
| * always means READ_ACCESS+WRITE_ACCESS, PUBLISH_ACCESS always means | |
| * READ_ACCESS+WRITE_ACCESS+PUBLISH_ACCESS). | |
| * | |
| * This means that when you retrieve a permission you are guaranteed to | |
| * retrieve one of the values (0, 1, 3 or 7). Using bit flags might suggest | |
| * otherwise, but note that other combinations are not possible. | |
| * | |
| * This also means that you can use either bitwise operations or comparison | |
| * when checking a permission: | |
| * | |
| * if (AccessController::getPermission($aContext, $anObjectId) >= | |
| * AccessController::ACCESS_LEVEL_READ) { ... } | |
| * | |
| * is equivalent to: | |
| * | |
| * if (AccessController::getPermission($aContext, $anObjectId) & | |
| * AccessController::ACCESS_LEVEL_READ) { ... } | |
| * | |
| * Note that AccessController::checkPermission(...) is probably easier | |
| * to use. | |
| * | |
| * Another feature is that Scrivo users are differentiated into three | |
| * catagories which limits the range of atainable permissions: | |
| * | |
| * * Members (Users::STATUS_MEMBER), these users represent the group of users | |
| * that visit the actual site: Their users access levels can only be one | |
| * of NO_ACCESS or READ_ACCESS. | |
| * * Editors (Users::STATUS_EDITOR), the users that login in to do editing | |
| * work Scrivo: These users have access level can range from READ_ACCESS to | |
| * PUBLISH_ACCESS. | |
| * * Super users (Users::STATUS_ADMIN), users that can access everything. These | |
| * users always have PUBLISH_ACCESS. | |
| * | |
| * In other words members can read what they are allowed to but never write, | |
| * editors can write (and possibly publish) what they are allowed to and always | |
| * read and admins can do everthing. | |
| * | |
| * For a description of Scrivo user see the Scrivo::User class and to see | |
| * how access rights are granted to users see the Scrivo::Role class. | |
| */ | |
| class AccessController { | |
| /** | |
| * Bit flag that indicates that the user has read access. | |
| */ | |
| const READ_ACCESS = 1; | |
| /** | |
| * Bit flag that indicates that the user has write access. | |
| */ | |
| const WRITE_ACCESS = 2; | |
| /** | |
| * Bit flag that indicates that the user has publiser rights. | |
| */ | |
| const PUBLISH_ACCESS = 4; | |
| /** | |
| * Check the permission of a user on an object (page or asset). | |
| * | |
| * Note that a valid user and object id are assumed. Invalid user ids | |
| * will raise an exception but invalid object ids are accepted and | |
| * the given permission will then be the checked against the minimum | |
| * access permission for the given user. | |
| * | |
| * @param Context $context A connection to a Scrivo database. | |
| * @param int $perm The permission to test (READ_ACCESS || WRITE_ACCESS | |
| * || PUBLISH_ACCESS) | |
| * @param int $objectId A valid object id of a page or asset. | |
| * | |
| * @return boolean True if the user has the specified permission on the | |
| * object. | |
| */ | |
| public static function checkPermission( | |
| Context $context, $perm, $objectId=null) { | |
| \Scrivo\ArgumentCheck::assertArgs(func_get_args(), array( | |
| null, | |
| array(\Scrivo\ArgumentCheck::TYPE_INTEGER, array(0,1,2,3,4,5,6,7)), | |
| array(\Scrivo\ArgumentCheck::TYPE_INTEGER) | |
| ), 2); | |
| return self::getPermission($context, $objectId) >= $perm; | |
| } | |
| /** | |
| * Get the permission of a user on an object (page or asset). | |
| * | |
| * Note that a valid user and object id are assumed. Invalid user ids | |
| * will raise an exception but invalid object ids are accepted and | |
| * assigned the minimum access permission for the given user. | |
| * | |
| * @param Context $context A connection to a Scrivo database. | |
| * @param int $objectId A valid object id of a page or asset. | |
| * | |
| * @return int The user's permission on the object (A bitwise combination | |
| * of READ_ACCESS, WRITE_ACCESS and PUBLISH_ACCESS). | |
| */ | |
| public static function getPermission(Context $context, $objectId=null) { | |
| \Scrivo\ArgumentCheck::assertArgs(func_get_args(), array( | |
| null, | |
| array(\Scrivo\ArgumentCheck::TYPE_INTEGER) | |
| ), 1); | |
| try { | |
| // Status (admin/editor/member) of given user. | |
| $us = $context->principal->status; | |
| // Determine minimum access permission for given user. | |
| $p = 0; | |
| if ($us == User::STATUS_EDITOR) { | |
| $p = self::READ_ACCESS; | |
| } | |
| if ($us == User::STATUS_ADMIN) { | |
| $p = self::READ_ACCESS | self::WRITE_ACCESS | |
| | self::PUBLISH_ACCESS; | |
| } | |
| // For admin users we're finished, more work for others. | |
| if ($us != User::STATUS_ADMIN) { | |
| // Check if there are object roles match the user roles. | |
| $sth = $context->connection->prepare( | |
| "SELECT MAX(is_publisher) | |
| FROM user_role UR, object_role DR, role R | |
| WHERE UR.instance_id = :instId AND DR.instance_id = :instId | |
| AND R.instance_id = :instId AND R.type = :type | |
| AND R.role_id = UR.role_id AND R.role_id = DR.role_id | |
| AND UR.user_id = :userId AND DR.page_id = :objectId"); | |
| $context->connection->bindInstance($sth); | |
| $sth->bindValue(":userId", \Scrivo\User::patchId( | |
| $context->principal->id), \PDO::PARAM_INT); | |
| $sth->bindValue(":type", $us == User::STATUS_MEMBER ? | |
| Role::PUBLIC_ROLE : Role::EDITOR_ROLE, \PDO::PARAM_INT); | |
| $sth->bindValue(":objectId", $objectId, \PDO::PARAM_INT); | |
| $sth->execute(); | |
| // Add additional permissions depending on the values retrieved | |
| // from the query. | |
| if (null !== ($publ = $sth->fetchColumn(0))) { | |
| if ($us == User::STATUS_EDITOR) { | |
| $p |= self::WRITE_ACCESS; | |
| if ($publ) { | |
| $p |= self::PUBLISH_ACCESS; | |
| } | |
| } else if ($us = User::STATUS_MEMBER) { | |
| $p |= self::READ_ACCESS; | |
| } | |
| } | |
| } | |
| return $p; | |
| } catch(\PDOException $e) { | |
| throw new ResourceException($e); | |
| } | |
| } | |
| /** | |
| * Get the permissions on a series of objects for a given user. | |
| * | |
| * @deprecated Access to pages and assets should be checked through role | |
| * mapping. | |
| * | |
| * @param Context $context A connection to a Scrivo database. | |
| * @param string[] $queryParts An array that contains SQL fragments to | |
| * do the proper select statments for the given case. | |
| * @param int $parentId An optional parent id to use in the selection of | |
| * the objects. | |
| * | |
| * @return int[] Array in which the keys are the object ids and the | |
| * values the user's permissions (A bitwise combination of READ_ACCESS, | |
| * WRITE_ACCESS and PUBLISH_ACCESS) on the objects. | |
| */ | |
| private static function getPermissionsOnObjects( | |
| Context $context, $queryParts, $parentId=-1) { | |
| try { | |
| // Array for the return values. | |
| $ret = array(); | |
| // Prepare the parent clause to us in queries. | |
| $pc = $parentId != -1 ? $queryParts["parentClause"] : ""; | |
| // Status (admin/editor/member) of given user. | |
| $us = $context->principal->status; | |
| // Determine minimum access permission for given user. | |
| $p = 0; | |
| if ($us == User::STATUS_EDITOR) { | |
| $p = self::READ_ACCESS; | |
| } | |
| if ($us == User::STATUS_ADMIN) { | |
| $p = self::READ_ACCESS | self::WRITE_ACCESS | |
| | self::PUBLISH_ACCESS; | |
| } | |
| // Get the object ids from the database. | |
| $sth = $context->connection->prepare( | |
| $queryParts["listIds"] . $pc); | |
| $context->connection->bindInstance($sth); | |
| if ($parentId != -1) { | |
| $sth->bindValue(":parentId", $parentId, \PDO::PARAM_INT); | |
| } | |
| $sth->execute(); | |
| // Fill the result array with the object id as keys and the | |
| // minimum access permission for the values. | |
| while ($id = $sth->fetchColumn()) { | |
| $ret[$id] = $p; | |
| } | |
| // For admin users we're finished, more work for others. | |
| if ($us != User::STATUS_ADMIN) { | |
| // Select the object ids of objects with object roles that | |
| // match the user roles. | |
| $sth = $context->connection->prepare( | |
| $queryParts["listIdsForRoles"] . $pc . | |
| " GROUP BY id"); | |
| $context->connection->bindInstance($sth); | |
| $sth->bindValue(":userId", \Scrivo\User::patchId( | |
| $context->principal->id), \PDO::PARAM_INT); | |
| $sth->bindValue(":type", $us == User::STATUS_MEMBER ? | |
| Role::PUBLIC_ROLE : Role::EDITOR_ROLE, \PDO::PARAM_INT); | |
| if ($parentId != -1) { | |
| $sth->bindValue(":parentId", $parentId, \PDO::PARAM_INT); | |
| } | |
| $sth->execute(); | |
| // Add additional permissions depending on the values retrieved | |
| // from the query. | |
| while ($rd = $sth->fetch(\PDO::FETCH_ASSOC)) { | |
| $p2 = $p ? $ret[$rd["id"]] : 0; | |
| if ($us == User::STATUS_EDITOR) { | |
| $p2 |= self::WRITE_ACCESS; | |
| if ($rd["is_publisher"]) { | |
| $p2 |= self::PUBLISH_ACCESS; | |
| } | |
| } else if ($us = User::STATUS_MEMBER) { | |
| $p2 |= self::READ_ACCESS; | |
| } | |
| $ret[$rd["id"]] = $p2; | |
| } | |
| } | |
| return $ret; | |
| } catch(\PDOException $e) { | |
| throw new ResourceException($e); | |
| } | |
| } | |
| /** | |
| * Get the permissions of a user on a set of pages. | |
| * | |
| * You can either get the permissions of the user on all pages or retrieve | |
| * them for all pages directly underneath (not recursive) the page that is | |
| * identified by the optional parent id. | |
| * | |
| * @deprecated Access to pages and assets should be checked through role | |
| * mapping. | |
| * | |
| * @param Context $context A connection to a Scrivo database. | |
| * @param int $parentId An optional parent id to make a subselection of | |
| * pages. | |
| * | |
| * @return int[] Array in which the keys are the object ids and the | |
| * values the user's permissions (A bitwise combination of READ_ACCESS, | |
| * WRITE_ACCESS and PUBLISH_ACCESS) on the objects. | |
| */ | |
| public static function getPermissionsOnPages(Context $context, $parentId=-1) { | |
| \Scrivo\ArgumentCheck::assertArgs(func_get_args(), array( | |
| null, | |
| array(\Scrivo\ArgumentCheck::TYPE_INTEGER) | |
| ), 1); | |
| $sql = array( | |
| "listIds" => | |
| "SELECT DISTINCT page_id id | |
| FROM page D WHERE D.instance_id = :instId AND version <= 0", | |
| "parentClause" => | |
| " AND D.parent_id = :parentId", | |
| "listIdsForRoles" => | |
| "SELECT DR.page_id id, MAX(is_publisher) is_publisher | |
| FROM role R, user_role UR, object_role DR, page D | |
| WHERE D.instance_id = :instId AND UR.instance_id = :instId | |
| AND DR.instance_id = :instId AND R.instance_id = :instId | |
| AND R.role_id = UR.role_id AND R.role_id = DR.role_id | |
| AND DR.page_id = D.page_id AND R.type = :type | |
| AND UR.user_id = :userId AND D.version <= 0" | |
| ); | |
| return self::getPermissionsOnObjects($context, $sql, $parentId); | |
| } | |
| /** | |
| * Get the permissions of a user on a set of assets. | |
| * | |
| * You can either get the permissions of the user on all assets or retrieve | |
| * them for all assets in a folder (not recursive) that is identified by | |
| * the optional parent id. | |
| * | |
| * @deprecated Access to pages and assets should be checked through role | |
| * mapping. | |
| * | |
| * @param Context $context A connection to a Scrivo database. | |
| * @param int $parentId An optional parent id to make a subselection of | |
| * pages. | |
| * | |
| * @return int[] Array in which the keys are the object ids and the | |
| * values the user's permissions (A bitwise combination of READ_ACCESS, | |
| * WRITE_ACCESS and PUBLISH_ACCESS) on the objects. | |
| */ | |
| public static function getPermissionsOnAssets(Context $context, $parentId=-1) { | |
| \Scrivo\ArgumentCheck::assertArgs(func_get_args(), array( | |
| null, | |
| array(\Scrivo\ArgumentCheck::TYPE_INTEGER) | |
| ), 1); | |
| $sql = array( | |
| "listIds" => | |
| "SELECT DISTINCT asset_id id | |
| FROM asset D WHERE D.instance_id = :instId", | |
| "parentClause" => | |
| " AND D.parent_id = :parentId", | |
| "listIdsForRoles" => | |
| "SELECT DR.page_id id, MAX(is_publisher) is_publisher | |
| FROM role R, user_role UR, object_role DR, asset D | |
| WHERE UR.instance_id = :instId AND DR.instance_id = :instId | |
| AND R.instance_id = :instId AND D.instance_id = :instId | |
| AND R.role_id = UR.role_id AND R.role_id = DR.role_id | |
| AND DR.page_id = D.asset_id AND R.type = :type | |
| AND UR.user_id = :userId" | |
| ); | |
| return self::getPermissionsOnObjects($context, $sql, $parentId); | |
| } | |
| } | |