Revision: 2659
          https://sourceforge.net/p/mrbs/code/2659/
Author:   jberanek
Date:     2013-01-25 18:54:20 +0000 (Fri, 25 Jan 2013)
Log Message:
-----------
Added Diego Zuccato's initial patch which adds preliminary ACL support.

Modified Paths:
--------------
    mrbs/branches/acl_devel/web/Themes/default/header.inc
    mrbs/branches/acl_devel/web/admin.php
    mrbs/branches/acl_devel/web/del_entry_ajax.php
    mrbs/branches/acl_devel/web/edit_area_room.php
    mrbs/branches/acl_devel/web/edit_entry.php
    mrbs/branches/acl_devel/web/edit_entry_handler.php
    mrbs/branches/acl_devel/web/js/admin.js.php
    mrbs/branches/acl_devel/web/js/edit_entry.js.php
    mrbs/branches/acl_devel/web/js/multiple.js.php
    mrbs/branches/acl_devel/web/js/report.js.php
    mrbs/branches/acl_devel/web/js/resizable.js.php
    mrbs/branches/acl_devel/web/mrbs_auth.inc
    mrbs/branches/acl_devel/web/pending.php
    mrbs/branches/acl_devel/web/report.php
    mrbs/branches/acl_devel/web/search.php
    mrbs/branches/acl_devel/web/systemdefaults.inc.php
    mrbs/branches/acl_devel/web/view_entry.php

Added Paths:
-----------
    mrbs/branches/acl_devel/web/ECAPS.php

Added: mrbs/branches/acl_devel/web/ECAPS.php
===================================================================
--- mrbs/branches/acl_devel/web/ECAPS.php                               (rev 0)
+++ mrbs/branches/acl_devel/web/ECAPS.php       2013-01-25 18:54:20 UTC (rev 
2659)
@@ -0,0 +1,167 @@
+<?php
+
+/**
+ * ACL management class
+ *
+ * Contributed by Diego Zuccato <[email protected]>
+ */
+/*
+ * From mail sent to the ML:
+ * > Basically, any group of controls should be
+ * > protected by three caps: "create/delete", "access(read)" and 
"edit(write)".
+ * > Caps sets can be represented by strings (so requiring only the addition
+ * > of a "caps varchar" column to users table and "acl varchar" to
+ * > areas/rooms tables) and can leverage current method to override strings
+ * > with lists of options (making it more user-friendly).
+ * >
+ * > A good way to represent caps could be a list of comma-separed words, and
+ * > a good way to represent an ACL could be a set of 3 (extended: a cap
+ * > could be prefixed by a '-' to indicate that it must *not* be set) caps
+ * > sets separed by pipes. More formally:
+ * > LETNUM := 'a'..'z' | 'A'..'Z' | '0'..'9' | '_'
+ * > CAP := LETNUM | LETNUM CAP
+ * > CAPS := '' | CAP | CAP ',' CAPS
+ * > ECAP := CAP | '-' CAP
+ * > ECAPS := '' | ECAP | ECAP ',' ECAPS
+ * > ACL := ECAPS '|' ECAPS' '|' ECAPS
+ * Actually I modified this last, to represent full CRUD set, so
+ * ACL := ECAPS '|' ECAPS '|' ECAPS '|' ECAPS
+ * (caps respectively for Create, Read, Update and Delete)
+ *
+ * Extension: caps and ACLs can include levels! cap[=lev][,cap=lev]*
+ * A cap w/o level is equal to a cap with level=0.
+ * An acl is verified iif:
+ * - the checked caps set is a superset of acl's caps
+ * - every cap in acl's set have a level <= the corresponding compared cap 
level
+ *
+ * For example, given an ACL of "level=2,-disabled":
+ * - "level=5,disabled" is rejected ('disabled' is set)
+ * - "level=1,admin" is rejected (1 < 2)
+ * - "level=2,foo=3,bar=0" is accepted ('foo' and 'bar' aren't in ACL, but 
it's OK)
+ * - "foo=100,bar=abc" is rejected (missing required 'level')
+*/
+
+define('ALPHA1', "abcdefghijklmnopqrstuvwxyz");
+define('ALPHA2', "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+define('NUMBERS', "0123456789");
+define('LETNUM', ALPHA1.ALPHA2.NUMBERS."_");
+
+class ECAPS {
+    // Accept an array (one extended capability per *key* -- value is the 
level) or a string
+    // (packed extended capabilities. Duplicated capabilities are overwritten 
and only the
+    // last one remains)
+    public function __construct($ecaps) {
+       if(is_array($ecaps)) {
+           // Sanitize input array:
+           // - remove illegal keys (not valid caps)
+           // - make sure values are positive integers
+           foreach($ecaps as $k => $v) {
+               $t=$this->parse_ecap("$k=$v", TRUE);
+               if(FALSE===$t || $t['key']!=$k) { // invalid cap or different 
key: discard
+                   unset($ecaps[$k]);
+               } else {
+                   $ecaps[$k]=$t['val'];       // sanitize value
+               }
+           }
+       } else if(is_string($ecaps)) {
+           $ecaps=$this->parse_ecaps_string($ecaps, true);
+       } else {        // Ops! Don't know what to do, so I leave caps empty
+           trigger_error("Bad caps constructor parameter -- leaving caps 
empty!", E_USER_WARNING);
+           return;
+       }
+       foreach($ecaps as $cap => $v) {
+           if(substr($cap, 0, 1) == '-') {
+               $this->neg_caps[substr($cap, 1)]=$v;
+           } else {
+               $this->pos_caps[$cap]=$v;
+           }
+       }
+
+       $this->pcaps=count($this->pos_caps); // Useless to count every time 
it's needed to verify a CAPS set
+
+       $tst=array_intersect(array_keys($this->pos_caps), 
array_keys($this->neg_caps));
+       if(count($tst)) {
+           trigger_error("Never-satisfiable CAPS: pos and neg caps overlap for 
". implode(",",$tst), E_USER_WARNING);
+       }
+    }
+
+    // Gets a capability set and returns if the CAPs allows access (TRUE) or 
not (FALSE)
+    public function test($cap) {
+       if(is_array($cap)) $caparr=$cap;
+       else if(is_string($cap)) $caparr=$this->parse_ecaps_string($cap);
+       else return FALSE;
+
+       $pos=count(array_intersect(array_keys($this->pos_caps), 
array_keys($caparr)));
+       $neg=count(array_intersect(array_keys($this->neg_caps), 
array_keys($caparr)));
+
+       // There must be *NO* negatives and *ALL* positives
+       $rv=($neg==0)&&($pos==$this->pcaps);
+
+       if($rv) { // Time to check levels!
+           foreach($this->pos_caps as $cap => $lev) {
+               if($caparr[$cap]<$lev) {
+                   $rv=FALSE;
+                   break; // bail out: at least one level too low!
+               }
+           }
+       }
+
+       return $rv;
+    }
+
+    public function __toString() {
+       $tmp=$this->pos_caps;
+       foreach($this->neg_caps as $ncap => $nval) {
+           $tmp["-".$ncap]=$nval;
+       }
+       $out="";
+       foreach($tmp as $k => $v) {
+           $out .= ($out==""?"":",")."$k=$v";
+       }
+       return $out;
+    }
+
+    //************************
+    private function  parse_ecaps_string($caps, $allow_neg=FALSE) {
+       $tcaps = array();
+       $tmp=explode(",", $caps);
+       foreach($tmp as $tok) { // Probably this can be optimized
+           if(""==$tok) continue;
+
+           $t=$this->parse_ecap($tok, $allow_neg);
+           if(is_array($t)) {
+               $tcaps[$t['key']] = $t['val'];
+           }
+       }
+       return $tcaps;
+    }
+
+    // Gets a single ecap string and returns an array:
+    // 'key' => ecap name
+    // 'val' => ecap value
+    // Returns FALSE if the capability is invalid
+    private function parse_ecap($tok, $allow_neg) {
+       $rv=array();
+
+       $start=0;
+       if($tok[0]=='-' && $allow_neg) { // *Possibly* a neg cap
+           $start=1; // Skip the initial '-'
+       }
+       $kl=strspn($tok, LETNUM, $start);
+       if($kl) { // at least one valid char as key
+           $kl+=$start;
+           $rv['key'] = substr($tok, 0, $kl);
+           $rv['val'] = 0;
+           if(strlen($tok)>$kl && "="==$tok[$kl]) {
+               // value present
+               $rv['val'] = (int)substr($tok, $kl+1, strspn($tok, NUMBERS, 
$kl+1));
+           }
+       } else {        // Invalid key ==> invalid cap (no name and val)
+           $rv=FALSE;
+       }
+
+       return $rv;
+    }
+
+    private $pos_caps=array(), $neg_caps=array(), $pcaps=0;
+}


Property changes on: mrbs/branches/acl_devel/web/ECAPS.php
___________________________________________________________________
Added: svn:keywords
## -0,0 +1 ##
+Id
\ No newline at end of property
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Modified: mrbs/branches/acl_devel/web/Themes/default/header.inc
===================================================================
--- mrbs/branches/acl_devel/web/Themes/default/header.inc       2013-01-25 
18:46:36 UTC (rev 2658)
+++ mrbs/branches/acl_devel/web/Themes/default/header.inc       2013-01-25 
18:54:20 UTC (rev 2659)
@@ -20,7 +20,7 @@
   
   $page = basename($PHP_SELF, ".php");
   $user = getUserName();
-  $is_admin = (authGetUserLevel($user) >= $max_level);
+  $is_admin = getAuthorised($sys_caps['admin']);
   
   // Need to set the timezone before we can use date()
   get_area_settings($area);
@@ -152,7 +152,7 @@
         // (if there are any enabled areas where we require bookings to be 
approved)
 
         $approval_somewhere = some_area('approval_enabled', TRUE);
-        if ($approval_somewhere && (authGetUserLevel($user) >= 1))
+        if ($approval_somewhere && getAuthorised($sys_caps['regular']))
         {
           $sql_approval_enabled = some_area_predicate('approval_enabled');
           // Find out how many bookings are awaiting approval

Modified: mrbs/branches/acl_devel/web/admin.php
===================================================================
--- mrbs/branches/acl_devel/web/admin.php       2013-01-25 18:46:36 UTC (rev 
2658)
+++ mrbs/branches/acl_devel/web/admin.php       2013-01-25 18:54:20 UTC (rev 
2659)
@@ -34,8 +34,7 @@
 
 // Also need to know whether they have admin rights
 $user = getUserName();
-$required_level = (isset($max_level) ? $max_level : 2);
-$is_admin = (authGetUserLevel($user) >= $required_level);
+$is_admin = getAuthorised($sys_caps['admin']);
 
 print_header($day, $month, $year, isset($area) ? $area : "", isset($room) ? 
$room : "");
 

Modified: mrbs/branches/acl_devel/web/del_entry_ajax.php
===================================================================
--- mrbs/branches/acl_devel/web/del_entry_ajax.php      2013-01-25 18:46:36 UTC 
(rev 2658)
+++ mrbs/branches/acl_devel/web/del_entry_ajax.php      2013-01-25 18:54:20 UTC 
(rev 2659)
@@ -29,8 +29,7 @@
 
 // Check that the user has the highest level of admin rights
 $user = getUserName();
-$level = authGetUserLevel($user);
-if ($level < $max_level)
+if (!getAuthorised($sys_caps['admin']))
 {
   exit;
 }

Modified: mrbs/branches/acl_devel/web/edit_area_room.php
===================================================================
--- mrbs/branches/acl_devel/web/edit_area_room.php      2013-01-25 18:46:36 UTC 
(rev 2658)
+++ mrbs/branches/acl_devel/web/edit_area_room.php      2013-01-25 18:54:20 UTC 
(rev 2659)
@@ -214,8 +214,7 @@
 
 // Also need to know whether they have admin rights
 $user = getUserName();
-$required_level = (isset($max_level) ? $max_level : 2);
-$is_admin = (authGetUserLevel($user) >= $required_level);
+$is_admin = getAuthorised($sys_caps['admin']);
 
 // Done changing area or room information?
 if (isset($change_done))

Modified: mrbs/branches/acl_devel/web/edit_entry.php
===================================================================
--- mrbs/branches/acl_devel/web/edit_entry.php  2013-01-25 18:46:36 UTC (rev 
2658)
+++ mrbs/branches/acl_devel/web/edit_entry.php  2013-01-25 18:54:20 UTC (rev 
2659)
@@ -592,7 +592,7 @@
 
 // Also need to know whether they have admin rights
 $user = getUserName();
-$is_admin = (authGetUserLevel($user) >= 2);
+$is_admin = getAuthorised($sys_caps['admin']);
 // You're only allowed to make repeat bookings if you're an admin
 // or else if $auth['only_admin_can_book_repeat'] is not set
 $repeats_allowed = $is_admin || empty($auth['only_admin_can_book_repeat']);

Modified: mrbs/branches/acl_devel/web/edit_entry_handler.php
===================================================================
--- mrbs/branches/acl_devel/web/edit_entry_handler.php  2013-01-25 18:46:36 UTC 
(rev 2658)
+++ mrbs/branches/acl_devel/web/edit_entry_handler.php  2013-01-25 18:54:20 UTC 
(rev 2659)
@@ -12,7 +12,7 @@
 
 // Also need to know whether they have admin rights
 $user = getUserName();
-$is_admin = (authGetUserLevel($user) >= 2);
+$is_admin = getAuthorised($sys_caps['admin']);
 
 
 

Modified: mrbs/branches/acl_devel/web/js/admin.js.php
===================================================================
--- mrbs/branches/acl_devel/web/js/admin.js.php 2013-01-25 18:46:36 UTC (rev 
2658)
+++ mrbs/branches/acl_devel/web/js/admin.js.php 2013-01-25 18:54:20 UTC (rev 
2659)
@@ -13,7 +13,7 @@
 }
 
 $user = getUserName();
-$is_admin = (authGetUserLevel($user) >= $max_level);
+$is_admin = getAuthorised($sys_caps['admin']);
 
 // 
=================================================================================
 

Modified: mrbs/branches/acl_devel/web/js/edit_entry.js.php
===================================================================
--- mrbs/branches/acl_devel/web/js/edit_entry.js.php    2013-01-25 18:46:36 UTC 
(rev 2658)
+++ mrbs/branches/acl_devel/web/js/edit_entry.js.php    2013-01-25 18:54:20 UTC 
(rev 2659)
@@ -13,7 +13,7 @@
 }
 
 $user = getUserName();
-$is_admin = (authGetUserLevel($user) >= $max_level);
+$is_admin = getAuthorised($sys_caps['admin']);
 
 
 // Function to display the secondary repeat type fieldset appropriate

Modified: mrbs/branches/acl_devel/web/js/multiple.js.php
===================================================================
--- mrbs/branches/acl_devel/web/js/multiple.js.php      2013-01-25 18:46:36 UTC 
(rev 2658)
+++ mrbs/branches/acl_devel/web/js/multiple.js.php      2013-01-25 18:54:20 UTC 
(rev 2659)
@@ -13,7 +13,7 @@
 }
 
 $user = getUserName();
-$is_admin = (authGetUserLevel($user) >= $max_level);
+$is_admin = getAuthorised($sys_caps['admin']);
 
 // 
=================================================================================
 

Modified: mrbs/branches/acl_devel/web/js/report.js.php
===================================================================
--- mrbs/branches/acl_devel/web/js/report.js.php        2013-01-25 18:46:36 UTC 
(rev 2658)
+++ mrbs/branches/acl_devel/web/js/report.js.php        2013-01-25 18:54:20 UTC 
(rev 2659)
@@ -13,7 +13,7 @@
 }
 
 $user = getUserName();
-$is_admin = (authGetUserLevel($user) >= $max_level);
+$is_admin = getAuthorised($sys_caps['admin']);
 
 
 

Modified: mrbs/branches/acl_devel/web/js/resizable.js.php
===================================================================
--- mrbs/branches/acl_devel/web/js/resizable.js.php     2013-01-25 18:46:36 UTC 
(rev 2658)
+++ mrbs/branches/acl_devel/web/js/resizable.js.php     2013-01-25 18:54:20 UTC 
(rev 2659)
@@ -13,7 +13,7 @@
 }
 
 $user = getUserName();
-$is_admin = (authGetUserLevel($user) >= $max_level);
+$is_admin = getAuthorised($sys_caps['admin']);
 
 
 // function to reverse a collection of jQuery objects

Modified: mrbs/branches/acl_devel/web/mrbs_auth.inc
===================================================================
--- mrbs/branches/acl_devel/web/mrbs_auth.inc   2013-01-25 18:46:36 UTC (rev 
2658)
+++ mrbs/branches/acl_devel/web/mrbs_auth.inc   2013-01-25 18:54:20 UTC (rev 
2659)
@@ -1,6 +1,9 @@
 <?php
 // $Id$
 
+// include ACL basic infrastructure
+require_once 'ECAPS.php';
+
 // include the authentification wrappers
 require_once "auth/auth_$auth[type].inc";
 
@@ -47,17 +50,17 @@
  * 
  * Check to see if the current user has a certain level of rights
  * 
- * $level - The access level required
+ * $acl - The access control list to satisfy to access the page
  * 
  * Returns:
  *   0        - The user does not have the required access
  *   non-zero - The user has the required access
  */
-function getAuthorised($level)
+function getAuthorised($acl)
 {
-  // If the minimum level is zero (or not set) then they are
+  // If the acl is empty (or not set) then they are
   // authorised, whoever they are
-  if (empty($level))
+  if (empty($acl))
   {
     return TRUE;
   }
@@ -70,7 +73,8 @@
     return 0;
   }
 
-  return authGetUserLevel($user) >= $level;
+  $req=new ECAPS($acl);
+  return $req->test(authGetUserCaps($user));
 }
 
 /* checkAuthorised()
@@ -84,26 +88,47 @@
  */
 function checkAuthorised()
 {
-  global $page_level, $max_level;
+  global $page_level, $page_acl, $max_level, $sys_caps;
   global $day, $month, $year, $area, $room;
   global $PHP_SELF;
-  
+
+  $acl="";
+
+  $pagename=basename($PHP_SELF);
   // Get the minimum authorisation level for this page
-  if (isset($page_level[basename($PHP_SELF)]))
+  if (isset($page_acl[$pagename]))
   {
-    $required_level = $page_level[basename($PHP_SELF)];
+    $acl=$page_acl[$pagename];
   }
-  elseif (isset($max_level))
-  {
-    $required_level = $max_level;
+  else {
+    if (isset($page_level[$pagename]))
+    {
+       // Need to map page level to an ACL
+       if($page_level[$pagename]) {
+           
$acl=($page_level[$pagename]>1)?$sys_caps['admin']:$sys_caps['regular'];
+       } else {
+           $acl=""; // Always allowed
+       }
+    }
+    else
+    {
+      $acl=$sys_caps['admin'];
+    }
   }
-  else
+
+  // Add to ACL area-/room- specific constraints
+  if (is_array($acl))
   {
-    $required_level = 2;
+    $newacl="";
+    if (isset($acl["a$area"]))
+      $newacl.=$acl["a$area"];
+    if (isset($acl["r$room"]))
+      $newacl.=$acl["r$room"];
+    $acl=$newacl;
   }
-  
+
   // Check that the user has this level
-  if (!getAuthorised($required_level))
+  if (!getAuthorised($acl))
   {
     // If we dont know the right date then use today's
     if (!isset($day) or !isset($month) or !isset($year))
@@ -190,15 +215,15 @@
  */
 function auth_can_edit_user($user, $target)
 {
-  global $min_user_editing_level;
-  
+  global $sys_caps;
+
   // Always allowed to modify your own stuff
   if(strcasecmp($user, $target) == 0)
   {
     return 1;
   }
 
-  if(authGetUserLevel($user) >= $min_user_editing_level)
+  if(getAuthorised($sys_caps['edit_users']))
   {
     return 1;
   }
@@ -220,6 +245,8 @@
 //           the room; otherwise FALSE
 function auth_book_admin($user, $room)
 {
-  return (authGetUserLevel($user) >= 2);
+  global $sys_caps;
+
+  return getAuthorised($sys_caps['admin']);
 }
 ?>

Modified: mrbs/branches/acl_devel/web/pending.php
===================================================================
--- mrbs/branches/acl_devel/web/pending.php     2013-01-25 18:46:36 UTC (rev 
2658)
+++ mrbs/branches/acl_devel/web/pending.php     2013-01-25 18:54:20 UTC (rev 
2659)
@@ -196,7 +196,7 @@
 
 // Also need to know whether they have admin rights
 $user = getUserName();
-$is_admin = (authGetUserLevel($user) >= 2);
+$is_admin = getAuthorised($sys_caps['admin']);
 
 print_header($day, $month, $year, $area, isset($room) ? $room : "");
 

Modified: mrbs/branches/acl_devel/web/report.php
===================================================================
--- mrbs/branches/acl_devel/web/report.php      2013-01-25 18:46:36 UTC (rev 
2658)
+++ mrbs/branches/acl_devel/web/report.php      2013-01-25 18:54:20 UTC (rev 
2659)
@@ -1083,8 +1083,7 @@
   checkAuthorised();
   // Also need to know whether they have admin rights
   $user = getUserName();
-  $user_level = authGetUserLevel($user);
-  $is_admin =  ($user_level >= 2);
+  $is_admin = getAuthorised($sys_caps['admin']);
 }
 
 // If we're running in CLI mode we're passing the parameters in from the 
command line

Modified: mrbs/branches/acl_devel/web/search.php
===================================================================
--- mrbs/branches/acl_devel/web/search.php      2013-01-25 18:46:36 UTC (rev 
2658)
+++ mrbs/branches/acl_devel/web/search.php      2013-01-25 18:54:20 UTC (rev 
2659)
@@ -127,7 +127,7 @@
 
 // Also need to know whether they have admin rights
 $user = getUserName();
-$is_admin =  (isset($user) && authGetUserLevel($user)>=2) ;
+$is_admin =  (isset($user) && getAuthorised($sys_caps['admin'])) ;
 
 // Set up for Ajax.   We need to know whether we're capable of dealing with 
Ajax
 // requests, which will only be if (a) the browser is using DataTables and (b)

Modified: mrbs/branches/acl_devel/web/systemdefaults.inc.php
===================================================================
--- mrbs/branches/acl_devel/web/systemdefaults.inc.php  2013-01-25 18:46:36 UTC 
(rev 2658)
+++ mrbs/branches/acl_devel/web/systemdefaults.inc.php  2013-01-25 18:54:20 UTC 
(rev 2659)
@@ -1083,4 +1083,14 @@
 // Default description for new bookings
 $default_description = "";
 
+/****************
+ * Capabilities *
+ ***************/
+
+$sys_caps['admin']='level=2';
+$sys_caps['regular']='level=1';
+// Note that if you change $min_user_editing_level in config.inc.php, you have 
to
+// redefine the edit_users element!!!
+$sys_caps['edit_users']="level=$min_user_editing_level";
+
 ?>

Modified: mrbs/branches/acl_devel/web/view_entry.php
===================================================================
--- mrbs/branches/acl_devel/web/view_entry.php  2013-01-25 18:46:36 UTC (rev 
2658)
+++ mrbs/branches/acl_devel/web/view_entry.php  2013-01-25 18:54:20 UTC (rev 
2659)
@@ -115,7 +115,7 @@
 
 // Also need to know whether they have admin rights
 $user = getUserName();
-$is_admin = (authGetUserLevel($user) >= 2);
+$is_admin = getAuthorised($sys_caps['admin']);
 // You're only allowed to make repeat bookings if you're an admin
 // or else if $auth['only_admin_can_book_repeat'] is not set
 $repeats_allowed = $is_admin || empty($auth['only_admin_can_book_repeat']);
------------------------------------------------------------------------------
Master Visual Studio, SharePoint, SQL, ASP.NET, C# 2012, HTML5, CSS,
MVC, Windows 8 Apps, JavaScript and much more. Keep your skills current
with LearnDevNow - 3,200 step-by-step video tutorials by Microsoft
MVPs and experts. ON SALE this month only -- learn more at:
http://p.sf.net/sfu/learnnow-d2d
_______________________________________________
Mrbs-commits mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mrbs-commits

Reply via email to