95 lines
3.6 KiB
PHP
95 lines
3.6 KiB
PHP
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Models\Admin;
|
|
use App\Models\AdminRole;
|
|
use App\Models\User;
|
|
use Illuminate\Console\Command;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Spatie\Permission\Models\Role;
|
|
use Spatie\Permission\PermissionRegistrar;
|
|
|
|
/**
|
|
* Replace the legacy scoped roles Admin\{id}\admin by a role from admin_roles.
|
|
*
|
|
* Admin profiles used to get the role Admin\{id}\admin, with every permission of that moment and never synced
|
|
* (admin_roles has no "admin"). The permission checks now accept any role on the profile, so these roles are
|
|
* no longer needed. For every such role, on any installation:
|
|
* 1. each holder gets Admin\{id}\{default role} (admin_settings.default_admin_role, 'administrator'),
|
|
* created and synced from its definition when missing; nobody loses access;
|
|
* 2. then the legacy role is removed.
|
|
* Without a definition for the default role nothing changes. Running it again changes nothing.
|
|
* Called by `php artisan database:update`; run it with --dry-run first to see what it would do.
|
|
*/
|
|
class AdminRolesMigrate extends Command
|
|
{
|
|
protected $signature = 'admin-roles:migrate {--dry-run : Show what would change, change nothing}';
|
|
|
|
protected $description = 'Replace the legacy Admin\\{id}\\admin roles by the default role from admin_roles';
|
|
|
|
/** Matches Admin\{id}\admin */
|
|
public const LEGACY_PATTERN = '/^Admin\\\\(\d+)\\\\admin$/';
|
|
|
|
public function handle(): int
|
|
{
|
|
$dryRun = (bool) $this->option('dry-run');
|
|
$defaultName = AdminRole::defaultName();
|
|
$definition = AdminRole::where('name', $defaultName)->first();
|
|
|
|
$legacyRoles = Role::where('guard_name', 'web')->get()
|
|
->filter(fn (Role $role) => preg_match(self::LEGACY_PATTERN, $role->name));
|
|
|
|
if ($legacyRoles->isEmpty()) {
|
|
$this->info('No legacy Admin\\{id}\\admin roles: nothing to do.');
|
|
return self::SUCCESS;
|
|
}
|
|
|
|
if (!$definition) {
|
|
$this->error("The default admin role '{$defaultName}' (admin_settings.default_admin_role) has no admin_roles definition: nothing changed.");
|
|
return self::FAILURE;
|
|
}
|
|
|
|
$this->info(($dryRun ? '[dry run] ' : '') . "Replacing {$legacyRoles->count()} legacy role(s) by '{$defaultName}':");
|
|
|
|
foreach ($legacyRoles as $legacy) {
|
|
preg_match(self::LEGACY_PATTERN, $legacy->name, $m);
|
|
$adminId = (int) $m[1];
|
|
$exists = Admin::whereKey($adminId)->exists();
|
|
|
|
$holderIds = DB::table('model_has_roles')
|
|
->where('role_id', $legacy->id)
|
|
->where('model_type', User::class)
|
|
->pluck('model_id');
|
|
|
|
$this->line(" {$legacy->name}: {$holderIds->count()} holder(s)" . ($exists ? '' : ' (admin profile no longer exists)'));
|
|
|
|
if ($exists) {
|
|
foreach (User::whereKey($holderIds)->get() as $user) {
|
|
$target = "Admin\\{$adminId}\\{$defaultName}";
|
|
if ($user->hasRole($target)) {
|
|
$this->line(" user #{$user->id} already has {$target}");
|
|
continue;
|
|
}
|
|
$this->line(" user #{$user->id} gets {$target}");
|
|
if (!$dryRun) {
|
|
$definition->assignScopedTo($user, $adminId);
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->line(" remove {$legacy->name}");
|
|
if (!$dryRun) {
|
|
$legacy->delete();
|
|
}
|
|
}
|
|
|
|
if (!$dryRun) {
|
|
app(PermissionRegistrar::class)->forgetCachedPermissions();
|
|
$this->info('Done.');
|
|
}
|
|
|
|
return self::SUCCESS;
|
|
}
|
|
}
|