Skip to content

feat(authz): add step 1 for the role assignment wizard#109

Open
bra-i-am wants to merge 1 commit intoopenedx:masterfrom
eduNEXT:bc/add-role-assignment-wizard
Open

feat(authz): add step 1 for the role assignment wizard#109
bra-i-am wants to merge 1 commit intoopenedx:masterfrom
eduNEXT:bc/add-role-assignment-wizard

Conversation

@bra-i-am
Copy link
Copy Markdown
Contributor

@bra-i-am bra-i-am commented Mar 25, 2026

Caution

This PR requires the validation endpoint introduced in the PR openedx/openedx-authz#245

This PR introduces Step 1 of the Role Assignment Wizard, replacing the previous modal-based approach for adding team members.

What's included:

  • A new /assign-role route with a two-step Stepper component (AssignRoleWizardPage + AssignRoleWizard)
  • Step 1 — Who and Role: users can enter one or more usernames/emails (comma-separated) and select a role. Invalid users are highlighted inline in red via HighlightedUsersInput, a custom textarea with a synchronized overlay layer
  • User validation via a new POST /api/authz/v1/users/validate endpoint before advancing to Step 2
  • Role options are filtered based on the current user's actual permissions (MANAGE_LIBRARY_TEAM / MANAGE_COURSE_TEAM), with course roles visible but disabled pending a future update
  • Step 2 — Where it applies is scaffolded but not yet implemented; it will define the application scope in a follow-up PR
  • Constants for library and course roles/permissions are centralized in authz-module/constants.ts and re-exported where needed
  • The "Assign Role" button in LibrariesTeamManager now navigates to the wizard instead of opening a modal
    Not in scope for this PR: Step 2 implementation, course role assignment.

Additional Information

Resolves #91

Screenshots

image

How to test

1. Open the wizard from the home page
  1. Navigate to /authz.
  2. Click Assign Role in the top-right header.
  3. Confirm you land on /authz/assign-role and the wizard Step 1 is shown.
2. Open the wizard pre-filled from the user audit view
  1. Navigate to a library team page (/authz/libraries/<lib-id>).
  2. Click Assign Role in the header → confirm the wizard opens at /authz/assign-role with the users input empty and from= pointing back to the team page.
  3. Go back, click on any team member edit action to open their audit page (/authz/libraries/<lib-id>/<username>).
  4. Click Assign Role in the header → confirm the wizard opens with that team member's username already filled in the users input.
3. Role selection
  1. Confirm roles are grouped under Courses and Libraries.
  2. Select a library role, then click a course role — confirm only one radio is checked at a time.
  3. Confirm Course Editor and Course Auditor are visible but disabled (greyed out with a tooltip).
4. Users input — invalid user
  1. Type a username that does not exist, select any role, click Next.
  2. Confirm the invalid username is highlighted in red inside the input.
  3. Confirm the message "X username(s) not associated with an account" appears below.
  4. Edit the input → confirm the red highlight and error message both disappear immediately.
5. Advance to Step 2
  1. Type a valid username, select a role, and click Next.
  2. Confirm the wizard advances to Step 2.
  3. Click Back → confirm you return to Step 1 with the username and role still selected.
6. Cancel and breadcrumb return to the originating view
  1. Open the wizard from /authz, click Cancel → confirm you land on /authz.
  2. Open the wizard from a user audit page, click Cancel → confirm you return to that user audit page, not /authz.
  3. Repeat step 2 but click the breadcrumb link instead of Cancel.

AssignRoleWizard — Step 1 Test Coverage

Status Test
Cancel returns to the previous view
opens with the user pre-populated when provided via initialUsers
shows only course roles when user only has manage_course_team permission
shows only library roles when user only has manage_library_team permission
shows both course and library roles when user has both permissions
selecting a different role replaces the previous selection
blocks progression and highlights the unknown user
advances to Step 2 when all users are valid
shows a network error and blocks progression when the validation call fails
clears the invalid user highlights when the input is edited

@openedx-webhooks
Copy link
Copy Markdown

openedx-webhooks commented Mar 25, 2026

Thanks for the pull request, @bra-i-am!

This repository is currently maintained by @openedx/committers-frontend.

Once you've gone through the following steps feel free to tag them in a comment and let them know that your changes are ready for engineering review.

🔘 Get product approval

If you haven't already, check this list to see if your contribution needs to go through the product review process.

  • If it does, you'll need to submit a product proposal for your contribution, and have it reviewed by the Product Working Group.
    • This process (including the steps you'll need to take) is documented here.
  • If it doesn't, simply proceed with the next step.
🔘 Provide context

To help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:

  • Dependencies

    This PR must be merged before / after / at the same time as ...

  • Blockers

    This PR is waiting for OEP-1234 to be accepted.

  • Timeline information

    This PR must be merged by XX date because ...

  • Partner information

    This is for a course on edx.org.

  • Supporting documentation
  • Relevant Open edX discussion forum threads
🔘 Get a green build

If one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green.

Details
Where can I find more information?

If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources:

When can I expect my changes to be merged?

Our goal is to get community contributions seen and reviewed as efficiently as possible.

However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:

  • The size and impact of the changes that it introduces
  • The need for product review
  • Maintenance status of the parent repository

💡 As a result it may take up to several weeks or months to complete a review and merge your PR.

@openedx-webhooks openedx-webhooks added the open-source-contribution PR author is not from Axim or 2U label Mar 25, 2026
@github-project-automation github-project-automation bot moved this to Needs Triage in Contributions Mar 25, 2026
@bra-i-am bra-i-am force-pushed the bc/add-role-assignment-wizard branch 4 times, most recently from b1d55c0 to e2a71c0 Compare March 26, 2026 14:39
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 26, 2026

Codecov Report

❌ Patch coverage is 96.11650% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 95.73%. Comparing base (3125965) to head (e04f840).
⚠️ Report is 6 commits behind head on master.

Files with missing lines Patch % Lines
src/authz-module/wizard/AssignRoleWizard.tsx 91.89% 6 Missing ⚠️
...-module/libraries-manager/LibrariesTeamManager.tsx 75.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #109      +/-   ##
==========================================
+ Coverage   95.35%   95.73%   +0.38%     
==========================================
  Files          53       59       +6     
  Lines        1055     1244     +189     
  Branches      208      251      +43     
==========================================
+ Hits         1006     1191     +185     
- Misses         46       50       +4     
  Partials        3        3              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@bra-i-am bra-i-am force-pushed the bc/add-role-assignment-wizard branch 2 times, most recently from 3b13e69 to 5c518c8 Compare March 26, 2026 19:39
@mphilbrick211 mphilbrick211 moved this from Needs Triage to Waiting on Author in Contributions Mar 27, 2026
@bra-i-am bra-i-am force-pushed the bc/add-role-assignment-wizard branch from 6e294c5 to d5990f0 Compare March 30, 2026 13:39
@bra-i-am bra-i-am marked this pull request as ready for review April 13, 2026 14:14
@bra-i-am bra-i-am requested a review from dcoa April 13, 2026 19:34
@bra-i-am bra-i-am force-pushed the bc/add-role-assignment-wizard branch 2 times, most recently from eddffbd to 15be337 Compare April 14, 2026 15:08
@bra-i-am bra-i-am requested a review from arbrandes April 14, 2026 18:47
@bra-i-am bra-i-am force-pushed the bc/add-role-assignment-wizard branch from 319a15a to bf1004c Compare April 14, 2026 21:43
@bra-i-am bra-i-am marked this pull request as draft April 14, 2026 21:51
@bra-i-am bra-i-am force-pushed the bc/add-role-assignment-wizard branch from 4ac83a8 to 49aeb2c Compare April 14, 2026 22:53
@bra-i-am bra-i-am marked this pull request as ready for review April 14, 2026 23:03
@arbrandes
Copy link
Copy Markdown

What is the PR (or PRs) that introduces the required endpoints? Has it already merged?

@bra-i-am
Copy link
Copy Markdown
Contributor Author

bra-i-am commented Apr 15, 2026

@arbrandes, sorry for not including it in the cover letter earlier. The PR is openedx/openedx-authz#245 and has already been merged.

@dcoa
Copy link
Copy Markdown
Contributor

dcoa commented Apr 16, 2026

As a minor comment in the UI can we keep the space between the text area and the feedback?

The design

image

Currently

image

Also the background for the form section in the current implementation is white but the design presented in the issue is light - Nothing blocking but I mention it just in case.

Comment thread src/authz-module/wizard/messages.ts Outdated
Comment thread src/authz-module/data/api.test.ts Outdated
Comment thread src/authz-module/wizard/DefineApplicationScopeStep.tsx Outdated
Comment thread src/authz-module/wizard/HighlightedUsersInput.tsx Outdated
Comment thread src/authz-module/wizard/AssignRoleWizard.test.tsx Outdated
Comment thread src/authz-module/wizard/messages.ts Outdated
onChange={setUsers}
invalidUsers={invalidUsers}
placeholder={intl.formatMessage(messages['wizard.step1.users.placeholder'])}
hasNetworkError={!!validationError || invalidUsers.length > 0}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not a big fan of this presentation of the error in terms of the service availability (network errors), because can be confused as a validation error.

Image

I think is better having a separate component for it, maybe a alert on top or maybe the toast message (if still relevant for the redesign)

I would like to have others option including @maguilarUXUI

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Error (2) When is happened this kind of error? In the proposal the error happen when the user click next and the user is not part of the initiative

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Yes, and the validation error is presented as is showing in the proposal design. I'm not discussing that.

What I am talking about is error 500, the validation service is not available. I think those ones should be presented in another way, such as Alert on top instead of the red border and red message at the bottom.

@maguilarUXUI

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

In this case I think the best option is a toast with the retry option if is possible.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

thanks @dcoa and @maguilarUXUI

I already added the toast:

image

Comment thread src/authz-module/constants.test.ts Outdated
Comment thread src/authz-module/roles-permissions/library/constants.ts
Comment thread .eslintrc.js Outdated
@bra-i-am bra-i-am force-pushed the bc/add-role-assignment-wizard branch from e6a5b2c to 12c25ea Compare April 16, 2026 17:17
Comment thread src/authz-module/roles-permissions/library/constants.ts
Copy link
Copy Markdown

@arbrandes arbrandes left a comment

Choose a reason for hiding this comment

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

Overall the implementation is solid. But I found a few issues with Claude's help (see below). Also, can you please add some explicit test instructions so I know what to look for?

Acceptance criteria gaps

The PR resolves most of the Step 1 acceptance criteria, but three items from issue #91 are not met:

1. Pre-populate Step 1 when arriving from the user audit view

AC: "When accessing the wizard from the user audit view, the user input in Step 1 is pre-populated with that user."

AssignRoleWizardPage.tsx#L11-L12 reads ?users= off the URL and forwards it to the wizard, but nothing in the diff actually navigates to the wizard with that query param. git diff master -- src/authz-module/libraries-manager/ is empty, so LibrariesUserManager still cannot launch the wizard with a pre-filled user. The plumbing exists; the caller does not.

2. Role groups filtered by the current user's scopes

AC: "A user only sees the groups they have permissions to assign. If they have no library scopes, they do not see libraries. If they have no course scopes, they do not see courses."

AssignRoleWizard.tsx#L16 passes the unconditional union [...courseRolesMetadata, ...libraryRolesMetadata] to the step, and no permission lookup is performed anywhere in the wizard. The two ❌ rows in the PR body's test table (shows only course roles when user only has manage_course_team permission / shows only library roles when user only has manage_library_team permission) confirm this is a known gap.

3. Breadcrumb and Cancel must return to the previous view

AC: "The wizard has a breadcrumb that exits the flow and returns to the previous view at any point." / "A Cancel button is available at the bottom of both steps. Clicking it returns to the previous view."

Both actions hardcode ROUTES.HOME_PATH:

A user who reaches the wizard from LibrariesUserManager or LibrariesTeamManager will be dropped on /authz rather than returned to the scoped view they came from. Consider navigate(-1) or threading the origin through the wizard URL (e.g., a from= query param) so the exit paths honor the AC.

Issues

1. Drop all console.log statements before landing

Two debug logs remain in the diff and should be removed:

  • DefineApplicationScopeStep.tsx#L12console.log(selectedRole, selectedScopes, onScopeToggle); runs on every render once the user reaches Step 2.
  • AssignRoleWizard.tsx#L97-L99console.log('[AssignRoleWizard] handleSave', ...) is guarded by an eslint-disable-next-line and should be replaced with a TODO for the follow-up API call.

2. Network validationError is not cleared when the user edits the input

src/authz-module/wizard/AssignRoleWizard.tsx#L45-L48

const handleUsersChange = useCallback((value: string) => {
  setInvalidUsers((prev) => (prev.length > 0 ? [] : prev));
  setUsers(value);
}, []);

invalidUsers is cleared on edit (covered by the clears the invalid user highlights when the input is edited test), but validationError is not. After a transient network failure the red "An error occurred while validating users..." line remains visible while the user types a new address, which is inconsistent with how invalid-user feedback behaves and can be confusing. Reset validationError here as well.

3. Catch-all * route is declared before ASSIGN_ROLE_WIZARD_PATH

src/authz-module/index.tsx#L33-L34

<Route path="*" element={<NotFoundError />} />
<Route path={ROUTES.ASSIGN_ROLE_WIZARD_PATH} element={<AssignRoleWizardPage />} />

React Router v6 ranks matches by specificity regardless of source order, so the wizard route does win and this works today. But declaring "*" in the middle of the list is a footgun: a future edit that (for example) replaces Routes with source-order matching, or adds a child route that shares a prefix, will silently break. Move the catch-all to be the last <Route> in the block.

4. Dead fallback for contextType

src/authz-module/wizard/SelectUsersAndRoleStep.tsx#L48-L49

const rolesByContext = roles.reduce<Record<string, RoleMetadata[]>>((acc, role) => {
  const context = role.contextType || 'library';

Every entry in courseRolesMetadata and libraryRolesMetadata now sets contextType explicitly, and the only caller passes those arrays, so the || 'library' fallback is unreachable and misleading (a future course role added without contextType would silently render under Libraries). Either drop the fallback or tighten RoleMetadata.contextType to a required field.

@bra-i-am bra-i-am force-pushed the bc/add-role-assignment-wizard branch from 26fd465 to f6b054f Compare April 17, 2026 15:02
@bra-i-am
Copy link
Copy Markdown
Contributor Author

@dcoa and @arbrandes, thank you so much for your time reviewing this PR ✨

@dcoa: I already addressed the changes to keep consistency with courseRolesMetadata d9b405b

@arbrandes: I already addressed the changes you asked for f6b054f. Here is a summary of all the points with a brief description of each one

# Item Status
AC-1 Pre-populate Step 1 when arriving from user audit view ✅ Fixed — LibrariesUserManager now navigates to buildWizardPath({ users, from }), pre-filling the username and setting the return path
AC-2 Role groups filtered by the current user's scopes ⚠️ Partial — AssignRoleWizard now accepts a roles prop so callers can pass a filtered subset; full filtering is blocked on a permission-lookup API that doesn't exist yet
AC-3 Breadcrumb and Cancel must return to the previous view ✅ Fixed — AssignRoleWizardPage reads ?from= and uses it for both the breadcrumb to and the onClose handler; defaults to ROUTES.HOME_PATH when absent
I-1a console.log in DefineApplicationScopeStep.tsx ✅ Fixed — removed; replaced with a TODO comment
I-1b console.log in AssignRoleWizard.tsx handleSave ✅ Fixed — removed along with the eslint-disable-next-line; TODO comment now names the three values needed for the API call
I-2 Network validationError not cleared when input is edited ✅ Fixed — handleUsersChange now calls setValidationError(null); covered by a new test
I-3 Catch-all * route declared before ASSIGN_ROLE_WIZARD_PATH ✅ Fixed — * moved to be the last <Route> in the block
I-4 Dead || 'library' fallback for contextType ✅ Fixed — contextType made required in RoleMetadata (in types.ts and the local interface), contextType added to all rolesObject/rolesLibraryObject entries, fallback removed

@bra-i-am bra-i-am force-pushed the bc/add-role-assignment-wizard branch 3 times, most recently from de07733 to 3fa07f3 Compare April 17, 2026 16:00
@bra-i-am bra-i-am requested review from arbrandes and dcoa April 17, 2026 16:00
@bra-i-am bra-i-am force-pushed the bc/add-role-assignment-wizard branch from 7979429 to d340ee9 Compare April 17, 2026 17:43
- Added AssignRoleWizard component for assigning roles to users.
- Created AssignRoleWizardPage to handle routing and initial user input.
- Implemented SelectUsersAndRoleStep for user and role selection with validation.
- Developed DefineApplicationScopeStep as a placeholder for defining application scopes.
- Introduced HighlightedUsersInput for enhanced user input with invalid user highlighting.
- Added tests for all new components to ensure functionality and correctness.
- Updated messages for internationalization support.
- Enhanced types for RoleMetadata to include contextType and disabled properties.
@bra-i-am bra-i-am force-pushed the bc/add-role-assignment-wizard branch from d340ee9 to e04f840 Compare April 17, 2026 20:26
@dcoa dcoa changed the title feat: add step 1 for the role assignment wizard feat(authz): add step 1 for the role assignment wizard Apr 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

open-source-contribution PR author is not from Axim or 2U

Projects

Status: Waiting on Author

Development

Successfully merging this pull request may close these issues.

Task - RBAC AuthZ - US: M2.8 Assign role wizard - Step 1 usernames and role

6 participants