Pergunta

Estou lutando para implementar ACL no CakePHP.Depois de ler a documentação no manual de bolo bem como vários outros tutoriais, postagens de blog, etc., encontrei o excelente tutorial de Aran Johnson, que ajudou a preencher muitas lacunas.Seus exemplos parecem entrar em conflito com outros que já vi em alguns lugares - especificamente na estrutura de árvore ARO que ele usa.

No dele exemplos seus grupos de usuários são configurados como uma árvore em cascata, com o tipo de usuário mais geral no topo da árvore e seus filhos se ramificando para cada tipo de acesso mais restrito.Em outros lugares, normalmente vejo cada tipo de usuário como filho do mesmo tipo de usuário genérico.

Como você configura seus AROs e ACOs no CakePHP?Toda e qualquer dica será apreciada!

Foi útil?

Solução

O sistema ACL integrado do CakePHP é realmente poderoso, mas mal documentado em termos de detalhes reais de implementação.Um sistema que usamos com algum sucesso em vários projetos baseados no CakePHP é o seguinte.

É uma modificação de alguns sistemas de acesso em nível de grupo que foram documentado em outro lugar.O objetivo do nosso sistema é ter um sistema simples onde os usuários sejam autorizados em nível de grupo, mas possam ter direitos adicionais específicos sobre itens que foram criados por eles, ou por usuário.Queríamos evitar ter que criar uma entrada específica para cada usuário (ou, mais especificamente, para cada ARO) no aros_acos mesa.

Temos uma tabela Usuários e uma tabela Funções.

Usuários

user_id, user_name, role_id

Funções

id, role_name

Crie a árvore ARO para cada função (geralmente temos 4 funções - Convidado não autorizado (id 1), Usuário autorizado (id 2), Moderador do site (id 3) e Administrador (id 4)):

cake acl create aro / Role.1

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

Depois disso, você deve usar SQL ou phpMyAdmin ou similar para adicionar aliases para todos eles, já que a ferramenta de linha de comando cake não faz isso.Usamos 'Role-{id}' e 'User-{id}' para todos os nossos.

Em seguida, criamos um ROOT ACO -

cake acl create aco / 'ROOT'

e então crie ACOs para todos os controladores neste ROOT:

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

Até agora tudo normal.Adicionamos um campo adicional na tabela aros_acos chamado _editown que podemos usar como uma ação adicional no actionMap do 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;

Podemos então configurar o componente Auth para usar o método 'crud', que valida o controlador/ação solicitado em relação a um AclComponent::check().No app_controller temos 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 ...
    }
}

Novamente, isso é algo bastante padrão do CakePHP.Temos então um método checkAccess no AppController que adiciona itens em nível de grupo para verificar se é necessário verificar o acesso de um ARO de grupo ou de um ARO de usuário:

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('*');
        }
    }
}

O setupAuth() e checkAccess() métodos são chamados no AppControllerde beforeFilter() ligar de volta.Há uma isMine também no AppControler (veja abaixo) que apenas verifica se o user_id do item solicitado é o mesmo do usuário atualmente autenticado.Deixei isso de fora para maior clareza.

Isso é realmente tudo que existe para fazer.Você pode então permitir/negar a grupos específicos o acesso a ações específicas -

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'

Tenho certeza que você entendeu.

De qualquer forma, esta resposta é muito mais longa do que eu pretendia e provavelmente não faz quase nenhum sentido, mas espero que seja de alguma ajuda para você ...

--editar--

Conforme solicitado, aqui está uma edição (apenas para maior clareza - há muitas coisas em nosso código padrão que não fazem sentido aqui) isMine() método que temos em nosso AppController.Também removi muitas coisas de verificação de erros, mas esta é a essência:

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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top