From ef1913fa638d66792768a7ee8a1a4606d8cd73b8 Mon Sep 17 00:00:00 2001 From: Dan Harrin Date: Thu, 4 Mar 2021 10:21:00 +0000 Subject: [PATCH] Custom user model improvements --- resources/lang/en/resources/user-resource.php | 7 +- routes/web.php | 1 + src/AuthorizationManager.php | 4 +- src/Commands/MakeUserCommand.php | 10 ++- src/Http/Livewire/EditAccount.php | 13 ++-- src/Http/Middleware/Authenticate.php | 8 ++- src/Models/Concerns/IsFilamentUser.php | 26 ++++++-- src/Models/User.php | 2 + src/Resources/UserResource.php | 41 ++++++++---- src/Resources/UserResource/Pages/EditUser.php | 13 ++-- src/View/Components/Avatar.php | 6 +- tests/Feature/EditAccount.php | 64 +++++++++---------- 12 files changed, 127 insertions(+), 68 deletions(-) diff --git a/resources/lang/en/resources/user-resource.php b/resources/lang/en/resources/user-resource.php index 1199b19846..934611f4f4 100644 --- a/resources/lang/en/resources/user-resource.php +++ b/resources/lang/en/resources/user-resource.php @@ -13,7 +13,12 @@ return [ ], 'isAdmin' => [ - 'label' => 'Administrator?', + 'label' => 'Filament admin?', + 'helpMessage' => 'Filament admins are able to access all areas of Filament and manage other users.', + ], + + 'isUser' => [ + 'label' => 'Filament user?', ], 'name' => [ diff --git a/routes/web.php b/routes/web.php index bc0e2ebaa6..96c824001e 100644 --- a/routes/web.php +++ b/routes/web.php @@ -6,6 +6,7 @@ use Filament\Http\Controllers; use Filament\Http\Livewire; use Filament\Http\Middleware\Authenticate; use Filament\Http\Middleware\AuthorizeAdmins; +use Filament\Http\Middleware\AuthorizeUsers; use Filament\Http\Middleware\RedirectIfAuthenticated; use Filament\Resources\UserResource; use Illuminate\Routing\Middleware\ValidateSignature; diff --git a/src/AuthorizationManager.php b/src/AuthorizationManager.php index 40d1a93607..87a43a2028 100644 --- a/src/AuthorizationManager.php +++ b/src/AuthorizationManager.php @@ -52,7 +52,7 @@ class AuthorizationManager if ($this->mode === 'allow') { foreach ($this->authorizations as $authorization) { - if ($user->hasRole($authorization->role)) { + if ($user->hasFilamentRole($authorization->role)) { return true; } } @@ -62,7 +62,7 @@ class AuthorizationManager if ($this->mode === 'deny') { foreach ($this->authorizations as $authorization) { - if ($user->hasRole($authorization->role)) { + if ($user->hasFilamentRole($authorization->role)) { return false; } } diff --git a/src/Commands/MakeUserCommand.php b/src/Commands/MakeUserCommand.php index b13d813b58..0b239144f9 100644 --- a/src/Commands/MakeUserCommand.php +++ b/src/Commands/MakeUserCommand.php @@ -26,10 +26,14 @@ class MakeUserCommand extends Command $details['password'] = Hash::make($this->validateInput(fn () => $this->secret('Password'), 'password', ['required', 'min:8'])); - if ($userModel::getFilamentAdminColumn() !== null) { - $column = $userModel::getFilamentAdminColumn(); + $adminColumn = $userModel::getFilamentAdminColumn(); + if ($adminColumn !== null) { + $details[$adminColumn] = $this->confirm('Would you like this user to be an administrator?', true); + } - $details[$column] = $this->confirm('Would you like this user to be an administrator?', true); + $userColumn = $userModel::getFilamentUserColumn(); + if ($userColumn !== null) { + $details[$userColumn] = true; } $user = $userModel::create($details); diff --git a/src/Http/Livewire/EditAccount.php b/src/Http/Livewire/EditAccount.php index 2f06a4c120..ce5e0864b6 100644 --- a/src/Http/Livewire/EditAccount.php +++ b/src/Http/Livewire/EditAccount.php @@ -50,15 +50,18 @@ class EditAccount extends Page $this->validate(); - if ($this->record->password) { - $this->record->password = Hash::make($this->record->password); - } - - unset($this->record->password); unset($this->record->passwordConfirmation); + if ($this->record->password) { + $this->record->password = Hash::make($this->record->password); + } else { + unset($this->record->password); + } + $this->record->save(); + $this->record->password = null; + $this->notify(__('filament::edit-account.messages.saved')); } } diff --git a/src/Http/Middleware/Authenticate.php b/src/Http/Middleware/Authenticate.php index 1294793a1e..871772ef68 100644 --- a/src/Http/Middleware/Authenticate.php +++ b/src/Http/Middleware/Authenticate.php @@ -8,8 +8,12 @@ class Authenticate extends Middleware { protected function authenticate($request, array $guards) { - if ($this->auth->guard('filament')->check()) { - return $this->auth->shouldUse('filament'); + $guard = config('filament.auth.guard'); + + if ($this->auth->guard($guard)->check()) { + abort_unless($this->auth->guard($guard)->user()->canAccessFilament(), 403); + + return $this->auth->shouldUse($guard); } $this->unauthenticated($request, $guards); diff --git a/src/Models/Concerns/IsFilamentUser.php b/src/Models/Concerns/IsFilamentUser.php index 858a8a6f1f..88c27cce51 100644 --- a/src/Models/Concerns/IsFilamentUser.php +++ b/src/Models/Concerns/IsFilamentUser.php @@ -13,6 +13,15 @@ trait IsFilamentUser return null; } + public static function getFilamentAvatarColumn() + { + if (property_exists(static::class, 'filamentAvatarColumn')) { + return static::$filamentAvatarColumn; + } + + return null; + } + public static function getFilamentRolesColumn() { if (property_exists(static::class, 'filamentRolesColumn')) { @@ -35,25 +44,34 @@ trait IsFilamentUser { $column = static::getFilamentUserColumn(); - return $column ? + return $column !== null ? $this->{$column} : true; } - public function hasRole($role) + public function hasFilamentRole($role) { $column = static::getFilamentRolesColumn(); - return $column ? + return $column !== null ? in_array($role, $this->{$column}) : true; } + public function getFilamentAvatar() + { + $column = static::getFilamentAvatarColumn(); + + return $column !== null ? + $this->{$column} : + null; + } + public function isFilamentAdmin() { $column = static::getFilamentAdminColumn(); - return $column ? + return $column !== null ? $this->{$column} : true; } diff --git a/src/Models/User.php b/src/Models/User.php index 3ebf24e4ac..8603fa9589 100644 --- a/src/Models/User.php +++ b/src/Models/User.php @@ -19,6 +19,8 @@ class User extends Authenticatable implements FilamentUser public static $filamentAdminColumn = 'is_admin'; + public static $filamentAvatarColumn = 'avatar'; + public static $filamentRolesColumn = 'roles'; protected $casts = [ diff --git a/src/Resources/UserResource.php b/src/Resources/UserResource.php index 378196b9f8..3d6d103941 100644 --- a/src/Resources/UserResource.php +++ b/src/Resources/UserResource.php @@ -60,6 +60,21 @@ class UserResource extends Resource Components\Grid::make(function () { $schema = []; + $userColumn = Filament::auth()->getProvider()->getModel()::getFilamentUserColumn(); + if ($userColumn !== null) { + $schema[] = Components\Checkbox::make($userColumn) + ->label('filament::resources/user-resource.form.isUser.label') + ->except(EditAccount::class); + } + + $adminColumn = Filament::auth()->getProvider()->getModel()::getFilamentAdminColumn(); + if ($adminColumn !== null) { + $schema[] = Components\Checkbox::make($adminColumn) + ->label('filament::resources/user-resource.form.isAdmin.label') + ->helpMessage('filament::resources/user-resource.form.isAdmin.helpMessage') + ->except(EditAccount::class); + } + $rolesColumn = Filament::auth()->getProvider()->getModel()::getFilamentRolesColumn(); if ($rolesColumn !== null) { $schema[] = Components\MultiSelect::make($rolesColumn) @@ -69,21 +84,19 @@ class UserResource extends Resource collect(Filament::getRoles()) ->mapWithKeys(fn ($role) => [$role => Str::ucfirst($role::getLabel())]) ->toArray(), - ); + ) + ->except(EditAccount::class); } - $adminColumn = Filament::auth()->getProvider()->getModel()::getFilamentAdminColumn(); - if ($adminColumn !== null) { - $schema[] = Components\Checkbox::make($adminColumn) - ->label('filament::resources/user-resource.form.isAdmin.label'); + $avatarColumn = Filament::auth()->getProvider()->getModel()::getFilamentAvatarColumn(); + if ($avatarColumn !== null) { + $schema[] = Components\FileUpload::make('avatar') + ->label('filament::resources/user-resource.form.avatar.label') + ->avatar() + ->directory('filament-avatars') + ->disk(config('filament.default_filesystem_disk')); } - $schema[] = Components\FileUpload::make('avatar') - ->label('filament::resources/user-resource.form.avatar.label') - ->avatar() - ->directory('filament-avatars') - ->disk(config('filament.default_filesystem_disk')); - return $schema; }), ]); @@ -96,7 +109,11 @@ class UserResource extends Resource public static function navigationItems() { - return []; + if (static::getModel()::getFilamentUserColumn() === null) { + return []; + } + + return parent::navigationItems(); } public static function table(Table $table) diff --git a/src/Resources/UserResource/Pages/EditUser.php b/src/Resources/UserResource/Pages/EditUser.php index f6763ff226..0b1b36e27e 100644 --- a/src/Resources/UserResource/Pages/EditUser.php +++ b/src/Resources/UserResource/Pages/EditUser.php @@ -18,15 +18,18 @@ class EditUser extends EditRecord $this->validate(); - if ($this->record->password) { - $this->record->password = Hash::make($this->record->password); - } - - unset($this->record->password); unset($this->record->passwordConfirmation); + if ($this->record->password) { + $this->record->password = Hash::make($this->record->password); + } else { + unset($this->record->password); + } + $this->record->save(); + $this->record->password = null; + $this->notify(__(static::$savedMessage)); } } diff --git a/src/View/Components/Avatar.php b/src/View/Components/Avatar.php index 73995d35bc..4c479c495e 100644 --- a/src/View/Components/Avatar.php +++ b/src/View/Components/Avatar.php @@ -23,12 +23,14 @@ class Avatar extends Component public function src($dpr = 1) { - if (! $this->user->avatar) { + $avatar = $this->user->getFilamentAvatar(); + + if ($avatar === null) { return Gravatar::src($this->user->email, $this->size * $dpr); } return get_image_url( - $this->user->avatar, + $avatar, [ 'dpr' => $dpr, 'fit' => 'crop', diff --git a/tests/Feature/EditAccount.php b/tests/Feature/EditAccount.php index cb50db56a8..815d20cb40 100644 --- a/tests/Feature/EditAccount.php +++ b/tests/Feature/EditAccount.php @@ -40,14 +40,14 @@ class EditAccountTest extends TestCase ->assertSet('record.email', $user->email) ->assertSet('record.name', $user->name) // ->set('newAvatar', $newAvatar) - ->set('newPassword', $newPassword) - ->set('newPasswordConfirmation', $newPassword) ->set('record.email', $newUserDetails->email) ->set('record.name', $newUserDetails->name) + ->set('record.password', $newPassword) + ->set('record.passwordConfirmation', $newPassword) ->call('save') // ->assertSet('newAvatar', null) - ->assertSet('newPassword', null) - ->assertSet('newPasswordConfirmation', null) + ->assertNotSet('record.password', $newPassword) + ->assertNotSet('record.passwordConfirmation', $newPassword) ->assertDispatchedBrowserEvent('notify'); $user->refresh(); @@ -57,7 +57,7 @@ class EditAccountTest extends TestCase $this->assertEquals($newUserDetails->name, $user->name); $this->assertTrue(Auth::attempt([ 'email' => $newUserDetails->email, - 'password' => $newPassword, + 'record.password' => $newPassword, ])); } @@ -101,33 +101,6 @@ class EditAccountTest extends TestCase // ->assertHasErrors(['newAvatar' => 'image']); // } - /** @test */ - public function new_password_contains_minimum_8_characters() - { - $user = User::factory()->create(); - - $this->be($user); - - Livewire::test(EditAccount::class) - ->set('newPassword', 'pass') - ->call('save') - ->assertHasErrors(['newPassword' => 'min']); - } - - /** @test */ - public function new_password_is_confirmed() - { - $user = User::factory()->create(); - - $this->be($user); - - Livewire::test(EditAccount::class) - ->set('newPassword', 'password') - ->set('newPasswordConfirmation', 'different-password') - ->call('save') - ->assertHasErrors(['newPasswordConfirmation' => 'same']); - } - /** @test */ public function record_email_is_required() { @@ -166,4 +139,31 @@ class EditAccountTest extends TestCase ->call('save') ->assertHasErrors(['record.name' => 'required']); } + + /** @test */ + public function record_password_contains_minimum_8_characters() + { + $user = User::factory()->create(); + + $this->be($user); + + Livewire::test(EditAccount::class) + ->set('record.password', 'pass') + ->call('save') + ->assertHasErrors(['record.password' => 'min']); + } + + /** @test */ + public function record_password_is_confirmed() + { + $user = User::factory()->create(); + + $this->be($user); + + Livewire::test(EditAccount::class) + ->set('record.password', 'record.password') + ->set('record.passwordConfirmation', 'different-password') + ->call('save') + ->assertHasErrors(['record.passwordConfirmation' => 'same']); + } }