Reputation: 13476
I have X number of pages, and I wish to set permissions for users on a page per page basis.
Initially I was thinking of using a bitmask. However I have realised that if my number of pages exceeds a certain amount (could only store 64 pages in a MySQL unsigned BIGINT
column) then the decimal equivalent of my bitmasks could become too great.
e.g. The decimal equivalent of page 64 would be 18,446,744,073,709,551,615
, and that's just letting it view one page.
So how would you go about setting view permissions for a large number of pages on a per-page, per-user basis?
Upvotes: 0
Views: 1810
Reputation: 48357
<?php
/**
* acl_parser.inc.php
* this is not a formal system of acls but a simplification
* there are a number of attribuates known which are given
* a value of 1, unknown attributes are numbered 0
* then logical combinations of attributes are evaluated
*
* example of rule is:
* personnel and manager not (plonker or temp)
* note that rules are NOT case sensitive
* @package simple_acl
* @author Colin McKinnon <colin.mckinnon at google's public mail service>
* @copyright 24th November 2007
*/
/**
* implements the parser
*
* IMORTANT: this method uses PHP's 'eval()' function - this has SERIOUS security implications unless you are 100%
* sure of the provenance of data supplied to it.
* The class has no built-in data access and must be populated with facts and a rule before evaluation
*/
class acl_parser
{
/**
* @var array of fact=>value, private (use method to update)
* e.g. $fact['timenow']=date('His'); $fact['manager']=true;
*/
var $facts;
/**
* @var string the acl to check, private (use method to update)
* e.g. $rule="personnel and manager not (plonker or temp) and (timenow > '0900' and timenow < '1730')";
*/
var $rule;
/**
* @var string the expression which was eval'd - for debugging
*/
var $rewrite;
/**
* constructor
* @param $facts array can be set/updated later
* @see var $facts
* @return null
*/
function acl_parser($facts=false)
{
// set up default subsitutions for operators....
$this->facts=array('and' => '*', // must come between expressions
'or' => '+', // must come between expressions
'not' => '!', // must come before expression
'true' => '1'
);
// did we get some data to set up?
if (is_array($facts)) {
foreach ($facts as $name=>$val) {
$this->add_fact($name, $val);
}
}
$this->rule===false;
}
/**
* wrapper to control access to $this->rule
* @param string
* @return bool - true if successful
*
* could be used to set site specific policies relating to rules - e.g. no less than / greater than
*/
function set_rule($rule)
{
$this->rule=$rule;
return(true);
}
/**
* set a single fact for addition
*/
function add_fact($name, $value)
{
$this->facts[$name]=$value;
}
/**
* evaluate the rule applying the known facts
* @return bool
*/
function test($rule=false)
{
if ($rule!=false) {
$this->rule=$rule;
}
if (($this->rule===false) || (!count($this->facts))) {
trigger_error("acl_parser not initialised with rule and facts");
return(false);
}
$match=array();
$replace=array();
foreach ($this->facts as $name=>$val) {
$match[]='/([^a-z]|^)(' . $name . ')([^a-z]|$)/i';
$replace[]='${1}' . $val . '${3}';
}
// this macro gets added on end to pick up on undefined elements
$match[]='/[a-z]+/i';
$replace[]='0';
$rewrite=preg_replace($match,$replace,$this->rule);
$this->rewrite=$rewrite;
return((bool)eval("return($rewrite);"));
}
}
?>
Upvotes: 0
Reputation: 27486
I once saw a system that had a table of user permissions that was similar to UNIX file permissions - users could read or write (well, edit content, it was a CMS) any page in this table. "Pages" were identified by a unique name so that each page knew it's own name and when the page was accessed it also knew which user was accessing it and then looked up the permisions for that user for the page and displayed the appropriate edit controls when available.
Examples:
users user_name (other columns) ---------- bob lisa ADMIN pages page_id page_name (other columns) ---------------------- 1 landing_page 2 products 3 corporate_about_us page_permissions page_id user_name read write ------------------------------ 1 Bob Y 1 ADMIN Y Y 2 ADMIN Y
This tells us that when ADMIN
logs in and loads the page with ID=1, they will be able to make changes to the page. Bob
, however, will not.
The actual system I saw was a bit more complicated than this (I am pretty sure it actually used things like rwx
for permissions instead of indicator columns, which I prefer) and I can't recall the details so I'm simplifying.
You can modify and customize this scheme as you need.
If you're concerned about huge volumes of data caused by storing permission records for thousands of users by hundreds (or thousands) of pages AND you've run actual permformance tests to show that it's a major problem, you could come up with a defaulting schema such as: users always have read-only everywhere, unless explicitly stated otherwise. Then you could do this:
users user_name read_only_user (other columns) --------------------------------------------- bob Y lisa Y ADMIN pages page_id page_name (other columns) ---------------------- 1 landing_page 2 products 3 corporate_about_us page_permissions page_id user_name read write ------------------------------ 1 ADMIN Y Y 2 ADMIN Y
This way, you only need to store page_permission
records for users where read_only_user <> 'Y'
. The downside is you need a little more logic to handle this kind of setup.
Upvotes: 2
Reputation: 1678
Why not just store them as many-to-many relationship in your database?
Like
user_id | page_id
1 | 1
1 | 2
2 | 1
2 | 3
Then you can fetch the pages a user can see with SELECT * from users_pages WHERE user_id =?
or you can determine if a user is allowed to see a particular page by doing SELECT * from users_pages WHERE page_id = ? and user_id = ?
.
Upvotes: 3
Reputation: 191729
I would keep it simple and just have Pages
Users
and UserPagePermissions
tables. The last one would just be a map between pages and users.
Upvotes: 0