Skip to main content

Roles

Overview

A role is a named set of privileges, granted to users and groups. Privileges are never granted directly to a user, so a role is the only way a privilege reaches anyone.

A role holds objects, and for each one a list of privileges, each carrying an ALLOW or DENY condition. A privilege binds only to object types it supports, so CREATE_TABLE binds to a metalake, catalog, or schema, never to a table. Whoever creates a role owns it, and can alter or delete it.

How a request is evaluated against the roles a caller holds, including how DENY interacts with ALLOW, is described in Access Control.

Quick Start

1. Create a role. Reading one schema takes three privileges: USE_CATALOG and USE_SCHEMA to reach it, and SELECT_TABLE to read what is in it. Object names are not validated, so a typo produces a role that grants nothing.

curl -sS -X POST "https://{gravitino_host}/api/metalakes/{metalake}/roles" \
-H "Accept: application/vnd.gravitino.v1+json" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{
"name": "schema_reader",
"properties": {},
"securableObjects": [
{
"fullName": "{catalog}",
"type": "CATALOG",
"privileges": [{"name": "USE_CATALOG", "condition": "ALLOW"}]
},
{
"fullName": "{catalog}.{schema}",
"type": "SCHEMA",
"privileges": [
{"name": "USE_SCHEMA", "condition": "ALLOW"},
{"name": "SELECT_TABLE", "condition": "ALLOW"}
]
}
]
}'

2. Grant it to a group. Granting to a group reaches every member, which is how access is usually managed for a team. Granting to a single user works the same way with users in the path.

curl -sS -X PUT "https://{gravitino_host}/api/metalakes/{metalake}/permissions/groups/{group}/grant" \
-H "Accept: application/vnd.gravitino.v1+json" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"roleNames": ["schema_reader"]}'

Privileges and What They Allow

Grantable On lists the object types a privilege can be bound to, and the object it is bound to sets the scope of the grant. Binding a privilege to a type not listed for it is rejected.

Data Object Privileges

PrivilegeGrantable OnWhat It Allows
CREATE_CATALOGMetalakeCreate catalogs
USE_CATALOGMetalake, CatalogUse any catalog in scope, and reach the objects inside it
CREATE_SCHEMAMetalake, Catalog, SchemaCreate schemas or nested schemas in scope
USE_SCHEMAMetalake, Catalog, SchemaUse any schema in scope, and reach the objects inside it
CREATE_TABLEMetalake, Catalog, SchemaCreate tables in any schema in scope
SELECT_TABLEMetalake, Catalog, Schema, TableRead any table in scope
MODIFY_TABLEMetalake, Catalog, Schema, TableRead and write to, and alter the schema of, any table in scope
CREATE_VIEWMetalake, Catalog, SchemaCreate views in any schema in scope
SELECT_VIEWMetalake, Catalog, Schema, ViewRead any view in scope
CREATE_TOPICMetalake, Catalog, SchemaCreate topics in any schema in scope
CONSUME_TOPICMetalake, Catalog, Schema, TopicConsume from any topic in scope
PRODUCE_TOPICMetalake, Catalog, Schema, TopicConsume from, produce to, and alter any topic in scope
CREATE_FILESETMetalake, Catalog, SchemaCreate filesets in any schema in scope
READ_FILESETMetalake, Catalog, Schema, FilesetRead any fileset in scope
WRITE_FILESETMetalake, Catalog, Schema, FilesetRead, write, and alter any fileset in scope
REGISTER_MODELMetalake, Catalog, SchemaRegister models in any schema in scope
LINK_MODEL_VERSIONMetalake, Catalog, Schema, ModelLink versions to any model in scope
USE_MODELMetalake, Catalog, Schema, ModelRead the metadata of, and download versions of, any model in scope
REGISTER_FUNCTIONMetalake, Catalog, SchemaRegister functions in any schema in scope
EXECUTE_FUNCTIONMetalake, Catalog, Schema, FunctionRead the metadata of, and execute, any function in scope
MODIFY_FUNCTIONMetalake, Catalog, Schema, FunctionAlter any function in scope

Either SELECT_TABLE or MODIFY_TABLE is enough to load a table's metadata, and the same pairing holds for views, topics, and filesets.

CREATE_MODEL and CREATE_MODEL_VERSION are deprecated aliases for REGISTER_MODEL and LINK_MODEL_VERSION. They resolve to identical authorization, so existing grants keep working, but they will be removed in a future release. Use the current names in new roles.

Governance and Administrative Privileges

PrivilegeGrantable OnWhat It Allows
MANAGE_USERSMetalakeAdd and remove users
MANAGE_GROUPSMetalakeAdd and remove groups
CREATE_ROLEMetalakeCreate roles
MANAGE_GRANTSMetalake, Catalog, Schema, Table, View, Topic, Fileset, Model, FunctionGrant and revoke privileges on any object in scope
CREATE_TAGMetalakeCreate tags
APPLY_TAGMetalake, TagAttach tags to metadata objects
CREATE_POLICYMetalakeCreate policies
APPLY_POLICYMetalake, PolicyAttach policies to metadata objects
REGISTER_JOB_TEMPLATEMetalakeRegister job templates
USE_JOB_TEMPLATEMetalake, JobTemplateRun jobs from a job template
RUN_JOBMetalakeRun jobs

MANAGE_GRANTS bound to a metalake additionally allows granting and revoking roles for users and groups across that metalake. Bound to anything else it covers privilege management only, on that object and its descendants.

APPLY_TAG, APPLY_POLICY, and USE_JOB_TEMPLATE scope differently from every other privilege on this page. The object they bind to is the instrument the holder may use, not the object the operation acts on. Granting APPLY_POLICY on the policy pii_masking lets the holder attach that one policy and no other, while granting it on the metalake lets them attach any policy in the metalake.

Attaching a tag or a policy is checked twice: the holder needs APPLY_TAG or APPLY_POLICY for the tag or policy in question, and separately needs access to the metadata object being tagged. A user cannot tag an object they could not otherwise reach.

Required Privileges

Four rules apply throughout, so they are not repeated below:

  • Owning the object, or any ancestor of it, satisfies any check on it once the object can be reached. Owner in the tables means ownership is the only route, because no privilege grants that operation.
  • Reaching an object inside a catalog and a schema requires USE_CATALOG and USE_SCHEMA, unless the caller owns the catalog or schema above it. Owning the object itself does not replace them.
  • An owner can always read an object's owner and transfer its ownership, with no grants on its parents, so ownership can never be stranded. The UI hides objects whose parents the caller cannot reach, so an owner without those grants makes the transfer through the API.
  • A privilege counts whether it is held on the object itself or on any ancestor.

List operations never fail. They return the entries the caller is entitled to see, which for a metalake owner is all of them.

Data Objects

ObjectCreateLoadAlterDrop
CatalogCREATE_CATALOGUSE_CATALOGOwnerOwner
SchemaCREATE_SCHEMAUSE_SCHEMAOwnerOwner
TableCREATE_TABLESELECT_TABLE or MODIFY_TABLEMODIFY_TABLEOwner
ViewCREATE_VIEWSELECT_VIEWOwnerOwner
TopicCREATE_TOPICCONSUME_TOPIC or PRODUCE_TOPICPRODUCE_TOPICOwner
FilesetCREATE_FILESETREAD_FILESET or WRITE_FILESETWRITE_FILESETOwner
ModelREGISTER_MODELUSE_MODELOwnerOwner
FunctionREGISTER_FUNCTIONEXECUTE_FUNCTION or MODIFY_FUNCTIONMODIFY_FUNCTIONOwner

Table statistics follow the table itself: reading them takes SELECT_TABLE or MODIFY_TABLE, writing them takes MODIFY_TABLE. Model versions follow the model: USE_MODEL to read, owner to alter or delete. Fetching a credential takes whatever loading the object takes.

Renaming a table or view into a different schema is the one operation needing a privilege on a second object: the owner of the table or view, plus CREATE_TABLE or CREATE_VIEW on the target schema.

Metalake Objects

ObjectCreateReadAlter or deleteUse
MetalakeService administratorMembershipOwner
UserMANAGE_USERSMANAGE_USERS, or the user themselvesMANAGE_USERS
GroupMANAGE_GROUPSMANAGE_GROUPS, or a memberMANAGE_GROUPS
RoleCREATE_ROLEMANAGE_GRANTS, or a holder or ownerOwnerGrant or revoke: MANAGE_GRANTS
TagCREATE_TAGAPPLY_TAGOwnerAttach: APPLY_TAG and access to the object
PolicyCREATE_POLICYAPPLY_POLICYOwnerAttach: APPLY_POLICY and access to the object
Job templateREGISTER_JOB_TEMPLATEUSE_JOB_TEMPLATEOwnerRun a job: RUN_JOB and USE_JOB_TEMPLATE
JobOwnerOwner

Granting or revoking a privilege on an object takes MANAGE_GRANTS on that object or an ancestor. Granting or revoking a role, and overriding a role's privileges, takes MANAGE_GRANTS on the metalake. Setting an owner takes ownership.

Narrowing Access with Active Roles

By default, a request is evaluated against every role the caller holds. The X-Gravitino-Active-Roles header narrows that set for the request that carries it, so a workload runs with only the roles it needs instead of every role its user has been granted.

X-Gravitino-Active-Roles: analyst,reader
ValueMeaning
analystActivate one named role
analyst,readerActivate several; access is the union of just these
ALLActivate every role the caller holds
NONEActivate no role
(absent or empty)Same as ALL

Role names are matched exactly, and ALL and NONE are recognized only in upper case, so all is read as the name of a role. Surrounding whitespace is trimmed and repeated names collapse.

What Narrowing Changes

Narrowing only ever subtracts. The server validates the declaration against the roles the caller actually holds, so the header can never widen access, and a caller that omits it is evaluated exactly as before.

  • DENY stays global. A deny carried by any role the caller holds still applies even when that role is not active, so narrowing cannot be used to escape a denial.
  • Ownership is untouched. Access that comes from owning an object is granted to the owner directly rather than through a role, so an owner keeps it even under NONE.
  • Every decision in the request is narrowed, not only direct checks. List results are filtered against the active set, and so are the privileges behind credential vending: an Iceberg caller whose active roles no longer carry MODIFY_TABLE is vended a read-only storage credential in place of a writable one.

Errors

ConditionResponse
An empty entry, such as the trailing comma in analyst,400 Bad Request
ALL or NONE combined with anything else, such as ALL,analyst400 Bad Request
A well-formed value naming a role the caller does not hold403 Forbidden

A role that does not exist and a role the caller was never granted both return 403, so the response cannot be used to discover which role names exist. An unheld role is rejected rather than ignored, which surfaces a typo immediately instead of silently reducing access.

Sending the Header

Apache Spark forwards any header.* catalog property to the Iceberg REST catalog:

spark.sql.catalog.{catalog}.header.X-Gravitino-Active-Roles = analyst

Trino 481 and later forwards headers configured on the catalog:

iceberg.rest-catalog.http-headers = X-Gravitino-Active-Roles: analyst

Both are catalog-level and static, so the same value applies to every user and session using that catalog. The Java client sets the header per client instance:

GravitinoClient.builder(uri)
.withMetalake("metalake")
.withHeaders(ImmutableMap.of("X-Gravitino-Active-Roles", "analyst"))
.build();

Scope

Narrowing applies where Gravitino enforces authorization itself: the native REST API and the Iceberg REST catalog. Catalogs that push enforcement down to an external system evaluate against the mapped user and groups and never see the declaration, so the header has no effect there. See Authorization Pushdown.

Endpoints

Paths are relative to https://{gravitino_host}/api/metalakes/{metalake}. For request and response schemas, see the Gravitino REST API.

OperationMethodPath
Create a rolePOST/roles
List rolesGET/roles
Get a roleGET/roles/{role}
Delete a roleDELETE/roles/{role}
Grant privileges to a rolePUT/permissions/roles/{role}/{object_type}/{object_name}/grant
Revoke privileges from a rolePUT/permissions/roles/{role}/{object_type}/{object_name}/revoke
Replace a role's privilegesPUT/permissions/roles/{role}/
List the roles bound to an objectGET/objects/{object_type}/{object_name}/roles

Add ?details=true to the list path to get full roles instead of names. Granting a role to a user or group is covered on Users and Groups.

Replacing a role's privileges is destructive: afterwards the role holds exactly what the request body contains, and any object absent from it is dropped.

Java Client

Most calls map directly onto a GravitinoClient method and are covered by the Java doc. Two take arguments that are hard to derive from the signature alone.

A role's objects are built as a nested path rather than a dotted string:

SecurableObject table =
SecurableObjects.ofTable(
SecurableObjects.ofSchema(
SecurableObjects.ofCatalog("catalog1", Collections.emptyList()),
"schema1",
Collections.emptyList()),
"table1",
Lists.newArrayList(Privileges.SelectTable.allow()));

Role role = client.createRole("schema_reader", ImmutableMap.of(), Lists.newArrayList(table));

Privileges are passed as a Set, and each carries its condition. The List overloads of these two methods are deprecated:

MetadataObject schema =
MetadataObjects.of(Lists.newArrayList("catalog1", "schema1"), MetadataObject.Type.SCHEMA);

client.grantPrivilegesToRole("schema_reader", schema, ImmutableSet.of(Privileges.SelectTable.allow()));
client.revokePrivilegesFromRole("schema_reader", schema, ImmutableSet.of(Privileges.SelectTable.deny()));