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 Privileges.

Quick start​

The example creates a role that can read one schema and gives it to a group. Reading one schema takes three privileges: USE_CATALOG and USE_SCHEMA to reach it, and SELECT_TABLE to read what is in it.

  1. Create the role. Open Authorize → Roles, click + Role, enter the Role name, and click Add role. Members can also be chosen here, under Member type.

  2. Grant privileges on the catalog. Open the role, select its Privileges tab, and click + Privilege. Choose Catalog as the Object type and the catalog as the Object, leave the Effect at Allow, select Use Catalog, and click Grant privilege. The privilege list fills in once an Object is chosen, and offers only what can be granted on that object.

  3. Grant privileges on the schema. Click + Privilege again, choose Schema and the schema, select Use Schema and Select Table, and click Grant privilege.

  4. Give the role to a group. Select the role's Members tab, click + Members, choose Groups, select the group, and click Add members. Granting to a group reaches every member, which is how access is usually managed for a team.

Adding a user or group to a role also adds it to the metalake, so assigning a role is how someone from the server's directory gets access to a metalake. To give several users the same roles at once, select them on Authorize → Users and click + Roles.

Privileges​

See Privileges for a comprehensive list of privilege grants, and Required privileges for what each operation needs.

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.

Activate two roles
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 catalog property
spark.sql.catalog.{catalog}.header.X-Gravitino-Active-Roles = analyst

Trino 481 and later forwards headers configured on the catalog:

Trino catalog property
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:

Set the header on the Java client
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:

Build a securable table object
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:

Grant and revoke privileges on a role
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()));