Defender é um package ACL para Laravel 5 que utiliza grupos e permissões. Com Segurança e Usabilidade em mente, este projeto tem como objetivo prover o controle de acesso da sua aplicação.
Estado Atual do Package
Estatísticas
Dicas
Defender pode ser instalado através do composer.
Para que o package seja adicionado automaticamente ao seu arquivo composer.json
execute o seguinte comando:
composer require artesaos/defender
ou se preferir, adicione o seguinte trecho manualmente:
{
"require": {
"artesaos/defender": "~0.6"
}
}
Para usar o Defender em sua aplicação Laravel, é necessário registrar o package no seu arquivo config/app.php
. Adicione o seguinte código no fim da seção providers
// file START ommited
'providers' => [
// other providers ommited
'Artesaos\Defender\Providers\DefenderServiceProvider',
],
// file END ommited
Na sua classe de usuário, adicione a trait Artesaos\Defender\Traits\HasDefender
para disponibilizar a criação de grupos e permissões:
<?php
namespace App;
use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Artesaos\Defender\Traits\HasDefender;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
class User extends Model implements AuthenticatableContract, CanResetPasswordContract
{
use Authenticatable, CanResetPassword, HasDefender;
Se você está utilizando o Laravel 5.2, há uma pequena diferença:
<?php
namespace App;
use Artesaos\Defender\Traits\HasDefender;
use Illuminate\Foundation\Auth\User as Authenticatable;
class User extends Authenticatable
{
use HasDefender;
...
Para publicar o arquivo de configuração padrão e as migrations que acompanham o package, execute o seguinte comando:
php artisan vendor:publish
Execute as migrations, para que sejam criadas as tabelas no banco de dados:
php artisan migrate
Você também pode publicar separadamente utilizando a flag --tag
php artisan vendor:publish --tag=config
Ou
php artisan vendor:publish --tag=migrations
Se você já publicou os arquivos, mas por algum motivo precisa sobrescrevê-los, adicione a flag --force
no final dos comandos anteriores.
Para usar a facade Defender
, você precisa registrá-la no seu arquivo config/app.php
adicionando o seguinte código na seção aliases
:
// config.php file
// file START ommited
'aliases' => [
// other Facades ommited
'Defender' => 'Artesaos\Defender\Facades\Defender',
],
// file END ommited
Caso você tenha a necessidade de realizar o controle de acesso diretamente nas rotas, o Defender possui alguns middlewares (nativos) que abordam os casos mais comuns. Para utilizá-los é necessário registrá-los no seu arquivo app/Http/Kernel.php
.
protected $routeMiddleware = [
'auth' => 'App\Http\Middleware\Authenticate',
'auth.basic' => 'Illuminate\Auth\Middleware\AuthenticateWithBasicAuth',
'guest' => 'App\Http\Middleware\RedirectIfAuthenticated',
// Controle de acesso usando permissões
'needsPermission' => 'Artesaos\Defender\Middlewares\NeedsPermissionMiddleware',
// Controle de acesso mais simples, utiliza apenas os grupos
'needsRole' => 'Artesaos\Defender\Middlewares\NeedsRoleMiddleware'
];
A utilização desses middlewares é explicada na próxima seção.
Caso os middlewares padrões do Defender não atendam as suas necessidades, você pode criar seu próprio middleware e utilizar a API do Defender para realizar o controle de acesso.
O Defender realiza apenas o controle de acesso em sua aplicação, ou seja, a tarefa de autenticação é realizada pelo Auth
que faz parte do core do Laravel.
Nota: Se você utilizar um model diferente para os usuários ou mudou o namespace, atualize a chave user_model
no seu arquivo de configuração do defender
O Defender lida apenas com o acesso à sua aplicação. A autenticação ainda é feita pelo Laravel Auth.
Para criar grupos e permissões estão disponíveis os seguintes comandos:
php artisan defender:make:role admin # creates the role admin
php artisan defender:make:role member --user=1 # creates the role admin and attaches this role to the user where id=1
php artisan defender:make:permission users.index "List all the users" # creates the permission
php artisan defender:make:permission users.create "Create user" --user=1 # creates the permission and attaches it to user where id=1
php artisan defender:make:permission users.destroy "Delete user" --role=admin # creates the permission and attaches it to the role admin
Para criar os grupos e as permissões para a sua aplicação, basta utilizar a API do defender. Você pode realizar esse processo em um seeder ou diretamente no php artisan tinker
por exemplo.
use App\User;
$grupoAdmin = Defender::createRole('admin');
// O primeiro parâmetro é o nome da permissão
// O segundo é a "versão amigável" desse nome. (geralmente para você mostrar ela na sua aplicação).
$permissaoCriarUsuario = Defender::createPermission('user.create', 'Criar usuários');
// Aqui eu posso atribuir essa permissão diretamente para um usuário
$user = User::find(1);
$user->attachPermission($permissaoCriarUsuario);
// ou posso adicionar o usuário a um grupo e esse grupo tem a regra de poder criar usuários
$grupoAdmin->attachPermission($permissaoCriarUsuario);
//Agora esse usuário está no grupo dos Administradores
$user->attachRole($grupoAdmin);
Para proteger suas rotas, você pode utilizar os middlewares padrões do Defender.
O Defender depende do Auth padrão do Laravel, portanto declare o middleware
auth
antes do middleware do Defender que você deseja usar.
Route::get('foo', ['middleware' => ['auth', 'needsPermission'], 'shield' => 'user.create', function()
{
return 'Sim eu posso!';
}]);
Você também pode passar um array de permissões a serem checadas.
Route::get('foo', ['middleware' => ['auth', 'needsPermission'], 'shield' => ['user.index', 'user.create'], function()
{
return 'Sim eu posso!';
}]);
Quando você passa um array de permissões, a rota é executada apenas se o usuário possui todas as permissões. Caso você queira que a rota execute quando o usuário tem pelo menos uma das permissões, basta adicionar 'any' => true
.
Route::get('foo', ['middleware' => ['auth', 'needsPermission'], 'shield' => ['user.index', 'user.create'], 'any' => true, function()
{
return 'Sim eu posso!';
}]);
Funciona de maneira semelhante ao middleware anterior, porém apenas os grupos são verificados, ou seja, não leva em consideração as permissões.
Route::get('foo', ['middleware' => ['auth', 'needsRole'], 'is' => 'admin', function()
{
return 'Sim eu sou!';
}]);
Você também pode passar um array de permissões a serem checadas.
Route::get('foo', ['middleware' => ['auth', 'needsRole'], 'shield' => ['admin', 'member'], function()
{
return 'Sim eu sou!';
}]);
Quando você passa um array de permissões, a rota é executada apenas se o usuário possui todas as permissões. Caso você queira que a rota execute quando o usuário tem pelo menos uma das permissões, basta adicionar 'any' => true
.
Route::get('foo', ['middleware' => ['auth', 'needsRole'], 'is' => ['admin', 'member'], 'any' => true, function()
{
return 'Sim eu sou!';
}]);
Extensões do Blade para facilitar o uso do defender.
@shield('user.index')
aqui mostra algo relacionado a essa permissão
@endshield
@shield('user.index')
aqui mostra algo relacionado ao usuário a essa permissão
@else
aqui mostra as informações pra quem não tem a permissão user.index
@endshield
@is('admin')
Mostra informações para o usuário logado e que esteja no grupo admin
@endis
@is('admin')
Mostra informações para o usuário logado e que esteja no grupo admin
@else
Aqui mostra um bloqueio ou qualquer coisa não relacionada ao grupo admin
@endis
Com a facade do defender você pode acessar a API e utilizá-la em qualquer parte de sua aplicação.
Verifica se o usuário logado possui a permissão $permission
.
Verifica se o usuário logado possui a permissão $permission
utilizando apenas os grupos.
Verifica se o usuário logado pertence ao grupo $roleName
.
Verifica se o grupo $roleName
existe no banco de dados.
Verifica se a permissão $permissionName
existe no banco de dados.
Busca no banco de dados o grupo de nome $roleName
.
Busca no banco de dados o grupo de ID roleId
.
Busca no banco de dados a permissão de nome $permissionName
.
Busca no banco de dados a permissão de ID $permissionId
.
Cria um novo grupo no banco de dados
Cria uma nova permissão no banco de dados.
Para adicionar as funcionalidades do Defender, é necessário adicionar trait HasDefender
no seu modelo de usuário (normalmente o App\User
).
<?php namespace App;
// Declaração dos outros namespaces omitida
use Artesaos\Defender\HasDefender;
class User extends Model implements AuthenticatableContract, CanResetPasswordContract {
use Authenticatable, CanResetPassword, HasDefender;
// Restante da classe
}
Esta trait, além de configurar os relacionamentos, adicionará os seguintes métodos no seu object App\User
:
#####public function hasPermission($permission)
:
Este método verifica se o usuário logado no sistema possui a permissão $permission
No Defender, existem 2 tipos de permissões: Permissões de Usuário
e Permissões de Grupo
. Por padrão as permissões que o usuário herda, são permissões dos grupos que ele pertence. Porém, sempre que uma permissão de usuário for definida, ela terá precedência sobre a permissão de grupo.
public function foo(Authenticable $user)
{
if ($user->hasPermission('user.create');
}
Este método funciona praticamente da mesma forma que o método anterior, a única diferença é que as permissões de usuário não são consideradas, ou seja, apenas as permissões dos grupos que o usuário pertence são usadas na hora de verificar a permissão.
public function foo(Authenticable $user)
{
if ($user->roleHasPermission('user.create');
}
Adiciona o usuário no grupo $role
. A variável $role
pode ser um objeto do tipo Artesaos\Defender\Role
ou um array de com os ids
dos grupos.
public function foo(Authenticable $user)
{
$role = Defender::findRole('admin'); // Retorna um Artesao\Defender\Role
$user->attachRole($role);
// ou
$roles = [1, 2, 3]; // Usando array de ids
$user->attachRole($roles);
}
Remove o grupo $role
do usuário (método inverso ao attachRole()
).
public function foo(Authenticable $user)
{
$role = Defender::findRole('admin'); // Retorna um Artesao\Defender\Role
$user->detachRole($role);
// ou
$roles = [1, 2, 3]; // Usando array de ids
$user->detachRole($roles);
}
Semelhante ao attachRole()
, porém apenas os grupos presentes no array $roles
estarão presentes no relacionamento após a execução deste método. $roles
é um array de ids
dos grupos desejados.
public function foo(Authenticable $user)
{
$roles = [1, 2, 3]; // Usando array de ids
$user->syncRoles($roles);
}
Vincula o usuário a permissão $permission
. A variável $permission
é uma instância da classe Artesaos\Defender\Permission
.
public function foo(Authenticable $user)
{
$permission = Defender::findPermission('user.create');
$user->attachPermission($permission, [
'value' => true // true = tem a permissão, false = não tem a permissão,
]);
}
Remove a permissão $permission
do usuário. A variável $permission
pode ser uma instância da classe Artesaos\Defender\Permission
ou um array de ids
com os ids das permissões a serem removidas.
public function foo(Authenticable $user)
{
$permission = Defender::findPermission('user.create');
$user->detachPermission($permission);
// ou
$permissions = [1, 3];
$user->detachPermission($permissions);
}
Semelhante ao método syncRoles
. Apenas as permissões presentes no array $permissions
farão parte do relacionamento após a execução deste método.
public function foo(Authenticable $user)
{
$permissions = [
1 => ['value' => false],
2 => ['value' => true],
3 => ['value' => true]
];
$user->syncPermissions($permissions);
}
Remove todas as permissões do usuário.
public function foo(Authenticable $user)
{
$user->revokePermissions();
}
Remove todas as permissões temporárias expiradas do usuário. Veja mais a respeito de permissões temporárias na próxima seção.
public function foo(Authenticable $user)
{
$user->revokeExpiredPermissions();
}
Um dos recursos mais interessantes do Defender é possibilidade de atribuir permissões temporárias a um grupo ou um usuário em questão.
O usuário X é grupo 'admins', porém eu desejo remover temporariamente o poder dele de criar novos usuários
Neste caso precisamos atribuir um permissão de usuário com o valor false
, explicitamente proibindo o usuário de executar aquela ação. É necessário adicionar esta permissão com o valor false
, já que por padrão as permissões de usuário herdam os valores das permissões de seus grupos. Ao atribuir uma permissão de usuário, esta sempre terá precedência.
No exemplo abaixo retiramos por 7 dias a permissão criar usuários (neste exemplo user.create
) do admin em questão.
public function foo()
{
$userX = App\User::find(3); // considere '3' a ID do usuário 'X'
$permission = Defender::findPermission('user.create');
$userX->attachPermission($permission, [
'value' => false, // false significa que ele não terá essa permissão,
'expires' => \Carbon\Carbon::now()->addDays(7) // Daqui a quanto tempo essa permissão irá expirar
]);
}
Após passados 7 dias, o usuário em questão terá a permissão restabelecida.
Permitir que um usuário realize determinada ação temporariamente.
Para permitir temporariamente que um usuário realize determinada ação, basta informar o valor do campo expires
. O campo value
é considerado true
por padrão.
public function foo()
{
$user = App\User::find(1);
$permission = Defender::findPermission('user.create');
$user->attachPermission($permission, [
'expires' => \Carbon\Carbon::now()->addDays(7)
];
}
Para utilizar suas próprias classes para os modelos Role e Permission, primeiramente defina os valores para as chaves role_model
e permission_model
no arquivo de configuração defender.php
.
A seguir dois exemplos de como devem ser implementados os modelos de Role e Permission para MongoDB usando o driver jenssegers/laravel-mongodb:
<?php
// Role model
namespace App;
use Jenssegers\Mongodb\Eloquent\Model;
use Artesaos\Defender\Traits\Models\Role;
use Artesaos\Defender\Contracts\Role as RoleInterface;
/**
* Class Role.
*/
class Role extends Model implements RoleInterface {
use Role;
}
<?php
// Permission model
namespace App;
use Jenssegers\Mongodb\Eloquent\Model;
use Artesaos\Defender\Traits\Models\Permission;
use Artesaos\Defender\Contracts\Permission as PermissionInterface;
/**
* Class Permission.
*/
class Permission extends Model implements PermissionInterface
{
use Permission;
}
Você deve utilizar os traits corretos e cada classe deve implementar o contrato de interface correspondente.