added team create permission

This commit is contained in:
2026-04-12 21:23:23 +02:00
parent 6f19d12964
commit 709efc9fb0
7 changed files with 62 additions and 12 deletions
+5 -7
View File
@@ -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,
];
}
+1 -1
View File
@@ -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);
@@ -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),
],
+1 -1
View File
@@ -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;
}
/**
+2
View File
@@ -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']));
}
/**
+2 -2
View File
@@ -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');
@@ -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.
*/