Skip to main content

Privileges

Overview​

Apache Gravitino federates the catalogs of many systems under a single metalake, so permissions are defined once there rather than separately in each system. When authorization is enabled, the server checks every request before the operation runs and rejects it if the caller is not entitled to it.

Two things decide the answer:

  • Ownership comes with creation. Whoever creates an object owns it, and owning it carries the right to alter it, drop it, and hand it to someone else. Ownership reaches down, so owning a catalog means administrative control over the schemas and tables inside it.
  • Privileges are named permissions, each authorizing one kind of operation: SELECT_TABLE reads a table, CREATE_SCHEMA creates a schema in a catalog. A privilege is never given to a person directly. Privileges are collected into a role, and the role is granted to users and to groups.

Three rules govern how those apply:

  • Grants reach downward. A grant covers everything beneath the object it is made on, both what exists now and what is created later. SELECT_TABLE on a schema covers every table in it.
  • Nothing is permitted unless granted. A user added to a metalake and given nothing can see the metalake and nothing else.
  • An explicit deny overrides everything. Each privilege in a role carries an ALLOW or a DENY condition, and a DENY beats an ALLOW held in any other role and at any other level of the hierarchy. A denial cannot be undone by granting something elsewhere, which makes DENY the way to carve one object out of a broad grant.

Roles, users, and groups each have a page of their own: Roles covers creating roles and granting them, Users covers service administrators and membership, and Groups covers where group membership comes from.

Quick start​

The Helm chart turns authorization on by default. Name the service administrators in my-values.yaml, as in Install with Helm:

my-values.yaml
gravitino:
authorization:
enable: true
serviceAdmins: admin

enable is already true in the chart, so that line only makes the setting explicit. serviceAdmins takes a comma-separated list of user names.

Service administrators are the only users who can create metalakes, and everything after that is done through the UI or the API. The Walkthrough runs a full sequence end to end, from an empty server to a user with read access to one schema. Server Configuration covers the remaining settings, including how a caller's identity reaches the server.

Authorization model​

Principals and objects​

Users and groups​

Users and groups are the principals that privileges and ownership are assigned to. A user must be added to a metalake before it can do anything there, and a role granted to a group applies to every member. See Users and Groups.

Service administrators​

Service administrators are named in serviceAdmins rather than granted anything, and their one authorization right is creating metalakes. No privilege grants that right, so every metalake is created by a service administrator and starts out owned by them, and letting anyone else create metalakes means adding them to serviceAdmins in the server configuration. Everything else about a metalake, including altering it, dropping it, and setting the owner of anything in it, belongs to the metalake's owner. Inside a metalake they do not own, a service administrator has only what roles and ownership give them, like any other user.

Only the current owner can transfer a metalake, so keep that owner reachable. A metalake owned by a user who has left, or by a group with no members, has no one who can administer it.

Objects​

Everything Gravitino manages is an object with a type and a name. The name is the dotted path to it below the metalake, so a table is {catalog}.{schema}.{table}, and requests identify an object by both type and name, since the same name can exist at more than one type.

Access to an object is controlled by privileges, granted through roles, and by ownership.

Everything sits under a metalake, but only the data objects nest below a catalog:

Object hierarchy
Metalake (top level)
├── Catalog (represents a data source)
│ └── Schema
│ ├── Table
│ ├── View
│ ├── Topic
│ ├── Fileset
│ ├── Model
│ └── Function
├── Tag
├── Policy
├── Job Template
├── Role
└── Job

Three things about that tree are worth noting:

  • Roles and jobs are controlled by ownership alone, since no privilege binds to them, though CREATE_ROLE and RUN_JOB on the metalake gate creating them.
  • Users and groups are not objects. They are the principals that privileges and ownership are assigned to.

Grants​

Privileges​

A privilege authorizes one kind of operation. The chart lists every privilege and the levels it can be granted at. A ✓ under Metalake, Catalog, or Schema means the privilege can be granted on an object of that type, where it covers every matching object beneath it. The Object column names the object the privilege can also be granted on directly. A role that binds a privilege anywhere else is rejected when it is created.

PrivilegeAuthorizesMetalakeCatalogSchemaObject
CREATE_CATALOGCreate a catalog in the metalake✓
USE_CATALOGReach a catalog and list it. Required to reach anything inside the catalog✓✓
CREATE_SCHEMACreate a schema in a catalog✓✓✓
USE_SCHEMAReach a schema and list it. Required to reach anything inside the schema✓✓✓
CREATE_TABLECreate a table in a schema✓✓✓
SELECT_TABLELoad a table and read its data✓✓✓Table
MODIFY_TABLEWrite data to a table and alter its schema✓✓✓Table
CREATE_VIEWCreate a view in a schema✓✓✓
SELECT_VIEWLoad a view and read from it✓✓✓View
CREATE_FILESETCreate a fileset in a schema✓✓✓
READ_FILESETRead the files in a fileset✓✓✓Fileset
WRITE_FILESETWrite the files in a fileset✓✓✓Fileset
CREATE_TOPICCreate a topic in a schema✓✓✓
CONSUME_TOPICConsume messages from a topic✓✓✓Topic
PRODUCE_TOPICProduce messages to a topic✓✓✓Topic
REGISTER_MODELRegister a model in a schema✓✓✓
LINK_MODEL_VERSIONLink a new version to a model✓✓✓Model
USE_MODELView a model's metadata and download its versions✓✓✓Model
REGISTER_FUNCTIONRegister a function in a schema✓✓✓
EXECUTE_FUNCTIONInvoke a function✓✓✓Function
MODIFY_FUNCTIONAlter a function's metadata✓✓✓Function
CREATE_TAGCreate a tag in the metalake✓
APPLY_TAGAttach a tag to objects. Granted on the tag, not on the objects✓Tag
CREATE_POLICYCreate a policy in the metalake✓
APPLY_POLICYAttach a policy to objects. Granted on the policy, not the objects✓Policy
REGISTER_JOB_TEMPLATERegister a job template in the metalake✓
USE_JOB_TEMPLATERun jobs from a job template✓Job Template
RUN_JOBRun a job in the metalake✓
MANAGE_USERSAdd users to the metalake and remove them✓
MANAGE_GROUPSAdd groups to the metalake and remove them✓
CREATE_ROLECreate a role in the metalake✓
MANAGE_GRANTSGrant and revoke privileges on the object. On the metalake, also grant roles to users and groups✓✓✓Table, View, Fileset, Topic, Model, Function

Reaching an object takes the traversal privileges as well. SELECT_TABLE on a table authorizes nothing without USE_CATALOG on its catalog and USE_SCHEMA on its schema; see Evaluating a Request.

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

APPLY_TAG, APPLY_POLICY, and USE_JOB_TEMPLATE scope differently from every other privilege in the chart. 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.

CREATE_MODEL and CREATE_MODEL_VERSION are still accepted as deprecated names for REGISTER_MODEL and LINK_MODEL_VERSION, and behave identically.

A role is a named set of privileges granted to users and groups. See Roles.

Ownership​

Whoever creates an object owns it. Ownership carries the administrative rights that no privilege in the chart grants: altering the object, dropping it, and transferring it to someone else. It reaches down, so owning a catalog gives the same control over the schemas and tables inside it. An owner still needs the traversal privileges to reach an object below a catalog they do not own; see Evaluating a Request.

Ownership can be held by a group as well as a user, in which case every member of that group holds it. It applies to metalakes, catalogs, schemas, tables, views, topics, filesets, models, functions, roles, tags, policies, job templates, and jobs.

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.

Resolution​

Evaluating a request​

Every authorized endpoint declares the conditions under which a caller may invoke it, and the check passes if any one of them holds. Loading a table, for example, succeeds when:

  • The caller owns the metalake or the catalog.
  • The caller owns the schema and holds USE_CATALOG.
  • The caller holds both USE_CATALOG and USE_SCHEMA, and additionally owns the table or holds SELECT_TABLE or MODIFY_TABLE.

Note the third case. Granting SELECT_TABLE on a schema covers every table in that schema, but on its own it authorizes nothing, because the traversal privileges are still missing. Owning the table is the same: an owner who cannot reach the catalog and schema above it cannot load the table, though they can still read its owner and transfer it.

A failed check returns 403 Forbidden. Some read paths return 404 Not Found instead, so that a caller cannot infer the existence of an object they are not entitled to see. List operations do not fail; they return only the entries the caller is entitled to see.

When the conditions are checked, a privilege held with DENY at any level counts as not held, which is why a denial cannot be worked around by a grant elsewhere.

Allow and deny​

DENY always wins. It beats an ALLOW in the same role, an ALLOW from any other role the user holds, and an ALLOW at any other level of the hierarchy, in either direction: a DENY on a catalog survives an ALLOW on its metalake, and a DENY on a metalake survives an ALLOW on its catalog. So a denial cannot be circumvented by granting something elsewhere.

Sibling privileges are independent of each other. DENY MODIFY_TABLE leaves ALLOW SELECT_TABLE intact, and the same holds for PRODUCE_TOPIC and CONSUME_TOPIC, and for READ_FILESET and WRITE_FILESET. To withhold both read and write, deny both.

Server configuration​

The chart renders these settings into the server configuration from my-values.yaml, with authorization on by default. The table lists them with the server's own defaults.

Setting *DescriptionDefault
enableEnable or disable authorization. The Helm chart sets truefalse
serviceAdminsComma-separated service administrator usernames. Required when enable is true(none)
implMetadata authorization implementation†
threadPoolSizeThread pool size for metadata authorization requests10
jcasbin.cacheExpirationSecsHow long a cache entry stays valid. Lowering it reduces staleness and increases backend reads3600
jcasbin.roleCacheSizeMaximum size of each role-related cache. Applied to three caches, so real memory use is about 3x this value10000
jcasbin.ownerCacheSizeMaximum size of the owner cache100000
jcasbin.metadataIdCacheSizeMaximum size of the metadata name-to-ID cache100000
jcasbin.changePollIntervalSecsHow often the server polls for entity and owner changes to invalidate its caches. Must be greater than zero3

* Setting names omit the leading gravitino.authorization. prefix.

† The default is org.apache.gravitino.server.authorization.jcasbin.JcasbinAuthorizer.

The default authorizer keeps role and ownership information in Caffeine caches, so most authorization decisions need no backend read. When privileges or ownership change through the Gravitino API, the server handling the change invalidates the affected entries immediately. Other nodes in a multi-node deployment pick the change up on their next poll, so a revocation can take up to jcasbin.changePollIntervalSecs to take effect across the cluster. Lower the interval if that window is unacceptable for your environment.

Authentication​

Authorization decides what a caller may do; authentication establishes who the caller is. gravitino.authenticators in my-values.yaml selects the mechanism: basic for local accounts, oauth for your identity provider, or oauth,basic for both. The chart refuses to render with simple, which accepts any asserted user name without verifying it. For OAuth:

my-values.yaml
gravitino:
authenticators: "oauth,basic"
authenticator:
oauth:
jwksUri: "{jwks_uri}"
serviceAudience: "{audience}"
# The JWT claims that become the Gravitino user name and group memberships
principalFields: preferred_username
groupsFields: groups

Two of those settings connect a token to this page:

  • principalFields names the JWT claim whose value becomes the Gravitino user name, so it must produce the same strings you add to metalakes and grant roles to. It defaults to sub, which is usually an opaque provider ID rather than a name anyone would type.
  • groupsFields names the claim supplying group membership, which is how a role granted to a group reaches a user.

See How to Authenticate for the full set of options.

Behavior notes​

  • Add users to a metalake before creating metadata objects in it.
  • A request that carries no credentials is rejected. There is no anonymous access.
  • When authorization is enabled, the creator of a metalake is automatically added to it as a user.

Walkthrough​

Three identities act in turn, from an empty server to a user reading one schema. The service administrator bootstraps the metalake and hands it off, manager runs it, and staff builds and shares the data. Each presents its own bearer token.

Every call sends the same two headers, so the examples use a helper:

Define the request helper
GRAVITINO=http://localhost:8090/api/metalakes/{metalake}

gravitino() {
curl -sS -H "Accept: application/vnd.gravitino.v1+json" \
-H "Content-Type: application/json" "$@"
}
  1. The service administrator creates the metalake and hands it over. Creating it adds the creator as a user and makes them owner, which is what authorizes the next two calls. After the transfer, manager owns the metalake and the service administrator has no further part to play.

    Create the metalake and transfer ownership
    gravitino -X POST "http://localhost:8090/api/metalakes" \
    -H "Authorization: Bearer $ADMIN_TOKEN" \
    -d '{"name": "{metalake}", "comment": "example metalake", "properties": {}}'

    gravitino -X POST "$GRAVITINO/users" \
    -H "Authorization: Bearer $ADMIN_TOKEN" \
    -d '{"name": "manager"}'

    gravitino -X PUT "$GRAVITINO/owners/metalake/{metalake}" \
    -H "Authorization: Bearer $ADMIN_TOKEN" \
    -d '{"name": "manager", "type": "USER"}'
  2. manager delegates catalog creation to staff. Owning the metalake lets manager add users and create roles without any grant. The role carries CREATE_CATALOG on the metalake, so it covers every catalog staff creates, now and later.

    Delegate catalog creation to staff
    gravitino -X POST "$GRAVITINO/users" \
    -H "Authorization: Bearer $MANAGER_TOKEN" \
    -d '{"name": "staff"}'

    gravitino -X POST "$GRAVITINO/roles" \
    -H "Authorization: Bearer $MANAGER_TOKEN" \
    -d '{
    "name": "catalog_manager",
    "properties": {},
    "securableObjects": [
    {
    "fullName": "{metalake}",
    "type": "METALAKE",
    "privileges": [{"name": "CREATE_CATALOG", "condition": "ALLOW"}]
    }
    ]
    }'

    gravitino -X PUT "$GRAVITINO/permissions/users/staff/grant" \
    -H "Authorization: Bearer $MANAGER_TOKEN" \
    -d '{"roleNames": ["catalog_manager"]}'
  3. staff builds out the data. Creating the catalog makes staff its owner, and that ownership carries everything inside it, so the schema needs no further grant. The example uses Hive; any provider works, with its own properties.

    Create the catalog and schema
    gravitino -X POST "$GRAVITINO/catalogs" \
    -H "Authorization: Bearer $STAFF_TOKEN" \
    -d '{
    "name": "{catalog}",
    "type": "RELATIONAL",
    "provider": "hive",
    "properties": {"metastore.uris": "thrift://{hive_host}:9083"}
    }'

    gravitino -X POST "$GRAVITINO/catalogs/{catalog}/schemas" \
    -H "Authorization: Bearer $STAFF_TOKEN" \
    -d '{"name": "{schema}"}'
  4. staff gives analyst read access. 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. analyst must already be a user in the metalake.

    Grant analyst read access to the schema
    gravitino -X POST "$GRAVITINO/roles" \
    -H "Authorization: Bearer $STAFF_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"}
    ]
    }
    ]
    }'

    gravitino -X PUT "$GRAVITINO/permissions/users/analyst/grant" \
    -H "Authorization: Bearer $STAFF_TOKEN" \
    -d '{"roleNames": ["schema_reader"]}'

analyst can now read every table in {catalog}.{schema}, including tables created there later, and can do nothing anywhere else in the metalake. Queries through the Gravitino connector are authorized against the same ownership and privileges that governed the metadata calls above.

Review grants​

The privileges view in the UI lists every grant in the metalake by object, so a reviewer can answer "what can be done to this, and through which role" without opening each role in turn. Each row is one role's grant on one object: the object, its type, the role, the privileges the role allows there, and the privileges it denies. The view can be searched and filtered by object, type, and role.

To review an object, find it by name or filter by type, then read its rows. Each row names a role and what it allows and denies on the object. Roles are granted to users and to groups, so the people behind a row are the role's members; see Users and Groups. Grants are changed on the role that holds them rather than in the view; see Roles.

Reading a row​

A row shows a grant where the role records it. Grants reach downward, so a grant on a catalog appears once, on the catalog's row, while applying to every schema and table beneath it. To see everything that applies to a table, read the rows for the table, its schema, its catalog, and the metalake.

DENY wins over ALLOW wherever the two meet, so a privilege denied on any of those rows is denied on the table, whatever another row allows. See Allow and Deny.

What the view does not show​

Ownership is not a grant, so owners do not appear in the view. An owner holds administrative rights over an object and everything beneath it without any role, so check ownership separately in a review.

Membership is not shown either. The view names roles, and who holds each role is recorded on the users and groups it is granted to.

Ownership endpoints​

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

OperationMethodPath
Get or set an object's ownerGET, PUT/owners/{object_type}/{object_name}

In the Java client, an owner is set with an Owner.Type, not a string:

Set an owner from the Java client
client.setOwner(schema, "analyst", Owner.Type.USER);