RBAC / AuthZ – Milestone 0 (M0)

RBAC / AuthZ – Milestone 0 (M0)

1. Overview

Milestone 0 introduces the foundational integration between the Open edX platform and the new AuthZ authorization framework, focusing on Course Authoring. The goal of this milestone is to establish the foundation required to migrate from the legacy role management system used in Course Authoring to a policy-based authorization system powered by Casbin, while maintaining compatibility with the existing system.

This milestone focuses on:

  • Establishing the migration path between legacy roles and AuthZ policies

  • Providing safe rollback capabilities

  • Adding initial frontend support for managing roles

  • Ensuring operational safety during the transition period

  • Introduce feature flags to safely enable and test the new functionality.

The changes implemented in this milestone allow the new AuthZ system to coexist with the legacy authorization model, enabling incremental adoption.


2. Release Notes

New Features

1. Migration tooling between legacy roles and AuthZ

  • Introduced migration scripts to convert legacy CourseAccessRole assignments into AuthZ roles.

  • Added reverse migration capability to restore legacy roles from AuthZ policies.

Key PRs:

2. AuthZ integration in the platform

  • Introduced initial integration points between openedx-platform and the AuthZ service.

  • Role assignments can now be propagated through the new authorization API layer.

3. Frontend integration

  • Added frontend changes to support the new role management flows in the authoring interface.

Key PR:

4. Authorization service improvements

  • Improvements to role assignment APIs

  • Additional tooling for role migration

  • Enhancements to the internal AuthZ data model

Related issue:

  • openedx-authz#180


3. Feature Flag (Waffle Flag)

To safely introduce the new authorization functionality, Milestone 0 includes a Waffle feature flag that controls whether the AuthZ-based role management is enabled.

Feature flags in Open edX allow developers and operators to gradually roll out functionality and safely test new features in production environments without impacting all users.

The feature flag introduced in this milestone enables the platform to:

  • Toggle the use of the AuthZ authorization layer.

  • Allow testing and validation in controlled environments.

  • Provide a rollback mechanism if issues arise.

When the flag is disabled, the platform continues to rely entirely on the legacy authorization system.

When enabled, role assignments and authorization checks can be routed through the new AuthZ framework while still maintaining compatibility with the legacy role storage.

This incremental rollout strategy ensures that the migration to the new authorization system can be performed safely and progressively.

Testing instructions

How to use

# Import the flag from openedx.core.toggles import AUTHZ_COURSE_AUTHORING_FLAG # Check if it's enabled, passing the course_key is_enabled = AUTHZ_COURSE_AUTHORING_FLAG.is_enabled(course_key=course_key)

To enable it globally:

  1. In the Django Admin, go to Home > django-waffle > Flags (/admin/waffle/flag/)

  2. Click on ADD FLAG

  3. Set the Name to "authz.enable_course_authoring"

  4. Set Everyone: "Yes"

  5. Click "Save"

To enable for a specific course (or override if it was previously enabled globally):

  1. In the Django Admin, go to Home > Waffle_Utils > Waffle flag course overrides (/admin/waffle_utils/waffleflagcourseoverridemodel/)

  2. Click on "ADD WAFFLE FLAG COURSE OVERRIDE"

  3. Set Waffle flag to "authz.enable_course_authoring"

  4. Set Course id to the desired course key

  5. Set Override choice to the desired effect

  6. Check the "Enabled" box

  7. Click "Save"

To enable for an org (or override if it was previously enabled globally):

  1. In the Django Admin, go to Home > Waffle_Utils > Add Waffle flag org override (/admin/waffle_utils/waffleflagorgoverridemodel/)

  2. Click on "ADD WAFFLE FLAG ORG OVERRIDE"

  3. Set Waffle flag to "authz.enable_course_authoring"

  4. Set Course id to the desired Org name

  5. Set Override choice to the desired effect

  6. Check the "Enabled" box

  7. Click "Save"

How will the flag behave:

  • Course level flags take precedence over Org level flags.

  • Both override the global Flag.

  • Course and Org level flags need to be "Enabled" for the override to apply

Caveats:

Waffle Flags allow configuring the flag to be enabled to specific users or groups of users. This won't be supported by the openedx-authz implementation because it would cause validation inconsistencies. A Waffle Switch would have been a better fit, but there are no existing ways to override Waffle Switchs for specific courses or orgs.


4. Migration Strategy

The migration process converts legacy course authoring roles stored in CourseAccessRole into AuthZ role assignments.

Each legacy role entry is translated into an equivalent role assignment using the AuthZ role assignment API.

Migration is executed using a Django management command.


Migration Command

./manage.py authz_migrate_course_authoring

Required Filters

To prevent accidental full migrations, the command requires a filter.

You must specify either:

  • --course-id-list

  • --org-id

Example:

./manage.py authz_migrate_course_authoring \ --course-id-list course-v1:edX+DemoX+Demo_Course

Example using organization ID:

./manage.py authz_migrate_course_authoring \ --org-id edX

Both options cannot be used together.


Optional Flag: Delete Legacy Roles

Legacy roles can be removed after successful migration using the --delete flag.

Example:

./manage.py authz_migrate_course_authoring \ --org-id edX \ --delete

If this flag is used, the command prompts for confirmation before deleting legacy roles.

Only successfully migrated roles are deleted.


Operational Behavior

In the final system behavior, migrations are expected to be executed with the equivalent of the --delete option enabled (this is currently not implemented and is expected to be available in the next milestone).

The compatibility layer assumes that once a course is migrated to the AuthZ system, the legacy CourseAccessRole entries should no longer exist for that course. Maintaining role assignments in both systems simultaneously may lead to inconsistent authorization behavior.

For this reason, production migrations should remove legacy roles after a successful migration.


Automation via Feature Flags

The current CLI-based migration command is primarily intended for development, testing, and operational debugging.

In the final rollout process, migrations will be automatically triggered when enabling or disabling the relevant AuthZ feature flags. This ensures that role assignments remain consistent with the active authorization system without requiring manual intervention.


Migration Safety

The migration command includes several safety mechanisms:

  • Requires explicit filtering (--course-id-list or --org-id)

  • Prevents using both filters simultaneously

  • Requires confirmation before deletion

  • Runs inside a database transaction

  • Only deletes roles that were successfully migrated

If the migration fails due to an unexpected error, the transaction is rolled back.


5. Rollback Strategy

If issues occur after enabling the AuthZ system, administrators can restore legacy role assignments.

The rollback process reads role assignments from the AuthZ system and recreates equivalent entries in the CourseAccessRole model.


Rollback Command

./manage.py authz_rollback_course_authoring

Required Filters

Rollback requires specifying either:

  • --course-id-list

  • --org-id

Example:

./manage.py authz_rollback_course_authoring \ --course-id-list course-v1:edX+DemoX+Demo_Course

Example using organization ID:

./manage.py authz_rollback_course_authoring \ --org-id edX

Optional Flag: Remove AuthZ Assignments

To remove AuthZ assignments after restoring legacy roles:

--delete

Example:

./manage.py authz_rollback_course_authoring \ --org-id edX \ --delete

When enabled, the command prompts for confirmation before removing the AuthZ assignments.


6. Known Limitations

The M0 implementation introduces several temporary limitations.

1. Partial system coverage

AuthZ currently supports only a limited subset of role scopes and use cases.

Some services still rely exclusively on legacy authorization.

2. Dual authorization systems

Both legacy and AuthZ systems coexist during the transition.

This may cause temporary complexity in debugging authorization decisions.

3. Limited UI coverage

The UI only exposes a subset of the new authorization capabilities.

Advanced policy management remains backend-only.

4. Role parity assumptions

The migration assumes that legacy roles have direct equivalents in the AuthZ model.

Custom or non-standard roles may require manual validation.


7. Operational Playbook

This section provides guidance for running the migration and handling issues during the transition.


Running the Migration

Example migration:

./manage.py authz_migrate_course_authoring \ --org-id edX --delete

Example migrating specific courses:

./manage.py authz_migrate_course_authoring \ --course-id-list course-v1:edX+DemoX+Demo_Course --delete

Verifying Migration

Administrators can verify migration results by checking both systems.

Check legacy roles

SELECT * FROM student_courseaccessrole WHERE course_id = '<COURSE_ID>';

If --delete was used, successfully migrated entries will not appear.


Check AuthZ policies

AuthZ role assignments are stored in the Casbin rule table.

Example query:

SELECT * FROM openedx_authz_extendedcasbinrule;

Administrators should verify that the expected role assignments exist.


Debugging Migration Failures

Migration errors may occur if:

  • a role mapping does not exist

  • a role assignment fails

  • a database error occurs

Typical log messages include:

Unknown access level

or

Failed to migrate permission for User

When failures occur:

  1. review application logs

  2. identify affected users or courses

  3. correct the issue

  4. re-run the migration for the affected scope


Re-running Migration

The migration command can be safely re-run.

Existing role assignments will not create duplicate assignments in the AuthZ system.


Performing a Rollback

If problems occur, administrators can restore legacy roles.

Example rollback:

./manage.py authz_rollback_course_authoring \ --org-id edX

Example for a specific course:

./manage.py authz_rollback_course_authoring \ --course-id-list course-v1:edX+DemoX+Demo_Course

Removing AuthZ Assignments After Rollback

To remove AuthZ assignments after restoring legacy roles:

./manage.py authz_rollback_course_authoring \ --org-id edX \ --delete

Recovery Procedure

If issues occur after migration:

  1. run the rollback command

  2. verify legacy roles were restored

  3. investigate migration errors

  4. correct any data or mapping issues

  5. re-run the migration

Because migration and rollback run inside database transactions, unexpected failures will automatically roll back database changes.