openedx_authz package#

Subpackages#

Submodules#

openedx_authz.admin module#

Admin configuration for openedx_authz.

class openedx_authz.admin.AuthzCourseAuthoringMigrationRunAdmin(model, admin_site)

Bases: ModelAdmin

Admin for AuthzCourseAuthoringMigrationRun to display additional metadata.

fields = ('scope_type', 'scope_key', 'migration_type', 'status', 'pretty_metadata', 'completed_at', 'created_at', 'updated_at')
list_display = ('id', 'scope_type', 'scope_key', 'migration_type', 'status', 'created_at', 'updated_at')
list_filter = ('scope_type', 'migration_type', 'status')
property media
pretty_metadata(obj)

Return formatted JSON for the metadata field.

readonly_fields = ('scope_type', 'scope_key', 'migration_type', 'status', 'pretty_metadata', 'completed_at', 'created_at', 'updated_at')
search_fields = ('scope_type', 'scope_key', 'migration_type', 'status')
class openedx_authz.admin.CasbinRuleAdmin(model, admin_site)

Bases: ModelAdmin

Admin for CasbinRule to display additional metadata.

form

alias of CasbinRuleForm

inlines = [<class 'openedx_authz.admin.ExtendedCasbinRuleInline'>]
list_display = ('id', 'ptype', 'v0', 'v1', 'v2', 'v3', 'v4', 'v5')
list_filter = ('ptype',)
property media
search_fields = ('ptype', 'v0', 'v1', 'v2', 'v3', 'v4', 'v5')
class openedx_authz.admin.CasbinRuleForm(*args, **kwargs)

Bases: ModelForm

Custom form for CasbinRule to make v3, v4, v5 fields optional.

class Meta

Bases: object

Meta class for CasbinRuleForm.

fields = '__all__'
model

alias of CasbinRule

base_fields = {'ptype': <django.forms.fields.CharField object>, 'v0': <django.forms.fields.CharField object>, 'v1': <django.forms.fields.CharField object>, 'v2': <django.forms.fields.CharField object>, 'v3': <django.forms.fields.CharField object>, 'v4': <django.forms.fields.CharField object>, 'v5': <django.forms.fields.CharField object>}
declared_fields = {}
property media

Return all media required to render the widgets on this form.

class openedx_authz.admin.ExtendedCasbinRuleInline(parent_model, admin_site)

Bases: StackedInline

Inline admin for ExtendedCasbinRule to display additional metadata.

can_delete = False
extra = 0
fields = ('casbin_rule_key', 'scope', 'subject', 'description', 'metadata', 'created_at', 'updated_at')
property media
model

alias of ExtendedCasbinRule

readonly_fields = ('casbin_rule_key', 'scope', 'subject', 'created_at', 'updated_at')
class openedx_authz.admin.RoleAssignmentAuditAdmin(model, admin_site)

Bases: ModelAdmin

Read-only admin for the role assignment audit log.

date_hierarchy = 'timestamp'
display_role(obj)

Role name without the namespace prefix.

display_scope(obj)

Scope key without the namespace prefix.

display_subject(obj)

Subject key without the namespace prefix.

has_add_permission(request)

Audit records are created by the system only.

has_change_permission(request, obj=None)

Audit records must not be modified after creation.

has_delete_permission(request, obj=None)

Audit records must not be deleted through the admin.

list_display = ('operation', 'display_subject', 'display_role', 'display_scope', 'actor_id', 'timestamp')
list_filter = ('operation', <class 'openedx_authz.admin.ScopeTypeFilter'>)
property media
readonly_fields = ('operation', 'subject', 'role', 'scope', 'actor_id', 'timestamp')
search_fields = ('subject', 'role', 'scope')
class openedx_authz.admin.ScopeTypeFilter(request, params, model, model_admin)

Bases: SimpleListFilter

Filter audit records by scope type (content library, course, etc.).

lookups(request, model_admin)

Return the available scope type choices.

Audit records are independent from live Casbin tables and scope objects: there are no FK references to filter on. The namespace prefix in the stored scope string (e.g. lib^, course-v1^) is the only available signal for categorizing records by scope type.

parameter_name = 'scope_type'
queryset(request, queryset)

Filter the queryset by scope namespace prefix.

title = 'scope type'
openedx_authz.admin.pretty_json(value) str

Return an indented JSON representation of a value.

openedx_authz.apps module#

openedx_authz Django application initialization.

class openedx_authz.apps.OpenedxAuthzConfig(app_name, app_module)

Bases: AppConfig

Configuration for the openedx_authz Django application.

default_auto_field = 'django.db.models.BigAutoField'
name = 'openedx_authz'
plugin_app = {'settings_config': {'cms.djangoapp': {'common': {'relative_path': 'settings.common'}, 'production': {'relative_path': 'settings.production'}, 'test': {'relative_path': 'settings.test'}}, 'lms.djangoapp': {'common': {'relative_path': 'settings.common'}, 'production': {'relative_path': 'settings.production'}, 'test': {'relative_path': 'settings.test'}}}, 'url_config': {'cms.djangoapp': {'namespace': 'openedx-authz', 'regex': '^api/', 'relative_path': 'urls'}, 'lms.djangoapp': {'namespace': 'openedx-authz', 'regex': '^api/', 'relative_path': 'urls'}}}
ready()

Import signal handlers when Django starts.

verbose_name = 'Open edX AuthZ'

openedx_authz.data module#

Top-level data classes for actions and permissions.

These are defined here (rather than in openedx_authz.api.data) to avoid a circular import between openedx_authz.api.data and openedx_authz.constants.permissions.

class openedx_authz.data.ActionData(external_key: str = '', namespaced_key: str = '')

Bases: AuthZData

An action represents an operation that can be performed in the authorization system.

NAMESPACE

‘act’ for actions.

Type:

ClassVar[str]

external_key

The action identifier (e.g., ‘content_libraries.view_library’).

Type:

str

namespaced_key

The action identifier with namespace (e.g., ‘act^content_libraries.view_library’).

Type:

str

Examples

>>> action = ActionData(external_key='content_libraries.delete_library')
>>> action.namespaced_key
'act^content_libraries.delete_library'
>>> action.name
'Content Libraries > Delete Library'
NAMESPACE: ClassVar[str] = 'act'
property name: str

The human-readable name of the action (e.g., ‘Content Libraries > Delete Library’).

class openedx_authz.data.AuthZData(external_key: str = '', namespaced_key: str = '')

Bases: AuthzBaseClass

Base class for all authz data classes.

external_key: str
namespaced_key: str
class openedx_authz.data.AuthzBaseClass

Bases: object

Base class for all authz classes.

NAMESPACE: ClassVar[str] = None
SEPARATOR: ClassVar[str] = '^'
class openedx_authz.data.PermissionData(action: ActionData = None, effect: Literal['allow', 'deny'] = 'allow')

Bases: object

A permission combines an action with an effect (allow or deny).

action

The action being permitted or denied (ActionData instance).

Type:

openedx_authz.data.ActionData

effect

The effect of the permission, either ‘allow’ or ‘deny’ (default: ‘allow’).

Type:

Literal[‘allow’, ‘deny’]

Examples

>>> read_action = ActionData(external_key='read')
>>> permission = PermissionData(action=read_action, effect='allow')
>>> str(permission)
'Read - allow'
action: ActionData
effect: Literal['allow', 'deny']
property identifier: str

Get the permission identifier.

openedx_authz.handlers module#

Signal handlers for the authorization framework.

These handlers ensure proper cleanup and consistency when models are deleted.

openedx_authz.handlers.create_audit_record_on_role_assignment_change(sender, role_assignment, **kwargs)

Create an audit record when a role assignment is created or deleted.

This handler listens for both creation and deletion of role assignments and logs the changes for auditing purposes.

Parameters:
  • sender – The signal class (ROLE_ASSIGNMENT_CREATED or ROLE_ASSIGNMENT_DELETED).

  • role_assignment – RoleAssignmentEventData carrying the operation, subject, role, scope, and actor.

  • **kwargs – Additional keyword arguments from the signal.

openedx_authz.handlers.delete_casbin_rule_on_extended_rule_deletion(sender, instance, **kwargs)

Delete the companion CasbinRule after its ExtendedCasbinRule disappears.

The handler keeps authorization data symmetric with three common flows:

  • Direct ExtendedCasbinRule deletes (API/UI) trigger removal of the linked CasbinRule.

  • Cascades from Scope or Subject deletions clear their ExtendedCasbinRule rows and, via this handler, the matching CasbinRule entries.

  • Cascades initiated from the CasbinRule side (enforcer cleanups) leave the query as a no-op because the row is already gone.

Running on post_delete ensures database cascades complete before the cleanup runs, so enforcer-driven deletions no longer raise false errors.

Parameters:
  • sender – The model class (ExtendedCasbinRule).

  • instance – The ExtendedCasbinRule instance being deleted.

  • **kwargs – Additional keyword arguments from the signal.

openedx_authz.handlers.get_effective_state(record: None, global_flag_enabled: bool) bool

Return whether the feature is effectively active for the override and global flag.

An enabled override forces on or off, otherwise the result follows the global flag.

Parameters:
  • record (WaffleOverrideRecord | None) – The waffle flag record to evaluate.

  • global_flag_enabled (bool) – The state of the global flag.

Returns:

True if the feature is active, False otherwise.

Return type:

bool

openedx_authz.handlers.get_excluded_course_ids_for_org_migration(org_id: str, override_choice: str) frozenset[str]

Collect course-level authoring flag overrides for an org that oppose the new org-level state.

When the org flag changes, we need to exclude course ids that have a course-level authoring flag override that opposes the new org-level state.

Parameters:
  • org_id (str) – Organization short name.

  • override_choice (str) – The override choice of the org waffle flag.

Returns:

course ids excluded from org migration

Return type:

frozenset[str]

openedx_authz.handlers.get_migration_type(current_record: None, previous_record: None, global_flag_enabled: bool) MigrationType | None

Determine the migration type by comparing the effective state before and after the transaction.

This accounts for the global flag state, meaning a transition could be triggered by removing a FORCE_OFF override when the global flag is ON.

Parameters:
  • current_record (WaffleOverrideRecord) – The state of the record in the current transaction.

  • previous_record (WaffleOverrideRecord | None) – The state of the record prior to the current transaction.

  • global_flag_enabled (bool) – The state of the global flag.

Returns:

If the flag is newly forced on. MigrationType.ROLLBACK: If the forced-on state is removed. None: If there is no effective change in the flag’s behavior.

Return type:

MigrationType.FORWARD

openedx_authz.handlers.handle_course_waffle_flag_change(sender, instance, **kwargs) None

Handle changes to course-level waffle flags.

When the authz.enable_course_authoring flag is changed for a course, trigger the appropriate migration run. Only trigger if automatic migration is enabled in the settings.

Parameters:
  • sender – The model class (WaffleFlagCourseOverrideModel)

  • instance – The flag override instance being saved

  • **kwargs – Additional keyword arguments from the signal

openedx_authz.handlers.handle_org_waffle_flag_change(sender, instance, **kwargs) None

Handle changes to organization-level waffle flags.

When the authz.enable_course_authoring flag is changed for an organization, trigger the appropriate migration run. Only trigger if automatic migration is enabled in the settings.

Parameters:
  • sender – The model class (WaffleFlagOrgOverrideModel)

  • instance – The flag override instance being saved

  • **kwargs – Additional keyword arguments from the signal

openedx_authz.handlers.trigger_course_authoring_migration(sender: type[None], instance: None, scope_key: str) None

Trigger a migration run in response to a waffle flag change.

Determines the migration direction from the flag state, guards against no-op saves, and delegates execution to run_course_authoring_migration which handles tracking and concurrent-run protection.

Parameters:
  • sender – The model class (WaffleOverrideRecord).

  • instance – The waffle flag instance that triggered the migration.

  • scope_key (str) – Course ID or organization name.

openedx_authz.handlers.unassign_roles_on_user_retirement(sender, user, **kwargs)

Unassign roles from a user when they are retired.

This handler is triggered when a user is retired in the LMS. It ensures that any roles assigned to the user are removed, maintaining the integrity of the authorization system.

Parameters:
  • sender – The model class (User).

  • user – The user instance being retired.

  • **kwargs – Additional keyword arguments from the signal.

openedx_authz.urls module#

Open edX AuthZ API URLs.

openedx_authz.utils module#

General utility functions for Open edX AuthZ.

openedx_authz.utils.get_user_by_username_or_email(username_or_email: str) User

Retrieve a user by their username or email address.

Parameters:

username_or_email (str) – The username or email address to search for.

Returns:

The User object if found and not retired.

Return type:

User

Raises:

User.DoesNotExist – If no user matches the provided username or email, or if the user has an associated retirement request.

openedx_authz.utils.is_user_staff_or_superuser(username: str) bool

Return True if the user with the given username is staff or superuser.

Uses RequestCache to avoid repeated DB lookups within the same request. Returns False if the user does not exist or has a retirement request.

Module contents#

Open edX AuthZ provides the architecture and foundations of the authorization framework.