source: branches/eli_branch/lib/ACL.inc.php @ 442

Last change on this file since 442 was 439, checked in by anonymous, 11 years ago

added public and private keywords to all properties and methods, changed old classname constructor function to construct, removed more ?> closing tags

File size: 28.3 KB
Line 
1<?php
2/**
3 * The Strangecode Codebase - a general application development framework for PHP
4 * For details visit the project site: <http://trac.strangecode.com/codebase/>
5 * Copyright 2001-2012 Strangecode, LLC
6 *
7 * This file is part of The Strangecode Codebase.
8 *
9 * The Strangecode Codebase is free software: you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as published by the
11 * Free Software Foundation, either version 3 of the License, or (at your option)
12 * any later version.
13 *
14 * The Strangecode Codebase is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * details.
18 *
19 * You should have received a copy of the GNU General Public License along with
20 * The Strangecode Codebase. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23/*
24* ACL.inc.php
25*
26* Uses the ARO/ACO/AXO model of Access Control Lists.
27* Uses Modified Preorder Tree Traversal to maintain a tree-structure.
28* See: http://www.sitepoint.com/print/hierarchical-data-database
29* Includes a command-line tool for managing rights (codebase/bin/acl.cli.php).
30*
31*
32* @author   Quinn Comendant <quinn@strangecode.com>
33* @version  1.0
34* @since    14 Jun 2006 22:35:11
35*/
36
37require_once dirname(__FILE__) . '/Cache.inc.php';
38
39class ACL {
40
41    // Configuration parameters for this object.
42    private $_params = array(
43       
44        // If false nothing will be cached or retrieved. Useful for testing realtime data requests.
45        'enable_cache' => true,
46
47        // Automatically create table and verify columns. Better set to false after site launch.
48        'create_table' => false,
49    );
50
51    /**
52     * Constructor.
53     */
54    public function __construct()
55    {
56        $app =& App::getInstance();
57
58        // Configure the cache object.
59        $this->cache = new Cache('acl');
60        $this->cache->setParam(array('enabled' => true));
61
62        // Get create tables config from global context.
63        if (!is_null($app->getParam('db_create_tables'))) {
64            $this->setParam(array('create_table' => $app->getParam('db_create_tables')));
65        }
66    }
67
68    /**
69     * This method enforces the singleton pattern for this class.
70     *
71     * @return  object  Reference to the global ACL object.
72     * @access  public
73     * @static
74     */
75    public static function &getInstance()
76    {
77        static $instance = null;
78
79        if ($instance === null) {
80            $instance = new ACL();
81        }
82
83        return $instance;
84    }
85
86    /**
87     * Set (or overwrite existing) parameters by passing an array of new parameters.
88     *
89     * @access public
90     *
91     * @param  array    $params     Array of parameters (key => val pairs).
92     */
93    public function setParam($params)
94    {
95        $app =& App::getInstance();
96   
97        if (isset($params) && is_array($params)) {
98            // Merge new parameters with old overriding only those passed.
99            $this->_params = array_merge($this->_params, $params);
100        } else {
101            $app->logMsg(sprintf('Parameters are not an array: %s', $params), LOG_ERR, __FILE__, __LINE__);
102        }
103    }
104
105    /**
106     * Return the value of a parameter, if it exists.
107     *
108     * @access public
109     * @param string $param        Which parameter to return.
110     * @return mixed               Configured parameter value.
111     */
112    public function getParam($param)
113    {
114        $app =& App::getInstance();
115   
116        if (isset($this->_params[$param])) {
117            return $this->_params[$param];
118        } else {
119            $app->logMsg(sprintf('Parameter is not set: %s', $param), LOG_NOTICE, __FILE__, __LINE__);
120            return null;
121        }
122    }
123
124    /**
125     * Setup the database tables for this class.
126     *
127     * @access  public
128     * @author  Quinn Comendant <quinn@strangecode.com>
129     * @since   04 Jun 2006 16:41:42
130     */
131    public function initDB($recreate_db=false)
132    {
133        $app =& App::getInstance();
134        $db =& DB::getInstance();
135
136        static $_db_tested = false;
137
138        if ($recreate_db || !$_db_tested && $this->getParam('create_table')) {
139
140            if ($recreate_db) {
141                $db->query("DROP TABLE IF EXISTS acl_tbl");
142                $db->query("DROP TABLE IF EXISTS aro_tbl");
143                $db->query("DROP TABLE IF EXISTS aco_tbl");
144                $db->query("DROP TABLE IF EXISTS axo_tbl");
145                $app->logMsg(sprintf('Dropping and recreating tables acl_tbl, aro_tbl, aco_tbl, axo_tbl.', null), LOG_INFO, __FILE__, __LINE__);
146            }
147           
148            // acl_tbl
149            $db->query("
150                CREATE TABLE IF NOT EXISTS acl_tbl (
151                    aro_id SMALLINT(11) UNSIGNED NOT NULL DEFAULT '0',
152                    aco_id SMALLINT(11) UNSIGNED NOT NULL DEFAULT '0',
153                    axo_id SMALLINT(11) UNSIGNED NOT NULL DEFAULT '0',
154                    access ENUM('allow', 'deny') DEFAULT NULL,
155                    added_datetime DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
156                    UNIQUE KEY (aro_id, aco_id, axo_id),
157                    KEY (access)
158                ) ENGINE=MyISAM
159            ");
160            if (!$db->columnExists('acl_tbl', array(
161                'aro_id',
162                'aco_id',
163                'axo_id',
164                'access',
165                'added_datetime',
166            ), false, false)) {
167                $app->logMsg(sprintf('Database table acl_tbl has invalid columns. Please update this table manually.', null), LOG_ALERT, __FILE__, __LINE__);
168                trigger_error(sprintf('Database table acl_tbl has invalid columns. Please update this table manually.', null), E_USER_ERROR);
169            } else {
170                // Insert root node data if nonexistant, dely all by default.
171                $qid = $db->query("SELECT 1 FROM acl_tbl");
172                if (mysql_num_rows($qid) == 0) {
173                    $qid = $db->query("REPLACE INTO acl_tbl VALUES ('1', '1', '1', 'deny', NOW())");                   
174                }               
175            }
176
177            // aro_tbl, aco_tbl, axo_tbl
178            foreach (array('aro', 'aco', 'axo') as $a_o) {
179                $db->query("
180                    CREATE TABLE IF NOT EXISTS {$a_o}_tbl (
181                        {$a_o}_id SMALLINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
182                        name VARCHAR(32) NOT NULL DEFAULT '',
183                        lft MEDIUMINT(9) UNSIGNED NOT NULL DEFAULT '0',
184                        rgt MEDIUMINT(9) UNSIGNED NOT NULL DEFAULT '0',
185                        added_datetime DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00',
186                        UNIQUE KEY name (name(15)),
187                        KEY transversal (lft, rgt)
188                    ) ENGINE=MyISAM;
189                ");
190
191                if (!$db->columnExists("{$a_o}_tbl", array(
192                    "{$a_o}_id",
193                    'name',
194                    'lft',
195                    'rgt',
196                    'added_datetime',
197                ), false, false)) {
198                    $app->logMsg(sprintf('Database table %s has invalid columns. Please update this table manually.', "{$a_o}_tbl"), LOG_ALERT, __FILE__, __LINE__);
199                    trigger_error(sprintf('Database table %s has invalid columns. Please update this table manually.', "{$a_o}_tbl"), E_USER_ERROR);
200                } else {
201                    // Insert root node data if nonexistent.
202                    $qid = $db->query("SELECT 1 FROM {$a_o}_tbl WHERE name = 'root'");
203                    if (mysql_num_rows($qid) == 0) {
204                        $qid = $db->query("REPLACE INTO {$a_o}_tbl (name, lft, rgt, added_datetime) VALUES ('root', 1, 2, NOW())");                   
205                    }                   
206                }
207
208            }
209        }
210        $_db_tested = true;
211        return true;
212    }
213
214    /*
215    * Add a node to one of the aro/aco/axo tables.
216    *
217    * @access   public
218    * @param    string $name A unique identifier for the new node.
219    * @param    string $parent The name of the parent under-which to attach the new node.
220    * @param    string $type The tree to add to, one of: aro, aco, axo.
221    * @return   bool | int False on error, or the last_insert_id primary key of the new node.
222    * @author   Quinn Comendant <quinn@strangecode.com>
223    * @version  1.0
224    * @since    14 Jun 2006 22:39:29
225    */
226    public function add($name, $parent=null, $type)
227    {
228        $app =& App::getInstance();
229        $db =& DB::getInstance();
230       
231        $this->initDB();
232       
233        switch ($type) {
234        case 'aro' :
235            $tbl = 'aro_tbl';
236            break;
237        case 'aco' :
238            $tbl = 'aco_tbl';
239            break;
240        case 'axo' :
241            $tbl = 'axo_tbl';
242            break;
243        default :
244            $app->logMsg(sprintf('Invalid access object type: %s', $type), LOG_ERR, __FILE__, __LINE__);
245            return false;
246            break;
247        }
248       
249        // If $parent is null, use root object.
250        if (is_null($parent)) {
251            $parent = 'root';
252        }
253       
254        // Ensure node and parent name aren't empty.
255        if ('' == trim($name) || '' == trim($parent)) {
256            $app->logMsg(sprintf('Cannot add node, parent (%s) or name (%s) missing.', $name, $parent), LOG_WARNING, __FILE__, __LINE__);
257            return false;
258        }
259       
260        // Ensure node is unique.
261        $qid = $db->query("SELECT 1 FROM $tbl WHERE name = '" . $db->escapeString($name) . "'");
262        if (mysql_num_rows($qid) > 0) {
263            $app->logMsg(sprintf('Cannot add %s node, already exists: %s', $type, $name), LOG_INFO, __FILE__, __LINE__);
264            return false;
265        }
266       
267        // Select the rgt of $parent.
268        $qid = $db->query("SELECT rgt FROM $tbl WHERE name = '" . $db->escapeString($parent) . "'");
269        if (!list($parent_rgt) = mysql_fetch_row($qid)) {
270            $app->logMsg(sprintf('Cannot add %s node to nonexistent parent: %s', $type, $parent), LOG_WARNING, __FILE__, __LINE__);
271            return false;
272        }
273
274        // Update transversal numbers for all nodes to the rgt of $parent.
275        $db->query("UPDATE $tbl SET lft = lft + 2 WHERE lft >= $parent_rgt");
276        $db->query("UPDATE $tbl SET rgt = rgt + 2 WHERE rgt >= $parent_rgt");
277       
278        // Insert new node just below parent. Lft is parent's old rgt.
279        $db->query("
280            INSERT INTO $tbl (name, lft, rgt, added_datetime)
281            VALUES ('" . $db->escapeString($name) . "', $parent_rgt, $parent_rgt + 1, NOW())
282        ");
283
284        $app->logMsg(sprintf('Added %s node %s to parent %s.', $type, $name, $parent), LOG_INFO, __FILE__, __LINE__);
285        return mysql_insert_id($db->getDBH());
286    }
287
288    // Alias functions for the different object types.
289    public function addRequestObject($name, $parent=null)
290    {
291        return $this->add($name, $parent, 'aro');
292    }
293    public function addControlObject($name, $parent=null)
294    {
295        return $this->add($name, $parent, 'aco');
296    }
297    public function addXtraObject($name, $parent=null)
298    {
299        return $this->add($name, $parent, 'axo');
300    }
301
302    /*
303    * Remove a node from one of the aro/aco/axo tables.
304    *
305    * @access   public
306    * @param    string $name The identifier for the node to remove.
307    * @param    string $type The tree to modify, one of: aro, aco, axo.
308    * @return   bool | int False on error, or true on success.
309    * @author   Quinn Comendant <quinn@strangecode.com>
310    * @version  1.0
311    * @since    14 Jun 2006 22:39:29
312    */
313    public function remove($name, $type)
314    {
315        $app =& App::getInstance();
316        $db =& DB::getInstance();
317       
318        $this->initDB();
319
320        switch ($type) {
321        case 'aro' :
322            $tbl = 'aro_tbl';
323            $primary_key = 'aro_id';
324            break;
325        case 'aco' :
326            $tbl = 'aco_tbl';
327            $primary_key = 'aco_id';
328            break;
329        case 'axo' :
330            $tbl = 'axo_tbl';
331            $primary_key = 'axo_id';
332            break;
333        default :
334            $app->logMsg(sprintf('Invalid access object type: %s', $type), LOG_ERR, __FILE__, __LINE__);
335            return false;
336            break;
337        }
338       
339        // Ensure node name isn't empty.
340        if ('' == trim($name)) {
341            $app->logMsg(sprintf('Cannot add node, name missing.', null), LOG_WARNING, __FILE__, __LINE__);
342            return false;
343        }
344       
345        // Select the lft and rgt of $name to use for selecting children and reordering transversals.
346        $qid = $db->query("SELECT lft, rgt FROM $tbl WHERE name = '" . $db->escapeString($name) . "'");
347        if (!list($lft, $rgt) = mysql_fetch_row($qid)) {
348            $app->logMsg(sprintf('Cannot delete nonexistent %s name: %s', $type, $name), LOG_WARNING, __FILE__, __LINE__);
349            return false;
350        }
351       
352        // Remove node and all children of node, as well as acl_tbl links.
353        $db->query("
354            DELETE $tbl, acl_tbl
355            FROM $tbl
356            LEFT JOIN acl_tbl ON ($tbl.$primary_key = acl_tbl.$primary_key)
357            WHERE $tbl.lft BETWEEN $lft AND $rgt
358        ");
359        $num_deleted_nodes = mysql_affected_rows($db->getDBH());
360
361        // Update transversal numbers for all nodes to the rgt of $parent, taking in to account the absence of it's children.
362        $db->query("UPDATE $tbl SET lft = lft - ($rgt - $lft + 1) WHERE lft > $lft");
363        $db->query("UPDATE $tbl SET rgt = rgt - ($rgt - $lft + 1) WHERE rgt > $rgt");
364
365        $app->logMsg(sprintf('Removed %s node %s along with %s children.', $type, $name, $num_deleted_nodes - 1), LOG_INFO, __FILE__, __LINE__);
366        return true;
367    }
368   
369    // Alias functions for the different object types.
370    public function removeRequestObject($name)
371    {
372        return $this->remove($name, 'aro');
373    }
374    public function removeControlObject($name)
375    {
376        return $this->remove($name, 'aco');
377    }
378    public function removeXtraObject($name)
379    {
380        return $this->remove($name, 'axo');
381    }
382
383    /*
384    * Move a node to a new parent in one of the aro/aco/axo tables.
385    *
386    * @access   public
387    * @param    string $name The identifier for the node to remove.
388    * @param    string $new_parent The name of the parent under-which to attach the new node.
389    * @param    string $type The tree to modify, one of: aro, aco, axo.
390    * @return   bool | int False on error, or the last_insert_id primary key of the new node.
391    * @author   Quinn Comendant <quinn@strangecode.com>
392    * @version  1.0
393    * @since    14 Jun 2006 22:39:29
394    */
395    public function move($name, $new_parent, $type)
396    {
397        $app =& App::getInstance();
398        $db =& DB::getInstance();
399       
400        $this->initDB();
401
402        switch ($type) {
403        case 'aro' :
404            $tbl = 'aro_tbl';
405            $primary_key = 'aro_id';
406            break;
407        case 'aco' :
408            $tbl = 'aco_tbl';
409            $primary_key = 'aco_id';
410            break;
411        case 'axo' :
412            $tbl = 'axo_tbl';
413            $primary_key = 'axo_id';
414            break;
415        default :
416            $app->logMsg(sprintf('Invalid access object type: %s', $type), LOG_ERR, __FILE__, __LINE__);
417            return false;
418            break;
419        }
420       
421        // If $new_parent is null, use root object.
422        if (is_null($new_parent)) {
423            $new_parent = 'root';
424        }
425       
426        // Ensure node and parent name aren't empty.
427        if ('' == trim($name) || '' == trim($new_parent)) {
428            $app->logMsg(sprintf('Cannot add node, parent (%s) or name (%s) missing.', $name, $new_parent), LOG_WARNING, __FILE__, __LINE__);
429            return false;
430        }
431       
432        // Select the lft and rgt of $name to use for selecting children and reordering transversals.
433        $qid = $db->query("SELECT lft, rgt FROM $tbl WHERE name = '" . $db->escapeString($name) . "'");
434        if (!list($lft, $rgt) = mysql_fetch_row($qid)) {
435            $app->logMsg(sprintf('Cannot move nonexistent %s name: %s', $type, $name), LOG_WARNING, __FILE__, __LINE__);
436            return false;
437        }
438       
439        // Total number of transversal values (that is, the count of self plus all children times two).
440        $total_transversal_value = ($rgt - $lft + 1);
441
442        // Select the rgt of the new parent.
443        $qid = $db->query("SELECT rgt FROM $tbl WHERE name = '" . $db->escapeString($new_parent) . "'");
444        if (!list($new_parent_rgt) = mysql_fetch_row($qid)) {
445            $app->logMsg(sprintf('Cannot move %s node to nonexistent parent: %s', $type, $new_parent), LOG_WARNING, __FILE__, __LINE__);
446            return false;
447        }
448       
449        // Ensure the new parent is not a child of the node being moved.
450        if ($new_parent_rgt <= $rgt && $new_parent_rgt >= $lft) {
451            $app->logMsg(sprintf('Cannot move %s node %s to parent %s because it is a child of itself.', $type, $name, $new_parent), LOG_WARNING, __FILE__, __LINE__);
452            return false;
453        }
454       
455        // Collect unique ids of all nodes being moved. The transversal numbers will become duplicated so these will be needed to identify these.
456        $qid = $db->query("
457            SELECT $primary_key
458            FROM $tbl
459            WHERE lft BETWEEN $lft AND $rgt
460            AND rgt BETWEEN $lft AND $rgt
461        ");
462        $ids = array();
463        while (list($id) = mysql_fetch_row($qid)) {
464            $ids[] = $id;
465        }
466
467        // Update transversal numbers for all nodes to the rgt of the node being moved, taking in to account the absence of it's children.
468        // This will temporarily "remove" the node from the tree, and its transversal values will be duplicated.
469        $db->query("UPDATE $tbl SET lft = lft - $total_transversal_value WHERE lft > $rgt");
470        $db->query("UPDATE $tbl SET rgt = rgt - $total_transversal_value WHERE rgt > $rgt");
471
472        // Apply transformation to new parent rgt also.
473        $new_parent_rgt = $new_parent_rgt > $rgt ? $new_parent_rgt - $total_transversal_value : $new_parent_rgt;
474       
475        // Update transversal values of moved node and children.
476        $db->query("
477            UPDATE $tbl SET
478                lft = lft - ($lft - $new_parent_rgt),
479                rgt = rgt - ($lft - $new_parent_rgt)
480            WHERE $primary_key IN ('" . join("','", $ids) . "')
481        ");
482
483        // Update transversal values of all nodes to the rgt of moved node.
484        $db->query("UPDATE $tbl SET lft = lft + $total_transversal_value WHERE lft >= $new_parent_rgt AND $primary_key NOT IN ('" . join("','", $ids) . "')");
485        $db->query("UPDATE $tbl SET rgt = rgt + $total_transversal_value WHERE rgt >= $new_parent_rgt AND $primary_key NOT IN ('" . join("','", $ids) . "')");
486
487        $app->logMsg(sprintf('Moved %s node %s to new parent %s.', $type, $name, $new_parent), LOG_INFO, __FILE__, __LINE__);
488        return true;
489    }
490   
491    // Alias functions for the different object types.
492    public function moveRequestObject($name, $new_parent=null)
493    {
494        return $this->move($name, $new_parent, 'aro');
495    }
496    public function moveControlObject($name, $new_parent=null)
497    {
498        return $this->move($name, $new_parent, 'aco');
499    }
500    public function moveXtraObject($name, $new_parent=null)
501    {
502        return $this->move($name, $new_parent, 'axo');
503    }
504   
505    /*
506    * Add an entry to the acl_tbl to allow (or deny) a truple with the specified
507    * ARO -> ACO -> AXO entry.
508    *
509    * @access   public
510    * @param    string|null $aro Identifier of an existing ARO object (or null to use root).
511    * @param    string|null $aco Identifier of an existing ACO object (or null to use root).
512    * @param    string|null $axo Identifier of an existing AXO object (or null to use root).
513    * @return   bool False on error, true on success.
514    * @author   Quinn Comendant <quinn@strangecode.com>
515    * @version  1.0
516    * @since    15 Jun 2006 01:58:48
517    */
518    public function grant($aro=null, $aco=null, $axo=null, $access='allow')
519    {
520        $app =& App::getInstance();
521        $db =& DB::getInstance();
522
523        $this->initDB();
524
525        // If any access objects are null, assume using root values.
526        // However if they're empty we don't want to escalate the grant command to root!
527        $aro = is_null($aro) ? 'root' : $aro;
528        $aco = is_null($aco) ? 'root' : $aco;
529        $axo = is_null($axo) ? 'root' : $axo;
530       
531        // Flush old cached values.
532        $cache_hash = $aro . '|' . $aco . '|' . $axo;
533        $this->cache->delete($cache_hash);
534
535        // Ensure values exist.
536        $qid = $db->query("SELECT aro_tbl.aro_id FROM aro_tbl WHERE aro_tbl.name = '" . $db->escapeString($aro) . "'");
537        if (!list($aro_id) = mysql_fetch_row($qid)) {
538            $app->logMsg(sprintf('Grant failed, aro_tbl.name = "%s" does not exist.', $aro), LOG_WARNING, __FILE__, __LINE__);
539            return false;
540        }
541        $qid = $db->query("SELECT aco_tbl.aco_id FROM aco_tbl WHERE aco_tbl.name = '" . $db->escapeString($aco) . "'");
542        if (!list($aco_id) = mysql_fetch_row($qid)) {
543            $app->logMsg(sprintf('Grant failed, aco_tbl.name = "%s" does not exist.', $aco), LOG_WARNING, __FILE__, __LINE__);
544            return false;
545        }
546        $qid = $db->query("SELECT axo_tbl.axo_id FROM axo_tbl WHERE axo_tbl.name = '" . $db->escapeString($axo) . "'");
547        if (!list($axo_id) = mysql_fetch_row($qid)) {
548            $app->logMsg(sprintf('Grant failed, axo_tbl.name = "%s" does not exist.', $axo), LOG_WARNING, __FILE__, __LINE__);
549            return false;
550        }
551
552        // Access must be 'allow' or 'deny'.
553        $allow = 'allow' == $access ? 'allow' : 'deny';
554       
555        $db->query("REPLACE INTO acl_tbl VALUES ('$aro_id', '$aco_id', '$axo_id', '$allow', NOW())");
556        $app->logMsg(sprintf('Set %s: %s -> %s -> %s.', $allow, $aro, $aco, $axo), LOG_INFO, __FILE__, __LINE__);
557       
558        return true;
559    }
560
561    /*
562    * Add an entry to the acl_tbl to deny a truple with the specified
563    * ARO -> ACO -> AXO entry. This calls the ACL::grant function to create the entry
564    * but uses 'deny' as the fourth argument.
565    *
566    * @access   public
567    * @param    string|null $aro Identifier of an existing ARO object (or null to use root).
568    * @param    string|null $aco Identifier of an existing ACO object (or null to use root).
569    * @param    string|null $axo Identifier of an existing AXO object (or null to use root).
570    * @return   bool False on error, true on success.
571    * @author   Quinn Comendant <quinn@strangecode.com>
572    * @version  1.0
573    * @since    15 Jun 2006 04:35:54
574    */
575    public function revoke($aro=null, $aco=null, $axo=null)
576    {
577        return $this->grant($aro, $aco, $axo, 'deny');
578    }
579   
580    /*
581    * Delete an entry from the acl_tbl completely to allow other permissions to cascade down.
582    * Null values act as a "wildcard" and will cause ALL matches in that column to be deleted.
583    *
584    * @access   public
585    * @param    string|null $aro Identifier of an existing ARO object (or null for *).
586    * @param    string|null $aco Identifier of an existing ACO object (or null for *).
587    * @param    string|null $axo Identifier of an existing AXO object (or null for *).
588    * @return   bool False on error, true on success.
589    * @author   Quinn Comendant <quinn@strangecode.com>
590    * @version  1.0
591    * @since    20 Jun 2006 20:16:12
592    */
593    public function delete($aro=null, $aco=null, $axo=null)
594    {
595        $app =& App::getInstance();
596        $db =& DB::getInstance();
597
598        $this->initDB();
599
600        // If any access objects are null, assume using root values.
601        // However if they're empty we don't want to escalate the grant command to root!
602        $where = array();
603        $where[] = is_null($aro) ? "aro_tbl.name IS NOT NULL" : "aro_tbl.name = '" . $db->escapeString($aro) . "' ";
604        $where[] = is_null($aco) ? "aco_tbl.name IS NOT NULL" : "aco_tbl.name = '" . $db->escapeString($aco) . "' ";
605        $where[] = is_null($axo) ? "axo_tbl.name IS NOT NULL" : "axo_tbl.name = '" . $db->escapeString($axo) . "' ";
606
607        // If any access objects are null, assume using root values.
608        // However if they're empty we don't want to escalate the grant command to root!
609        $aro = is_null($aro) ? 'root' : $aro;
610        $aco = is_null($aco) ? 'root' : $aco;
611        $axo = is_null($axo) ? 'root' : $axo;
612       
613        // Flush old cached values.
614        $cache_hash = $aro . '|' . $aco . '|' . $axo;
615        $this->cache->delete($cache_hash);
616
617        $final_where = join(' AND ', $where);
618        if (mb_substr_count($final_where, 'IS NOT NULL') == 3) {
619            // Null on all three tables will delete ALL entries including the root -> root -> root = deny.
620            $app->logMsg('Cannot allow deletion of ALL acl entries.', LOG_WARNING, __FILE__, __LINE__);
621            return false;
622        }
623       
624        $qid = $db->query("
625            DELETE acl_tbl
626            FROM acl_tbl
627            LEFT JOIN aro_tbl ON (acl_tbl.aro_id = aro_tbl.aro_id)
628            LEFT JOIN aco_tbl ON (acl_tbl.aco_id = aco_tbl.aco_id)
629            LEFT JOIN axo_tbl ON (acl_tbl.axo_id = axo_tbl.axo_id)
630            WHERE $final_where
631        ");
632
633        $app->logMsg(sprintf('Deleted %s acl_tbl links: %s -> %s -> %s', mysql_affected_rows($db->getDBH()), $aro, $aco, $axo), LOG_INFO, __FILE__, __LINE__);
634       
635        return true;
636    }
637   
638    /*
639    * Calculates the most specific cascading privilege found for a requested
640    * ARO -> ACO -> AXO entry. Returns FALSE if the entry is denied. By default,
641    * all entries are denied, unless some point in the hierarchy is set to "allow."
642    *
643    * @access   public
644    * @param    string $aro Identifier of an existing ARO object.
645    * @param    string $aco Identifier of an existing ACO object (or null to use root).
646    * @param    string $axo Identifier of an existing AXO object (or null to use root).
647    * @return   bool False if denied, true on allowed.
648    * @author   Quinn Comendant <quinn@strangecode.com>
649    * @version  1.0
650    * @since    15 Jun 2006 03:58:23
651    */
652    public function check($aro, $aco=null, $axo=null)
653    {
654        $app =& App::getInstance();
655        $db =& DB::getInstance();
656       
657        $this->initDB();
658
659        // If any access objects are null or empty, assume using root values.
660        $aro = is_null($aro) || '' == trim($aro) ? 'root' : $aro;
661        $aco = is_null($aco) || '' == trim($aco) ? 'root' : $aco;
662        $axo = is_null($axo) || '' == trim($axo) ? 'root' : $axo;
663       
664        $cache_hash = $aro . '|' . $aco . '|' . $axo;
665        if ($this->cache->exists($cache_hash) && true === $this->getParam('enable_cache')) {
666            // Access value is cached.
667            $access = $this->cache->get($cache_hash);
668        } else {
669            // Retrieve access value from db.
670            $qid = $db->query("
671                SELECT acl_tbl.access
672                FROM acl_tbl
673                LEFT JOIN aro_tbl ON (acl_tbl.aro_id = aro_tbl.aro_id)
674                LEFT JOIN aco_tbl ON (acl_tbl.aco_id = aco_tbl.aco_id)
675                LEFT JOIN axo_tbl ON (acl_tbl.axo_id = axo_tbl.axo_id)
676                WHERE (aro_tbl.lft <= (SELECT lft FROM aro_tbl WHERE name = '" . $db->escapeString($aro) . "') AND aro_tbl.rgt >= (SELECT rgt FROM aro_tbl WHERE name = '" . $db->escapeString($aro) . "'))
677                AND (aco_tbl.lft <= (SELECT lft FROM aco_tbl WHERE name = '" . $db->escapeString($aco) . "') AND aco_tbl.rgt >= (SELECT rgt FROM aco_tbl WHERE name = '" . $db->escapeString($aco) . "'))
678                AND (axo_tbl.lft <= (SELECT lft FROM axo_tbl WHERE name = '" . $db->escapeString($axo) . "') AND axo_tbl.rgt >= (SELECT rgt FROM axo_tbl WHERE name = '" . $db->escapeString($axo) . "'))
679                ORDER BY aro_tbl.lft DESC, aco_tbl.lft DESC, axo_tbl.lft DESC
680                LIMIT 1
681            ");
682            if (!list($access) = mysql_fetch_row($qid)) {
683                $this->cache->set($cache_hash, 'deny');
684                $app->logMsg(sprintf('Access denied: %s -> %s -> %s. No records found.', $aro, $aco, $axo), LOG_DEBUG, __FILE__, __LINE__);
685                return false;
686            }
687            $this->cache->set($cache_hash, $access);
688        }
689       
690        if ('allow' == $access) {
691            $app->logMsg(sprintf('Access granted: %s -> %s -> %s.', $aro, $aco, $axo), LOG_DEBUG, __FILE__, __LINE__);
692            return true;
693        } else {
694            $app->logMsg(sprintf('Access denied: %s -> %s -> %s.', $aro, $aco, $axo), LOG_DEBUG, __FILE__, __LINE__);
695            return false;
696        }
697    }
698
699} // End class.
700
701
Note: See TracBrowser for help on using the repository browser.