Page 1 of 1

Building upp a community ACL

Posted: Sat Apr 05, 2008 11:32 am
by anto91
Hiho all funny people out there :=)

I'm currently developing a community using php,mysql and zend framework (1.5.0RC1)

The thing is i dont know how to store the different user groups so far i have come this far

Note: i noticed i posted in wrong forum so if a moderator could move it :P
Acl_groups

Code: Select all

 
CREATE TABLE `acl_groups` (
  `id` tinyint(3) NOT NULL AUTO_INCREMENT,
  `parent_id` tinyint(3) NOT NULL DEFAULT '0',
  `groupname` varchar(35) character SET latin1 NOT NULL,
  `visible` tinyint(1) DEFAULT NULL,
  PRIMARY KEY  (`id`),
  UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 
 
Acl_permissions

Code: Select all

 
CREATE TABLE `acl_permissions` (
  `group_id` tinyint(3) NOT NULL,
  `key` varchar(35) NOT NULL,
  `permission` tinyint(1) NOT NULL,
  KEY `group_id` (`group_id`),
  CONSTRAINT `acl_permissions_ibfk_1` FOREIGN KEY (`group_id`) REFERENCES `acl_groups` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
Module/Group.php

Code: Select all

 
<?php
class Group implements Zend_Acl_Role_Interface {
    
    private $_data;
    
    public function __construct(stdClass $data) {
        $this->_data = $data;   
    }
    
    /**
     * @return boolean
     */
    public function isVisible() {
        return (boolean) $this->_data->visible;
    }
    
    /**
     * Implements Zend_Acl_Role_Interface
     */
    public function getRoleId() {
        return $this->__get('id');
    }
    
    /**
     * Magic php function
     */
    public function __get($key)
    {
        switch($key)
        {
            case 'id':
            case 'group_id':
                return $this->_data->id;
                break;
            
            case 'name':
            case 'groupname':
                return $this->_data->groupname;
                break;
            
            case 'parent_id':
                return $this->_data->parent_id;
                break;
            
            default:
                return null;
                break;
        }
    }
}
?>
 
./Module/Groups.php

Code: Select all

 
<?php
class Groups extends Zend_Acl {
    
    // String containg the current group
    private $group;
    
    // Array containing a reference name => id
    private $_groups = array();
    
    /**
     * @param String|Int
     */
    public function __construct($param) {
        $this->load();
        
        if(is_numeric($param)) {
            $group = $this->getRole($param)->name;
        } else {
            $group = $param;
        }
    }
    
    /**
     * TODO: clean it up
     */ 
    private function load() {
        $query = new Zend_Db_Select(Zend_Registry::get('db'));
        $query->from('acl_groups',array('groupname','visible','id','parent_id'));
        
        $groups = $query->query(Zend_Db::FETCH_OBJ)->fetchAll();
        
        foreach($groups as $group) {
            $group = new Group($group);
            $this->addRole($group, $group->parent_id);
            
            $this->_groups[strtolower($group->name)] = $group->id;
        }
        
        $query->reset();
        $query->from('acl_permissions');
        
        foreach($query->query()->fetchAll() as $permission) {
            
            $key = explode('.',$permission['key']);
            
            if(!$this->has($key[0])) {
                $this->add(new Zend_Acl_Resource($key[0]));
            }
            
            $this->allow($permission['group_id'], $key[0], $key[1]);
        }
    }
    
    /**
     *@return Boolean
     */
    public function isAllowed($key) {
        $key = explode('.', $key);
        return (boolean) parent::isAllowed($this->_groups[$this->group], $key[0], $key[1]);
    }
    
    /**
     * @param Boolean default false
     * @return Group|Null
     */
    public function getVisibleGroup($override = false) {
        
        $group = $this->getGroup();
        
        if(!$group->isVisible()) {
            while(!$group->isVisible()) {
                $group = $this->getRole($group->parent_id);
            }
        }
        
        if($override) {
            $this->setGroup($group->name);
        }
        
        return $group;
    }
    
    /**
     * @return Group
     */
    public function getGroup() {
        return $this->getRole($this->_groups[$this->group]);
    }
    
    /**
     * @param String
     */
    public function setGroup($group) {
        $this->group = $group;
    }
    
    #######################
    ### Magic functions ###
    ####################### 
 
    public function __get($key) {
        switch($key)
        {
            case 'group':
                return $this->getGroup();
                break;
            
            case 'id':
            case 'group_id':
                $this->id;
                break;
            
            default:
                return null;
        }
    }
    
    public function __set($key, $value) {
        switch($key)
        {
            case 'group':
                $this->setGroup($value);
                break;
        }
    }
}
?>
 
So what you think so far/ is there a better way to load the users?

Positivies
* Remote control over user groups.
* User group inheritans
* Invisible users

Negatives
* Every page load requires 2 queries to the database

Final question, is it possible to store my Groups object in a session so that i dont have to requery the database everytime i reload the page.

Re: Building upp a community ACL

Posted: Wed May 14, 2008 1:47 pm
by freeformer
I've made an ACL similar to yours for a site i've been working on.

I used Zend_Cache in order to prevent querying the database each time a request is made - it seems to work well.

In my case, users are added by administrators on the backend and this is the only time that the ACL will ever be changed, so setting the cached item to never expire and only recreated the ACL when a user's permissions are added/edited works a treat! :-)