Skip to main content

Role-Based Access Control (RBAC)

In MLflow, admins grant users permissions by assigning them roles. A role is a named group of permissions defined once and used multiple times. With workspaces (--enable-workspaces), roles become workspace-scoped.

Prerequisite

Authentication enabled (mlflow server --app-name basic-auth).

For setting up authentication and managing users (login, signup, admin status, password updates), see Username and Password; this page picks up once users exist.

Concepts

Three entities - users, roles, resources - connected by two relations: role assignment and permission grants. Roles (and the grants they carry) are workspace-scoped when workspaces are enabled; see Workspace isolation for the on/off delta.

  • User - an actor who wants to access resources.
  • Resource - a target entity in MLflow that permissions are managed on, such as an experiment, a registered model, a prompt, a scorer, or an AI Gateway resource (a secret, an endpoint, or a model definition).
  • Role - a named bundle of permissions on resources.

Roles

A role is a named list of (resource_type, resource_pattern, permission) grants. Grants can target a single resource (experiment:42) or every resource of a type (experiment:*), so one role can express both fine-grained and broad access. For example, an editor role might carry (experiment, *, EDIT) to let its members edit every experiment, while a narrower role could combine (experiment, 42, READ) with (prompt, 7, EDIT).

Permission levels

The grantable permission levels for resource-scoped permissions are:

PermissionCan readCan useCan updateCan deleteCan manage
READYesNoNoNoNo
USEYesYesNoNoNo
EDITYesYesYesNoNo
MANAGEYesYesYesYesYes

USE is for consuming a resource without modifying it (invoking a gateway endpoint, referencing a model definition, creating new experiments / registered models within a workspace).

NO_PERMISSIONS is the resolver's deny sentinel for workspace-boundary cases - e.g., a user with no workspace membership when grant_default_workspace_access is off. It is not grantable as a role permission or a direct grant; the auth server rejects attempts to assign it. The effective permission when no role grant matches is the server's default_permission (READ by default; see Permission resolution).

Because grants are folded via a max (see Permission resolution), RBAC has no explicit-deny override. To restrict access to a specific resource, grant access more narrowly (per-resource or resource-type wildcard) rather than holding a broad workspace-level grant and trying to except the resource.

Permission resolution

For each authorization check, MLflow evaluates the user's effective permission as follows:

  1. Platform Admin bypass: is_admin = true short-circuits to allow.
  2. Role-derived grants: all of the user's roles in the request's workspace contribute. Grants that apply to the resource fold via a max, and the highest-priority permission wins (MANAGE > EDIT > USE > READ). (workspace, *, MANAGE) folds into every per-resource check - it grants management of every resource in the workspace. (workspace, *, USE) does not fold into per-resource checks; it confers workspace membership and create rights only (see Workspace isolation).
  3. Server default_permission (defaults to READ): a floor max'd into the resolution result. When no role grant matches at all, the floor is the final answer. The floor does not lift NO_PERMISSIONS - when the resolver returns it for a workspace-boundary reason (no workspace membership, missing workspace, lookup error), that deny stands. See Workspace isolation for when the floor applies in multi-workspace mode.

Workspace isolation

A workspace is an isolated container for MLflow resources. Workspaces are off by default. To turn them on, start the server with --enable-workspaces. Once enabled, every experiment, registered model, prompt, scorer, and AI Gateway resource belongs to exactly one workspace, and nothing crosses the boundary.

Roles and permissions follow the same boundary. A role named editor in workspace foo is a completely separate row from editor in workspace bar, and a permission grant made in one workspace has no effect in any other. How that boundary shows up depends on whether multi-workspace support is enabled.

Default (no --enable-workspaces). A single reserved default workspace exists. All roles, all assignments, all permissions live there. The server's default_permission config (when set) applies as a floor for any (user, resource) pair.

Multi-workspace (--enable-workspaces). Named workspaces become first-class. The home page renders a workspace switcher; the admin UI gets a per-workspace entry point (/admin/ws?workspace=<name>); roles must be created in a specific workspace. The server's default_permission applies as the floor in named workspaces only when grant_default_workspace_access is set; otherwise the effective floor in each named workspace is deny.

Two grants on the special (resource_type='workspace', resource_pattern='*') slot define workspace-wide access:

  • USE on (workspace, *) - the Workspace Member grant.
    • Confers workspace membership: the user can list and enter the workspace.
    • Allows creating new resources (e.g., experiments, registered models) within it.
    • Does not itself grant per-resource access. Read access on individual resources comes from the server default_permission floor (defaults to READ); higher tiers require an explicit per-resource or resource-type wildcard grant.
  • MANAGE on (workspace, *) - the Workspace Manager grant. Full authority within the workspace, including creating roles, granting permissions, and managing role assignments. Cannot perform system-wide operations such as deleting users.

The seeded admin and user roles each carry one of these grants; see Default roles.

User tiers

The admin UI uses Platform Admin for the first tier and Workspace Manager for the second; the doc uses the same labels.

TierHow it's expressedCapability
Platform Adminis_admin = true on the user rowUnrestricted system-wide. Sole bearer of user delete and bulk operations.
Workspace ManagerHolds (workspace, *, MANAGE) via any roleFull authority within those workspaces; can manage roles, users, and grants.
Regular userAny other authenticated identityNo admin UI access; authorization flows through role-derived permissions only.

Direct grants without a role

For the one-user one-resource case, you don't need to author a single-user role. Each user has a reserved per-user role that holds their direct grants; the grant_user_permission / revoke_user_permission APIs and the admin UI's Direct permissions section write to it directly. From an operator's perspective it's one call or one form field - the reserved role is an implementation detail you never touch.

Admin UI: /adminUsers → click the user → Edit access → in the Direct permissions section, add the (resource, permission) grant → review → apply.

API:

python
auth_client.grant_user_permission("alice", "experiment", "42", "EDIT")

grant_user_permission is gated by per-resource MANAGE on the target resource - the same authority check the legacy create_experiment_permission() family used - so an experiment owner who holds (experiment, 42, MANAGE) can grant access without holding workspace-wide MANAGE. The role API itself requires workspace MANAGE or Platform Admin, since authoring a role is an administrative action.

For reads, the same family includes auth_client.get_user_permission(username, resource_type, resource_id) - returns the resolved effective permission via the same resolver the runtime authorization check uses - and auth_client.list_user_permissions(username), which returns every grant the user holds across all their roles, enriched with role_id / role_name / workspace.

Common scenarios

The big shift from the legacy permission model: every "give X access to Y" used to be a one-off per-resource POST. Now you build the permission set once as a role and assign users to it. Both the admin UI and the AuthServiceClient go through the same role-based primitives.

The examples below assume multi-workspace mode and that you've authenticated as a Platform Admin or as a Workspace Manager of ml-research.

python
from mlflow.server import get_app_client

tracking_uri = "http://localhost:5000"
auth_client = get_app_client("basic-auth", tracking_uri=tracking_uri)

1. Give Alice EDIT on experiment 42

  1. Navigate to /adminRoles.
  2. Click Create role.
  3. In Permissions, add experiment:42 → EDIT.
  4. In Assigned users, add alice.
  5. Click Create.

2. Give a team READ access to every experiment in a workspace

A wildcard pattern lets the role apply to resources that don't exist yet; adding a new experiment automatically inherits the grant.

  1. Navigate to /adminRoles.
  2. Click Create role.
  3. In Permissions, add resource type experiment, pattern * (rendered as "All experiments"), permission READ.
  4. In Assigned users, add alice, bob, carol.
  5. Click Create.

3. Make a user a Workspace Manager

Every newly created workspace is seeded with a default admin role (and a user role; see Default roles). Promoting a user means assigning them the seeded admin role for that workspace. The role explicitly carries (workspace, *, MANAGE).

  1. Navigate to /adminUsers.
  2. Click alice.
  3. Click Edit access.
  4. In Role assignments, add ml-research/admin.
  5. Review changes → apply.

4. Onboard a team of N users with the same access

Create one role with desired permissions or reuse the seeded <workspace>/user role.

  1. Navigate to /adminRoles → click the role.
  2. Click Edit role.
  3. In Assigned users, add all the users.
  4. Review → apply.

Admin UI

The admin UI is the operator-facing surface for everything above. It's reached from two entry points:

  • Platform Admins (is_admin = true) navigate to /admin via the sidebar Manage entry. The page renders cross-workspace: every user in the system, every role in every workspace.
  • Workspace Managers click a gear icon on the home-page workspaces table next to a workspace they administer. The link lands at /admin/ws?workspace=<name>, the per-workspace view scoped to roles and users they can see.

Both views share the same layout: a Users tab and a Roles tab.

Managing Users

Admin UI showing Users and Roles tabs

The Users tab lists every user with their visible roles. Click a username to open the user detail page, then Edit access to manage that user's roles, direct permissions, and admin status (Platform Admins only). Changes are previewed in a Review step before they're applied.

User detail page showing role assignments

Managing Roles

The Roles tab lists roles in the active scope (all roles for Platform Admins; the active workspace's roles for Workspace Managers). Create role opens a single-page form with three sections (Role details, Permissions, Assigned users) and submits them in one shot. Edit role on the role detail page mirrors the same shape.

Roles tab listing roles across workspaces

Edit role form with Role details and Permissions sections

Default roles

When MLFLOW_RBAC_SEED_DEFAULT_ROLES is on (it is, by default), MLflow seeds two roles into every newly created workspace:

RolePermissionIntent
admin(workspace, *, MANAGE)Workspace Manager. Full authority within the workspace.
user(workspace, *, USE)Workspace Member. Reads every resource in the workspace; can create experiments and registered models.

The user who creates the workspace is automatically assigned the seeded admin role for that workspace.

To disable the seeding (for installations that prefer to define roles manually):

bash
export MLFLOW_RBAC_SEED_DEFAULT_ROLES=false

Migrating from the legacy permission model

If you're upgrading from a release before MLflow 3.13 that used the per-resource permission endpoints, the auth-store backfill migration translates the legacy permission tables into role_permissions rows. Existing grants survive the upgrade - only the API surface changes.

The wire surface and AuthServiceClient shape change as follows:

Pre-RBAC surfaceStatusReplacement
Per-resource permission REST endpoints + AuthServiceClient methods (create_experiment_permission(), etc.)Removedgrant_user_permission / revoke_user_permission for one-off direct grants, or the role API for shared sets.
Workspace permission REST endpoints + AuthServiceClient methods (set_workspace_permission(), etc.)RemovedThe role API: assign the seeded admin / user role, or author a role carrying (workspace, *, ...).

At the wire level, the ~24 legacy per-resource REST endpoints consolidated into four under /mlflow/users/permissions/*: grant, revoke, get, list.

There is no deprecation-warning window: basic-auth was still marked experimental, so the carry cost of dozens of deprecation-warning-emitting methods outweighed the soft-transition benefit. After the upgrade, the one-user one-resource workflow is:

python
# Replace this:
# auth_client.create_experiment_permission(experiment_id, username, "EDIT")
auth_client.grant_user_permission(username, "experiment", experiment_id, "EDIT")

Per-resource MANAGE retains delegation. A user with (experiment, 42, MANAGE) can still grant other users access to experiment 42 - the new grant_user_permission / revoke_user_permission are gated by the same per-resource MANAGE check the legacy endpoints used.

Rollback

The legacy permission tables (experiment_permissions, registered_model_permissions, the four gateway_*_permissions, scorer_permissions, workspace_permissions) remain on disk for rollback safety through at least one full release cycle. To revert the auth-store schema:

bash
mlflow db downgrade <previous-revision> --backend-store-uri <auth-db-uri>

The downgrade leaves the legacy tables in place and removes the role_permissions rows the backfill produced. Re-running the upgrade re-applies the backfill. The legacy tables are dropped in a subsequent migration after one full release cycle, so plan upgrade timing accordingly.

API reference

The full list of role/permission/user-assignment methods and their signatures lives in the auto-generated API docs: