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_TABLEreads a table,CREATE_SCHEMAcreates 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_TABLEon 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
ALLOWor aDENYcondition, and aDENYbeats anALLOWheld in any other role and at any other level of the hierarchy. A denial cannot be undone by granting something elsewhere, which makesDENYthe 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:
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:
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_ROLEandRUN_JOBon 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.
| Privilege | Authorizes | Metalake | Catalog | Schema | Object |
|---|---|---|---|---|---|
CREATE_CATALOG | Create a catalog in the metalake | ✓ | |||
USE_CATALOG | Reach a catalog and list it. Required to reach anything inside the catalog | ✓ | ✓ | ||
CREATE_SCHEMA | Create a schema in a catalog | ✓ | ✓ | ✓ | |
USE_SCHEMA | Reach a schema and list it. Required to reach anything inside the schema | ✓ | ✓ | ✓ | |
CREATE_TABLE | Create a table in a schema | ✓ | ✓ | ✓ | |
SELECT_TABLE | Load a table and read its data | ✓ | ✓ | ✓ | Table |
MODIFY_TABLE | Write data to a table and alter its schema | ✓ | ✓ | ✓ | Table |
CREATE_VIEW | Create a view in a schema | ✓ | ✓ | ✓ | |
SELECT_VIEW | Load a view and read from it | ✓ | ✓ | ✓ | View |
CREATE_FILESET | Create a fileset in a schema | ✓ | ✓ | ✓ | |
READ_FILESET | Read the files in a fileset | ✓ | ✓ | ✓ | Fileset |
WRITE_FILESET | Write the files in a fileset | ✓ | ✓ | ✓ | Fileset |
CREATE_TOPIC | Create a topic in a schema | ✓ | ✓ | ✓ | |
CONSUME_TOPIC | Consume messages from a topic | ✓ | ✓ | ✓ | Topic |
PRODUCE_TOPIC | Produce messages to a topic | ✓ | ✓ | ✓ | Topic |
REGISTER_MODEL | Register a model in a schema | ✓ | ✓ | ✓ | |
LINK_MODEL_VERSION | Link a new version to a model | ✓ | ✓ | ✓ | Model |
USE_MODEL | View a model's metadata and download its versions | ✓ | ✓ | ✓ | Model |
REGISTER_FUNCTION | Register a function in a schema | ✓ | ✓ | ✓ | |
EXECUTE_FUNCTION | Invoke a function | ✓ | ✓ | ✓ | Function |
MODIFY_FUNCTION | Alter a function's metadata | ✓ | ✓ | ✓ | Function |
CREATE_TAG | Create a tag in the metalake | ✓ | |||
APPLY_TAG | Attach a tag to objects. Granted on the tag, not on the objects | ✓ | Tag | ||
CREATE_POLICY | Create a policy in the metalake | ✓ | |||
APPLY_POLICY | Attach a policy to objects. Granted on the policy, not the objects | ✓ | Policy | ||
REGISTER_JOB_TEMPLATE | Register a job template in the metalake | ✓ | |||
USE_JOB_TEMPLATE | Run jobs from a job template | ✓ | Job Template | ||
RUN_JOB | Run a job in the metalake | ✓ | |||
MANAGE_USERS | Add users to the metalake and remove them | ✓ | |||
MANAGE_GROUPS | Add groups to the metalake and remove them | ✓ | |||
CREATE_ROLE | Create a role in the metalake | ✓ | |||
MANAGE_GRANTS | Grant 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_CATALOGandUSE_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
| Object | Create | Load | Alter | Drop |
|---|---|---|---|---|
| Catalog | CREATE_CATALOG | USE_CATALOG | Owner | Owner |
| Schema | CREATE_SCHEMA | USE_SCHEMA | Owner | Owner |
| Table | CREATE_TABLE | SELECT_TABLE or MODIFY_TABLE | MODIFY_TABLE | Owner |
| View | CREATE_VIEW | SELECT_VIEW | Owner | Owner |
| Topic | CREATE_TOPIC | CONSUME_TOPIC or PRODUCE_TOPIC | PRODUCE_TOPIC | Owner |
| Fileset | CREATE_FILESET | READ_FILESET or WRITE_FILESET | WRITE_FILESET | Owner |
| Model | REGISTER_MODEL | USE_MODEL | Owner | Owner |
| Function | REGISTER_FUNCTION | EXECUTE_FUNCTION or MODIFY_FUNCTION | MODIFY_FUNCTION | Owner |
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
| Object | Create | Read | Alter or delete | Use |
|---|---|---|---|---|
| Metalake | Service administrator | Membership | Owner | |
| User | MANAGE_USERS | MANAGE_USERS, or the user themselves | MANAGE_USERS | |
| Group | MANAGE_GROUPS | MANAGE_GROUPS, or a member | MANAGE_GROUPS | |
| Role | CREATE_ROLE | MANAGE_GRANTS, or a holder or owner | Owner | Grant or revoke: MANAGE_GRANTS |
| Tag | CREATE_TAG | APPLY_TAG | Owner | Attach: APPLY_TAG and access to the object |
| Policy | CREATE_POLICY | APPLY_POLICY | Owner | Attach: APPLY_POLICY and access to the object |
| Job template | REGISTER_JOB_TEMPLATE | USE_JOB_TEMPLATE | Owner | Run a job: RUN_JOB and USE_JOB_TEMPLATE |
| Job | Owner | Owner |
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_CATALOGandUSE_SCHEMA, and additionally owns the table or holdsSELECT_TABLEorMODIFY_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 * | Description | Default |
|---|---|---|
enable | Enable or disable authorization. The Helm chart sets true | false |
serviceAdmins | Comma-separated service administrator usernames. Required when enable is true | (none) |
impl | Metadata authorization implementation | † |
threadPoolSize | Thread pool size for metadata authorization requests | 10 |
jcasbin.cacheExpirationSecs | How long a cache entry stays valid. Lowering it reduces staleness and increases backend reads | 3600 |
jcasbin.roleCacheSize | Maximum size of each role-related cache. Applied to three caches, so real memory use is about 3x this value | 10000 |
jcasbin.ownerCacheSize | Maximum size of the owner cache | 100000 |
jcasbin.metadataIdCacheSize | Maximum size of the metadata name-to-ID cache | 100000 |
jcasbin.changePollIntervalSecs | How often the server polls for entity and owner changes to invalidate its caches. Must be greater than zero | 3 |
* 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:
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:
principalFieldsnames 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 tosub, which is usually an opaque provider ID rather than a name anyone would type.groupsFieldsnames 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:
GRAVITINO=http://localhost:8090/api/metalakes/{metalake}
gravitino() {
curl -sS -H "Accept: application/vnd.gravitino.v1+json" \
-H "Content-Type: application/json" "$@"
}
-
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,
managerowns the metalake and the service administrator has no further part to play.Create the metalake and transfer ownershipgravitino -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"}' -
managerdelegates catalog creation tostaff. Owning the metalake letsmanageradd users and create roles without any grant. The role carriesCREATE_CATALOGon the metalake, so it covers every catalogstaffcreates, now and later.Delegate catalog creation to staffgravitino -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"]}' -
staffbuilds out the data. Creating the catalog makesstaffits 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 schemagravitino -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}"}' -
staffgivesanalystread access. Reading one schema takes three privileges:USE_CATALOGandUSE_SCHEMAto reach it, andSELECT_TABLEto read what is in it. Object names are not validated, so a typo produces a role that grants nothing.analystmust already be a user in the metalake.Grant analyst read access to the schemagravitino -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.
| Operation | Method | Path |
|---|---|---|
| Get or set an object's owner | GET, PUT | /owners/{object_type}/{object_name} |
In the Java client, an owner is set with an Owner.Type, not a string:
client.setOwner(schema, "analyst", Owner.Type.USER);
Related
- Roles, Users, and Groups, for each principal and the privileges granted to it
- Gravitino REST API, for the request and response schemas of every user, group, role, permission, and owner endpoint
- Java Client and Python Client, for calling those endpoints from a client library
org.apache.gravitino.authorization, the Java classes behind this page:Privilege,Privileges,SecurableObject, andOwner- Authorization Pushdown, for pushing enforcement down to the underlying data source or to an external system such as Apache Ranger
- How to Authenticate, for establishing who the caller is
- Directory, for local accounts and the server-wide user directory