Compare commits
9 Commits
Author | SHA1 | Date |
---|---|---|
![]() |
a050a9c222 | |
![]() |
434ee8003f | |
![]() |
11ffe28750 | |
![]() |
4d49aef1ab | |
![]() |
259a389595 | |
|
783c4017b0 | |
|
718f846e77 | |
|
124ab91abb | |
![]() |
ff3153e3f9 |
|
@ -27,73 +27,7 @@ jobs:
|
|||
# laravel: [ ]
|
||||
# stability: [ prefer-lowest, prefer-stable ]
|
||||
include:
|
||||
# Laravel 5.5
|
||||
- php: 7.1
|
||||
laravel: 5.5.*
|
||||
phpunit: ~6.0
|
||||
- php: 7.2
|
||||
laravel: 5.5.*
|
||||
phpunit: ~6.0
|
||||
- php: 7.3
|
||||
laravel: 5.5.*
|
||||
phpunit: ~6.0
|
||||
|
||||
# Laravel 5.6
|
||||
- php: 7.1
|
||||
laravel: 5.6.*
|
||||
phpunit: ~7.0
|
||||
- php: 7.2
|
||||
laravel: 5.6.*
|
||||
phpunit: ~7.0
|
||||
- php: 7.3
|
||||
laravel: 5.6.*
|
||||
phpunit: ~7.0
|
||||
|
||||
# Laravel 5.7
|
||||
- php: 7.1
|
||||
laravel: 5.7.*
|
||||
phpunit: ~7.5
|
||||
- php: 7.2
|
||||
laravel: 5.7.*
|
||||
phpunit: ~7.5
|
||||
- php: 7.3
|
||||
laravel: 5.7.*
|
||||
phpunit: ~7.5
|
||||
|
||||
# Laravel 5.8
|
||||
- php: 7.1
|
||||
laravel: 5.8.*
|
||||
phpunit: ~7.5
|
||||
- php: 7.2
|
||||
laravel: 5.8.*
|
||||
phpunit: ~8.0
|
||||
- php: 7.3
|
||||
laravel: 5.8.*
|
||||
phpunit: ~8.0
|
||||
|
||||
# Laravel 6.x
|
||||
- php: 7.2
|
||||
laravel: 6.*
|
||||
phpunit: ~8.0
|
||||
- php: 7.3
|
||||
laravel: 6.*
|
||||
phpunit: ~8.0
|
||||
|
||||
# Laravel 7.x
|
||||
- php: 7.3
|
||||
laravel: 7.*
|
||||
phpunit: ~9.0
|
||||
- php: 7.4
|
||||
laravel: 7.*
|
||||
phpunit: ~9.0
|
||||
|
||||
# Laravel 8.x
|
||||
- php: 7.3
|
||||
laravel: 8.*
|
||||
phpunit: ~9.0
|
||||
- php: 7.4
|
||||
laravel: 8.*
|
||||
phpunit: ~9.0
|
||||
- php: 8.0
|
||||
laravel: 8.*
|
||||
phpunit: ~9.0
|
||||
|
|
20
README.md
20
README.md
|
@ -8,7 +8,7 @@
|
|||
|
||||
<p align="center">
|
||||
<a href="https://github.com/php-casbin/laravel-authz/actions">
|
||||
<img src="https://github.com/php-casbin/laravel-authz/workflows/build/badge.svg?branch=master" alt="Build Status">
|
||||
<img src="https://github.com/php-casbin/laravel-authz/actions/workflows/build.yml/badge.svg?branch=master" alt="Build Status">
|
||||
</a>
|
||||
<a href="https://coveralls.io/github/php-casbin/laravel-authz">
|
||||
<img src="https://coveralls.io/repos/github/php-casbin/laravel-authz/badge.svg" alt="Coverage Status">
|
||||
|
@ -35,6 +35,7 @@ All you need to learn to use `Casbin` first.
|
|||
* [Using a middleware](#using-a-middleware)
|
||||
* [basic Enforcer Middleware](#basic-enforcer-middleware)
|
||||
* [HTTP Request Middleware ( RESTful is also supported )](#http-request-middleware--restful-is-also-supported-)
|
||||
* [Using Gates](#using-gates)
|
||||
* [Multiple enforcers](#multiple-enforcers)
|
||||
* [Using artisan commands](#using-artisan-commands)
|
||||
* [Cache](#using-cache)
|
||||
|
@ -105,7 +106,7 @@ use Enforcer;
|
|||
Enforcer::addPermissionForUser('eve', 'articles', 'read');
|
||||
// adds a role for a user.
|
||||
Enforcer::addRoleForUser('eve', 'writer');
|
||||
// adds permissions to a rule
|
||||
// adds permissions to a role
|
||||
Enforcer::addPolicy('writer', 'articles','edit');
|
||||
|
||||
```
|
||||
|
@ -277,6 +278,19 @@ Route::group(['middleware' => ['http_request']], function () {
|
|||
});
|
||||
```
|
||||
|
||||
### Using Gates
|
||||
|
||||
You can use Laravel Gates to check if a user has a permission, provided that you have set an existing user instance as the currently authenticated user.
|
||||
|
||||
```php
|
||||
$user->can('articles,read');
|
||||
// For multiple enforcers
|
||||
$user->can('articles,read', 'second');
|
||||
// The methods cant, cannot, canAny, etc. also work
|
||||
```
|
||||
|
||||
If you require custom Laravel Gates, you can disable the automatic registration by setting `enabled_register_at_gates` to `false` in the lauthz file. After that, you can use `Gates::before` or `Gates::after` in your ServiceProvider to register custom Gates. See [Gates](https://laravel.com/docs/11.x/authorization#gates) for more details.
|
||||
|
||||
### Multiple enforcers
|
||||
|
||||
If you need multiple permission controls in your project, you can configure multiple enforcers.
|
||||
|
@ -335,6 +349,8 @@ Adds a role for a user:
|
|||
|
||||
```bash
|
||||
php artisan role:assign eve writer
|
||||
# Specify the ptype of the role assignment by using the --ptype option.
|
||||
php artisan role:assign eve writer --ptype=g2
|
||||
```
|
||||
|
||||
### Using cache
|
||||
|
|
|
@ -20,18 +20,17 @@
|
|||
],
|
||||
"license": "Apache-2.0",
|
||||
"require": {
|
||||
"php": ">=7.1.0",
|
||||
"illuminate/support": "~5.5|~6.0|~7.0|~8.0|~9.0|~10.0|~11.0",
|
||||
"illuminate/database": "~5.5|~6.0|~7.0|~8.0|~9.0|~10.0|~11.0",
|
||||
"illuminate/console": "~5.5|~6.0|~7.0|~8.0|~9.0|~10.0|~11.0",
|
||||
"casbin/casbin": "~3.1",
|
||||
"casbin/psr3-bridge": "^1.1"
|
||||
"php": ">=8.0",
|
||||
"illuminate/support": "~8.0|~9.0|~10.0|~11.0",
|
||||
"illuminate/database": "~8.0|~9.0|~10.0|~11.0",
|
||||
"illuminate/console": "~8.0|~9.0|~10.0|~11.0",
|
||||
"casbin/casbin": "~4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~7.0|~8.0|~9.0|~10.5",
|
||||
"php-coveralls/php-coveralls": "^2.4",
|
||||
"phpunit/phpunit": "~9.0|~10.5",
|
||||
"php-coveralls/php-coveralls": "^2.7",
|
||||
"mockery/mockery": "^1.0",
|
||||
"laravel/laravel": "~5.5|~6.0|~7.0|~8.0|~9.0|~10.0|~11.0"
|
||||
"laravel/laravel": "~9.0|~10.0|~11.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
|
|
@ -6,17 +6,27 @@ return [
|
|||
*/
|
||||
'default' => 'basic',
|
||||
|
||||
/*
|
||||
* Lauthz Localizer
|
||||
*/
|
||||
'localizer' => [
|
||||
// changes whether enforcer will register at gates.
|
||||
'enabled_register_at_gates' => true
|
||||
],
|
||||
|
||||
'basic' => [
|
||||
/*
|
||||
* Casbin model setting.
|
||||
*/
|
||||
'model' => [
|
||||
// Available Settings: "file", "text"
|
||||
// Available Settings: "file", "text", "url"
|
||||
'config_type' => 'file',
|
||||
|
||||
'config_file_path' => __DIR__ . DIRECTORY_SEPARATOR . 'lauthz-rbac-model.conf',
|
||||
|
||||
'config_text' => '',
|
||||
|
||||
'config_url' => ''
|
||||
],
|
||||
|
||||
/*
|
||||
|
|
|
@ -17,7 +17,8 @@ class RoleAssign extends Command
|
|||
*/
|
||||
protected $signature = 'role:assign
|
||||
{user : the identifier of user}
|
||||
{role : the name of role}';
|
||||
{role : the name of role}
|
||||
{--ptype= : the ptype of role}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
|
@ -35,8 +36,9 @@ class RoleAssign extends Command
|
|||
{
|
||||
$user = $this->argument('user');
|
||||
$role = $this->argument('role');
|
||||
$ptype = $this->option('ptype') ?: 'g';
|
||||
|
||||
$ret = Enforcer::addRoleForUser($user, $role);
|
||||
$ret = Enforcer::addNamedGroupingPolicy($ptype, $user, $role);
|
||||
if ($ret) {
|
||||
$this->info('Added `'.$role.'` role to `'.$user.'` successfully');
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
namespace Lauthz\Contracts;
|
||||
|
||||
|
||||
use Casbin\Model\Model;
|
||||
|
||||
interface ModelLoader
|
||||
{
|
||||
/**
|
||||
* Loads model definitions into the provided model object.
|
||||
*
|
||||
* @param Model $model
|
||||
* @return void
|
||||
*/
|
||||
function loadModel(Model $model): void;
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
namespace Lauthz;
|
||||
|
||||
use Illuminate\Contracts\Auth\Access\Authorizable;
|
||||
use Illuminate\Contracts\Auth\Access\Gate;
|
||||
use Lauthz\Facades\Enforcer;
|
||||
|
||||
class EnforcerLocalizer
|
||||
{
|
||||
/**
|
||||
* The application instance.
|
||||
*
|
||||
* @var \Illuminate\Foundation\Application
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* Create a new localizer instance.
|
||||
*
|
||||
* @param \Illuminate\Foundation\Application $app
|
||||
*/
|
||||
public function __construct($app)
|
||||
{
|
||||
$this->app = $app;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the localizer based on the configuration.
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
if ($this->app->config->get('lauthz.localizer.enabled_register_at_gates')) {
|
||||
$this->registerAtGate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the localizer at the gate.
|
||||
*/
|
||||
protected function registerAtGate()
|
||||
{
|
||||
$this->app->make(Gate::class)->before(function (Authorizable $user, string $ability, array $guards) {
|
||||
/** @var \Illuminate\Contracts\Auth\Authenticatable $user */
|
||||
$identifier = $user->getAuthIdentifier();
|
||||
if (method_exists($user, 'getAuthzIdentifier')) {
|
||||
/** @var \Lauthz\Tests\Models\User $user */
|
||||
$identifier = $user->getAuthzIdentifier();
|
||||
}
|
||||
$identifier = strval($identifier);
|
||||
$ability = explode(',', $ability);
|
||||
if (empty($guards)) {
|
||||
return Enforcer::enforce($identifier, ...$ability);
|
||||
}
|
||||
|
||||
foreach ($guards as $guard) {
|
||||
return Enforcer::guard($guard)->enforce($identifier, ...$ability);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -2,14 +2,15 @@
|
|||
|
||||
namespace Lauthz;
|
||||
|
||||
use Casbin\Bridge\Logger\LoggerBridge;
|
||||
use Casbin\Enforcer;
|
||||
use Casbin\Model\Model;
|
||||
use Casbin\Log\Log;
|
||||
use Casbin\Log\Logger\DefaultLogger;
|
||||
use Lauthz\Contracts\Factory;
|
||||
use Lauthz\Models\Rule;
|
||||
use Illuminate\Support\Arr;
|
||||
use InvalidArgumentException;
|
||||
use Lauthz\Loaders\ModelLoaderManager;
|
||||
|
||||
/**
|
||||
* @mixin \Casbin\Enforcer
|
||||
|
@ -79,19 +80,17 @@ class EnforcerManager implements Factory
|
|||
|
||||
if ($logger = Arr::get($config, 'log.logger')) {
|
||||
if (is_string($logger)) {
|
||||
$logger = $this->app->make($logger);
|
||||
$logger = new DefaultLogger($this->app->make($logger));
|
||||
}
|
||||
|
||||
Log::setLogger(new LoggerBridge($logger));
|
||||
Log::setLogger($logger);
|
||||
}
|
||||
|
||||
$model = new Model();
|
||||
$configType = Arr::get($config, 'model.config_type');
|
||||
if ('file' == $configType) {
|
||||
$model->loadModel(Arr::get($config, 'model.config_file_path', ''));
|
||||
} elseif ('text' == $configType) {
|
||||
$model->loadModelFromText(Arr::get($config, 'model.config_text', ''));
|
||||
}
|
||||
$loader = $this->app->make(ModelLoaderManager::class);
|
||||
$loader->initFromConfig($config);
|
||||
$loader->loadModel($model);
|
||||
|
||||
$adapter = Arr::get($config, 'adapter');
|
||||
if (!is_null($adapter)) {
|
||||
$adapter = $this->app->make($adapter, [
|
||||
|
@ -99,7 +98,7 @@ class EnforcerManager implements Factory
|
|||
]);
|
||||
}
|
||||
|
||||
return new Enforcer($model, $adapter, Arr::get($config, 'log.enabled', false));
|
||||
return new Enforcer($model, $adapter, $logger, Arr::get($config, 'log.enabled', false));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
namespace Lauthz;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Lauthz\EnforcerLocalizer;
|
||||
use Lauthz\Loaders\ModelLoaderManager;
|
||||
use Lauthz\Models\Rule;
|
||||
use Lauthz\Observers\RuleObserver;
|
||||
|
||||
|
@ -30,6 +32,8 @@ class LauthzServiceProvider extends ServiceProvider
|
|||
$this->mergeConfigFrom(__DIR__ . '/../config/lauthz.php', 'lauthz');
|
||||
|
||||
$this->bootObserver();
|
||||
|
||||
$this->registerLocalizer();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,5 +54,23 @@ class LauthzServiceProvider extends ServiceProvider
|
|||
$this->app->singleton('enforcer', function ($app) {
|
||||
return new EnforcerManager($app);
|
||||
});
|
||||
|
||||
$this->app->singleton(ModelLoaderManager::class, function ($app) {
|
||||
return new ModelLoaderManager($app);
|
||||
});
|
||||
|
||||
$this->app->singleton(EnforcerLocalizer::class, function ($app) {
|
||||
return new EnforcerLocalizer($app);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a gate that allows users to use Laravel's built-in Gate to call Enforcer.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function registerLocalizer()
|
||||
{
|
||||
$this->app->make(EnforcerLocalizer::class)->register();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace Lauthz\Loaders;
|
||||
|
||||
use Casbin\Model\Model;
|
||||
use Illuminate\Support\Arr;
|
||||
use Lauthz\Contracts\ModelLoader;
|
||||
|
||||
class FileLoader implements ModelLoader
|
||||
{
|
||||
/**
|
||||
* The path to the model file.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $filePath;
|
||||
|
||||
/**
|
||||
* Constructor to initialize the file path.
|
||||
*
|
||||
* @param array $config
|
||||
*/
|
||||
public function __construct(array $config)
|
||||
{
|
||||
$this->filePath = Arr::get($config, 'model.config_file_path', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads model from file.
|
||||
*
|
||||
* @param Model $model
|
||||
* @return void
|
||||
* @throws \Casbin\Exceptions\CasbinException
|
||||
*/
|
||||
public function loadModel(Model $model): void
|
||||
{
|
||||
$model->loadModel($this->filePath);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
<?php
|
||||
|
||||
namespace Lauthz\Loaders;
|
||||
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Support\Manager;
|
||||
use InvalidArgumentException;
|
||||
|
||||
/**
|
||||
* The model loader manager.
|
||||
*
|
||||
* A model loader is responsible for a loading model from an arbitrary source.
|
||||
* Developers can customize loading behavior by implementing
|
||||
* and register the custom loader in AppServiceProvider through `app(LoaderManager::class)->extend()`.
|
||||
*
|
||||
* Built-in loader implementations include:
|
||||
* - FileLoader: For loading model from file.
|
||||
* - TextLoader: Suitable for model defined as a multi-line string.
|
||||
* - UrlLoader: Handles model loading from URL.
|
||||
*
|
||||
* To utilize a built-in or custom loader, set 'model.config_type' in the configuration to match one of the above types.
|
||||
*/
|
||||
class ModelLoaderManager extends Manager
|
||||
{
|
||||
|
||||
/**
|
||||
* The array of the lauthz driver configuration.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* Initialize configuration for the loader manager instance.
|
||||
*
|
||||
* @param array $config the lauthz driver configuration.
|
||||
*/
|
||||
public function initFromConfig(array $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default driver from the configuration.
|
||||
*
|
||||
* @return string The default driver name.
|
||||
*/
|
||||
public function getDefaultDriver()
|
||||
{
|
||||
return Arr::get($this->config, 'model.config_type', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new TextLoader instance.
|
||||
*
|
||||
* @return TextLoader
|
||||
*/
|
||||
public function createTextDriver()
|
||||
{
|
||||
return new TextLoader($this->config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new UrlLoader instance.
|
||||
*
|
||||
* @return UrlLoader
|
||||
*/
|
||||
public function createUrlDriver()
|
||||
{
|
||||
return new UrlLoader($this->config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new FileLoader instance.
|
||||
*
|
||||
* @return FileLoader
|
||||
*/
|
||||
public function createFileDriver()
|
||||
{
|
||||
return new FileLoader($this->config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new driver instance.
|
||||
*
|
||||
* @param string $driver
|
||||
* @return mixed
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
protected function createDriver($driver)
|
||||
{
|
||||
if(empty($driver)) {
|
||||
throw new InvalidArgumentException('Unsupported empty model loader type.');
|
||||
}
|
||||
|
||||
if (isset($this->customCreators[$driver])) {
|
||||
return $this->callCustomCreator($driver);
|
||||
}
|
||||
$method = 'create' . Str::studly($driver) . 'Driver';
|
||||
if (method_exists($this, $method)) {
|
||||
return $this->$method();
|
||||
}
|
||||
|
||||
throw new InvalidArgumentException("Unsupported model loader type: {$driver}.");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace Lauthz\Loaders;
|
||||
|
||||
use Casbin\Model\Model;
|
||||
use Illuminate\Support\Arr;
|
||||
use Lauthz\Contracts\ModelLoader;
|
||||
|
||||
class TextLoader implements ModelLoader
|
||||
{
|
||||
/**
|
||||
* Model text.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $text;
|
||||
|
||||
/**
|
||||
* Constructor to initialize the model text.
|
||||
*
|
||||
* @param array $config
|
||||
*/
|
||||
public function __construct(array $config)
|
||||
{
|
||||
$this->text = Arr::get($config, 'model.config_text', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads model from text.
|
||||
*
|
||||
* @param Model $model
|
||||
* @return void
|
||||
* @throws \Casbin\Exceptions\CasbinException
|
||||
*/
|
||||
public function loadModel(Model $model): void
|
||||
{
|
||||
$model->loadModelFromText($this->text);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
<?php
|
||||
|
||||
namespace Lauthz\Loaders;
|
||||
|
||||
use Casbin\Model\Model;
|
||||
use Illuminate\Support\Arr;
|
||||
use Lauthz\Contracts\ModelLoader;
|
||||
use RuntimeException;
|
||||
|
||||
class UrlLoader implements ModelLoader
|
||||
{
|
||||
/**
|
||||
* The url to fetch the remote model string.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $url;
|
||||
|
||||
/**
|
||||
* Constructor to initialize the url path.
|
||||
*
|
||||
* @param array $config
|
||||
*/
|
||||
public function __construct(array $config)
|
||||
{
|
||||
$this->url = Arr::get($config, 'model.config_url', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads model from remote url.
|
||||
*
|
||||
* @param Model $model
|
||||
* @return void
|
||||
* @throws \Casbin\Exceptions\CasbinException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function loadModel(Model $model): void
|
||||
{
|
||||
$contextOptions = [
|
||||
'http' => [
|
||||
'method' => 'GET',
|
||||
'header' => "Accept: text/plain\r\n",
|
||||
'timeout' => 3
|
||||
]
|
||||
];
|
||||
|
||||
$context = stream_context_create($contextOptions);
|
||||
$response = @file_get_contents($this->url, false, $context);
|
||||
if ($response === false) {
|
||||
$error = error_get_last();
|
||||
throw new RuntimeException(
|
||||
"Failed to fetch remote model " . $this->url . ": " . $error['message']
|
||||
);
|
||||
}
|
||||
|
||||
$model->loadModelFromText($response);
|
||||
}
|
||||
}
|
|
@ -31,6 +31,7 @@ class EnforcerMiddleware
|
|||
$user = Auth::user();
|
||||
$identifier = $user->getAuthIdentifier();
|
||||
if (method_exists($user, 'getAuthzIdentifier')) {
|
||||
/** @var \Lauthz\Tests\Models\User $user */
|
||||
$identifier = $user->getAuthzIdentifier();
|
||||
}
|
||||
$identifier = strval($identifier);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Lauthz\Tests\Commands;
|
||||
|
||||
use Casbin\Model\Model;
|
||||
use Lauthz\Facades\Enforcer;
|
||||
use Lauthz\Tests\TestCase;
|
||||
use Illuminate\Foundation\Testing\DatabaseMigrations;
|
||||
|
@ -19,5 +20,21 @@ class RoleAssignTest extends TestCase
|
|||
$exitCode = Artisan::call('role:assign', ['user' => 'eve', 'role' => 'writer']);
|
||||
$this->assertFalse(0 === $exitCode);
|
||||
$this->assertTrue(Enforcer::hasRoleForUser('eve', 'writer'));
|
||||
|
||||
$model = Model::newModel();
|
||||
$model->addDef('r', 'r', 'sub, obj, act');
|
||||
$model->addDef('p', 'p', 'sub, obj, act');
|
||||
$model->addDef('g', 'g', '_, _');
|
||||
$model->addDef('g', 'g2', '_, _');
|
||||
$model->addDef('e', 'e', 'some(where (p.eft == allow))');
|
||||
$model->addDef('m', 'm', 'g(r.sub, p.sub) && g2(r.obj, p.obj) && r.act == p.act');
|
||||
Enforcer::setModel($model);
|
||||
Enforcer::loadPolicy();
|
||||
$this->assertFalse(Enforcer::hasNamedGroupingPolicy('g2', 'eve', 'writer'));
|
||||
$exitCode = Artisan::call('role:assign', ['user' => 'eve', 'role' => 'writer', '--ptype' => 'g2']);
|
||||
$this->assertTrue(0 === $exitCode);
|
||||
$exitCode = Artisan::call('role:assign', ['user' => 'eve', 'role' => 'writer', '--ptype' => 'g2']);
|
||||
$this->assertFalse(0 === $exitCode);
|
||||
$this->assertTrue(Enforcer::hasNamedGroupingPolicy('g2', 'eve', 'writer'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,11 +2,9 @@
|
|||
|
||||
namespace Lauthz\Tests;
|
||||
|
||||
use Enforcer;
|
||||
use Lauthz\Models\Rule;
|
||||
use Illuminate\Foundation\Testing\DatabaseMigrations;
|
||||
use Casbin\Persist\Adapters\Filter;
|
||||
use Casbin\Exceptions\InvalidFilterTypeException;
|
||||
use Lauthz\Facades\Enforcer;
|
||||
|
||||
class DatabaseAdapterForCacheTest extends TestCase
|
||||
{
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
namespace Lauthz\Tests;
|
||||
|
||||
use Enforcer;
|
||||
use Illuminate\Foundation\Testing\DatabaseMigrations;
|
||||
use Casbin\Persist\Adapters\Filter;
|
||||
use Casbin\Exceptions\InvalidFilterTypeException;
|
||||
use Lauthz\Facades\Enforcer;
|
||||
|
||||
class DatabaseAdapterTest extends TestCase
|
||||
{
|
||||
|
@ -309,7 +309,7 @@ class DatabaseAdapterTest extends TestCase
|
|||
$this->assertEquals([
|
||||
['bob', 'data2', 'write']
|
||||
], Enforcer::getPolicy());
|
||||
|
||||
|
||||
// Filter
|
||||
$filter = new Filter(['v2'], ['read']);
|
||||
Enforcer::loadFilteredPolicy($filter);
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Contracts\Auth\Access\Gate;
|
||||
use Illuminate\Foundation\Testing\DatabaseMigrations;
|
||||
use Lauthz\Tests\TestCase;
|
||||
|
||||
class EnforcerCustomLocalizerTest extends TestCase
|
||||
{
|
||||
use DatabaseMigrations;
|
||||
|
||||
public function testCustomRegisterAtGatesBefore()
|
||||
{
|
||||
$user = $this->user("alice");
|
||||
$this->assertFalse($user->can('data3,read'));
|
||||
|
||||
app(Gate::class)->before(function () {
|
||||
return true;
|
||||
});
|
||||
|
||||
$this->assertTrue($user->can('data3,read'));
|
||||
}
|
||||
|
||||
public function testCustomRegisterAtGatesDefine()
|
||||
{
|
||||
$user = $this->user("alice");
|
||||
$this->assertFalse($user->can('data3,read'));
|
||||
|
||||
app(Gate::class)->define('data3,read', function () {
|
||||
return true;
|
||||
});
|
||||
|
||||
$this->assertTrue($user->can('data3,read'));
|
||||
}
|
||||
|
||||
public function initConfig()
|
||||
{
|
||||
parent::initConfig();
|
||||
$this->app['config']->set('lauthz.localizer.enabled_register_at_gates', false);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Contracts\Auth\Access\Gate;
|
||||
use Illuminate\Foundation\Testing\DatabaseMigrations;
|
||||
use Lauthz\Facades\Enforcer;
|
||||
use Lauthz\Tests\TestCase;
|
||||
|
||||
class EnforcerLocalizerTest extends TestCase
|
||||
{
|
||||
use DatabaseMigrations;
|
||||
|
||||
public function testRegisterAtGates()
|
||||
{
|
||||
$user = $this->user('alice');
|
||||
$this->assertTrue($user->can('data1,read'));
|
||||
$this->assertFalse($user->can('data1,write'));
|
||||
$this->assertFalse($user->cannot('data2,read'));
|
||||
|
||||
Enforcer::guard('second')->addPolicy('alice', 'data1', 'read');
|
||||
$this->assertTrue($user->can('data1,read', 'second'));
|
||||
$this->assertFalse($user->can('data3,read', 'second'));
|
||||
}
|
||||
|
||||
public function testNotLogin()
|
||||
{
|
||||
$this->assertFalse(app(Gate::class)->allows('data1,read'));
|
||||
$this->assertTrue(app(Gate::class)->forUser($this->user('alice'))->allows('data1,read'));
|
||||
$this->assertFalse(app(Gate::class)->forUser($this->user('bob'))->allows('data1,read'));
|
||||
}
|
||||
|
||||
public function testAfterLogin()
|
||||
{
|
||||
$this->login('alice');
|
||||
$this->assertTrue(app(Gate::class)->allows('data1,read'));
|
||||
$this->assertTrue(app(Gate::class)->allows('data2,read'));
|
||||
$this->assertTrue(app(Gate::class)->allows('data2,write'));
|
||||
|
||||
$this->login('bob');
|
||||
$this->assertFalse(app(Gate::class)->allows('data1,read'));
|
||||
$this->assertTrue(app(Gate::class)->allows('data2,write'));
|
||||
}
|
||||
|
||||
public function initConfig()
|
||||
{
|
||||
parent::initConfig();
|
||||
$this->app['config']->set('lauthz.second.model.config_type', 'text');
|
||||
$this->app['config']->set(
|
||||
'lauthz.second.model.config_text',
|
||||
$this->getModelText()
|
||||
);
|
||||
}
|
||||
|
||||
protected function getModelText(): string
|
||||
{
|
||||
return <<<EOT
|
||||
[request_definition]
|
||||
r = sub, obj, act
|
||||
|
||||
[policy_definition]
|
||||
p = sub, obj, act
|
||||
|
||||
[policy_effect]
|
||||
e = some(where (p.eft == allow))
|
||||
|
||||
[matchers]
|
||||
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
|
||||
EOT;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
<?php
|
||||
|
||||
namespace Lauthz\Tests;
|
||||
|
||||
use Lauthz\Facades\Enforcer;
|
||||
use Lauthz\Loaders\ModelLoaderManager;
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
|
||||
|
||||
class ModelLoaderTest extends TestCase
|
||||
{
|
||||
public function testUrlLoader(): void
|
||||
{
|
||||
$this->initUrlConfig();
|
||||
|
||||
$this->assertFalse(Enforcer::enforce('alice', 'data', 'read'));
|
||||
|
||||
Enforcer::addPolicy('data_admin', 'data', 'read');
|
||||
Enforcer::addRoleForUser('alice', 'data_admin');
|
||||
$this->assertTrue(Enforcer::enforce('alice', 'data', 'read'));
|
||||
}
|
||||
|
||||
public function testTextLoader(): void
|
||||
{
|
||||
$this->initTextConfig();
|
||||
|
||||
Enforcer::addPolicy('data_admin', 'data', 'read');
|
||||
$this->assertFalse(Enforcer::enforce('alice', 'data', 'read'));
|
||||
$this->assertTrue(Enforcer::enforce('data_admin', 'data', 'read'));
|
||||
}
|
||||
|
||||
public function testFileLoader(): void
|
||||
{
|
||||
$this->assertFalse(Enforcer::enforce('alice', 'data', 'read'));
|
||||
|
||||
Enforcer::addPolicy('data_admin', 'data', 'read');
|
||||
Enforcer::addRoleForUser('alice', 'data_admin');
|
||||
$this->assertTrue(Enforcer::enforce('alice', 'data', 'read'));
|
||||
}
|
||||
|
||||
public function testCustomLoader(): void
|
||||
{
|
||||
$this->initCustomConfig();
|
||||
Enforcer::guard('second')->addPolicy('data_admin', 'data', 'read');
|
||||
$this->assertFalse(Enforcer::guard('second')->enforce('alice', 'data', 'read'));
|
||||
$this->assertTrue(Enforcer::guard('second')->enforce('data_admin', 'data', 'read'));
|
||||
}
|
||||
|
||||
public function testMultipleLoader(): void
|
||||
{
|
||||
$this->testFileLoader();
|
||||
$this->testCustomLoader();
|
||||
}
|
||||
|
||||
public function testEmptyModel(): void
|
||||
{
|
||||
Enforcer::shouldUse('third');
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->assertFalse(Enforcer::enforce('alice', 'data', 'read'));
|
||||
}
|
||||
|
||||
public function testEmptyLoaderType(): void
|
||||
{
|
||||
$this->app['config']->set('lauthz.basic.model.config_type', '');
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
|
||||
$this->assertFalse(Enforcer::enforce('alice', 'data', 'read'));
|
||||
}
|
||||
|
||||
public function testNotExistLoaderType(): void
|
||||
{
|
||||
$this->app['config']->set('lauthz.basic.model.config_type', 'not_exist');
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
|
||||
$this->assertFalse(Enforcer::enforce('alice', 'data', 'read'));
|
||||
}
|
||||
|
||||
public function testBadUrlConnection(): void
|
||||
{
|
||||
$this->initUrlConfig();
|
||||
$this->app['config']->set('lauthz.basic.model.config_url', 'http://filenoexists');
|
||||
$this->expectException(RuntimeException::class);
|
||||
|
||||
$this->assertFalse(Enforcer::enforce('alice', 'data', 'read'));
|
||||
}
|
||||
|
||||
protected function initUrlConfig(): void
|
||||
{
|
||||
$this->app['config']->set('lauthz.basic.model.config_type', 'url');
|
||||
$this->app['config']->set(
|
||||
'lauthz.basic.model.config_url',
|
||||
'https://raw.githubusercontent.com/casbin/casbin/master/examples/rbac_model.conf'
|
||||
);
|
||||
}
|
||||
|
||||
protected function initTextConfig(): void
|
||||
{
|
||||
$this->app['config']->set('lauthz.basic.model.config_type', 'text');
|
||||
$this->app['config']->set(
|
||||
'lauthz.basic.model.config_text',
|
||||
$this->getModelText()
|
||||
);
|
||||
}
|
||||
|
||||
protected function initCustomConfig(): void
|
||||
{
|
||||
$this->app['config']->set('lauthz.second.model.config_type', 'custom');
|
||||
$this->app['config']->set(
|
||||
'lauthz.second.model.config_text',
|
||||
$this->getModelText()
|
||||
);
|
||||
|
||||
$config = $this->app['config']->get('lauthz.second');
|
||||
$loader = $this->app->make(ModelLoaderManager::class);
|
||||
|
||||
$loader->extend('custom', function () use ($config) {
|
||||
return new \Lauthz\Loaders\TextLoader($config);
|
||||
});
|
||||
}
|
||||
|
||||
protected function getModelText(): string
|
||||
{
|
||||
return <<<EOT
|
||||
[request_definition]
|
||||
r = sub, obj, act
|
||||
|
||||
[policy_definition]
|
||||
p = sub, obj, act
|
||||
|
||||
[policy_effect]
|
||||
e = some(where (p.eft == allow))
|
||||
|
||||
[matchers]
|
||||
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
|
||||
EOT;
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ namespace Lauthz\Tests;
|
|||
use Lauthz\Middlewares\RequestMiddleware;
|
||||
use Illuminate\Foundation\Testing\DatabaseMigrations;
|
||||
use Illuminate\Http\Request;
|
||||
use Lauthz\Facades\Enforcer;
|
||||
use Lauthz\Models\Rule;
|
||||
|
||||
class RequestMiddlewareTest extends TestCase
|
||||
|
@ -34,11 +35,19 @@ class RequestMiddlewareTest extends TestCase
|
|||
$this->assertEquals($this->middleware(Request::create('/foo1/123', 'PUT')), 'Unauthorized Exception');
|
||||
|
||||
$this->assertEquals($this->middleware(Request::create('/proxy', 'GET')), 'Unauthorized Exception');
|
||||
|
||||
Enforcer::guard('second')->addPolicy('alice', '/foo1/*', '(GET|POST)');
|
||||
|
||||
$this->assertEquals($this->middleware(Request::create('/foo1/123', 'GET'), 'second'), 200);
|
||||
$this->assertEquals($this->middleware(Request::create('/foo1/123', 'POST'), 'second'), 200);
|
||||
$this->assertEquals($this->middleware(Request::create('/foo1/123', 'PUT'), 'second'), 'Unauthorized Exception');
|
||||
|
||||
$this->assertEquals($this->middleware(Request::create('/proxy', 'GET'), 'second'), 'Unauthorized Exception');
|
||||
}
|
||||
|
||||
protected function middleware($request)
|
||||
protected function middleware($request, ...$guards)
|
||||
{
|
||||
return parent::runMiddleware(RequestMiddleware::class, $request);
|
||||
return parent::runMiddleware(RequestMiddleware::class, $request, ...$guards);
|
||||
}
|
||||
|
||||
protected function initConfig()
|
||||
|
@ -62,6 +71,8 @@ e = some(where (p.eft == allow))
|
|||
m = g(r.sub, p.sub) && r.sub == p.sub && keyMatch2(r.obj, p.obj) && regexMatch(r.act, p.act)
|
||||
EOT;
|
||||
$this->app['config']->set('lauthz.basic.model.config_text', $text);
|
||||
$this->app['config']->set('lauthz.second.model.config_type', 'text');
|
||||
$this->app['config']->set('lauthz.second.model.config_text', $text);
|
||||
}
|
||||
|
||||
protected function initTable()
|
||||
|
|
|
@ -27,9 +27,9 @@ abstract class TestCase extends BaseTestCase
|
|||
});
|
||||
|
||||
$this->app->make(Kernel::class)->bootstrap();
|
||||
$this->initConfig();
|
||||
|
||||
$this->app->register(\Lauthz\LauthzServiceProvider::class);
|
||||
$this->initConfig();
|
||||
|
||||
$this->artisan('vendor:publish', ['--provider' => 'Lauthz\LauthzServiceProvider']);
|
||||
$this->artisan('migrate', ['--force' => true]);
|
||||
|
|
Loading…
Reference in New Issue