diff --git a/app/Enums/Permissions/RoleAndAppsPermissionEnum.php b/app/Enums/Permissions/RoleAndAppsPermissionEnum.php index e461fe6..3517609 100644 --- a/app/Enums/Permissions/RoleAndAppsPermissionEnum.php +++ b/app/Enums/Permissions/RoleAndAppsPermissionEnum.php @@ -11,4 +11,5 @@ enum RoleAndAppsPermissionEnum: string case Read = 'roles-and-apps:read'; case Update = 'roles-and-apps:update'; case Delete = 'roles-and-apps:delete'; + case ConnectApps = 'roles-and-apps:connect-apps'; } diff --git a/app/Models/ConnectedApp.php b/app/Models/ConnectedApp.php index 70b633c..4157f86 100644 --- a/app/Models/ConnectedApp.php +++ b/app/Models/ConnectedApp.php @@ -5,6 +5,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Attributes\Fillable; +use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\{Model, Relations\BelongsTo, Relations\BelongsToMany, SoftDeletes}; use Illuminate\Database\Eloquent\Relations\HasOne; use Spatie\Activitylog\Models\Concerns\LogsActivity; @@ -24,7 +25,7 @@ ])] class ConnectedApp extends Model { - use LogsActivity, SoftDeletes; + use HasFactory, LogsActivity, SoftDeletes; /** * @return HasOne diff --git a/database/factories/ConnectedAppFactory.php b/database/factories/ConnectedAppFactory.php new file mode 100644 index 0000000..9caf046 --- /dev/null +++ b/database/factories/ConnectedAppFactory.php @@ -0,0 +1,35 @@ + + */ +class ConnectedAppFactory extends Factory +{ + protected $model = ConnectedApp::class; + + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + $name = $this->faker->unique()->company().' '.$this->faker->randomElement(['SSO', 'Portal', 'OAuth', 'API', 'App']); + + return [ + 'name' => $name, + 'slug' => Str::slug($name), + 'connection_protocol_id' => ConnectionProtocol::inRandomOrder()->first()?->id ?? 1, + 'connection_provider_id' => ConnectionProvider::inRandomOrder()->first()?->id ?? 1, + 'connection_status_id' => ConnectionStatus::inRandomOrder()->first()?->id ?? 1, + ]; + } +} diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index e189687..8b90a38 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -21,5 +21,6 @@ public function run(): void $this->call(PermissionSeeder::class); $this->call(DefaultRoleWithPermissionSeeder::class); $this->call(ExampleUserWithRoleSeeder::class); + $this->call(MockDataSeeder::class); } } diff --git a/database/seeders/MockDataSeeder.php b/database/seeders/MockDataSeeder.php new file mode 100644 index 0000000..8d5b3a2 --- /dev/null +++ b/database/seeders/MockDataSeeder.php @@ -0,0 +1,79 @@ + $name) { + $role = Role::findOrCreate($name, 'web'); + $role->update(['priority' => $priorities[$index]]); + $roles[] = $role; + } + + // 2. Create 5 users and assign them role combinations + $users = User::factory()->count(5)->create(); + + // Assign combinations + $users[0]->assignRole(['Manager', 'Editor']); + $users[1]->assignRole(['Developer', 'Support']); + $users[2]->assignRole(['Auditor']); + $users[3]->assignRole(['Manager', 'Developer']); + $users[4]->assignRole(['Support']); + + // 3. Create 10 apps using ConnectedAppService to automatically register permissions + $apps = []; + $factory = ConnectedApp::factory(); + + for ($i = 0; $i < 10; $i++) { + $data = $factory->definition(); + $request = new ConnectAppRequest( + name: $data['name'], + connectionProviderId: $data['connection_provider_id'], + connectionProtocolId: $data['connection_protocol_id'], + slug: $data['slug'], + connectionStatusId: $data['connection_status_id'], + ); + + $this->connectedAppService->create($request); + $apps[] = ConnectedApp::where('slug', $data['slug'])->firstOrFail(); + } + + // 4. Connect apps to roles using AppRoleService to automatically assign permissions + $roleAppsMapping = [ + 'Manager' => [$apps[0]->id, $apps[1]->id, $apps[2]->id], + 'Editor' => [$apps[2]->id, $apps[3]->id, $apps[4]->id], + 'Auditor' => [$apps[5]->id], + 'Developer' => [$apps[6]->id, $apps[7]->id, $apps[8]->id], + 'Support' => [$apps[8]->id, $apps[9]->id], + ]; + + foreach ($roleAppsMapping as $roleName => $appIds) { + $role = Role::findByName($roleName, 'web'); + $this->appRoleService->assign(new AssignAppsToRoleData( + roleId: $role->id, + validUpto: now()->addYears(5)->toDateTimeString(), + appIds: $appIds + )); + } + } +} diff --git a/resources/views/pages/access-manager/roles-and-apps/⚡show.blade.php b/resources/views/pages/access-manager/roles-and-apps/⚡show.blade.php index 447aadc..a25fdb6 100644 --- a/resources/views/pages/access-manager/roles-and-apps/⚡show.blade.php +++ b/resources/views/pages/access-manager/roles-and-apps/⚡show.blade.php @@ -3,6 +3,7 @@ use App\Concerns\{Confirmation, HandlesOperations}; use App\Data\Role\{AssignAppsToRoleData, AssignUsersToRoleData, RoleAppData, UpdateRolePriorityData}; use App\Data\Ui\TableHeader; +use App\Enums\Permissions\RoleAndAppsPermissionEnum; use App\Livewire\Forms\AssignAppToRoleForm; use App\Models\Role; use App\Services\{AppRoleService, RoleService}; @@ -165,6 +166,8 @@ public function clearApps(): void public function assignApp(): void { + $this->authorize(RoleAndAppsPermissionEnum::ConnectApps->value); + $this->attempt( action: function (): void { $this->assignForm->validate(); @@ -183,6 +186,8 @@ public function assignApp(): void public function updateApp(): void { + $this->authorize(RoleAndAppsPermissionEnum::ConnectApps->value); + $this->attempt( action: function (): void { $this->assignForm->validate(); @@ -202,6 +207,8 @@ public function updateApp(): void public function detachApp(int $id): void { + $this->authorize(RoleAndAppsPermissionEnum::ConnectApps->value); + $this->attempt( action: function () use ($id): void { $this->appRoleService->detach($id); @@ -319,9 +326,11 @@ class="btn-sm btn-ghost btn-circle" - - Add App - + @can(RoleAndAppsPermissionEnum::ConnectApps->value) + + Add App + + @endcan @if ($this->apps->count()) @@ -339,18 +348,20 @@ class="btn-sm btn-ghost btn-circle" @scope('actions', $row)
- - + @can(RoleAndAppsPermissionEnum::ConnectApps->value) + + + @endcan
@endscope