Skip to content

Commit 765a20a

Browse files
author
Given Ncube
committed
feat: added the ability to generate permissions
1 parent 369bec0 commit 765a20a

File tree

8 files changed

+262
-4
lines changed

8 files changed

+262
-4
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
"laravel/pint": "^1.0",
2727
"nunomaduro/collision": "^6.2",
2828
"nunomaduro/larastan": "^2.0.1",
29-
"orchestra/testbench": "^7.0",
29+
"orchestra/testbench": "^7.6",
3030
"pestphp/pest": "^1.21",
3131
"pestphp/pest-plugin-laravel": "^1.1",
3232
"phpstan/extension-installer": "^1.1",

config/authorizer.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
<?php
22

3-
// config for FlixtechsLabs/LaravelAuthorizer
43
return [
5-
4+
'permissions' => [
5+
'create',
6+
'update',
7+
'delete',
8+
'view all',
9+
'view',
10+
'view own',
11+
'force delete',
12+
'restore',
13+
'restore own',
14+
],
615
];

database/database.sqlite

52 KB
Binary file not shown.

phpunit.xml.dist

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,9 @@
3636
<logging>
3737
<junit outputFile="build/report.junit.xml"/>
3838
</logging>
39+
40+
<php>
41+
<env name="DB_CONNECTION" value="testing"/>
42+
<env name="DB_DATABASE" value="database/database.sqlite"/>
43+
</php>
3944
</phpunit>
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
<?php
2+
3+
namespace FlixtechsLabs\LaravelAuthorizer\Commands;
4+
5+
use Illuminate\Console\Command;
6+
use Illuminate\Database\Eloquent\Model;
7+
use Illuminate\Support\Collection;
8+
use Illuminate\Support\Facades\File;
9+
use Illuminate\Support\Facades\Storage;
10+
use Illuminate\Support\Str;
11+
use ReflectionClass;
12+
use ReflectionException;
13+
use Spatie\Permission\Models\Permission;
14+
use SplFileInfo;
15+
use Symfony\Component\Filesystem\Filesystem;
16+
17+
class GeneratePermissionsCommand extends Command
18+
{
19+
/**
20+
* The name and signature of the console command.
21+
*
22+
* @var string
23+
*/
24+
protected $signature = 'authorizer:permissions:generate {--m|model= : The model to use for the policy}';
25+
26+
/**
27+
* The console command description.
28+
*
29+
* @var string
30+
*/
31+
protected $description = 'Generate permissions for your application';
32+
33+
/**
34+
* Run the command.
35+
* @return int
36+
*/
37+
public function handle(): int
38+
{
39+
$this->comment('Generating permissions...');
40+
41+
$model = $this->option('model');
42+
43+
if (is_null($model)) {
44+
$this->generatePermissionsForAllModels();
45+
46+
return self::SUCCESS;
47+
}
48+
49+
$this->generatePermissions($model);
50+
51+
return self::SUCCESS;
52+
}
53+
54+
/**
55+
* Generate all permissions.
56+
*/
57+
protected function generatePermissionsForAllModels(): void
58+
{
59+
$models = $this->getModels();
60+
61+
$models->each(fn(string $model) => $this->generatePermissions($model));
62+
}
63+
64+
/**
65+
* Generate permission for a given model.
66+
*
67+
* @param string $model
68+
* @return void
69+
*/
70+
public function generatePermissions(string $model): void
71+
{
72+
$permissions = config('authorizer.permissions');
73+
74+
collect($permissions)->each(
75+
fn(string $permission) => $this->generatePermission(
76+
$model,
77+
$permission
78+
)
79+
);
80+
}
81+
82+
public function generatePermission(string $model, string $permission)
83+
{
84+
if (
85+
Str::contains($permission, 'any') ||
86+
Str::contains($permission, 'all')
87+
) {
88+
return Permission::updateOrCreate([
89+
'name' => $permission . ' ' . Str::plural(Str::lower($model)),
90+
]);
91+
}
92+
93+
return Permission::updateOrCreate([
94+
'name' => $permission . ' ' . Str::lower($model),
95+
]);
96+
}
97+
98+
/**
99+
* Get all models.
100+
*
101+
* @return array
102+
*/
103+
public function getModels(): array|Collection
104+
{
105+
return collect(File::allFiles(app_path()))
106+
->map(function (SplFileInfo $info) {
107+
$path = $info->getRelativePathname();
108+
109+
return sprintf(
110+
'\%s%s',
111+
app()->getNamespace(),
112+
Str::replace('/', '\\', Str::beforeLast($path, '.'))
113+
);
114+
})
115+
->filter(function (string $class) {
116+
try {
117+
$reflection = new ReflectionClass($class);
118+
} catch (ReflectionException $throwable) {
119+
return false;
120+
}
121+
122+
return $reflection->isSubclassOf(Model::class) &&
123+
!$reflection->isAbstract();
124+
})
125+
->map(fn($model) => Str::afterLast($model, '\\'));
126+
}
127+
}

src/LaravelAuthorizerServiceProvider.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace FlixtechsLabs\LaravelAuthorizer;
44

5+
use FlixtechsLabs\LaravelAuthorizer\Commands\GeneratePermissionsCommand;
56
use Illuminate\Foundation\Console\AboutCommand;
67
use FlixtechsLabs\LaravelAuthorizer\Commands\LaravelAuthorizerCommand;
78
use Spatie\LaravelPackageTools\Package;
@@ -21,7 +22,10 @@ public function configurePackage(Package $package): void
2122
->hasConfigFile()
2223
->hasViews()
2324
->hasMigration('create_laravel-authorizer_table')
24-
->hasCommand(LaravelAuthorizerCommand::class);
25+
->hasCommands([
26+
LaravelAuthorizerCommand::class,
27+
GeneratePermissionsCommand::class,
28+
]);
2529
}
2630

2731
public function boot()

tests/GeneratePermissionsTest.php

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?php
2+
3+
namespace FlixtechsLabs\LaravelAuthorizer\Tests;
4+
5+
use FlixtechsLabs\LaravelAuthorizer\Commands\GeneratePermissionsCommand;
6+
use function Pest\Laravel\assertDatabaseHas;
7+
use function Pest\Laravel\assertDatabaseMissing;
8+
9+
it('can generate permissions', function () {
10+
collect([
11+
'User',
12+
'Post',
13+
'Comment',
14+
'Tag',
15+
'Product',
16+
'Category',
17+
'Order',
18+
'OrderItem',
19+
])->each(
20+
fn($model) => $this->artisan('make:model', [
21+
'name' => $model,
22+
])
23+
);
24+
25+
$this->artisan(GeneratePermissionsCommand::class)
26+
->expectsOutput('Generating permissions...')
27+
->assertSuccessful();
28+
29+
assertDatabaseHas('permissions', [
30+
'name' => 'create post',
31+
]);
32+
assertDatabaseHas('permissions', [
33+
'name' => 'update post',
34+
]);
35+
assertDatabaseHas('permissions', [
36+
'name' => 'delete post',
37+
]);
38+
assertDatabaseHas('permissions', [
39+
'name' => 'create comment',
40+
]);
41+
assertDatabaseHas('permissions', [
42+
'name' => 'update comment',
43+
]);
44+
assertDatabaseHas('permissions', [
45+
'name' => 'delete comment',
46+
]);
47+
assertDatabaseHas('permissions', [
48+
'name' => 'create tag',
49+
]);
50+
assertDatabaseHas('permissions', [
51+
'name' => 'update tag',
52+
]);
53+
});
54+
55+
it('can generate permission for just one model', function () {
56+
collect([
57+
'User',
58+
'Post',
59+
'Comment',
60+
'Tag',
61+
'Product',
62+
'Category',
63+
'Order',
64+
'OrderItem',
65+
])->each(
66+
fn($model) => $this->artisan('make:model', [
67+
'name' => $model,
68+
])
69+
);
70+
71+
$this->artisan(GeneratePermissionsCommand::class, [
72+
'--model' => 'Post',
73+
])
74+
->expectsOutput('Generating permissions...')
75+
->assertSuccessful();
76+
77+
assertDatabaseHas('permissions', [
78+
'name' => 'create post',
79+
]);
80+
81+
assertDatabaseHas('permissions', [
82+
'name' => 'update post',
83+
]);
84+
85+
assertDatabaseMissing('permissions', [
86+
'name' => 'delete tag',
87+
]);
88+
});

tests/TestCase.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Illuminate\Database\Eloquent\Factories\Factory;
77
use Illuminate\Support\Facades\File;
88
use Orchestra\Testbench\TestCase as Orchestra;
9+
use Spatie\Permission\PermissionServiceProvider;
910

1011
class TestCase extends Orchestra
1112
{
@@ -16,6 +17,7 @@ protected function setUp(): void
1617
$this->beforeApplicationDestroyed(function () {
1718
File::cleanDirectory(app_path('Models'));
1819
File::cleanDirectory(app_path('Policies'));
20+
File::deleteDirectory(app_path('Policies'));
1921
});
2022

2123
Factory::guessFactoryNamesUsing(
@@ -41,4 +43,27 @@ public function getEnvironmentSetUp($app)
4143
$migration->up();
4244
*/
4345
}
46+
47+
public function defineDatabaseMigrations()
48+
{
49+
$this->artisan('vendor:publish', [
50+
'--provider' => PermissionServiceProvider::class,
51+
]);
52+
53+
$this->artisan('migrate:fresh')->run();
54+
55+
$this->beforeApplicationDestroyed(function () {
56+
$this->artisan('migrate:rollback')->run();
57+
});
58+
}
59+
60+
/**
61+
* Ignore package discovery from.
62+
*
63+
* @return array<int, array>
64+
*/
65+
public function ignorePackageDiscoveriesFrom(): array
66+
{
67+
return [];
68+
}
4469
}

0 commit comments

Comments
 (0)