Part of the EllisLab Network
   
1 of 10
1
Khaos :: KhACL
Posted: 29 January 2008 11:49 AM   [ Ignore ]  
Grad Student
Avatar
Rank
Total Posts:  77
Joined  01-24-2008

Updated - 31/03/2008 - Download - Khaos::KhACL - 0.1-alpha5

Introduction
If you are looking for a fast, easy to use and quick to install user auth library then this library is not for you. If how ever you have an existing user auth library or are looking to develop your own and need extremely fine grained control over your users actions then this library may be what you are looking for.

KhACL takes a very traditional approach to ACL akin to the phpgacl script where you have a core ACL class which manages ARO, ACO and AXO objects. Obviously discussing the background of this approach and how it all works is way beyond the scope of a forum post so im just going to give a basic run down of the main methods available for use within the library. - edit - as sophistry pointed out below the The phpgACL manual gives a reasonable overview.

Quick Reference

/*
* Helper
* -------
* bool kh_acl_check  ( string $aro, string $aco [, string $axo ] )
*
* Library
* -------
* KhACL
* bool allow  ( string $aro, string $aco [, string $axo ] )
* bool deny  ( string $aro, string $aco [, string $axo ] )
* bool check  ( string $aro, string $aco [, string $axo ] )
*
* KhACL->ARO
* bool create ( string $aro [, string $aro_parent [, int $link ]] )
* bool delete ( string $aro )
*
* KhACL->ACO
* bool create ( string $aco [, string $aco_parent [, int $link ]] )
* bool delete ( string $aco )
*
* KhACL->AXO
* bool create ( string $axo )
* bool delete ( string $axo )
*/

// Helper Examples
$allowed = kh_acl_check('neophyte', 'news', 'comment');

// Library Examples - KhACL
$this->khacl->allow('editors', 'news', 'publish');
$this->khacl->deny('anonymous', 'news', 'comment');
$allowed = $this->khacl->check('neophyte', 'news', 'comment');

// Library Examples - KhACL->ARO
$this->khacl->aro->create('neophyte', 'editors');
$this->khacl->aro->delete('neophyte');

// Library Examples - KhACL->ACO
$this->khacl->aco->create('news', 'modules');
$this->khacl->aco->delete('news');

// Examples - KhACL->AXO
$this->khacl->axo->create('publish');
$this->khacl->axo->delete('publish');

General Usage - Typically you should only add the helper to autoload for general page requests manually loading the library as and when you need to modify the ACL, this is to make checks as fast as possible.

AXO objects - As with phpgacl are completely optional however they are stored as a simple list so you cannot build up tree heirarchies of AXO objects like you can with the ARO and ACO objects (through use of a modified preorder tree traversal table schema). Personally i have never needed tree based heirarchies for the AXO objects even when i have needed extremely fine levels of control however if this did end up being a requested feature i could code it in.

Links - The link argument you see when creating ARO or ACO objects as you can see is completely optional and an idea i stole from cakephp which i think may come in handy for some people (myself included) where you can specify an ID to what this represents in your own database (such as a user_id) so if you wish to perform your own SQL queries you can join the relevent records.

Optional Dependencies
>= KhCache-0.3
If detected this will allow khacl to cache check results greatly increasing the speed of checks.

 Signature 

Khaos :: [ ACL - Cache - Event ] - Contact :: [ - IRC ]

Profile
 
 
Posted: 29 January 2008 12:42 PM   [ Ignore ]   [ # 1 ]  
Research Assistant
Avatar
RankRankRank
Total Posts:  547
Joined  03-08-2006

Hi Neophyte. This looks very interesting! Can’t wait to see a bit more of the flesh you mentioned and how it might be used in a small ‘real-world’ example application.

Thanks for sharing this library smile

 Signature 

Twitter | Flickr | Last.fm | Del.icio.us

Profile
 
 
Posted: 29 January 2008 01:35 PM   [ Ignore ]   [ # 2 ]  
Lab Technician
Avatar
RankRankRankRank
Total Posts:  1163
Joined  08-06-2006

I found the PDF manual for the phpgacl library to be helpful in understanding what Neophyte has contributed here.

The phpgACL manual

Nice work.

 Signature 

imap_pop get email | site_migrate port sites | OOCalendar | PhotoBox2 gallery | CI/EE 2 word_limiter, yep, wrote it

Profile
 
 
Posted: 30 January 2008 08:10 AM   [ Ignore ]   [ # 3 ]  
Grad Student
Avatar
Rank
Total Posts:  81
Joined  02-06-2007

good work smile
you saved me a lot of work

Profile
 
 
Posted: 30 January 2008 06:11 PM   [ Ignore ]   [ # 4 ]  
Sr. Research Associate
RankRankRankRankRank
Total Posts:  2634
Joined  06-10-2007

Great work Neophyte, You’re making some really nice contributions here.

 Signature 

URI Language Identifier | Modular Extensions - PHP5 | Modular Separation - PHP5 | Widget plugin | Access Control library

Profile
 
 
Posted: 30 January 2008 09:17 PM   [ Ignore ]   [ # 5 ]  
Summer Student
Total Posts:  18
Joined  10-07-2007

I found some bugs in the library when I tried to use it:

function map($aro_tree, $aco_tree)
    
{                
        $map        
= array(); // Holds final access map
        
$interested = array(); // Holds records for which we are interested in

        // Build the array of aro/aco pairs were interested in       
        
foreach ($aro_tree as $aro)
            foreach (
$aco_tree as $aco)
                
$interested[] = '('.$this->_Tables['access'].'.aro_id = '.$aro->id.' AND '.$this->_Tables['access'].'.aco_id = '.$aco->id.')';

// add - BEGIN
        
if (count($interested) == 0)
            return
false;
// add - END
[...]
            
// If there are any AXO build the extensions array
            
if ($rs_axo->num_rows(0) > 0)
            
{
// change next line
                
foreach ($rs_axo->result() as $axo_map)
function _set($aro, $aco, $axo = null, $allow = true)
[...]
        
/*
         * If needed create/modify the access -> action link in the access_actions table
         */
        
        
if ($axo !== null)
        
{
            
if (($rs = $this->_CI->db->query('SELECT id, allow FROM '.$this->_Tables['access_actions'].' WHERE access_id = ? AND axo_id = ? LIMIT 1', array($access_id, $axo_id))) !== false)
            
{
                
if ($rs->num_rows() === 0) // create link
                
{
// change next line
                    
if (!$this->_CI->db->query('INSERT INTO '.$this->_Tables['access_actions'].' (access_id, axo_id, allow) VALUES (?, ?, ?)', array($access_id, $axo_id, $allow)))
/**
     * Create ARO
     *
     * @param string $aro
     * @param string $parent
     * @param int    $link
     *
     * @return bool
     * @access public
     */
    
function create($aro, $parent = null, $link = null)
[...]
                
// Update all records past the left point by 2 to make room for the new ARO
                
$this->_CI->db->query('UPDATE '.$this->_Tables['aros'].' SET rgt = rgt + 2 WHERE rgt > '.$left);
                
$this->_CI->db->query('UPDATE '.$this->_Tables['aros'].' SET lft = lft + 2 WHERE lft > '.$left);
                
                
// Insert the record
// change next line
                
$this->_CI->db->query('INSERT INTO '.$this->_Tables['aros'].' (lft, rgt, name, link) VALUES ('.($left + 1).', '.($left + 2).', '.$this->_CI->db->escape($aro).', '.$link.')');
/**
     * Create ACO
     *
     * @param string $aco
     * @param string $parent
     * @param int    $link
     *
     * @return bool
     * @access public
     */
    
function create($aco, $parent = null, $link = null)
[...]
                
// Update all records past the left point by 2 to make room for the new ARO
                
$this->_CI->db->query('UPDATE '.$this->_Tables['acos'].' SET rgt = rgt + 2 WHERE rgt > '.$left);
                
$this->_CI->db->query('UPDATE '.$this->_Tables['acos'].' SET lft = lft + 2 WHERE lft > '.$left);
                
                
// Insert the record
// change next line
                
$this->_CI->db->query('INSERT INTO '.$this->_Tables['acos'].' (lft, rgt, name, link) VALUES ('.($left + 1).', '.($left + 2).', '.$this->_CI->db->escape($aco).', '.$link.')');
In your example you might want to change:
// Examples - KhACL->AXO
$this->khacl->aro->create('publish');
$this->khacl->aro->delete('publish');

into

// Examples - KhACL->AXO
$this->khacl->axo->create('publish');
$this->khacl->axo->delete('publish');
Profile
 
 
Posted: 31 January 2008 04:49 AM   [ Ignore ]   [ # 6 ]  
Grad Student
Avatar
Rank
Total Posts:  77
Joined  01-24-2008

ChangeLog - KhACL - 0.1-alpha2

Thanks to bardelot for picking up on these ones

Fixed - Updated khacl->map() to check interested array before continuing
Fixed - Bug in khacl->_set() which was using CI->query instead of CI->db->query
Fixed - Bug in khacl->aro->create() final insert was using right instead of rgt
Fixed - Bug in khacl->aco->create() final insert was using right instead of rgt
Fixed - Bug in khacl->check() extra paramater $axo is not needed when calling khacl->query()
Fixed - Bug in khacl->_set() which resulted in incorrect permissions being set

 Signature 

Khaos :: [ ACL - Cache - Event ] - Contact :: [ - IRC ]

Profile
 
 
Posted: 31 January 2008 10:14 AM   [ Ignore ]   [ # 7 ]  
Summer Student
Total Posts:  18
Joined  10-07-2007
/**
     * Check Access
     *
     * @param mixed $aro
     * @param mixed $aco
     * @param mixed $axo
     *
     * @return bool
     */
    
function check($aro, $aco, $axo = null)
    
{
        $result
= $this->query($aro, $aco, $axo);
[...]

In the last line $this->query there is an $axo argument but the query function misses exactly that argument. So it won’t look at the $axo and altough you might have access to a specific axo it will return “access = N”. So I added the $axo argument to the query function and changed the code where the result[‘access’] is assigned. I’m not sure if I haven’t broken anything, so you might want to check first.

/**
     * Query ARO Access
     *
     * Determines if the ARO has access to the ACO along with
     * any extra AXOs.
     *
     * Sample Response:
     *
     *   Array
     *   (
     *       [access] => Y
     *       [extensions] => Array
     *           (
     *               [publish] => N
     *               [create] => Y
     *               [delete] => N
     *           )
     *   )
     *
     * @param mixed $aro
     * @param mixed $aco
     *
     * @return array
     */
    
function query($aro, $aco, $axo)
    
{
[
...]
                    
if (($access['aro_id'] == $aro->id) && ($access['aco_id'] == $aco->id))
                    
{
                        
if ($axo != NULL && array_key_exists($axo, $access['extensions']))
                            
$result['access'] = $access['extensions'][$axo];
                        else    
                            
$result['access'] = $access['allow'];
                            
                        
$result['extensions'] = array_merge($result['extensions'], $access['extensions']);
                    
}
[
...]

Anyway, thanks for the library I really like it, you saved me lots of work.

Profile
 
 
Posted: 31 January 2008 11:03 AM   [ Ignore ]   [ # 8 ]  
Grad Student
Avatar
Rank
Total Posts:  77
Joined  01-24-2008

hey bardelot, adding $axo as an arg when calling khacl->query() from khacl->check() was a bug in the code.

khacl->query() just retrieves a full access array (this means it doesnt need to know the axo as it retrieves all of the AXO permissions assigned to the aro->aco relationship). its then upto khacl->check() to determine if the user should be allowed access or not as such your code will result in some odd behaviour under certain circumstances.

Just to clarify this ive restructured the check method to

function check($aro, $aco, $axo = null)
{
    $result
= $this->query($aro, $aco);
        
    
// ARO lacks any access - DENY
    
if ($result['access'] == 'N')
        return
false;
    
    if (
$axo === null)
    
{
        
/*
         * No AXO specified and we know the ARO has access to the ACO
         * from the above check. - ALLOW
         */
            
           
return true;            
    
}
    
else
    
{
        
/*
         * AXO specified and we know the ARO has access to the ACO so now we just
         * have to make sure the user also has access to the AXO.
         */
            
        
if ((isset($result['extensions'][$axo])) && ($result['extensions'][$axo] == 'Y'))
            return
true;  
        else
// AXO is set to deny - DENY
            
return false;         
    
}
        
    
// DENY    
    
return false;    
}

hopefuly this makes it a bit clearer

 Signature 

Khaos :: [ ACL - Cache - Event ] - Contact :: [ - IRC ]

Profile
 
 
Posted: 31 January 2008 11:33 AM   [ Ignore ]   [ # 9 ]  
Summer Student
Total Posts:  18
Joined  10-07-2007

Well the problem is I get the following result array.

Array
(
    
[access] => N
    [extensions]
=> Array
        (
            
[view] => Y
            [delete]
=> N
        
)

)

view is set to Y which is correct but access is N and so access gets denied. And I couldn’t yet find out why it is set to N.
This is how it is set up:

// There's an admin usergroup with the user admin_1 in it.
$this->khacl->aro->create('admin');
$this->khacl->aro->create('admin_1', 'admin');

// They manage the posts. Post 1 is the first post in that group
$this->khacl->aco->create('posts');
$this->khacl->aco->create('post_1', 'posts');

// It's possible to view or delete those posts
$this->khacl->axo->create('view');
$this->khacl->axo->create('delete');

// The admin group is allowed to view or delete them
$this->khacl->allow('admin', 'posts', 'view');
$this->khacl->allow('admin', 'posts', 'delete');

// admin_1 is not allowed to delete post_1
$this->khacl->deny('admin_1', 'post_1', 'delete');
// admin_1 is in the admin group and they are allowed to view the posts but it gets denied.
$this->khacl->check('admin_1', 'post_1', 'view')

Edit: Everything is fine as long as i do not use the deny function.

Profile
 
 
Posted: 31 January 2008 11:39 AM   [ Ignore ]   [ # 10 ]  
Grad Student
Avatar
Rank
Total Posts:  77
Joined  01-24-2008

access should be Y as you guessed, i’ll step over the code a bit later to see whats going on.

 Signature 

Khaos :: [ ACL - Cache - Event ] - Contact :: [ - IRC ]

Profile
 
 
   
1 of 10
1
 
Post Marker Legend
New Topic New posts Hot Topic Hot Topic with new posts New Poll New Poll Moved Topic Moved Topic Sticky Topic Sticky topic
Old Topic No new posts Hot Old Topic Hot Topic with no new posts Old Poll Old Poll Closed Topic Closed Topic Announcement Announcements
Theme
Change Theme
Visitor Statistics
The most visitors ever was 819, on March 11, 2010 11:15 AM
Total Registered Members: 120618 Total Logged-in Users: 65
Total Topics: 126651 Total Anonymous Users: 2
Total Replies: 665790 Total Guests: 539
Total Posts: 792441    
Members ( View Memberlist )