Skip to content

Commit c565a05

Browse files
committed
Merge branch 'master' of https://github.com/php-casbin/laravel-authz into feature-use-gates-1.0
2 parents fe9fd1a + 4d49aef commit c565a05

14 files changed

+330
-105
lines changed

Diff for: README.md

+7-4
Original file line numberDiff line numberDiff line change
@@ -279,14 +279,17 @@ Route::group(['middleware' => ['http_request']], function () {
279279

280280
### Using Gates
281281

282-
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.
282+
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.
283283

284284
```php
285-
if(Gate::allows('enforcer', ['articles', 'read'])) {
286-
// The user can read articles
287-
};
285+
$user->can('articles,read');
286+
// For multiple enforcers
287+
$user->can('articles,read', 'second');
288+
// The methods cant, cannot, canAny, etc. also work
288289
```
289290

291+
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.
292+
290293
### Multiple enforcers
291294

292295
If you need multiple permission controls in your project, you can configure multiple enforcers.

Diff for: config/lauthz.php

+8
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@
66
*/
77
'default' => 'basic',
88

9+
/*
10+
* Lauthz Localizer
11+
*/
12+
'localizer' => [
13+
// changes whether enforcer will register at gates.
14+
'enabled_register_at_gates' => true
15+
],
16+
917
'basic' => [
1018
/*
1119
* Casbin model setting.

Diff for: src/EnforcerLocalizer.php

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
namespace Lauthz;
4+
5+
use Illuminate\Contracts\Auth\Access\Authorizable;
6+
use Illuminate\Contracts\Auth\Access\Gate;
7+
use Lauthz\Facades\Enforcer;
8+
9+
class EnforcerLocalizer
10+
{
11+
public function registerAtGates(Gate $gate)
12+
{
13+
$gate->before(function (Authorizable $user, string $ability, array $guards) {
14+
/** @var \Illuminate\Contracts\Auth\Authenticatable $user */
15+
$identifier = $user->getAuthIdentifier();
16+
if (method_exists($user, 'getAuthzIdentifier')) {
17+
/** @var \Lauthz\Tests\Models\User $user */
18+
$identifier = $user->getAuthzIdentifier();
19+
}
20+
$identifier = strval($identifier);
21+
$ability = explode(',', $ability);
22+
if (empty($guards)) {
23+
return Enforcer::enforce($identifier, ...$ability);
24+
}
25+
26+
foreach ($guards as $guard) {
27+
return Enforcer::guard($guard)->enforce($identifier, ...$ability);
28+
}
29+
});
30+
}
31+
}

Diff for: src/EnforcerManager.php

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
use Casbin\Model\Model;
88
use Casbin\Log\Log;
99
use Lauthz\Contracts\Factory;
10-
use Lauthz\Contracts\ModelLoader;
1110
use Lauthz\Models\Rule;
1211
use Illuminate\Support\Arr;
1312
use InvalidArgumentException;
13+
use Lauthz\Loaders\ModelLoaderManager;
1414

1515
/**
1616
* @mixin \Casbin\Enforcer
@@ -87,7 +87,8 @@ protected function resolve($name)
8787
}
8888

8989
$model = new Model();
90-
$loader = $this->app->make(ModelLoader::class, $config);
90+
$loader = $this->app->make(ModelLoaderManager::class);
91+
$loader->initFromConfig($config);
9192
$loader->loadModel($model);
9293

9394
$adapter = Arr::get($config, 'adapter');

Diff for: src/LauthzServiceProvider.php

+15-15
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@
22

33
namespace Lauthz;
44

5-
use Illuminate\Support\Facades\Gate;
5+
use Illuminate\Contracts\Auth\Access\Gate;
66
use Illuminate\Support\ServiceProvider;
7-
use Lauthz\Contracts\ModelLoader;
8-
use Lauthz\Facades\Enforcer;
9-
use Lauthz\Loaders\ModelLoaderFactory;
7+
use Lauthz\EnforcerLocalizer;
8+
use Lauthz\Loaders\ModelLoaderManager;
109
use Lauthz\Models\Rule;
1110
use Lauthz\Observers\RuleObserver;
1211

@@ -34,6 +33,8 @@ public function boot()
3433
$this->mergeConfigFrom(__DIR__ . '/../config/lauthz.php', 'lauthz');
3534

3635
$this->bootObserver();
36+
37+
$this->registerLocalizer();
3738
}
3839

3940
/**
@@ -55,28 +56,27 @@ public function register()
5556
return new EnforcerManager($app);
5657
});
5758

58-
$this->app->bind(ModelLoader::class, function($app, $config) {
59-
return ModelLoaderFactory::createFromConfig($config);
59+
$this->app->singleton(ModelLoaderManager::class, function ($app) {
60+
return new ModelLoaderManager($app);
6061
});
6162

62-
$this->registerGates();
63+
$this->app->singleton(EnforcerLocalizer::class, function ($app) {
64+
return new EnforcerLocalizer();
65+
});
6366
}
6467

6568
/**
6669
* Register a gate that allows users to use Laravel's built-in Gate to call Enforcer.
6770
*
6871
* @return void
6972
*/
70-
protected function registerGates()
73+
protected function registerLocalizer()
7174
{
72-
Gate::define('enforcer', function ($user, ...$args) {
73-
$identifier = $user->getAuthIdentifier();
74-
if (method_exists($user, 'getAuthzIdentifier')) {
75-
$identifier = $user->getAuthzIdentifier();
75+
$this->app->afterResolving(Gate::class, function ($gate, $app) {
76+
if ($app->config->get('lauthz.localizer.enabled_register_at_gates')) {
77+
$localizer = $app->get(EnforcerLocalizer::class);
78+
$localizer->registerAtGates($gate);
7679
}
77-
$identifier = strval($identifier);
78-
79-
return Enforcer::enforce($identifier, ...$args);
8080
});
8181
}
8282
}

Diff for: src/Loaders/ModelLoaderFactory.php

-48
This file was deleted.

Diff for: src/Loaders/ModelLoaderManager.php

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
<?php
2+
3+
namespace Lauthz\Loaders;
4+
5+
use Illuminate\Support\Arr;
6+
use Illuminate\Support\Str;
7+
use Illuminate\Support\Manager;
8+
use InvalidArgumentException;
9+
10+
/**
11+
* The model loader manager.
12+
*
13+
* A model loader is responsible for a loading model from an arbitrary source.
14+
* Developers can customize loading behavior by implementing
15+
* and register the custom loader in AppServiceProvider through `app(LoaderManager::class)->extend()`.
16+
*
17+
* Built-in loader implementations include:
18+
* - FileLoader: For loading model from file.
19+
* - TextLoader: Suitable for model defined as a multi-line string.
20+
* - UrlLoader: Handles model loading from URL.
21+
*
22+
* To utilize a built-in or custom loader, set 'model.config_type' in the configuration to match one of the above types.
23+
*/
24+
class ModelLoaderManager extends Manager
25+
{
26+
27+
/**
28+
* The array of the lauthz driver configuration.
29+
*
30+
* @var array
31+
*/
32+
protected $config;
33+
34+
/**
35+
* Initialize configuration for the loader manager instance.
36+
*
37+
* @param array $config the lauthz driver configuration.
38+
*/
39+
public function initFromConfig(array $config)
40+
{
41+
$this->config = $config;
42+
}
43+
44+
/**
45+
* Get the default driver from the configuration.
46+
*
47+
* @return string The default driver name.
48+
*/
49+
public function getDefaultDriver()
50+
{
51+
return Arr::get($this->config, 'model.config_type', '');
52+
}
53+
54+
/**
55+
* Create a new TextLoader instance.
56+
*
57+
* @return TextLoader
58+
*/
59+
public function createTextDriver()
60+
{
61+
return new TextLoader($this->config);
62+
}
63+
64+
/**
65+
* Create a new UrlLoader instance.
66+
*
67+
* @return UrlLoader
68+
*/
69+
public function createUrlDriver()
70+
{
71+
return new UrlLoader($this->config);
72+
}
73+
74+
/**
75+
* Create a new FileLoader instance.
76+
*
77+
* @return FileLoader
78+
*/
79+
public function createFileDriver()
80+
{
81+
return new FileLoader($this->config);
82+
}
83+
84+
/**
85+
* Create a new driver instance.
86+
*
87+
* @param string $driver
88+
* @return mixed
89+
*
90+
* @throws \InvalidArgumentException
91+
*/
92+
protected function createDriver($driver)
93+
{
94+
if(empty($driver)) {
95+
throw new InvalidArgumentException('Unsupported empty model loader type.');
96+
}
97+
98+
if (isset($this->customCreators[$driver])) {
99+
return $this->callCustomCreator($driver);
100+
}
101+
$method = 'create' . Str::studly($driver) . 'Driver';
102+
if (method_exists($this, $method)) {
103+
return $this->$method();
104+
}
105+
106+
throw new InvalidArgumentException("Unsupported model loader type: {$driver}.");
107+
}
108+
}

Diff for: src/Middlewares/EnforcerMiddleware.php

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public function handle($request, Closure $next, ...$args)
3131
$user = Auth::user();
3232
$identifier = $user->getAuthIdentifier();
3333
if (method_exists($user, 'getAuthzIdentifier')) {
34+
/** @var \Lauthz\Tests\Models\User $user */
3435
$identifier = $user->getAuthzIdentifier();
3536
}
3637
$identifier = strval($identifier);

Diff for: tests/DatabaseAdapterTest.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
namespace Lauthz\Tests;
44

5-
use Enforcer;
65
use Illuminate\Foundation\Testing\DatabaseMigrations;
76
use Casbin\Persist\Adapters\Filter;
87
use Casbin\Exceptions\InvalidFilterTypeException;
8+
use Lauthz\Facades\Enforcer;
99

1010
class DatabaseAdapterTest extends TestCase
1111
{
@@ -309,7 +309,7 @@ public function testLoadFilteredPolicy()
309309
$this->assertEquals([
310310
['bob', 'data2', 'write']
311311
], Enforcer::getPolicy());
312-
312+
313313
// Filter
314314
$filter = new Filter(['v2'], ['read']);
315315
Enforcer::loadFilteredPolicy($filter);

Diff for: tests/EnforcerCustomLocalizerTest.php

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
use Illuminate\Contracts\Auth\Access\Gate;
4+
use Illuminate\Foundation\Testing\DatabaseMigrations;
5+
use Lauthz\Tests\TestCase;
6+
7+
class EnforcerCustomLocalizerTest extends TestCase
8+
{
9+
use DatabaseMigrations;
10+
11+
public function testCustomRegisterAtGatesBefore()
12+
{
13+
$user = $this->user("alice");
14+
$this->assertFalse($user->can('data3,read'));
15+
16+
app(Gate::class)->before(function () {
17+
return true;
18+
});
19+
20+
$this->assertTrue($user->can('data3,read'));
21+
}
22+
23+
public function testCustomRegisterAtGatesAfter()
24+
{
25+
$user = $this->user("alice");
26+
$this->assertFalse($user->can('data3,read'));
27+
28+
app(Gate::class)->after(function () {
29+
return true;
30+
});
31+
32+
$this->assertTrue($user->can('data3,read'));
33+
}
34+
35+
public function testCustomRegisterAtGatesDefine()
36+
{
37+
$user = $this->user("alice");
38+
$this->assertFalse($user->can('data3,read'));
39+
40+
app(Gate::class)->define('data3,read', function () {
41+
return true;
42+
});
43+
44+
$this->assertTrue($user->can('data3,read'));
45+
}
46+
47+
public function initConfig()
48+
{
49+
parent::initConfig();
50+
$this->app['config']->set('lauthz.localizer.enabled_register_at_gates', false);
51+
}
52+
}

0 commit comments

Comments
 (0)