mirror of
https://github.com/filamentphp/filament.git
synced 2026-09-24 15:42:09 +08:00
Custom user model improvements
This commit is contained in:
@@ -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' => [
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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']);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user