From 709efc9fb0eb0ed8432854727abe48f012785917 Mon Sep 17 00:00:00 2001 From: Twyco Date: Sun, 12 Apr 2026 21:23:23 +0200 Subject: [PATCH] added team create permission --- app/Enums/Permission.php | 12 ++--- app/Http/Controllers/TeamController.php | 2 +- app/Http/Middleware/HandleInertiaRequests.php | 2 +- app/Http/Requests/StoreTeamRequest.php | 2 +- app/Providers/AppServiceProvider.php | 2 + routes/web.php | 4 +- tests/Feature/Teams/TeamControllerTest.php | 50 +++++++++++++++++++ 7 files changed, 62 insertions(+), 12 deletions(-) diff --git a/app/Enums/Permission.php b/app/Enums/Permission.php index 9300cbb..976023c 100644 --- a/app/Enums/Permission.php +++ b/app/Enums/Permission.php @@ -14,6 +14,7 @@ enum Permission: string case ProjectRead = 'project:read'; case ProjectWrite = 'project:write'; case ProjectAdmin = 'project:admin'; + case TeamCreate = 'team:create'; case TeamInvite = 'team:invite'; case TeamManage = 'team:manage'; case TeamAdmin = 'team:admin'; @@ -26,6 +27,7 @@ enum Permission: string self::ProjectRead => 'Project read', self::ProjectWrite => 'Project write', self::ProjectAdmin => 'Project admin', + self::TeamCreate => 'Team create', self::TeamInvite => 'Team invite', self::TeamManage => 'Team manage', self::TeamAdmin => 'Team admin', @@ -35,7 +37,7 @@ enum Permission: string public function scope(): PermissionScope { return match ($this) { - self::GlobalAdmin, self::ProjectCreate => PermissionScope::Global, + self::GlobalAdmin, self::ProjectCreate, self::TeamCreate => PermissionScope::Global, self::ProjectRead, self::ProjectWrite, self::ProjectAdmin => PermissionScope::Project, self::TeamInvite, self::TeamManage, self::TeamAdmin => PermissionScope::Team, }; @@ -64,13 +66,8 @@ enum Permission: string return true; } - foreach ($this->impliedPermissions() as $impliedPermission) { - if ($impliedPermission->includes($permission)) { - return true; - } - } + return array_any($this->impliedPermissions(), fn ($impliedPermission) => $impliedPermission->includes($permission)); - return false; } public function isAssignableTo(Model|string $assignee): bool @@ -118,6 +115,7 @@ enum Permission: string { return [ self::GlobalAdmin, + self::TeamCreate, self::ProjectCreate, ]; } diff --git a/app/Http/Controllers/TeamController.php b/app/Http/Controllers/TeamController.php index 273e415..1d31f8e 100644 --- a/app/Http/Controllers/TeamController.php +++ b/app/Http/Controllers/TeamController.php @@ -24,7 +24,7 @@ class TeamController extends Controller public function index(Request $request): Response { $user = $request->user(); - $canCreateTeams = $this->permissionService->allows($user, Permission::GlobalAdmin); + $canCreateTeams = $this->permissionService->allows($user, Permission::TeamCreate); $manageableTeamIds = $this->permissionService->manageableTeamIds($user); abort_unless($canCreateTeams || $manageableTeamIds !== [], 403); diff --git a/app/Http/Middleware/HandleInertiaRequests.php b/app/Http/Middleware/HandleInertiaRequests.php index e874437..d43d301 100644 --- a/app/Http/Middleware/HandleInertiaRequests.php +++ b/app/Http/Middleware/HandleInertiaRequests.php @@ -48,7 +48,7 @@ class HandleInertiaRequests extends Middleware 'name' => config('app.name'), 'auth' => [ 'permissions' => [ - 'canManageTeams' => $user !== null && $this->permissionService->canManageAnyTeam($user), + 'canManageTeams' => $user !== null && ($this->permissionService->canManageAnyTeam($user) || $this->permissionService->allows($user, Permission::TeamCreate)), 'globalAdmin' => $user !== null && $this->permissionService->allows($user, Permission::GlobalAdmin), 'projectCreate' => $user !== null && $this->permissionService->allows($user, Permission::ProjectCreate), ], diff --git a/app/Http/Requests/StoreTeamRequest.php b/app/Http/Requests/StoreTeamRequest.php index 3cfb859..2af2e61 100644 --- a/app/Http/Requests/StoreTeamRequest.php +++ b/app/Http/Requests/StoreTeamRequest.php @@ -13,7 +13,7 @@ class StoreTeamRequest extends FormRequest */ public function authorize(): bool { - return $this->user()?->can('access-admin') ?? false; + return $this->user()?->can('create-team') ?? false; } /** diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index f00b42a..51e55d9 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -56,7 +56,9 @@ class AppServiceProvider extends ServiceProvider Gate::define('access-admin', fn (User $user): bool => $permissionService->allows($user, Permission::GlobalAdmin)); Gate::define('create-project', fn (User $user): bool => $permissionService->allows($user, Permission::ProjectCreate)); + Gate::define('create-team', fn (User $user): bool => $permissionService->allows($user, Permission::TeamCreate)); Gate::define('manage-teams', fn (User $user): bool => $permissionService->canManageAnyTeam($user)); + Gate::define('view-teams', fn (User $user): bool => Gate::forUser($user)->any(['create-team', 'manage-teams'])); } /** diff --git a/routes/web.php b/routes/web.php index 8ab29b2..078ebe4 100644 --- a/routes/web.php +++ b/routes/web.php @@ -105,10 +105,10 @@ Route::middleware(['auth', 'verified'])->group(function () { }); Route::get('teams', [TeamController::class, 'index']) - ->middleware('can:manage-teams') + ->middleware('can:view-teams') ->name('teams.index'); Route::post('teams', [TeamController::class, 'store']) - ->middleware('can:access-admin') + ->middleware('can:create-team') ->name('teams.store'); Route::patch('teams/{team}/members', [TeamController::class, 'updateMembers']) ->name('teams.members.update'); diff --git a/tests/Feature/Teams/TeamControllerTest.php b/tests/Feature/Teams/TeamControllerTest.php index d3cded2..f716444 100644 --- a/tests/Feature/Teams/TeamControllerTest.php +++ b/tests/Feature/Teams/TeamControllerTest.php @@ -88,6 +88,56 @@ class TeamControllerTest extends TestCase ]); } + /** + * Ensure admins can view the teams management page. + */ + public function test_non_admins_with_team_create_permission_can_view_the_teams_management_page(): void + { + $createTeamUser = User::factory()->create(); + $member = User::factory()->create([ + 'name' => 'Regular User', + 'email' => 'member@example.com', + ]); + $team = Team::factory()->create([ + 'name' => 'Platform Team', + ]); + $team->users()->attach($member); + + $this->syncGlobalPermissions($createTeamUser, ['team:create']); + + $this->actingAs($createTeamUser) + ->get(route('teams.index')) + ->assertOk() + ->assertInertia(fn (Assert $page) => $page + ->component('teams/Index') + ->where('canCreateTeams', true) + ->where('teams.0.name', 'Platform Team') + ->where('teams.0.user_ids.0', $member->id) + ->where('teams.0.users.0.email', 'member@example.com'), + ); + } + + /** + * Ensure admins can create teams. + */ + public function test_non_admins_with_team_create_permission_can_create_teams(): void + { + $createTeamUser = User::factory()->create(); + + $this->syncGlobalPermissions($createTeamUser, ['team:create']); + + $this->actingAs($createTeamUser) + ->post(route('teams.store'), [ + 'name' => 'Delivery Team', + ]) + ->assertRedirect(route('teams.index')) + ->assertSessionHas('status', 'Delivery Team was created.'); + + $this->assertDatabaseHas('teams', [ + 'name' => 'Delivery Team', + ]); + } + /** * Ensure users without team management access cannot view the teams page. */