2007-07-19 12:40:28 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
class Permission extends DataObject {
|
|
|
|
static $db = array(
|
|
|
|
"Code" => "Varchar",
|
2007-09-14 19:44:33 +02:00
|
|
|
"Arg" => "Int",
|
2007-07-19 12:40:28 +02:00
|
|
|
);
|
|
|
|
static $has_one = array(
|
|
|
|
"Group" => "Group",
|
|
|
|
);
|
|
|
|
static $indexes = array(
|
|
|
|
"Code" => true,
|
|
|
|
);
|
2007-09-14 19:44:33 +02:00
|
|
|
|
2007-09-16 04:15:16 +02:00
|
|
|
/**
|
|
|
|
* Permissions declared as belonging to the system. This is used to provide
|
|
|
|
* permission matrices.
|
|
|
|
*/
|
|
|
|
static $declared_permissions = null;
|
|
|
|
|
|
|
|
protected static $declared_permissions_list = null;
|
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
|
|
|
* @var $strict_checking Boolean Method to globally disable "strict" checking,
|
|
|
|
* which means a permission will be granted if the key does not exist at all.
|
|
|
|
*/
|
|
|
|
static $strict_checking = true;
|
|
|
|
|
2007-09-16 04:15:16 +02:00
|
|
|
/**
|
|
|
|
* If this setting is set, then permissions can imply other permissions
|
|
|
|
*/
|
|
|
|
static $implied_permissions = false;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set to false to prevent the 'ADMIN' permission from implying all permissions in the system
|
|
|
|
*/
|
|
|
|
static $admin_implies_all = true;
|
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
|
|
|
* Check that the current member has the given permission
|
|
|
|
*
|
2007-08-31 02:28:30 +02:00
|
|
|
* @param $code string|array Either a list of codes or a single code
|
2007-07-19 12:40:28 +02:00
|
|
|
* @param $arg string
|
|
|
|
* @param $memberID integer
|
|
|
|
* @param $strict Boolean
|
|
|
|
* @return Integer
|
|
|
|
*/
|
|
|
|
static function check($code, $arg = "any", $memberID = null, $strict = true) {
|
|
|
|
if(!$memberID) {
|
|
|
|
if(!Member::currentUser()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
$memberID = Member::currentUserID();
|
|
|
|
}
|
2007-09-14 19:44:33 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
return self::checkMember($memberID, $code, $arg);
|
|
|
|
}
|
2007-09-14 19:44:33 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
|
|
|
* Check that the given member has the given permission
|
|
|
|
*
|
|
|
|
* @param memberID The ID of the member to check. Leave blank for the current member
|
|
|
|
* @param $code string
|
|
|
|
* @param $arg string
|
|
|
|
* @param $strict Boolean
|
|
|
|
* @return Integer The ID of the Permission record if the permission exists; null otherwise
|
|
|
|
*/
|
|
|
|
static function checkMember($memberID, $code, $arg = "any", $strict = true) {
|
|
|
|
// Group component
|
2007-09-16 04:15:16 +02:00
|
|
|
$perms_list = self::get_declared_permissions_list();
|
|
|
|
|
|
|
|
if(self::$declared_permissions && is_array($perms_list) && !in_array($code, $perms_list)) {
|
|
|
|
// user_error("Permission '$code' has not been declared. Use Permission::declare_permissions() to add this permission", E_USER_WARNING);
|
|
|
|
}
|
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
$groupList = self::groupList($memberID);
|
|
|
|
if($groupList) {
|
|
|
|
$groupCSV = implode(", ", $groupList);
|
2007-09-14 19:44:33 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
// Arg component
|
|
|
|
switch($arg) {
|
|
|
|
case "any": $argClause = "";break;
|
|
|
|
case "all": $argClause = " AND Arg = -1"; break;
|
2007-09-14 19:44:33 +02:00
|
|
|
default:
|
2007-07-19 12:40:28 +02:00
|
|
|
if(is_numeric($arg)) $argClause = "AND Arg IN (-1, $arg) ";
|
|
|
|
else use_error("Permission::checkMember: bad arg '$arg'", E_USER_ERROR);
|
|
|
|
}
|
|
|
|
|
2007-08-31 02:28:30 +02:00
|
|
|
if(is_array($code)) $SQL_codeList = "'" . implode("', '", Convert::raw2sql($code)) . "'";
|
|
|
|
else $SQL_codeList = "'" . Convert::raw2sql($code) . "'";
|
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
if(!self::$strict_checking || !$strict) {
|
|
|
|
$hasPermission = DB::query("
|
|
|
|
SELECT COUNT(*)
|
|
|
|
FROM Permission
|
2007-08-31 02:28:30 +02:00
|
|
|
WHERE Code IN ('$SQL_codeList')
|
2007-07-19 12:40:28 +02:00
|
|
|
")->value();
|
|
|
|
if(!$hasPermission) return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Raw SQL for efficiency
|
2007-08-31 02:28:30 +02:00
|
|
|
return DB::query("SELECT ID FROM Permission WHERE Code IN ($SQL_codeList, 'ADMIN') AND GroupID IN ($groupCSV) $argClause")->value();
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
}
|
2007-09-14 19:44:33 +02:00
|
|
|
|
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
|
|
|
* Get the list of groups that the given member belongs to.
|
|
|
|
* Call without an argument to get the groups that the current member belongs to. In this case, the results will be session-cached
|
|
|
|
*/
|
|
|
|
static function groupList($memberID = null) {
|
|
|
|
// Default to current member, with session-caching
|
|
|
|
if(!$memberID) {
|
|
|
|
$member = Member::currentUser();
|
|
|
|
if($member && isset($_SESSION['Permission_groupList'][$member->ID])) return $_SESSION['Permission_groupList'][$member->ID];
|
|
|
|
} else {
|
|
|
|
$member = DataObject::get_by_id("Member", $memberID);
|
|
|
|
}
|
2007-09-14 19:44:33 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
if($member) {
|
|
|
|
// Build a list of the IDs of the groups. Most of the heavy lifting is done by Member::Groups
|
|
|
|
// NOTE: This isn't effecient; but it's called once per session so it's a low priority to fix.
|
|
|
|
$groups = $member->Groups();
|
2007-09-15 01:12:22 +02:00
|
|
|
$groupList = array();
|
|
|
|
|
|
|
|
if($groups) {
|
|
|
|
foreach($groups as $group)
|
|
|
|
$groupList[] = $group->ID;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Session caching
|
2007-07-19 12:40:28 +02:00
|
|
|
if(!$memberID) {
|
|
|
|
$_SESSION['Permission_groupList'][$member->ID] = $groupList;
|
|
|
|
}
|
|
|
|
|
|
|
|
return isset($groupList) ? $groupList : null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Grant the given permission code/arg to the given group
|
|
|
|
* @returns The new permission object
|
|
|
|
*/
|
|
|
|
static function grant($groupID, $code, $arg = "any") {
|
|
|
|
$perm = new Permission();
|
|
|
|
$perm->GroupID = $groupID;
|
|
|
|
$perm->Code = $code;
|
|
|
|
|
|
|
|
// Arg component
|
|
|
|
switch($arg) {
|
|
|
|
case "any": break;
|
|
|
|
case "all": $perm->Arg = -1;
|
|
|
|
default:
|
|
|
|
if(is_numeric($arg)) $perm->Arg = $arg;
|
|
|
|
else use_error("Permission::checkMember: bad arg '$arg'", E_USER_ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
$perm->write();
|
|
|
|
return $perm;
|
|
|
|
}
|
2007-09-14 19:44:33 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
function requireDefaultRecords() {
|
|
|
|
parent::requireDefaultRecords();
|
2007-09-14 19:44:33 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
// Add default content if blank
|
|
|
|
if(!DB::query("SELECT ID FROM Permission")->value()) {
|
|
|
|
$admins = DB::query("SELECT ID FROM `Group` WHERE CanCMSAdmin = 1")->column();
|
|
|
|
if(isset($admins)) {
|
|
|
|
foreach($admins as $admin) Permission::grant($admin, "ADMIN");
|
|
|
|
}
|
2007-09-14 19:44:33 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
$authors = DB::query("SELECT ID FROM `Group` WHERE CanCMS = 1")->column();
|
|
|
|
if(isset($authors)) {
|
|
|
|
foreach($authors as $author) {
|
|
|
|
Permission::grant($author, "CMS_ACCESS_CMSMain");
|
|
|
|
Permission::grant($author, "CMS_ACCESS_AssetAdmin");
|
|
|
|
Permission::grant($author, "CMS_ACCESS_NewsletterAdmin");
|
|
|
|
Permission::grant($author, "CMS_ACCESS_ReportAdmin");
|
|
|
|
}
|
|
|
|
}
|
2007-09-14 19:44:33 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
}
|
2007-09-14 19:44:33 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
/**
|
|
|
|
* Returns all members for a specific permission.
|
|
|
|
*
|
2007-08-28 03:53:36 +02:00
|
|
|
* @param $code String|array Either a single permission code, or a list of permission codes
|
2007-07-19 12:40:28 +02:00
|
|
|
* @return DataObjectSet
|
|
|
|
*/
|
|
|
|
static function get_members_by_permission($code) {
|
|
|
|
$groupIDs = array();
|
2007-08-28 03:53:36 +02:00
|
|
|
|
|
|
|
if(is_array($code)) $SQL_filter = "Permission.Code IN ('" . implode("','", Convert::raw2sql($code)) . "')";
|
|
|
|
else $SQL_filter = "Permission.Code = '" . Convert::raw2sql($code) . "'";
|
2007-07-19 12:40:28 +02:00
|
|
|
|
|
|
|
$toplevelGroups = DataObject::get(
|
|
|
|
'Group',
|
2007-08-28 03:53:36 +02:00
|
|
|
$SQL_filter, // filter
|
2007-07-19 12:40:28 +02:00
|
|
|
null, // limit
|
|
|
|
"LEFT JOIN `Permission` ON `Group`.`ID` = `Permission`.`GroupID`" // join
|
|
|
|
);
|
|
|
|
if(!$toplevelGroups) return false;
|
|
|
|
foreach($toplevelGroups as $group) {
|
|
|
|
$familyIDs = $group->collateFamilyIDs();
|
|
|
|
if(is_array($familyIDs)) {
|
|
|
|
$groupIDs = array_merge($groupIDs, array_values($familyIDs));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(!count($groupIDs)) return false;
|
|
|
|
|
|
|
|
$members = DataObject::get(
|
|
|
|
Object::getCustomClass('Member'),
|
|
|
|
$_filter = "`Group`.ID IN (" . implode(",",$groupIDs) . ")",
|
|
|
|
$_sort = "",
|
|
|
|
$_join = "LEFT JOIN `Group_Members` ON `Member`.`ID` = `Group_Members`.`MemberID` " .
|
|
|
|
"LEFT JOIN `Group` ON `Group_Members`.`GroupID` = `Group`.`ID` "
|
|
|
|
);
|
|
|
|
return $members;
|
|
|
|
}
|
2007-09-04 05:55:02 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Return all of the groups that have one of the given permission codes
|
|
|
|
* @param $codes array|string Either a single permission code, or an array of permission codes
|
|
|
|
* @return DataObjectSet The matching group objects
|
|
|
|
*/
|
|
|
|
static function get_groups_by_permission($codes) {
|
|
|
|
if(!is_array($codes)) $codes = array($codes);
|
|
|
|
|
|
|
|
$SQLa_codes = Convert::raw2sql($codes);
|
|
|
|
$SQL_codes = join("','", $SQLa_codes);
|
|
|
|
|
|
|
|
return DataObject::get(
|
|
|
|
'Group',
|
|
|
|
"Permission.Code IN ('$SQL_codes')",
|
|
|
|
"",
|
|
|
|
"LEFT JOIN Permission ON Group.ID = Permission.GroupID"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
static function get_codes($blankItemText = null) {
|
|
|
|
$classes = ClassInfo::implementorsOf('PermissionProvider');
|
2007-09-15 23:41:54 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
$allCodes = array();
|
|
|
|
if($blankItemText) $allCodes[''] = ($blankItemText === true) ? '(select)' : $blankItemText;
|
|
|
|
$allCodes['ADMIN'] = 'Full administrative rights';
|
2007-09-15 23:41:54 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
foreach($classes as $class) {
|
|
|
|
$SNG = singleton($class);
|
|
|
|
$someCodes = $SNG->providePermissions();
|
|
|
|
if($someCodes) foreach($someCodes as $k => $v) {
|
|
|
|
$allCodes[$k] = $v;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$otherPerms = DB::query("SELECT DISTINCT Code From Permission")->column();
|
|
|
|
foreach($otherPerms as $otherPerm) {
|
|
|
|
if(!array_key_exists($otherPerm, $allCodes)) $allCodes[$otherPerm] = $otherPerm;
|
|
|
|
}
|
|
|
|
|
2007-08-31 02:28:30 +02:00
|
|
|
asort($allCodes);
|
2007-07-19 12:40:28 +02:00
|
|
|
return $allCodes;
|
|
|
|
}
|
|
|
|
|
2007-09-04 05:55:02 +02:00
|
|
|
/*
|
2007-07-19 12:40:28 +02:00
|
|
|
* Controller action to list the codes available
|
|
|
|
*/
|
|
|
|
function listcodes() {
|
|
|
|
if(!Permission::check('ADMIN')) Security::permissionFailure();
|
2007-09-15 23:41:54 +02:00
|
|
|
|
2007-07-19 12:40:28 +02:00
|
|
|
echo "<h1>The following permission codes are defined</h1>";
|
|
|
|
$codes = self::get_codes();
|
|
|
|
echo "<pre>";
|
|
|
|
print_r($codes);
|
|
|
|
}
|
2007-09-16 04:15:16 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Declare an array of permissions for the system. Permissions can be grouped by nesting arrays
|
|
|
|
* scalar values are always treated as permissions.
|
|
|
|
*
|
|
|
|
* @param $permArray A (possibly nested) array of permissions to declare for the system.
|
|
|
|
*/
|
|
|
|
static function declare_permissions($permArray) {
|
|
|
|
if(is_array(self::$declared_permissions)) self::$declared_permissions = array_merge_recursive(self::$declared_permissions, $permArray);
|
|
|
|
else self::$declared_permissions = $permArray;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get a linear list of the permissions in the system.
|
|
|
|
*/
|
|
|
|
static function get_declared_permissions_list() {
|
|
|
|
if(!self::$declared_permissions) return null;
|
|
|
|
|
|
|
|
if(self::$declared_permissions_list) return self::$declared_permissions_list;
|
|
|
|
|
|
|
|
self::$declared_permissions_list = array();
|
|
|
|
|
|
|
|
self::traverse_declared_permissions(self::$declared_permissions, self::$declared_permissions_list);
|
|
|
|
return self::$declared_permissions_list;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Recursively traverse the nested list of declared permissions and create a linear list.
|
|
|
|
*
|
|
|
|
* @param $declared Nested structure of permissions.
|
|
|
|
* @param $list List of permissions in the structure
|
|
|
|
*/
|
|
|
|
protected static function traverse_declared_permissions($declared, &$list) {
|
|
|
|
if(!is_array($declared)) return;
|
|
|
|
|
|
|
|
foreach($declared as $perm => $value) {
|
|
|
|
if($value instanceof Permission_Group) {
|
|
|
|
$list[] = $value->getName();
|
2007-09-16 16:22:26 +02:00
|
|
|
self::traverse_declared_permissions($value->getPermissions(), $list);
|
2007-09-16 04:15:16 +02:00
|
|
|
} else $list[$perm] = $value;
|
|
|
|
}
|
|
|
|
}
|
2007-07-19 12:40:28 +02:00
|
|
|
}
|
|
|
|
|
2007-09-16 04:15:16 +02:00
|
|
|
class Permission_Group {
|
|
|
|
|
|
|
|
protected $name;
|
|
|
|
protected $permissions = array();
|
|
|
|
|
|
|
|
function __construct($name, $permissions) {
|
|
|
|
$this->name = $name;
|
|
|
|
$this->permissions = $permissions;
|
|
|
|
}
|
|
|
|
|
|
|
|
function getName() {
|
|
|
|
return $this->name;
|
|
|
|
}
|
|
|
|
|
|
|
|
function getPermissions() {
|
|
|
|
return $this->permissions;
|
|
|
|
}
|
|
|
|
}
|
2007-07-19 12:40:28 +02:00
|
|
|
?>
|