Skip to content

fix(admin-ui): unable to remove or reassign admin roles from user profile#2697

Merged
moabu merged 3 commits intomainfrom
admin-ui-issue-2688_1
Mar 10, 2026
Merged

fix(admin-ui): unable to remove or reassign admin roles from user profile#2697
moabu merged 3 commits intomainfrom
admin-ui-issue-2688_1

Conversation

@faisalsiddique4400
Copy link
Contributor

@faisalsiddique4400 faisalsiddique4400 commented Mar 10, 2026

fix(admin-ui): unable to remove or reassign admin roles from user profile (#2684)

Summary

This PR fixes an issue where admin roles could not be removed or reassigned from a user's profile in the Admin UI.

Issue

When attempting to remove roles such as jansAdminRole or userRole from a user (e.g., admin user), the operation fails and the roles cannot be updated from the UI.

Fix

  • Corrected the role update handling logic when modifying user roles.
  • Ensured that removing or reassigning roles properly updates the payload and triggers the correct API request.
  • Improved handling of role updates to prevent failures during role removal.

Result

  • Admin roles can now be successfully removed or reassigned from a user's profile.
  • The role update operation works as expected through the Admin UI.

🔗 Ticket

Closes: #2684

Summary by CodeRabbit

  • New Features

    • Toast notifications can include a redirect URL and will navigate automatically when a toast is closed.
    • Logout/error notifications now use the unified toast system for consistent behavior.
  • Bug Fixes

    • Multi-valued fields now preserve empty arrays as intentional clears.
    • Password change inputs adjusted for consistent visual styling.

…file

Signed-off-by: faisalsiddique4400 <faisalsiddique10886@gmail.com>
…file

Signed-off-by: faisalsiddique4400 <faisalsiddique10886@gmail.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 10, 2026

📝 Walkthrough

Walkthrough

Moves GluuToast out of AppAuthProvider and above AppLayout; adds an onCloseRedirectUrl field to Redux toast state and updateToast action; AppAuthProvider dispatches updateToast with a redirect URL for logout errors; GluuToast reads the redirect URL and performs a full-page redirect on close; preserves empty arrays in UserForm modified-fields logic.

Changes

Cohort / File(s) Summary
Toast State & Types
admin-ui/app/redux/features/toastSlice.ts, admin-ui/app/redux/types/index.ts
Added onCloseRedirectUrl to toast state and ToastState type; updated updateToast to accept and propagate the redirect URL.
Toast UI & Auth Flow
admin-ui/app/routes/Apps/Gluu/GluuToast.tsx, admin-ui/app/utils/AppAuthProvider.tsx
GluuToast now reads onCloseRedirectUrl (via typed hooks) and triggers a full-page redirect on close when present; AppAuthProvider dispatches updateToast(..., onCloseRedirectUrl) instead of using inline toast onClose.
Route Composition
admin-ui/app/components/App/AuthenticatedRouteSelector.tsx
Reordered JSX: GluuToast moved outside AppAuthProvider and positioned above AppLayout (fragment wrapper introduced).
Form Field Handling
admin-ui/plugins/user-management/components/UserForm.tsx
Adjusted modified-fields cleaning to preserve empty arrays (allowing intentional clearing of multi-valued fields).
Minor UI Prop Change
admin-ui/plugins/user-management/components/PasswordChangeModal.tsx
Passed isDark={false} to two GluuInputRow usages (pure prop addition).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant AppAuthProvider
    participant Redux
    participant GluuToast
    participant Browser

    User->>AppAuthProvider: Trigger action (e.g., logout/no-valid-role)
    AppAuthProvider->>Redux: dispatch updateToast(show=true, type='error', message, onCloseRedirectUrl=sessionEndpoint)
    Redux->>Redux: store showToast, type, message, onCloseRedirectUrl
    Redux->>GluuToast: state update -> render toast with onClose handler
    GluuToast->>User: display error toast
    User->>GluuToast: close toast (user or auto)
    GluuToast->>Browser: window.location.href = onCloseRedirectUrl (full-page redirect)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • duttarnab
  • syntrydy

Poem

🐰 I hopped through code, moved toast to Redux land,
Redirects tucked in state by a careful hand.
Empty arrays now safe, fields no longer thrown,
Routes tidied up neatly, layout clearly shown.
A tiny rabbit cheer — the UI hops on! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main bug fix—enabling removal/reassignment of admin roles from user profiles, which aligns with the changeset's core objective.
Linked Issues check ✅ Passed Changes address the core requirement from #2684 by fixing role removal/reassignment handling through improved payload building and Redux toast mechanism for notifications.
Out of Scope Changes check ✅ Passed Minor auxiliary change in PasswordChangeModal.tsx (isDark prop addition) is tangential but appears to be a related visual consistency fix; all other changes directly support role update functionality.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch admin-ui-issue-2688_1

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@mo-auto mo-auto added comp-admin-ui Component affected by issue or PR kind-bug Issue or PR is a bug in existing functionality labels Mar 10, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
admin-ui/app/routes/Apps/Gluu/GluuToast.tsx (1)

13-48: ⚠️ Potential issue | 🟠 Major

Capture the redirect URL per toast, not in a shared ref.

redirectUrlRef.current is mutable process-wide state for every active toast. If another toast is dispatched before the first one closes, Line 46 overwrites the ref, so the first toast's onClose can redirect to the wrong place or skip the redirect entirely.

Proposed fix
-import { useEffect, useRef } from 'react'
+import { useEffect } from 'react'
@@
-  const redirectUrlRef = useRef('')
-
-  const handleToastClose = () => {
-    if (redirectUrlRef.current) {
-      window.location.href = redirectUrlRef.current
-    }
-  }
-
   const showTheToast = () => {
-    const options = onCloseRedirectUrl ? { onClose: handleToastClose } : {}
+    const redirectUrl = onCloseRedirectUrl
+    const options = redirectUrl
+      ? {
+          onClose: () => {
+            window.location.assign(redirectUrl)
+          },
+        }
+      : {}
     if (type == 'success') {
       toast.success(<ToastDesign />, options)
     } else {
       toast.error(<ToastDesign />, options)
     }
@@
     if (showToast) {
-      redirectUrlRef.current = onCloseRedirectUrl || ''
       showTheToast()
       dispatch(updateToast(false, 'success', ''))
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@admin-ui/app/routes/Apps/Gluu/GluuToast.tsx` around lines 13 - 48, The shared
mutable ref redirectUrlRef is overwritten across concurrent toasts; instead
capture the per-toast redirect URL in a local variable and pass a dedicated
onClose closure to the toast so each toast uses its own URL. Modify showTheToast
to read const redirect = onCloseRedirectUrl || '' and create a local
handleClose() { if (redirect) window.location.href = redirect } (use this
closure when building options passed to toast.success/toast.error) and remove
reliance on redirectUrlRef and the separate handleToastClose function; keep
dispatch(updateToast(false, 'success', '')) inside the same useEffect after
showing the toast.
admin-ui/plugins/user-management/components/UserForm.tsx (1)

190-203: ⚠️ Potential issue | 🟠 Major

Don't preserve [] for claims that were never persisted.

Both paths now keep empty arrays in modifiedFields, but admin-ui/plugins/user-management/utils/attributeTransformUtils.ts:189-206 treats any present multi-valued key as a real update and will create a new customAttribute with values: [] when the attribute did not exist before. That turns “add claim, then remove it before saving” into a bogus clear operation instead of dropping the change. Only keep an empty array when the original user already had that multi-valued attribute.

Also applies to: 227-241

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@admin-ui/plugins/user-management/components/UserForm.tsx` around lines 190 -
203, The current updateModifiedFields callback (and the similar block at the
other location) treats an empty array as a deliberate change even if the
attribute didn't previously exist, which later causes attributeTransformUtils to
create a bogus empty customAttribute; modify the logic in updateModifiedFields
(and the other matching setter) to check the original user's attributes/state
before deciding to keep an empty array: if isEmptyValue(value) &&
Array.isArray(value) then remove the key from modifiedFields unless the original
user already had that multi-valued attribute (use the component's existing
user/originalUser prop/state to determine existence); otherwise keep non-empty
arrays and other values as before. Ensure you reference updateModifiedFields
(and the second setter block) and attributeTransformUtils behavior when
implementing this conditional.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@admin-ui/app/utils/AppAuthProvider.tsx`:
- Around line 245-253: The logout URL is built by string interpolation which
breaks if the stored postLogoutRedirectUri contains its own query/fragment;
replace the manual template with URL and URL.searchParams to construct
sessionEndpoint robustly: create a new URL using authConfigs?.endSessionEndpoint
(or fallback to empty string/throw if invalid), then call
searchParams.set('state', state) and
searchParams.set('post_logout_redirect_uri',
localStorage.getItem('postLogoutRedirectUri') ?? '') before passing the
resulting URL.toString() into updateToast (where sessionEndpoint is currently
used); update references to sessionEndpoint and keep the same
dispatch/updateToast usage and message (t('messages.no_valid_role_logout', {
seconds: LOGOUT_DELAY_SECONDS })) so behavior doesn’t change.

---

Outside diff comments:
In `@admin-ui/app/routes/Apps/Gluu/GluuToast.tsx`:
- Around line 13-48: The shared mutable ref redirectUrlRef is overwritten across
concurrent toasts; instead capture the per-toast redirect URL in a local
variable and pass a dedicated onClose closure to the toast so each toast uses
its own URL. Modify showTheToast to read const redirect = onCloseRedirectUrl ||
'' and create a local handleClose() { if (redirect) window.location.href =
redirect } (use this closure when building options passed to
toast.success/toast.error) and remove reliance on redirectUrlRef and the
separate handleToastClose function; keep dispatch(updateToast(false, 'success',
'')) inside the same useEffect after showing the toast.

In `@admin-ui/plugins/user-management/components/UserForm.tsx`:
- Around line 190-203: The current updateModifiedFields callback (and the
similar block at the other location) treats an empty array as a deliberate
change even if the attribute didn't previously exist, which later causes
attributeTransformUtils to create a bogus empty customAttribute; modify the
logic in updateModifiedFields (and the other matching setter) to check the
original user's attributes/state before deciding to keep an empty array: if
isEmptyValue(value) && Array.isArray(value) then remove the key from
modifiedFields unless the original user already had that multi-valued attribute
(use the component's existing user/originalUser prop/state to determine
existence); otherwise keep non-empty arrays and other values as before. Ensure
you reference updateModifiedFields (and the second setter block) and
attributeTransformUtils behavior when implementing this conditional.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4e741f78-c408-4fdd-ad23-a7806f90e109

📥 Commits

Reviewing files that changed from the base of the PR and between 92c1cd9 and 8202d18.

📒 Files selected for processing (6)
  • admin-ui/app/components/App/AuthenticatedRouteSelector.tsx
  • admin-ui/app/redux/features/toastSlice.ts
  • admin-ui/app/redux/types/index.ts
  • admin-ui/app/routes/Apps/Gluu/GluuToast.tsx
  • admin-ui/app/utils/AppAuthProvider.tsx
  • admin-ui/plugins/user-management/components/UserForm.tsx

Signed-off-by: faisalsiddique4400 <faisalsiddique10886@gmail.com>
@sonarqubecloud
Copy link

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
admin-ui/app/utils/AppAuthProvider.tsx (1)

249-252: 🧹 Nitpick | 🔵 Trivial

Minor: Consider omitting post_logout_redirect_uri when empty.

Currently, if localStorage.getItem('postLogoutRedirectUri') returns null, an empty string is set as the parameter value (post_logout_redirect_uri=). Some OAuth providers may behave unexpectedly with an empty value. Consider conditionally adding the parameter only when a value exists:

Suggested improvement
 endSessionUrl.searchParams.set('state', state)
-endSessionUrl.searchParams.set(
-  'post_logout_redirect_uri',
-  localStorage.getItem('postLogoutRedirectUri') ?? '',
-)
+const postLogoutRedirectUri = localStorage.getItem('postLogoutRedirectUri')
+if (postLogoutRedirectUri) {
+  endSessionUrl.searchParams.set('post_logout_redirect_uri', postLogoutRedirectUri)
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@admin-ui/app/utils/AppAuthProvider.tsx` around lines 249 - 252, The code
currently always sets post_logout_redirect_uri to
localStorage.getItem('postLogoutRedirectUri') (possibly an empty string) on
endSessionUrl; change this so
endSessionUrl.searchParams.set('post_logout_redirect_uri', ...) is only called
when the retrieved value is non-empty (truthy) — e.g., read const redirect =
localStorage.getItem('postLogoutRedirectUri') and if (redirect) then call
endSessionUrl.searchParams.set(...) — locate this logic in AppAuthProvider (the
endSessionUrl handling) and ensure no parameter is added when redirect is
null/empty.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@admin-ui/app/utils/AppAuthProvider.tsx`:
- Around line 249-252: The code currently always sets post_logout_redirect_uri
to localStorage.getItem('postLogoutRedirectUri') (possibly an empty string) on
endSessionUrl; change this so
endSessionUrl.searchParams.set('post_logout_redirect_uri', ...) is only called
when the retrieved value is non-empty (truthy) — e.g., read const redirect =
localStorage.getItem('postLogoutRedirectUri') and if (redirect) then call
endSessionUrl.searchParams.set(...) — locate this logic in AppAuthProvider (the
endSessionUrl handling) and ensure no parameter is added when redirect is
null/empty.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5b0ec723-9844-4b5b-808c-73da2b31c055

📥 Commits

Reviewing files that changed from the base of the PR and between 8202d18 and 34b1487.

📒 Files selected for processing (2)
  • admin-ui/app/utils/AppAuthProvider.tsx
  • admin-ui/plugins/user-management/components/PasswordChangeModal.tsx

@moabu moabu merged commit 4525554 into main Mar 10, 2026
9 checks passed
@moabu moabu deleted the admin-ui-issue-2688_1 branch March 10, 2026 18:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp-admin-ui Component affected by issue or PR kind-bug Issue or PR is a bug in existing functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(admin-ui): unable to remove/reassign admin roles

4 participants