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);
}
}