Skip to content

fix: gate studio home roles button on manage-team permission - #3205

Closed
Anas12091101 wants to merge 1 commit into
openedx:masterfrom
mitodl:anas/gate-roles-button-on-manage-team
Closed

fix: gate studio home roles button on manage-team permission#3205
Anas12091101 wants to merge 1 commit into
openedx:masterfrom
mitodl:anas/gate-roles-button-on-manage-team

Conversation

@Anas12091101

Copy link
Copy Markdown
Contributor

Description

The "Roles and permissions" button on the Studio Home page is gated on whether the user can view a team on any course or library (courses.view_course_team / content_libraries.view_library_team). However, the admin console's role assignment flow requires the corresponding manage permissions: RoleUserAPIView.put/delete in openedx-authz are decorated with @authz_permissions([MANAGE_LIBRARY_TEAM, COURSES_MANAGE_COURSE_TEAM]).

Of the four course roles, only course_admin holds courses.manage_course_team; course_staff, course_editor and course_auditor hold courses.view_course_team only. Those three therefore see the button, can walk the entire role assignment wizard, and only fail on the final step with a 403 permission_denied. The same applies on the library side, where only library_admin holds content_libraries.manage_library_team.

This changes the gate to the two manage_*_team permissions, so the button is only shown to users who can actually complete the action. The waffle flag and ADMIN_CONSOLE_URL conditions are unchanged.

Useful information to include:

  • Which user roles will this change impact? Course Author (specifically: Course Staff, Course Editor and Course Auditor no longer see the button; Course Admin is unaffected)
  • Include screenshots for changes to the UI (ideally, both "before" and "after" screenshots, if applicable).

Before

Studio Home as a user holding course_staff — the "Roles and permissions" button is rendered, and completing the assignment wizard from it fails with a 403.

before-staff

After

Same user, same page — the button is no longer rendered.

after-staff

A user holding course_admin still sees it.

after-admin

Supporting information

Upstream issue: openedx/wg-build-test-release#590

The original report was raised internally at MIT and is not publicly readable, so repeating it here:

To Reproduce

  1. Log in to Studio as a user with the Course Staff role or no role
  2. Navigate to the Studio home page
  3. Observe that the "Roles and Permissions" button is visible

Expected behavior: Users with the Course Staff role or no organization access should not see the "Roles and Permissions" button on the Studio home page.

Actual behavior: The button is visible to Course Staff users. The user can click through and complete the entire assignment process, but an error appears when finalizing (the action itself does not complete).

The "no role" half of that report was fixed by #3072 and #3151; this PR covers the Course Staff / Editor / Auditor half.

Testing instructions

You need a devstack/tutor with Studio and this MFE running. Total time: ~5 minutes.

Step 1 — Set up the flag, two users, and their roles

Open a Studio (CMS) Python shell:

  • Tutor: tutor dev exec cms ./manage.py cms shell
  • Devstack: make studio-shell, then ./manage.py cms shell

Change COURSE to any course that exists in your environment, then paste the whole block:

from django.contrib.auth import get_user_model
from common.djangoapps.student.models import UserProfile
from openedx_authz.api.data import RoleData, ScopeData, UserData
from openedx_authz.api.roles import assign_role_to_subject_in_scope
from waffle.models import Flag

COURSE = 'course-v1:OpenedX+DemoX+DemoCourse'   # <-- change me

Flag.objects.update_or_create(name='authz.enable_course_authoring', defaults={'everyone': True})

U = get_user_model()
for username, role in [('team_admin', 'course_admin'), ('team_staff', 'course_staff')]:
    user, _ = U.objects.get_or_create(username=username, defaults={'email': f'{username}@example.com'})
    user.is_active = True
    user.set_password(username)
    user.save()
    UserProfile.objects.get_or_create(user=user, defaults={'name': username})
    assign_role_to_subject_in_scope(
        UserData(external_key=username), RoleData(external_key=role), ScopeData(external_key=COURSE),
    )
    print('READY', username, '/', username, '->', role)

That turns on the authz.enable_course_authoring waffle flag and creates two users, each with the password equal to their username:

Sign in with Password Role
team_admin@example.com team_admin Course Admin
team_staff@example.com team_staff Course Staff

Step 2 — Make sure ADMIN_CONSOLE_URL is set

In your MFE config (e.g. .env.development):

ADMIN_CONSOLE_URL='http://localhost:2025/admin-console'

The admin console MFE does not need to be running — this test is only about whether the button appears. Restart the dev server if you changed this value.

Step 3 — Sign in as the Course Admin

Go to Studio home (e.g. http://localhost:2001/home) and sign in as team_admin@example.com / team_admin.

Expected: the "Roles and permissions" button is in the top-right of the header.

Step 4 — Sign in as the Course Staff

Open a new incognito window (so you don't have to log out), go to Studio home, and sign in as team_staff@example.com / team_staff.

Expected: the "Roles and permissions" button is not there. The rest of the page is unchanged — same courses, same tabs.

Step 5 — Confirm the permission check is what hid it (optional but recommended)

With the Course Staff window open, DevTools → Network → filter for validate. You should see a POST to <STUDIO_BASE_URL>/api/authz/v1/permissions/validate/me with this request body:

[{"action": "courses.manage_course_team"}, {"action": "content_libraries.manage_library_team"}]

and this response:

[{"action": "courses.manage_course_team", "allowed": false},
 {"action": "content_libraries.manage_library_team", "allowed": false}]

For team_admin, courses.manage_course_team comes back true.

If that request is missing entirely, then the button was hidden by the waffle flag or by ADMIN_CONSOLE_URL instead of by permissions — re-check steps 1 and 2.

Step 6 — See the old, broken behaviour (optional)

Temporarily put the gate back on the view permissions:

# macOS; on Linux use: sed -i 's/.../.../' ...
sed -i '' 's/MANAGE_COURSE_TEAM/VIEW_COURSE_TEAM/; s/MANAGE_LIBRARY_TEAM/VIEW_LIBRARY_TEAM/' src/authz/permissionHelpers.ts

Reload Studio home as team_staff: the button reappears, and clicking through the role assignment wizard ends in 403 permission_denied from PUT /api/authz/v1/roles/users/ — the bug this PR fixes.

Restore the fix with:

git checkout src/authz/permissionHelpers.ts

Automated tests

npm run test -- src/studio-home/StudioHome.test.tsx src/authz/permissionHelpers.test.ts

Other information

  • This does not depend on any other change. courses.manage_course_team already exists in openedx-authz (verified against 1.21.1) and is the permission its role assignment endpoints enforce; this PR only adds the constant to this repo's COURSE_PERMISSIONS map.
  • getManageTeamPermissions (renamed from getViewTeamPermissions) has a single consumer, StudioHome. The separate course-scoped getCourseTeamPermissions helper is untouched and still checks view_course_team, so the course-level "Roles and permissions" nav item and the help/info sidebar links are unaffected — reaching a read-only team list with view access is intended there.
  • validateUserPermissions fills any missing response key with false, so if a deployment's backend does not recognise the action the button hides rather than appearing without permission — the safe direction.
  • Security: this narrows who is shown a link into the admin console. It is a UI-level gate only; the underlying openedx-authz endpoints already enforced these permissions, which is why the previous behaviour surfaced as a late 403 rather than an actual privilege escalation.
  • Not in scope, noted for follow-up: the role assignment wizard in frontend-app-admin-console lists candidate scopes via useScopes() without the management_permission_only parameter, so it can still offer scopes the user may only view. That is a separate fix in that repo.

Best Practices Checklist

We're trying to move away from some deprecated patterns in this codebase. Please
check if your PR meets these recommendations before asking for a review:

  • Any new files are using TypeScript (.ts, .tsx).
  • Avoid propTypes and defaultProps in any new or modified code.
  • Tests should use the helpers in src/testUtils.tsx (specifically initializeMocks)
  • Do not add new fields to the Redux state/store. Use React Context to share state among multiple components.
  • Use React Query to load data from REST APIs. See any apiHooks.ts in this repo for examples.
  • All new i18n messages in messages.ts files have a description for translators to use.
  • Avoid using ../ in import paths. To import from parent folders, use @src, e.g. import { initializeMocks } from '@src/testUtils'; instead of from '../../../../testUtils'

@openedx-webhooks openedx-webhooks added the open-source-contribution PR author is not from Axim or 2U label Aug 25, 2026
@openedx-webhooks

Copy link
Copy Markdown

Thanks for the pull request, @Anas12091101!

This repository is currently maintained by @bradenmacdonald.

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.

@codecov

codecov Bot commented Aug 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.92%. Comparing base (de0e1c4) to head (cd2afac).
⚠️ Report is 3 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3205      +/-   ##
==========================================
+ Coverage   95.91%   95.92%   +0.01%     
==========================================
  Files        1397     1397              
  Lines       33558    33581      +23     
  Branches     7914     7921       +7     
==========================================
+ Hits        32187    32214      +27     
+ Misses       1312     1308       -4     
  Partials       59       59              

☔ View full report in Codecov by Harness.
📢 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.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Updates Studio Home’s “Roles and permissions” button to require team-management permission, preventing view-only users from entering an assignment flow they cannot complete.

Changes:

  • Gates the button on course or library manage_*_team permissions.
  • Adds the course team-management permission constant.
  • Updates permission-helper and Studio Home tests.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/studio-home/StudioHome.tsx Uses manage-team permissions for button visibility.
src/studio-home/StudioHome.test.tsx Tests manage-team visibility behavior.
src/authz/permissionHelpers.ts Provides scope-less manage-team checks.
src/authz/permissionHelpers.test.ts Verifies manage-team actions and scope behavior.
src/authz/constants.ts Adds MANAGE_COURSE_TEAM.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@Anas12091101 Anas12091101 moved this from Needs Triage to Ready for Review in Contributions Aug 25, 2026
@dcoa

dcoa commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Hi @Anas12091101 what are you describing is not an issue of Authoring workflow, the button is correctly wrapped in view permission.

The described issue in openedx/wg-build-test-release#590 is referring to admin console MFE (Assign Role button) where is already solved here openedx/frontend-app-admin-console#172 and backported to Verawood openedx/frontend-app-admin-console#177. The issue was duplicated openedx/wg-build-test-release#603, so I missed to close the one you worked on. My apologize for that.

Understanding that we can safety close this PR.

C.C @bradenmacdonald @BryanttV

@dcoa dcoa closed this Aug 26, 2026
@github-project-automation github-project-automation Bot moved this from Ready for Review to Done in Contributions Aug 26, 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: Done

Development

Successfully merging this pull request may close these issues.

4 participants