Skip to content

Commit

Permalink
Merge pull request #107 from bezhanSalleh/feature/new-options
Browse files Browse the repository at this point in the history
adds new features to shield
  • Loading branch information
bezhanSalleh committed Aug 27, 2022
2 parents 21c7f14 + 26ec56d commit 06f9582
Show file tree
Hide file tree
Showing 16 changed files with 437 additions and 293 deletions.
4 changes: 3 additions & 1 deletion config/filament-shield.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
'super_admin' => [
'enabled' => true,
'name' => 'super_admin',
'define_via_gate' => false,
'intercept_gate' => 'before' // after
],

'filament_user' => [
Expand Down Expand Up @@ -68,6 +70,6 @@
],

'register_role_policy' => [
'enabled' => false,
'enabled' => true,
],
];
9 changes: 7 additions & 2 deletions src/Commands/Concerns/CanGeneratePolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace BezhanSalleh\FilamentShield\Commands\Concerns;

use BezhanSalleh\FilamentShield\Support\Utils;
use Illuminate\Support\Str;

trait CanGeneratePolicy
Expand Down Expand Up @@ -42,15 +43,16 @@ protected function generatePolicyPath(array $entity): string

protected function generatePolicyStubVariables(array $entity): array
{
$stubVariables = collect(config('filament-shield.permission_prefixes.resource'))
$stubVariables = collect(Utils::getGeneralResourcePermissionPrefixes())
->reduce(function ($gates, $permission) use ($entity) {
$gates[Str::studly($permission)] = $permission.'_'.$entity['resource'];

return $gates;
}, collect())->toArray();

$stubVariables['auth_model_fqcn'] = config('filament-shield.auth_provider_model.fqcn');
$stubVariables['auth_model_fqcn'] = Utils::getAuthProviderFQCN();
$stubVariables['auth_model_name'] = Str::of($stubVariables['auth_model_fqcn'])->afterLast('\\');
$stubVariables['auth_model_variable'] = Str::of($stubVariables['auth_model_name'])->camel();

$reflectionClass = new \ReflectionClass($entity['fqcn']::getModel());
$namespace = $reflectionClass->getNamespaceName();
Expand All @@ -60,6 +62,9 @@ protected function generatePolicyStubVariables(array $entity): array
? 'App\Policies'
: Str::of($namespace)->replace('Models', 'Policies'); /** @phpstan-ignore-line */

$stubVariables['model_name'] = $entity['model'];
$stubVariables['model_fqcn'] = $namespace.'\\'.$entity['model'];
$stubVariables['model_variable'] = Str::of($entity['model'])->camel();
$stubVariables['modelPolicy'] = "{$entity['model']}Policy";

return $stubVariables;
Expand Down
172 changes: 153 additions & 19 deletions src/Commands/MakeShieldGenerateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,63 +3,197 @@
namespace BezhanSalleh\FilamentShield\Commands;

use BezhanSalleh\FilamentShield\FilamentShield;
use BezhanSalleh\FilamentShield\Support\Utils;
use Illuminate\Console\Command;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use Symfony\Component\Console\Attribute\AsCommand;

#[AsCommand(name: 'shield:generate')]
class MakeShieldGenerateCommand extends Command
{
use Concerns\CanGeneratePolicy;
use Concerns\CanManipulateFiles;

public $signature = 'shield:generate
{--option= : Override the config file option setting.}';
/**
* The resources to generate permissions or policies for, or should be exclude.
*
* @var array
*/
protected $resources = [];

/**
* The pages to generate permissions for, or should be excluded.
*
* @var array
*/
protected $pages = [];

/**
* The widgets to generate permissions for, or should be excluded.
*
* @var array
*/
protected $widgets = [];

public $description = '(Re)Discovers Filament entities and (re)generates Permissions and/or Policies.';
protected $generatorOption;
protected $excludeResources = false;
protected $excludePages = false ;
protected $excludeWidgets = false ;
protected $onlyResources = false ;
protected $onlyPages = false ;
protected $onlyWidget = false ;

protected $option;
/**
* The console command signature.
*
* @var string
*/
public $signature = 'shield:generate
{--all : Generate permissions/policies for all entities }
{--option= : Override the config generator option(<fg=green;options=bold>policies_and_permissions,policies,permissions</>)}
{--resource= : One or many resources separated by comma (,) }
{--page= : One or many pages separated by comma (,) }
{--widget= : One or many widgets separated by comma (,) }
{--exclude : Exclude the given entities during generation }
{--ignore-config-exclude : Ignore config `<fg=yellow;options=bold>exclude</>` option during generation }
';
// {--seeder : Exclude the given entities during generation }
// the idea is to generate a seeder that can be used on production deployment

/**
* The console command description.
*
* @var string
*/
public $description = 'Generate Permissions and/or Policies for Filament entities.';

public function handle(): int
{
$this->option = $this->option('option') ?? config('filament-shield.generator.option');
$this->determinGeneratorOptionAndEntities();

if ($this->option('exclude') && blank($this->option('resource')) && blank($this->option('page')) && blank($this->option('widget'))) {
$this->comment('<fg=red;>No entites provided for the generators ...</>');
$this->comment('<fg=yellow;options=bold>... generation SKIPPED</>');

if (config('filament-shield.entities.resources')) {
$resources = $this->generateForResources(FilamentShield::getResources());
return self::INVALID;
}

if (filled($this->option('resource')) || $this->option('all')) {
$resources = $this->generateForResources($this->generatableResources());
$this->resourceInfo($resources->toArray());
}

if (config('filament-shield.entities.pages')) {
$pages = $this->generateForPages(FilamentShield::getPages());
if (filled($this->option('page')) || $this->option('all')) {
$pages = $this->generateForPages($this->generatablePages());
$this->pageInfo($pages->toArray());
}

if (config('filament-shield.entities.widgets')) {
$widgets = $this->generateForWidgets(FilamentShield::getWidgets());
if (filled($this->option('widget')) || $this->option('all')) {
$widgets = $this->generateForWidgets($this->generatableWidgets());
$this->widgetInfo($widgets->toArray());
} else {
$this->comment('Please enable `entities` from config first.');
}


$this->info('Permission & Policies are generated according to your config or passed options.');
$this->info('Enjoy!');

if (cache()->has('shield_general_exclude')) {
Utils::enableGeneralExclude();
cache()->forget('shield_general_exclude');
}

return self::SUCCESS;
}

protected function determinGeneratorOptionAndEntities(): void
{
$this->generatorOption = $this->option('option') ?? Utils::getGeneratorOption();

if ($this->option('ignore-config-exclude') && Utils::isGeneralExcludeEnabled()) {
cache()->add('shield_general_exclude', true, 3600);
Utils::disableGeneralExclude();
}

$this->resources = (array) explode(',', $this->option('resource'));
$this->pages = (array) explode(',', $this->option('page'));
$this->widgets = (array) explode(',', $this->option('widget'));

$this->excludeResources = $this->option('exclude') && filled($this->option('resource'));
$this->excludePages = $this->option('exclude') && filled($this->option('page'));
$this->excludeWidgets = $this->option('exclude') && filled($this->option('widget'));

$this->onlyResources = ! $this->option('exclude') && filled($this->option('resource'));
$this->onlyPages = ! $this->option('exclude') && filled($this->option('page'));
$this->onlyWidgets = ! $this->option('exclude') && filled($this->option('widget'));
}

protected function generatableResources(): ?array
{
return collect(FilamentShield::getResources())
->filter(function ($resource) {
if ($this->excludeResources) {
return ! in_array(Str::of($resource['fqcn'])->afterLast('\\'), $this->resources);
}

if ($this->onlyResources) {
return in_array(Str::of($resource['fqcn'])->afterLast('\\'), $this->resources);
}

return true;
})
->toArray();
}

protected function generatablePages(): ?array
{
return collect(FilamentShield::getPages())
->filter(function ($page) {
if ($this->excludePages) {
return ! in_array($page, $this->pages);
}

if ($this->onlyPages) {
return in_array($page, $this->pages);
}

return true;
})
->toArray();
}

protected function generatableWidgets(): ?array
{
return collect(FilamentShield::getWidgets())
->filter(function ($widget) {
if ($this->excludeWidgets) {
return ! in_array($widget, $this->widgets);
}

if ($this->onlyWidgets) {
return in_array($widget, $this->widgets);
}

return true;
})
->toArray();
}

protected function generateForResources(array $resources): Collection
{
return collect($resources)
->values()
->each(function ($entity) {
if ($this->option === 'policies_and_permissions') {
if ($this->generatorOption === 'policies_and_permissions') {
$this->copyStubToApp('DefaultPolicy', $this->generatePolicyPath($entity), $this->generatePolicyStubVariables($entity));
FilamentShield::generateForResource($entity['resource']);
}

if ($this->option === 'policies') {
if ($this->generatorOption === 'policies') {
$this->copyStubToApp('DefaultPolicy', $this->generatePolicyPath($entity), $this->generatePolicyStubVariables($entity));
}

if ($this->option === 'permissions') {
if ($this->generatorOption === 'permissions') {
FilamentShield::generateForResource($entity['resource']);
}
});
Expand Down Expand Up @@ -92,11 +226,11 @@ protected function resourceInfo(array $resources): void
return [
'#' => $key + 1,
'Resource' => $resource['model'],
'Policy' => "{$resource['model']}Policy.php". ($this->option !== 'permissions' ? ' ✅' : ' ❌') ,
'Policy' => "{$resource['model']}Policy.php". ($this->generatorOption !== 'permissions' ? ' ✅' : ' ❌'),
'Permissions' =>
implode(',', collect(config('filament-shield.permission_prefixes.resource'))->map(function ($permission, $key) use ($resource) {
implode(','.PHP_EOL, collect(config('filament-shield.permission_prefixes.resource'))->map(function ($permission, $key) use ($resource) {
return $permission.'_'.$resource['resource'];
})->toArray()) . ($this->option !== 'policies' ? ' ✅' : ' ❌'),
})->toArray()) . ($this->generatorOption !== 'policies' ? ' ✅' : ' ❌'),
];
})
);
Expand Down
28 changes: 17 additions & 11 deletions src/Commands/MakeShieldInstallCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@
class MakeShieldInstallCommand extends Command
{
public $signature = 'shield:install
{--F|fresh}
{--F|fresh : re-run the migrations}
{--O|only : Only setups shield without generating permissions and creating super-admin}
';
public $description = "One Command to Rule them All 🔥";

public $description = "Setup Core Package requirements and Install Shield";

public function handle(): int
{
Expand All @@ -30,11 +32,7 @@ public function handle(): int
$this->info('- Publishes core package migration');
$this->warn(' - On fresh applications database will be migrated');
$this->warn(' - You can also force this behavior by supplying the --fresh option');
$this->info('- Creates a filament user');
$this->warn(' - Assigns Super Admin role if enabled in config');
$this->warn(' - And/Or Assigns Filament User role if enabled in config');
$this->info('- Discovers filament resources and generates Permissions and Policies accordingly');
$this->warn(' - Will override any existing policies if available');


$confirmed = $this->confirm('Do you wish to continue?', true);

Expand Down Expand Up @@ -120,11 +118,19 @@ protected function install(bool $fresh = false)

$this->call('migrate');

$this->info('Creating Super Admin...');

$this->call('shield:super-admin');
if (! $this->option('only')) {
$this->info('Generating permissions ...');
$this->call('shield:generate', [
'--all' => true,
]);

$this->call('shield:generate');
$this->info('Creating a filament user with Super Admin Role...');
$this->call('shield:super-admin');
} else {
$this->call('shield:generate', [
'--resource' => 'RoleResource',
]);
}

$this->info(Artisan::output());

Expand Down
Loading

0 comments on commit 06f9582

Please sign in to comment.