Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/php-casbin/laravel-authz
Browse files Browse the repository at this point in the history
…into feature-use-gates-1.0
  • Loading branch information
Dobmod committed Aug 6, 2024
2 parents fe9fd1a + 4d49aef commit caadc76
Show file tree
Hide file tree
Showing 14 changed files with 344 additions and 107 deletions.
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,14 +279,17 @@ 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 using `Auth::login`. See [Gates](https://laravel.com/docs/11.x/authorization#gates) for more details.
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
if(Gate::allows('enforcer', ['articles', 'read'])) {
// The user can read articles
};
$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.
Expand Down
8 changes: 8 additions & 0 deletions config/lauthz.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@
*/
'default' => 'basic',

/*
* Lauthz Localizer
*/
'localizer' => [
// changes whether enforcer will register at gates.
'enabled_register_at_gates' => true
],

'basic' => [
/*
* Casbin model setting.
Expand Down
61 changes: 61 additions & 0 deletions src/EnforcerLocalizer.php
Original file line number Diff line number Diff line change
@@ -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);
}
});
}
}
5 changes: 3 additions & 2 deletions src/EnforcerManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
use Casbin\Model\Model;
use Casbin\Log\Log;
use Lauthz\Contracts\Factory;
use Lauthz\Contracts\ModelLoader;
use Lauthz\Models\Rule;
use Illuminate\Support\Arr;
use InvalidArgumentException;
use Lauthz\Loaders\ModelLoaderManager;

/**
* @mixin \Casbin\Enforcer
Expand Down Expand Up @@ -87,7 +87,8 @@ protected function resolve($name)
}

$model = new Model();
$loader = $this->app->make(ModelLoader::class, $config);
$loader = $this->app->make(ModelLoaderManager::class);
$loader->initFromConfig($config);
$loader->loadModel($model);

$adapter = Arr::get($config, 'adapter');
Expand Down
28 changes: 11 additions & 17 deletions src/LauthzServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@

namespace Lauthz;

use Illuminate\Support\Facades\Gate;
use Illuminate\Support\ServiceProvider;
use Lauthz\Contracts\ModelLoader;
use Lauthz\Facades\Enforcer;
use Lauthz\Loaders\ModelLoaderFactory;
use Lauthz\EnforcerLocalizer;
use Lauthz\Loaders\ModelLoaderManager;
use Lauthz\Models\Rule;
use Lauthz\Observers\RuleObserver;

Expand Down Expand Up @@ -34,6 +32,8 @@ public function boot()
$this->mergeConfigFrom(__DIR__ . '/../config/lauthz.php', 'lauthz');

$this->bootObserver();

$this->registerLocalizer();
}

/**
Expand All @@ -55,28 +55,22 @@ public function register()
return new EnforcerManager($app);
});

$this->app->bind(ModelLoader::class, function($app, $config) {
return ModelLoaderFactory::createFromConfig($config);
$this->app->singleton(ModelLoaderManager::class, function ($app) {
return new ModelLoaderManager($app);
});

$this->registerGates();
$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 registerGates()
protected function registerLocalizer()
{
Gate::define('enforcer', function ($user, ...$args) {
$identifier = $user->getAuthIdentifier();
if (method_exists($user, 'getAuthzIdentifier')) {
$identifier = $user->getAuthzIdentifier();
}
$identifier = strval($identifier);

return Enforcer::enforce($identifier, ...$args);
});
$this->app->make(EnforcerLocalizer::class)->register();
}
}
48 changes: 0 additions & 48 deletions src/Loaders/ModelLoaderFactory.php

This file was deleted.

108 changes: 108 additions & 0 deletions src/Loaders/ModelLoaderManager.php
Original file line number Diff line number Diff line change
@@ -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}.");
}
}
1 change: 1 addition & 0 deletions src/Middlewares/EnforcerMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public function handle($request, Closure $next, ...$args)
$user = Auth::user();
$identifier = $user->getAuthIdentifier();
if (method_exists($user, 'getAuthzIdentifier')) {
/** @var \Lauthz\Tests\Models\User $user */
$identifier = $user->getAuthzIdentifier();
}
$identifier = strval($identifier);
Expand Down
4 changes: 2 additions & 2 deletions tests/DatabaseAdapterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -309,7 +309,7 @@ public function testLoadFilteredPolicy()
$this->assertEquals([
['bob', 'data2', 'write']
], Enforcer::getPolicy());

// Filter
$filter = new Filter(['v2'], ['read']);
Enforcer::loadFilteredPolicy($filter);
Expand Down
Loading

0 comments on commit caadc76

Please sign in to comment.