Feat: Show Immutable Id field if user has a SAML App
- added a ImmuatableId field in manage roles - Generate ImmutableId using user Id if not available
This commit is contained in:
parent
4849736c60
commit
a37b6a91be
@ -19,6 +19,7 @@ public function __construct(
|
||||
#[DataCollectionOf(class: RoleData::class)]
|
||||
public ?DataCollection $roles = null,
|
||||
public ?array $restrictedPermissionIds = null,
|
||||
public ?string $immutableId = null,
|
||||
) {}
|
||||
|
||||
public static function fromModel(User $user)
|
||||
@ -29,7 +30,8 @@ public static function fromModel(User $user)
|
||||
email: $user->email,
|
||||
initials: $user->initials(),
|
||||
roles: RoleData::collect($user->roles, DataCollection::class),
|
||||
restrictedPermissionIds: $user->restrictedPermissions->pluck('id')->toArray()
|
||||
restrictedPermissionIds: $user->restrictedPermissions->pluck('id')->toArray(),
|
||||
immutableId: $user->immutable_id
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,4 +24,10 @@ class AssignRolesForm extends Form
|
||||
*/
|
||||
#[Validate('nullable|array')]
|
||||
public array $permissions = [];
|
||||
|
||||
/**
|
||||
* Holds the SSO Immutable ID for the user.
|
||||
*/
|
||||
#[Validate('nullable|string|max:255')]
|
||||
public string $immutableId = '';
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
*
|
||||
* @method static Builder<Role> visible()
|
||||
*/
|
||||
#[Fillable(['priority'])]
|
||||
#[Fillable(['name', 'guard_name', 'priority'])]
|
||||
class Role extends SpatieRole
|
||||
{
|
||||
use LogsActivity;
|
||||
|
||||
@ -54,17 +54,20 @@ public function getRestrictedPermissionIds(int $userId): array
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign roles to a user.
|
||||
* Assign roles to a user and update their SSO settings.
|
||||
*/
|
||||
public function assignRoles(int $userId, array $roleIds): void
|
||||
public function assignRoles(int $userId, array $roleIds, ?string $immutableId = null): void
|
||||
{
|
||||
if ($userId <= 0) {
|
||||
throw new InvalidArgumentException('Invalid id');
|
||||
}
|
||||
|
||||
DB::transaction(function () use ($userId, $roleIds): void {
|
||||
DB::transaction(function () use ($userId, $roleIds, $immutableId): void {
|
||||
$user = User::query()->findOrFail($userId);
|
||||
|
||||
$user->immutable_id = $immutableId ?: null;
|
||||
$user->save();
|
||||
|
||||
$userPriority = auth()->user()?->roles()->min('priority') ?? 999999;
|
||||
|
||||
// Get user's existing roles that have priority <= $userPriority (which are forbidden to the logged in user)
|
||||
|
||||
@ -53,6 +53,23 @@ public function getUsers(): PaginatedDataCollection
|
||||
return $this->userService->getAll();
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function showSamlSettings(): bool
|
||||
{
|
||||
if (empty($this->form->roleIds)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return \App\Models\ConnectedApp::query()
|
||||
->whereHas('roles', function ($query) {
|
||||
$query->whereIn('roles.id', $this->form->roleIds);
|
||||
})
|
||||
->whereHas('protocol', function ($query) {
|
||||
$query->where('name', 'saml');
|
||||
})
|
||||
->exists();
|
||||
}
|
||||
|
||||
public function delete(int $userId): void
|
||||
{
|
||||
$this->attempt(
|
||||
@ -80,6 +97,7 @@ public function openAssignRolesModal(int $userId): void
|
||||
$this->form->reset();
|
||||
$this->searchRoles();
|
||||
$this->form->roleIds = $user->roles()->pluck('id')->toArray();
|
||||
$this->form->immutableId = $user->immutable_id ?? '';
|
||||
$this->hydratePermissionInForm();
|
||||
$this->showAssignRolesModal = true;
|
||||
},
|
||||
@ -185,7 +203,7 @@ public function saveRoles(): void
|
||||
action: function () {
|
||||
$this->form->validate();
|
||||
$deactivatedPermissions = $this->getDeactivatedPermissions();
|
||||
$this->userService->assignRoles($this->currentUserId, $this->form->roleIds);
|
||||
$this->userService->assignRoles($this->currentUserId, $this->form->roleIds, $this->form->immutableId);
|
||||
$this->userService->syncRestrictedPermissions($this->currentUserId, $deactivatedPermissions);
|
||||
$this->showAssignRolesModal = false;
|
||||
$this->roleWithPermission = null;
|
||||
@ -314,6 +332,17 @@ class="btn-sm btn-circle btn-error btn-soft"
|
||||
label="Roles"
|
||||
/>
|
||||
|
||||
@if($this->showSamlSettings)
|
||||
<div class="mt-5 border-t border-base-300 pt-4">
|
||||
<h3 class="font-semibold text-sm text-sky-400 mb-3">SAML SSO Settings</h3>
|
||||
<x-mary-input
|
||||
wire:model="form.immutableId"
|
||||
:label="__('SAML Immutable ID')"
|
||||
placeholder="e.g. user-123-immutable (leave blank to auto-generate)"
|
||||
/>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($roleWithPermission)
|
||||
<fieldset class="mt-5 space-y-4 w-full">
|
||||
<legend class="flex w-full items-center justify-between gap-4">
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
use App\Models\{ConnectedApp, ConnectionProtocol, ConnectionProvider, ConnectionStatus, User};
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Livewire\Livewire;
|
||||
|
||||
beforeEach(function (): void {
|
||||
// Generate testing keys if not already present
|
||||
@ -111,3 +112,48 @@
|
||||
// Verify session was cleared
|
||||
expect(session('saml_pending_request'))->toBeNull();
|
||||
});
|
||||
|
||||
test('user sso settings and immutable id can be assigned via service and mapped to DTO', function (): void {
|
||||
$userService = app(App\Services\UserService::class);
|
||||
|
||||
// Assign roles and update immutable ID
|
||||
$userService->assignRoles($this->user->id, [], 'new-custom-sso-immutable-id');
|
||||
|
||||
// Retrieve fresh user and assert
|
||||
$freshUser = $this->user->fresh();
|
||||
expect($freshUser->immutable_id)->toBe('new-custom-sso-immutable-id');
|
||||
|
||||
// Assert UserIndexData maps it correctly
|
||||
$dto = App\Data\User\UserIndexData::fromModel($freshUser);
|
||||
expect($dto->immutableId)->toBe('new-custom-sso-immutable-id');
|
||||
});
|
||||
|
||||
test('saml settings field is only visible if selected roles contain a SAML app', function (): void {
|
||||
// Create a role with a SAML connected app
|
||||
$samlRole = App\Models\Role::findOrCreate('SAML User');
|
||||
$samlRole->update(['priority' => 10]);
|
||||
$samlRole->apps()->attach($this->samlApp->id, ['duration' => 365]);
|
||||
|
||||
// Create a role with no connected apps
|
||||
$emptyRole = App\Models\Role::findOrCreate('Empty User');
|
||||
$emptyRole->update(['priority' => 20]);
|
||||
|
||||
// Log in the administrator so they can view users
|
||||
$admin = User::where('email', 'admin@example.com')->firstOrFail();
|
||||
$this->actingAs($admin);
|
||||
|
||||
// 1. Livewire component with no roles selected should return false for showSamlSettings
|
||||
Livewire::test('pages::access-manager.users.index')
|
||||
->set('form.roleIds', [])
|
||||
->assertSet('showSamlSettings', false);
|
||||
|
||||
// 2. Livewire component with emptyRole selected should return false
|
||||
Livewire::test('pages::access-manager.users.index')
|
||||
->set('form.roleIds', [$emptyRole->id])
|
||||
->assertSet('showSamlSettings', false);
|
||||
|
||||
// 3. Livewire component with samlRole selected should return true
|
||||
Livewire::test('pages::access-manager.users.index')
|
||||
->set('form.roleIds', [$samlRole->id])
|
||||
->assertSet('showSamlSettings', true);
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user