Pregunta

Estoy luchando por implementar ACL en CakePHP.Después de leer la documentación en el manual de pastel Además de varios otros tutoriales, publicaciones de blogs, etc., encontré el excelente tutorial de Aran Johnson que me ha ayudado a llenar muchos de los vacíos.Sus ejemplos parecen entrar en conflicto con otros que he visto en algunos lugares, específicamente en la estructura de árbol ARO que utiliza.

En su ejemplos Sus grupos de usuarios se configuran como un árbol en cascada, con el tipo de usuario más general en la parte superior del árbol y sus hijos ramificándose para cada tipo de acceso más restringido.En otros lugares, normalmente he visto cada tipo de usuario como hijo del mismo tipo de usuario genérico.

¿Cómo configuras tus ARO y ACO en CakePHP?¡Se agradecen todos y cada uno de los consejos!

¿Fue útil?

Solución

El sistema ACL integrado de CakePHP es realmente poderoso, pero está mal documentado en términos de detalles de implementación reales.Un sistema que hemos utilizado con cierto éxito en varios proyectos basados ​​en CakePHP es el siguiente.

Es una modificación de algunos sistemas de acceso a nivel de grupo que han sido documentado en otro lugar.El objetivo de nuestro sistema es tener un sistema simple en el que los usuarios estén autorizados a nivel de grupo, pero puedan tener derechos adicionales específicos sobre elementos creados por ellos o por usuario.Queríamos evitar tener que crear una entrada específica para cada usuario (o, más específicamente, para cada ARO) en el aros_acos mesa.

Tenemos una tabla de Usuarios y una tabla de Roles.

Usuarios

user_id, user_name, role_id

Roles

id, role_name

Cree el árbol ARO para cada rol (generalmente tenemos 4 roles: Invitado no autorizado (id 1), Usuario autorizado (id 2), Moderador del sitio (id 3) y Administrador (id 4)):

cake acl create aro / Role.1

cake acl create aro 1 Role.2 ... etc ...

Después de esto, debes usar SQL o phpMyAdmin o similar para agregar alias para todos estos, ya que la herramienta de línea de comando cake no lo hace.Usamos 'Role-{id}' y 'User-{id}' para todos los nuestros.

Luego creamos una ACO RAÍZ -

cake acl create aco / 'ROOT'

y luego cree ACO para todos los controladores bajo este ROOT:

cake acl create aco 'ROOT' 'MyController' ... etc ...

Hasta ahora todo normal.Agregamos un campo adicional en la tabla aros_acos llamado _editown que podemos usar como acción adicional en el actionMap del componente ACL.

CREATE TABLE IF NOT EXISTS `aros_acos` (
`id` int(11) NOT NULL auto_increment,
`aro_id` int(11) default NULL,
`aco_id` int(11) default NULL,
`_create` int(11) NOT NULL default '0',
`_read` int(11) NOT NULL default '0',
`_update` int(11) NOT NULL default '0',
`_delete` int(11) NOT NULL default '0',
`_editown` int(11) NOT NULL default '0',
PRIMARY KEY  (`id`),
KEY `acl` (`aro_id`,`aco_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

Luego podemos configurar el componente Auth para usar el método 'crud', que valida el controlador/acción solicitado contra un AclComponent::check().En app_controller tenemos algo como:

private function setupAuth() {
    if(isset($this->Auth)) {
        ....
        $this->Auth->authorize = 'crud';
        $this->Auth->actionMap = array( 'index'     => 'read',
                        'add'       => 'create',
                        'edit'      => 'update'
                        'editMine'  => 'editown',
                        'view'      => 'read'
                        ... etc ...
                        );
        ... etc ...
    }
}

Nuevamente, esto es algo bastante estándar de CakePHP.Luego tenemos un método checkAccess en AppController que agrega elementos a nivel de grupo para verificar si se debe verificar el acceso de una ARO de grupo o de un usuario:

private function checkAccess() {
    if(!$user = $this->Auth->user()) {
        $role_alias = 'Role-1';
        $user_alias = null;
    } else {
        $role_alias = 'Role-' . $user['User']['role_id'];
        $user_alias = 'User-' . $user['User']['id'];
    }

    // do we have an aro for this user?
    if($user_alias && ($user_aro = $this->User->Aro->findByAlias($user_alias))) {
        $aro_alias = $user_alias;
    } else {
        $aro_alias = $role_alias;
    }

    if ('editown' == $this->Auth->actionMap[$this->action]) {
        if($this->Acl->check($aro_alias, $this->name, 'editown') and $this->isMine()) {
            $this->Auth->allow();
        } else {
            $this->Auth->authorize = 'controller';
            $this->Auth->deny('*');
        }
    } else {
        // check this user-level aro for access
        if($this->Acl->check($aro_alias, $this->name, $this->Auth->actionMap[$this->action])) {
            $this->Auth->allow();
        } else {
            $this->Auth->authorize = 'controller';
            $this->Auth->deny('*');
        }
    }
}

El setupAuth() y checkAccess() Los métodos se llaman en el AppController's beforeFilter() llamar de vuelta.Hay un isMine También hay un método en AppControler (ver más abajo) que simplemente verifica que el ID de usuario del elemento solicitado sea el mismo que el del usuario actualmente autenticado.He omitido esto para mayor claridad.

Eso es realmente todo lo que hay que hacer.Luego puede permitir/denegar el acceso de grupos particulares a acos específicos.

cake acl grant 'Role-2' 'MyController' 'read'

cake acl grant 'Role-2' 'MyController' 'editown'

cake acl deny 'Role-2' 'MyController' 'update'

cake acl deny 'Role-2' 'MyController' 'delete'

Estoy seguro de que te haces una idea.

De todos modos, esta respuesta es mucho más larga de lo que pretendía y probablemente no tenga casi ningún sentido, pero espero que te ayude...

-- editar --

Según lo solicitado, aquí hay una versión editada (solo para mayor claridad; hay muchas cosas en nuestro código repetitivo que no tienen sentido aquí) isMine() método que tenemos en nuestro AppController.También eliminé muchas cosas de verificación de errores, pero esta es la esencia:

function isMine($model=null, $id=null, $usermodel='User', $foreignkey='user_id') {
    if(empty($model)) {
        // default model is first item in $this->uses array
        $model = $this->uses[0];
    }

    if(empty($id)) {
        if(!empty($this->passedArgs['id'])) {
        $id = $this->passedArgs['id'];
        } elseif(!empty($this->passedArgs[0])) {
            $id = $this->passedArgs[0];
        }
    }

    if(is_array($id)) {
        foreach($id as $i) {
            if(!$this->_isMine($model, $i, $usermodel, $foreignkey)) {
                return false;
            }
        }

        return true;
    }

    return $this->_isMine($model, $id, $usermodel, $foreignkey);
}


function _isMine($model, $id, $usermodel='User', $foreignkey='user_id') {
    $user = Configure::read('curr.loggedinuser'); // this is set in the UsersController on successful login

    if(isset($this->$model)) {
        $model = $this->$model;
    } else {
        $model = ClassRegistry::init($model);
    }

    //read model
    if(!($record = $model->read(null, $id))) {
        return false;
    }

    //get foreign key
    if($usermodel == $model->alias) {
        if($record[$model->alias][$model->primaryKey] == $user['User']['id']) {
            return true;
        }
    } elseif($record[$model->alias][$foreignkey] == $user['User']['id']) {
        return true;
    }

    return false;
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top