Features
Users & groups
Admin Users and Groups manage who can sign in and how they inherit roles. Both support soft-delete, restore, force-delete, and bulk actions. Groups attach members and Spatie roles so permissions (and role-tied collection/file ACL) flow through EffectivePermissionResolver.
Related
How group roles become effective permissions: Effective permissions. Role assignment UI: Roles & permissions. Drawer URLs: Routing — deep-links (?edit= / ?new=1).
Permissions
Exact PermissionEnum strings (do not invent others):
| Area | Permissions |
|---|---|
| Users | can-show-users, can-create-users, can-edit-users, can-delete-users, can-restore-users, can-force-delete-users |
| Groups | can-show-groups, can-create-groups, can-edit-groups, can-delete-groups, can-restore-groups, can-force-delete-groups |
Route middleware uses permission:… for most endpoints. Bulk actions authorize inside BulkUserActionRequest / BulkUserGroupActionRequest (maps delete / restore / force_delete to the matching permission).
Users
Controller & page
| Item | Detail |
|---|---|
| Controller | App\Http\Controllers\Admin\UserController |
| Model | App\Models\User (SoftDeletes, Spatie HasRoles, Fortify TwoFactorAuthenticatable + PasskeyAuthenticatable) |
| Frontend | resources/js/pages/admin/users/index.tsx |
| Routes | routes/admin.php under auth + verified |
Lifecycle
| Action | Route | Permission |
|---|---|---|
| List / search / trash filter | GET /users | can-show-users |
| Create | POST /users | can-create-users |
| Update | PUT /users/{user} | can-edit-users |
| Soft-delete | DELETE /users/{user} | can-delete-users |
| Restore | POST /users/{user}/restore | can-restore-users |
| Force-delete | DELETE /users/{user}/force | can-force-delete-users |
| Bulk | POST /users/bulk-actions | Per action in FormRequest |
Route binding uses withTrashed() so restore/force-delete can target soft-deleted users.
Bulk actions (BulkUserActionRequest): action ∈ delete | restore | force_delete, plus ids[].
Create / update fields
Validated by StoreUserRequest / UpdateUserRequest:
| Field | Notes |
|---|---|
first_name | Required |
last_name | Optional |
email | Required, unique |
username | Optional |
password | Required on create; optional on update |
is_active | Optional boolean |
role_ids | Optional array — synced via Spatie |
group_ids | Optional array — synced on groups() pivot |
last_login
On Illuminate\Auth\Events\Login, AppServiceProvider::registerAuthActivityListeners updates:
last_login_atlast_login_ip
via forceFill(...)->saveQuietly() on the user.
UI exposure
Those columns exist and are maintained on login. They are not currently exposed in UserResource or the admin users table (not listed/sortable in the UI).
Groups
Why groups (not Directus-style teams)
Groups are a thin inheritance layer for large user bases: attach Spatie roles to a group, add members, and every member inherits:
- Admin Spatie permissions from those roles (via
EffectivePermissionResolver) - Collection / file ACL tied to the same roles (
collection_permissions/file_permissionsmatrices on the role)
There is no separate group → collection_permissions matrix. Configure access on the role; scale membership via the group.
Controller & page
| Item | Detail |
|---|---|
| Controller | App\Http\Controllers\Admin\UserGroupController |
| Model | App\Models\UserGroup (SoftDeletes, resolveRouteBinding includes trash) |
| Pivots | user_group_user (members), user_group_role (roles) |
| Frontend | resources/js/pages/admin/groups/index.tsx |
Routes
| Action | Route | Permission |
|---|---|---|
| List / search / trash filter | GET /groups | can-show-groups |
| Create | POST /groups | can-create-groups |
| Update | PUT /groups/{group} | can-edit-groups |
| Soft-delete | DELETE /groups/{group} | can-delete-groups |
| Restore | POST /groups/{group}/restore | can-restore-groups |
| Force-delete | DELETE /groups/{group}/force | can-force-delete-groups |
| Bulk | POST /groups/bulk-actions | Per action in FormRequest |
Bulk actions (BulkUserGroupActionRequest): action ∈ delete | restore | force_delete, plus ids[].
Members + role attachment
Store/update accept:
| Field | Role |
|---|---|
name | Required |
description | Optional |
user_ids | Optional — sync group members when present (omit to leave members unchanged; empty array clears) |
role_ids | Optional — sync Spatie roles onto the group when present |
Index payloads include user_ids and users (id / name / email) so the edit drawer can hydrate the member multi-select.
Permission inheritance: EffectivePermissionResolver unions:
- Roles assigned directly on the user
- Roles attached to each of the user’s groups
effectiveRoleIds() feeds the same set into admin collection field ACL (CollectionPermissionGuard::rulesForUser). See Effective permissions.
Source map
| Concern | Location |
|---|---|
| Users controller | app/Http/Controllers/Admin/UserController.php |
| Groups controller | app/Http/Controllers/Admin/UserGroupController.php |
| Admin routes | routes/admin.php |
| Client route helpers | resources/js/lib/admin-routes.ts |
| Auth login timestamp | AppServiceProvider::registerAuthActivityListeners |
| Post-login home | App\Support\Auth\HomePath (uses EffectivePermissionResolver) |