diff --git a/app/Livewire/AppContainer.php b/app/Livewire/AppContainer.php index b857dc7..946a090 100644 --- a/app/Livewire/AppContainer.php +++ b/app/Livewire/AppContainer.php @@ -15,7 +15,10 @@ class AppContainer extends Component public function mount() { - $this->loadApps(); + // Only load data if user is authorized to view it + if (auth()->user()->can('viewAny', Application::class)) { + $this->loadApps(); + } } public function loadApps() @@ -38,6 +41,8 @@ class AppContainer extends Component public function deleteApp() { + $this->authorize('delete', $this->confirmDeleteApp); + $this->confirmDeleteApp->delete(); $deletedId = $this->confirmDeleteApp->id; $this->confirmDeleteApp = null; diff --git a/app/Livewire/Forms/NewApplication.php b/app/Livewire/Forms/NewApplication.php index 3643652..dc2e65c 100644 --- a/app/Livewire/Forms/NewApplication.php +++ b/app/Livewire/Forms/NewApplication.php @@ -16,6 +16,8 @@ class NewApplication extends Component public function create() { + $this->authorize('create', Application::class); + $this->validate(); Application::create([ diff --git a/app/Livewire/ManageUsers.php b/app/Livewire/ManageUsers.php index ceff231..d4b1746 100644 --- a/app/Livewire/ManageUsers.php +++ b/app/Livewire/ManageUsers.php @@ -16,12 +16,17 @@ class ManageUsers extends Component public function mount() { - $this->users = User::all(); - $this->invitations = Invitation::all(); + // Only load data if user is authorized to view it + if (auth()->user()->can('viewAny', User::class)) { + $this->users = User::all(); + $this->invitations = Invitation::all(); + } } public function inviteUser() { + $this->authorize('invite', User::class); + $inv = Invitation::create([ 'code' => str()->random(50), 'email' => $this->invite_email, @@ -29,6 +34,20 @@ class ManageUsers extends Component 'expires_at' => now()->addDays(7), ]); Flux::modal('invite-user')->close(); + + // Refresh the data + $this->invitations = Invitation::all(); + $this->invite_email = ''; + } + + public function deleteUser(User $user) + { + $this->authorize('delete', $user); + + $user->delete(); + + // Refresh the data + $this->users = User::all(); } public function render() diff --git a/app/Models/User.php b/app/Models/User.php index 71cafdf..6885e48 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -24,7 +24,8 @@ class User extends Authenticatable 'email', 'password', 'avatar', - 'preferred_username' + 'preferred_username', + 'is_admin' ]; /** @@ -47,6 +48,7 @@ class User extends Authenticatable return [ 'email_verified_at' => 'datetime', 'password' => 'hashed', + 'is_admin' => 'boolean', ]; } @@ -71,4 +73,9 @@ class User extends Authenticatable { return route('user.avatar', ['path' => $this->avatar]); } + + public function isAdmin(): bool + { + return $this->is_admin; + } } diff --git a/app/Policies/ApplicationPolicy.php b/app/Policies/ApplicationPolicy.php new file mode 100644 index 0000000..f7bbef5 --- /dev/null +++ b/app/Policies/ApplicationPolicy.php @@ -0,0 +1,65 @@ +isAdmin(); + } + + /** + * Determine whether the user can view the model. + */ + public function view(User $user, Application $application): bool + { + return $user->isAdmin(); + } + + /** + * Determine whether the user can create models. + */ + public function create(User $user): bool + { + return $user->isAdmin(); + } + + /** + * Determine whether the user can update the model. + */ + public function update(User $user, Application $application): bool + { + return $user->isAdmin(); + } + + /** + * Determine whether the user can delete the model. + */ + public function delete(User $user, Application $application): bool + { + return $user->isAdmin(); + } + + /** + * Determine whether the user can restore the model. + */ + public function restore(User $user, Application $application): bool + { + return $user->isAdmin(); + } + + /** + * Determine whether the user can permanently delete the model. + */ + public function forceDelete(User $user, Application $application): bool + { + return $user->isAdmin(); + } +} \ No newline at end of file diff --git a/app/Policies/UserPolicy.php b/app/Policies/UserPolicy.php new file mode 100644 index 0000000..0df9587 --- /dev/null +++ b/app/Policies/UserPolicy.php @@ -0,0 +1,75 @@ +isAdmin(); + } + + /** + * Determine whether the user can view the model. + */ + public function view(User $user, User $model): bool + { + // Users can view their own profile, or admins can view any user + return $user->id === $model->id || $user->isAdmin(); + } + + /** + * Determine whether the user can create models. + */ + public function create(User $user): bool + { + return $user->isAdmin(); + } + + /** + * Determine whether the user can update the model. + */ + public function update(User $user, User $model): bool + { + // Users can update their own profile, or admins can update any user + return $user->id === $model->id || $user->isAdmin(); + } + + /** + * Determine whether the user can delete the model. + */ + public function delete(User $user, User $model): bool + { + // Only admins can delete users, and they cannot delete themselves + return $user->isAdmin() && $user->id !== $model->id; + } + + /** + * Determine whether the user can restore the model. + */ + public function restore(User $user, User $model): bool + { + return $user->isAdmin(); + } + + /** + * Determine whether the user can permanently delete the model. + */ + public function forceDelete(User $user, User $model): bool + { + return $user->isAdmin() && $user->id !== $model->id; + } + + /** + * Determine whether the user can invite new users. + */ + public function invite(User $user): bool + { + return $user->isAdmin(); + } +} \ No newline at end of file diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index 584104c..0c99fba 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -29,6 +29,7 @@ class UserFactory extends Factory 'email_verified_at' => now(), 'password' => static::$password ??= Hash::make('password'), 'remember_token' => Str::random(10), + 'is_admin' => false, ]; } @@ -41,4 +42,14 @@ class UserFactory extends Factory 'email_verified_at' => null, ]); } + + /** + * Indicate that the user should be an admin. + */ + public function admin(): static + { + return $this->state(fn (array $attributes) => [ + 'is_admin' => true, + ]); + } } diff --git a/database/migrations/2025_08_02_013524_add_is_admin_to_users_table.php b/database/migrations/2025_08_02_013524_add_is_admin_to_users_table.php new file mode 100644 index 0000000..0b3c749 --- /dev/null +++ b/database/migrations/2025_08_02_013524_add_is_admin_to_users_table.php @@ -0,0 +1,28 @@ +boolean('is_admin')->default(false); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('is_admin'); + }); + } +}; diff --git a/database/seeders/UserSeeder.php b/database/seeders/UserSeeder.php index 98acb56..1a1893d 100644 --- a/database/seeders/UserSeeder.php +++ b/database/seeders/UserSeeder.php @@ -12,5 +12,24 @@ class UserSeeder extends Seeder /** * Run the database seeds. */ - public function run(): void {} + public function run(): void + { + // Create an admin user + User::create([ + 'name' => 'Admin User', + 'email' => 'admin@example.com', + 'password' => Hash::make('password'), + 'email_verified_at' => now(), + 'is_admin' => true, + ]); + + // Create a regular user + User::create([ + 'name' => 'Regular User', + 'email' => 'user@example.com', + 'password' => Hash::make('password'), + 'email_verified_at' => now(), + 'is_admin' => false, + ]); + } } diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php index bffac9d..002ebb9 100644 --- a/resources/views/dashboard.blade.php +++ b/resources/views/dashboard.blade.php @@ -1,16 +1,36 @@
-
- -
+ @can('viewAny', App\Models\User::class) +
+ +
+ @endcan +
+
-
- -
+ + @can('viewAny', App\Models\Application::class) +
+ +
+ @endcan + + @cannot('viewAny', App\Models\Application::class) +
+
+
+

Welcome to AuthentiKate

+

+ You can manage your profile and view your authentication tokens above. +

+
+
+
+ @endcannot
\ No newline at end of file diff --git a/resources/views/livewire/app-container.blade.php b/resources/views/livewire/app-container.blade.php index a5b35d5..a415d5e 100644 --- a/resources/views/livewire/app-container.blade.php +++ b/resources/views/livewire/app-container.blade.php @@ -1,17 +1,22 @@ +@can('viewAny', App\Models\Application::class)
+ @can('create', App\Models\Application::class) New App + @endcan
@foreach ($apps as $app)
+ @can('view', $app) + @endcan
@@ -30,5 +35,8 @@ @endif + @can('create', App\Models\Application::class) -
\ No newline at end of file + @endcan +
+@endcan \ No newline at end of file diff --git a/resources/views/livewire/manage-users.blade.php b/resources/views/livewire/manage-users.blade.php index be11430..b579c76 100644 --- a/resources/views/livewire/manage-users.blade.php +++ b/resources/views/livewire/manage-users.blade.php @@ -1,3 +1,4 @@ +@can('viewAny', App\Models\User::class)
Users @@ -9,15 +10,20 @@ {{$u->name}} {{$u->email}}
+ @can('delete', $u) + Delete + @endcan @endforeach
Invitations + @can('invite', App\Models\User::class)
Create
+ @endcan
@foreach ($invitations as $inv) @@ -35,12 +41,12 @@ {{$inv->status}} @endswitch
- Invite link: {{route('register', ['code' => $inv->code])}}
Copy invite link
@endforeach + @can('invite', App\Models\User::class) Invite User @@ -49,4 +55,6 @@ Create invitation -
\ No newline at end of file + @endcan + +@endcan \ No newline at end of file