This is an automated email from the ASF dual-hosted git repository.
jerryshao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/main by this push:
new 84fa299c5c [MINOR] docs: Add policy-on-tag migration guide (#13382)
84fa299c5c is described below
commit 84fa299c5c7ce42f5e757d1fec715cddc8e2e1af
Author: roryqi <[email protected]>
AuthorDate: Thu Sep 24 18:00:52 2026 +0800
[MINOR] docs: Add policy-on-tag migration guide (#13382)
### What changes were proposed in this pull request?
- Add a versioned Migration Guide covering the move from direct policy
associations to policy-on-tag, including API replacements, a migration
checklist, and rollback preparation.
- Update policy, tag, access control, UI, and maintenance service
documentation to describe the current policy-on-tag behavior.
### Why are the changes needed?
Direct object-policy associations were removed in #13355. Users need a
migration path and documentation that reflects the current APIs and
behavior.
Related: #13355
### Does this PR introduce _any_ user-facing change?
Yes. It adds migration instructions and updates user documentation. This
PR does not change APIs or properties.
### How was this patch tested?
- `./gradlew :docs:build`
- Checked links and anchors in all 15 changed Markdown files (0 broken
links).
- `git diff --check apache/main...HEAD`
---
docs/assets/webui-v2/policy-metadata-objects.png | Bin 125448 -> 0 bytes
docs/catalogs-and-schemas.md | 14 +-
docs/how-to-upgrade.md | 5 +-
docs/iceberg-compaction-policy.md | 12 +-
docs/index.md | 5 +
docs/manage-policies-in-gravitino.md | 140 ++++++++++-
docs/manage-tags-in-gravitino.md | 3 +
docs/metalakes.md | 6 +-
docs/migration-guide.md | 108 +++++++++
docs/policies.md | 267 ++++++++-------------
docs/security/access-control.md | 41 ++--
.../optimizer-configuration.md | 2 +-
docs/table-maintenance-service/optimizer.md | 6 +-
docs/tables-and-views.md | 4 +-
docs/topics.md | 9 +-
docs/webui-v2.md | 15 +-
16 files changed, 407 insertions(+), 230 deletions(-)
diff --git a/docs/assets/webui-v2/policy-metadata-objects.png
b/docs/assets/webui-v2/policy-metadata-objects.png
deleted file mode 100644
index 3d218048d4..0000000000
Binary files a/docs/assets/webui-v2/policy-metadata-objects.png and /dev/null
differ
diff --git a/docs/catalogs-and-schemas.md b/docs/catalogs-and-schemas.md
index e987795753..1b5505b423 100644
--- a/docs/catalogs-and-schemas.md
+++ b/docs/catalogs-and-schemas.md
@@ -20,8 +20,8 @@ A schema is the middle level, and what it means depends on
the system underneath
catalog it is a database. In a fileset catalog it groups filesets under a
location. In a model
catalog it is a namespace for models with no physical counterpart at all.
-Catalogs and schemas both carry tags and policies, and both are the level to
attach them at when
-something should apply broadly. A tag on a catalog reaches every object
beneath it.
+Catalogs and schemas can carry tag assignments. Assign a tag at either level
when its derived
+policies should apply broadly. A tag on a catalog reaches every object beneath
it.
## Quick Start
@@ -74,9 +74,9 @@ where they are.
### What Gravitino Stores
-Gravitino stores the catalog registration, the schemas and objects created
through it, and anything
-attached to those objects such as tags, policies, and ownership. It does not
store a copy of the
-source system's contents.
+Gravitino stores the catalog registration, the schemas and objects created
through it, and their tag
+assignments and ownership. Policies are derived from effective tags when they
are read. Gravitino
+does not store a copy of the source system's contents.
Listing tables in a schema reaches the source system at request time, so a
table created directly in
Hive appears the next time Gravitino is asked. The consequence worth knowing
is that Gravitino
@@ -88,8 +88,8 @@ The catalog list holds every catalog in the current metalake.
A catalog can be c
enabled or disabled, and deleted from there, and each one expands into its
schemas and their
contents.
-Tags and policies attach from the catalog and schema rows, which is the
fastest way to classify a
-whole subtree.
+Assign tags from the catalog and schema rows to classify a whole subtree.
Policies associated
+with those tags appear in object policy lookups.
## Deleting Catalogs and Schemas
diff --git a/docs/how-to-upgrade.md b/docs/how-to-upgrade.md
index 26b63ba3c9..b8b465d5b5 100644
--- a/docs/how-to-upgrade.md
+++ b/docs/how-to-upgrade.md
@@ -14,6 +14,9 @@ Gravitino 0.7.0 schema. Before attempting this project we
strongly recommend that you read through all of the steps in this
document and familiarize yourself with the required tools.
+For version-specific behavior and API changes, see the
+[Migration Guide](./migration-guide.md) before upgrading.
+
## Upgrade Steps
### Step 1: Shut Down the Gravitino Instance
@@ -171,7 +174,7 @@ you will want to compare your schema dump against the
contents of
:::note
The Gravitino Helm chart does not currently support automatic schema
migration. Before running
-`helm upgrade`, you must manually back up your database (see [Step
2](#step-2-backup-your-gravitino-instance))
+`helm upgrade`, you must manually back up your database (see [Step
2](#step-2-back-up-the-gravitino-instance))
and apply the appropriate SQL upgrade scripts (see [Step
5](#step-5-apply-the-upgrade-scripts)).
:::
diff --git a/docs/iceberg-compaction-policy.md
b/docs/iceberg-compaction-policy.md
index 86b492ee64..25a5520213 100644
--- a/docs/iceberg-compaction-policy.md
+++ b/docs/iceberg-compaction-policy.md
@@ -128,7 +128,11 @@ Policy policy =
</TabItem>
</Tabs>
-## Attach Policy to Metadata Objects
-
-After the policy is created, associate it with a catalog, schema, or table
through standard policy association APIs.
-The optimizer will read the generated rules and properties to evaluate
strategy triggering and job submission context.
+## Apply the Policy Through a Tag
+
+After creating the policy, create or reuse a tag, associate the policy with
that tag using the
+`ALL_VALUES` selector, and assign the tag to a table or one of its ancestors.
The optimizer reads
+the table's derived policies to evaluate strategy triggering and job
submission context. See
+[Manage
Policies](./manage-policies-in-gravitino.md#policy-to-tag-associations) for
association
+operations and the [Table maintenance service
walkthrough](./table-maintenance-service/optimizer.md#step-4-configure-a-compaction-policy-through-a-tag)
+for a complete REST example.
diff --git a/docs/index.md b/docs/index.md
index 017a084e99..d8e5a6fcbd 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -32,6 +32,11 @@ Gravitino also provides a playground to experience the whole
Gravitino system wi
See the [Gravitino playground
repository](https://github.com/apache/gravitino-playground)
and [How to use the playground](./how-to-use-the-playground.md).
+## Migration Guide
+
+Use the [Migration Guide](./migration-guide.md) for version-specific behavior
and API changes.
+For database schema upgrades, see [Upgrade Gravitino](./how-to-upgrade.md).
+
## Getting Started
To get started with Gravitino, see [Getting
started](./getting-started/index.md) for the details.
diff --git a/docs/manage-policies-in-gravitino.md
b/docs/manage-policies-in-gravitino.md
index 8654a89545..110f9498db 100644
--- a/docs/manage-policies-in-gravitino.md
+++ b/docs/manage-policies-in-gravitino.md
@@ -10,9 +10,10 @@ import TabItem from '@theme/TabItem';
## Introduction
-This page covers the Gravitino API for policies. For what a policy is, which
object types can carry one, what goes in
-policy content, how inheritance resolves, and how to work with policies in the
UI, see
-[Policies](./policies.md).
+This page covers the Gravitino API for policies and policy-to-tag
associations. For the policy
+model and how object policy lookup works, see
+[Policies](./policies.md). To move from direct object policy associations,
follow
+[Migration Guide](./migration-guide.md).
The Python client does not cover policies, so the examples below are REST and
Java only.
@@ -21,7 +22,10 @@ The Python client does not cover policies, so the examples
below are REST and Ja
### Create a Policy
A policy needs a name and a type. Content carries the rules, the object types
the policy supports,
-and optional properties. `supportedObjectTypes` cannot be changed after
creation.
+and optional properties. For a built-in policy, `supportedObjectTypes` cannot
be changed after
+creation. Updating custom policy content replaces the whole content and can
change this field.
+Object policy lookup does not filter by this field; consumers decide which
policy types they can
+use.
<Tabs groupId='language' queryString>
<TabItem value="shell" label="REST">
@@ -170,8 +174,8 @@ Policy policy = client.alterPolicy(
### Enable or Disable a Policy
-The flag is a marker for readers. Gravitino does not act on it, and disabling
a policy neither
-detaches it nor changes what a consumer receives.
+Disabling a policy keeps its tag associations, but removes it from object
policy lookup results.
+It remains available through policy and association listing APIs.
<Tabs groupId='language' queryString>
<TabItem value="shell" label="REST">
@@ -215,6 +219,104 @@ client.deletePolicy("retention_30d");
</TabItem>
</Tabs>
+## Policy-to-Tag Associations
+
+Create a policy and a tag in the same metalake before associating them. Each
policy-to-tag
+association has a selector:
+
+| Selector | Match condition
|
+|--------------|------------------------------------------------------------------------|
+| `ALL_VALUES` | The effective tag is present, with or without assignment
values. |
+| `TAG_VALUE` | One effective tag assignment value equals the specified
value. |
+
+The selector belongs to the association, not to the policy or the tag. An
existing association
+cannot be replaced by another add request. Remove it and add it again to
change its selector.
+
+### Associate a Policy with a Tag
+
+This example applies `retention_30d` when the effective `data_domain` tag has
the value `finance`.
+Create the tag first if it does not exist; see
+[Manage
tags](./manage-tags-in-gravitino.md#create-a-tag-with-a-value-constraint).
+
+<Tabs groupId='language' queryString>
+<TabItem value="shell" label="REST">
+
+```shell
+curl -X POST -H "Accept: application/vnd.gravitino.v1+json" \
+ -H "Content-Type: application/json" \
+ -d '{"selector": {"type": "TAG_VALUE", "value": "finance"}}' \
+
http://localhost:8090/api/metalakes/test/tags/data_domain/policies/retention_30d
+```
+
+Use `{"selector": {"type": "ALL_VALUES"}}` to match any assignment of
`data_domain`.
+
+</TabItem>
+<TabItem value="java" label="Java">
+
+```java
+PolicyTagAssociation association = client.addPolicyForTag(
+ "data_domain", "retention_30d", TagValueSelector.of("finance"));
+
+// Match any assignment of data_domain instead:
+// client.addPolicyForTag("data_domain", "retention_30d");
+```
+
+</TabItem>
+</Tabs>
+
+The request fails with a conflict if the policy is already associated with the
tag.
+
+### List Associations
+
+List associations from either side. By default the response contains names.
Set `details=true`
+to get policy or tag details together with each association's selector. These
lists show direct
+associations even when their selectors do not match any object's current tag
values.
+
+<Tabs groupId='language' queryString>
+<TabItem value="shell" label="REST">
+
+```shell
+curl -X GET -H "Accept: application/vnd.gravitino.v1+json" \
+
"http://localhost:8090/api/metalakes/test/tags/data_domain/policies?details=true"
+
+curl -X GET -H "Accept: application/vnd.gravitino.v1+json" \
+
"http://localhost:8090/api/metalakes/test/policies/retention_30d/tags?details=true"
+```
+
+</TabItem>
+<TabItem value="java" label="Java">
+
+```java
+PolicyTagAssociation[] policies =
client.listPolicyAssociationsForTag("data_domain");
+PolicyTagAssociation[] tags =
client.listTagAssociationsForPolicy("retention_30d");
+```
+
+</TabItem>
+</Tabs>
+
+### Remove an Association
+
+Removing the association stops this policy from being selected through the
tag. It leaves the
+policy, tag, and tag assignments in place.
+
+<Tabs groupId='language' queryString>
+<TabItem value="shell" label="REST">
+
+```shell
+curl -X DELETE -H "Accept: application/vnd.gravitino.v1+json" \
+
http://localhost:8090/api/metalakes/test/tags/data_domain/policies/retention_30d
+```
+
+</TabItem>
+<TabItem value="java" label="Java">
+
+```java
+client.removePolicyFromTag("data_domain", "retention_30d");
+```
+
+</TabItem>
+</Tabs>
+
## Object Operations
Object policies are read-only results derived from effective tags. To change
the policies that apply
@@ -222,28 +324,44 @@ to an object, associate a policy with a tag and then
assign or remove that tag o
of its ancestors. See [Manage tags in
Gravitino](./manage-tags-in-gravitino.md) for tag assignment
operations.
+For the `TAG_VALUE("finance")` association above, assign `data_domain=finance`
to a table or one
+of its ancestors. The nearest assignment of `data_domain` overrides farther
assignments of the
+same tag. For example, a schema assignment of `data_domain=risk` overrides
+`data_domain=finance` on its catalog for every table in the schema. A direct
table assignment
+overrides both.
+
+```shell
+curl -X POST -H "Accept: application/vnd.gravitino.v2+json" \
+ -H "Content-Type: application/vnd.gravitino.v2+json" \
+ -d '{"tagsToAdd": [{"name": "data_domain", "value": "finance"}]}' \
+
http://localhost:8090/api/metalakes/test/objects/table/catalog1.schema1.customers/tags
+```
+
### List Policies on an Object
The response includes policies derived from effective tags assigned to the
object or its ancestors.
With `details=true`, the response returns full policy objects instead of
policy names.
Each policy includes an `inherited` field, which is `true` when it matches
only through a tag
-assigned to an ancestor of the object.
+assigned to an ancestor of the object. Disabled policies do not appear in this
result.
<Tabs groupId='language' queryString>
<TabItem value="shell" label="REST">
```shell
curl -X GET -H "Accept: application/vnd.gravitino.v1+json" \
-
"http://localhost:8090/api/metalakes/test/objects/catalog/catalog1/policies?details=true"
+
"http://localhost:8090/api/metalakes/test/objects/table/catalog1.schema1.customers/policies?details=true"
```
</TabItem>
<TabItem value="java" label="Java">
```java
-Catalog catalog = client.loadCatalog("catalog1");
-String[] policyNames = catalog.supportsPolicies().listPolicies();
-Policy[] policies = catalog.supportsPolicies().listPolicyInfos();
+Table customers =
+ client.loadCatalog("catalog1")
+ .asTableCatalog()
+ .loadTable(NameIdentifier.of("schema1", "customers"));
+String[] policyNames = customers.supportsPolicies().listPolicies();
+Policy[] policies = customers.supportsPolicies().listPolicyInfos();
```
</TabItem>
diff --git a/docs/manage-tags-in-gravitino.md b/docs/manage-tags-in-gravitino.md
index f5308db080..5254582a1e 100644
--- a/docs/manage-tags-in-gravitino.md
+++ b/docs/manage-tags-in-gravitino.md
@@ -13,6 +13,9 @@ import TabItem from '@theme/TabItem';
This page covers the Gravitino API for tags. For what a tag is, which object
types can carry one, how inheritance
resolves, and how to work with tags in the UI, see [Tags](./tags.md).
+Policies can be associated with tags to govern objects carrying those tags. See
+[Policy-to-Tag
Associations](./manage-policies-in-gravitino.md#policy-to-tag-associations).
+
## Tag Operations
### Create a Tag
diff --git a/docs/metalakes.md b/docs/metalakes.md
index d0e33de413..52b5c02888 100644
--- a/docs/metalakes.md
+++ b/docs/metalakes.md
@@ -17,7 +17,7 @@ Nothing crosses that boundary:
user in each, and provisioning them is done per metalake
- Roles are defined and granted within one metalake, so a role held in one
carries no privilege in
another
-- A tag or policy created in one metalake cannot be attached to an object in
another
+- A tag can only be assigned to objects in its metalake, and a policy can only
be associated with tags in its metalake
- Names only have to be unique within one metalake
That makes a metalake the unit to reach for when separating environments,
business units, or tenants
@@ -50,8 +50,8 @@ Properties are free-form key and value pairs, with one
reserved key. `in-use` re
metalake is available, defaults to `true`, and is set through the enable and
disable operations
rather than by writing the property directly.
-A metalake cannot carry a tag or a policy, so there is no way to classify or
govern everything at
-once from the top. The widest attachment point is a catalog.
+A metalake cannot carry a tag assignment, so there is no way to classify or
govern everything at
+once from the top. The widest tag assignment point is a catalog. Policies
apply through tags.
### In Use and Not In Use
diff --git a/docs/migration-guide.md b/docs/migration-guide.md
new file mode 100644
index 0000000000..9bbecb698c
--- /dev/null
+++ b/docs/migration-guide.md
@@ -0,0 +1,108 @@
+---
+title: "Migration Guide"
+slug: "/migration-guide"
+keyword: "migration, upgrade, compatibility, Gravitino"
+license: "This software is licensed under the Apache License version 2."
+---
+
+This guide lists user-visible behavior changes by upgrade version and the
action needed for
+existing deployments. For database backup, schema scripts, and rollback
commands, see
+[Upgrade Gravitino](./how-to-upgrade.md).
+
+## Upgrading from Gravitino 1.3 to 2.0
+
+### Policies are selected through tags
+
+- Direct policy associations with metadata objects are no longer read or
written. The upgrade
+ does not convert existing direct associations. Before upgrading, export each
policy's directly
+ associated objects from `GET
/api/metalakes/{metalake}/policies/{policy}/objects`. Save the
+ policy name, object type, and full name, including direct associations on
ancestor objects.
+ Back up the metadata database before the schema upgrade.
+- To preserve the old scope, create one dedicated tag per policy, associate
the policy with that
+ tag using the `ALL_VALUES` selector, and assign the tag to every object that
had a direct
+ policy association. A tag on a catalog or schema reaches its descendants. If
a policy was
+ directly associated with both a parent and a child, assign the tag to both.
+- Object policy lookup at `GET
/api/metalakes/{metalake}/objects/{type}/{fullName}/policies`
+ remains available, but now returns enabled policies matching the object's
effective tags.
+ A policy matching through multiple tags appears once. With `details=true`,
`inherited` is
+ true only if the matching tag assignments are inherited. Disabled policies
remain associated
+ with tags but do not appear in object policy results.
+- In Gravitino 1.3, disabling a policy was advisory and did not change what
consumers received.
+ Object policy lookup now excludes disabled policies. Before upgrading,
inventory disabled
+ policies and re-enable any that consumers must continue to receive.
+- The legacy `supportedObjectTypes` field no longer filters object policy
lookup. Consumers
+ must decide which policy types they can use. For example, TMS should consume
the built-in
+ compaction policy type from each table's resolved policies.
+
+### REST and client API changes
+
+| Previous API or call |
Migration
|
+|-----------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `POST /objects/{type}/{fullName}/policies` |
Associate the policy with a tag using `POST /tags/{tag}/policies/{policy}`,
then assign the tag using `POST /objects/{type}/{fullName}/tags`. |
+| `GET /objects/{type}/{fullName}/policies/{policy}` | List
resolved policies with `GET /objects/{type}/{fullName}/policies?details=true`
and select the policy by name. |
+| `GET /policies/{policy}/objects` | Use
`GET /policies/{policy}/tags` to inspect tag associations. It does not list
every object reached through those tags. |
+| Java `supportsPolicies().associatePolicies(...)` and `getPolicy(...)` | Use
`GravitinoClient.addPolicyForTag(...)` and tag assignment APIs for writes; use
`supportsPolicies().listPolicies()` or `listPolicyInfos()` for reads. |
+| Java `Policy.associatedObjects()` | List
tag associations with `GravitinoClient.listTagAssociationsForPolicy(...)`, then
inspect affected objects separately. |
+
+Paths in this table are relative to `/api/metalakes/{metalake}`. A
policy-to-tag association
+requires a selector. `ALL_VALUES` matches tag presence, including an
assignment without a
+value; `TAG_VALUE` matches when one effective tag assignment value equals the
selector value.
+To change an existing selector, remove the association and add it again. See
+[Manage
Policies](./manage-policies-in-gravitino.md#policy-to-tag-associations) for
REST and Java
+examples.
+
+The Python client can manage tag assignments but does not provide policy APIs.
Use REST for
+policy-to-tag association changes.
+
+### Tag assignment values and inheritance
+
+- Existing string-based tag assignments remain supported. Use the v2
+ `application/vnd.gravitino.v2+json` request when adding or removing a
tag-value pair.
+ A tag can have no value, one value, or multiple values.
+- Allowed values are chosen when a tag is created and cannot be changed later.
Plan the
+ constraint before using a `TAG_VALUE` selector.
+- Resolution uses the nearest assignment for a tag name. A direct assignment
replaces all
+ inherited values, and a nearer ancestor overrides a farther one. For example,
+ `data_domain=risk` on a schema overrides `data_domain=finance` on its
catalog for every table
+ in that schema, so a `TAG_VALUE("finance")` association no longer matches
those tables. A
+ dedicated migration tag with `ALL_VALUES` avoids this interaction.
+
+See [Manage
Tags](./manage-tags-in-gravitino.md#create-a-tag-with-a-value-constraint)
+for tag creation and value assignment examples.
+
+### Authorization, UI, and consumers
+
+- Creating a migration tag requires `CREATE_TAG` on the metalake or ownership.
Associating
+ a policy with a tag requires access to both: `APPLY_POLICY` on the policy
and `APPLY_TAG`
+ on the tag, or the corresponding ownership. Assigning a tag also requires
access to the
+ target metadata object.
+- Read-only inspection requires `VIEW_TAG` or `APPLY_TAG` for tags and
`VIEW_POLICY` or
+ `APPLY_POLICY` for policies. Object policy results include only policies
visible to the caller;
+ use an account with the same policy visibility when comparing results before
and after migration.
+- The current web UI still displays direct policy controls that call the
removed
+ object-policy write API. Use REST or Java for policy-to-tag associations
during migration.
+- Update TMS and other policy consumers to read the resolved object policy
list. Confirm the
+ expected policy appears on representative tables before resuming maintenance
jobs.
+
+### Migration checklist
+
+1. On the old runtime, back up the database. Export direct policy associations
for **each
+ metalake**, inventory disabled policies, and record policy results on
representative
+ descendants. Re-enable disabled policies that consumers must continue to
receive. The old
+ `/policies/{policy}/objects` endpoint is unavailable after the upgrade.
+2. Map each direct relation to a tag assignment. A dedicated tag per policy
with
+ `ALL_VALUES` preserves the original scope; review existing tag values and
child overrides
+ before choosing `TAG_VALUE`.
+3. Stop direct policy writes and pause consumers that require complete policy
results. Upgrade
+ the server and schema using [Upgrade Gravitino](./how-to-upgrade.md).
+4. Create the tags, add policy-to-tag associations, and assign tags to every
object from the
+ exported direct-relation inventory.
+5. Compare resolved policy names and `inherited` values for every inventoried
object and
+ representative descendants. Check enabled and disabled policies, direct
child assignments,
+ and value selectors. Use the same policy visibility for before and after
comparisons.
+6. Resume consumers after verification. Keep the inventory and database backup
until the
+ deployment is stable. If rollback is needed, stop the new server and follow
the
+ [database rollback
procedure](./how-to-upgrade.md#rollback-if-upgrade-fails).
+
+There is no automatic converter or verification command for direct policy
relations. Do not
+edit relation tables directly as part of this migration.
diff --git a/docs/policies.md b/docs/policies.md
index b48f24f64b..fa87ebfaed 100644
--- a/docs/policies.md
+++ b/docs/policies.md
@@ -1,189 +1,116 @@
---
title: "Policies"
slug: "/policies"
-keyword: "policy, policies, governance, metadata object, Gravitino"
+keyword: "policy, policies, governance, tags, Gravitino"
license: "This software is licensed under the Apache License version 2."
---
## Introduction
-A policy is a named set of rules that you create once in a metalake and attach
to metadata objects.
-Attaching a policy to a catalog or schema applies it to everything beneath, so
a setting that varies
-by table can be expressed once at the level where it holds and overridden
where it does not.
+A policy is a named set of rules in a metalake. Associate it with a tag, then
assign that tag to
+metadata objects. When a client reads an object's policies, Gravitino finds
its effective tags and
+returns the enabled policies whose association selectors match. The policy
remains a separate
+object: changing its rules updates every object where it applies.
-Tags and policies are close cousins, and the difference is what they carry. A
tag classifies, and
-its content is its name. A policy prescribes, and its content is a set of
rules something acts on.
-
-Policies come in two kinds. A built-in policy has a type that Gravitino
defines and a consumer that
-acts on it. A custom policy carries rules of your own, which Gravitino stores,
inherits, and serves
-back to whatever system you build around it.
-
-Common uses:
-
-- Setting table maintenance behavior for a whole catalog rather than table by
table, and letting new
- tables pick it up without further work
-- Recording a rule once against metadata that lives in several catalogs, so
every engine reaching
- those objects through Gravitino sees the same rule
-- Feeding an external enforcement or scheduling system that reads policies
from Gravitino rather
- than keeping its own copy of what applies where
+Policies come in two kinds. Built-in policy types have rules and consumers
defined by Gravitino.
+Custom policies carry rules that your own system interprets. For example, the
table maintenance
+service consumes the built-in Iceberg compaction policy.
## Quick Start
-**1. Create the policy.** Policies are created from the policy list in the UI,
which creates custom
-policies. A policy needs a name, the object types it supports, and its rules.
Built-in policies are
-created over REST.
+1. [Create a policy](./manage-policies-in-gravitino.md#create-a-policy) and
+ [create a tag](./manage-tags-in-gravitino.md#create-a-tag) in the same
metalake.
+2. [Associate the policy with the
tag](./manage-policies-in-gravitino.md#associate-a-policy-with-a-tag).
+ Choose `ALL_VALUES` to match tag presence or `TAG_VALUE` to match when an
assignment contains
+ one specified value.
+3. [Assign the tag](./manage-tags-in-gravitino.md#object-operations) to an
object or its ancestor.
+4. [List the object's
policies](./manage-policies-in-gravitino.md#list-policies-on-an-object) to
+ confirm the result.
-**2. Attach it to an object.** Open the catalog, schema, table, fileset,
topic, model, view, or function you want to
-govern and add the policy from its policy control. Only policies that already
exist in the metalake
-are offered.
-
-**3. See where the policy is attached.** Selecting a policy name in the policy
list shows the
-objects it is attached to directly.
+For example, associate `retention_30d` with `data_domain` using
+`TAG_VALUE("finance")`. A table with an effective `data_domain=finance`
assignment receives the
+policy. A table with only `data_domain=risk` does not.
## The Policy Model
-### Policy Types
+### Policy Types and Content
-| Type | Rules |
Consumed by |
+| Type | Rules |
Consumer |
|-----------------------------|--------------------------------------|---------------------------|
| `system_iceberg_compaction` | Compaction thresholds and scheduling | Table
maintenance service |
-| `custom` | A free-form map you define | A
system you provide |
-
-A built-in type has a name beginning with `system_` and a content shape
Gravitino defines. The
-compaction policy is documented in [Iceberg compaction
policy](./iceberg-compaction-policy.md), and
-the service that acts on it in
-[Table maintenance service](./table-maintenance-service/optimizer.md).
-
-A custom policy has type `custom`, and Gravitino makes no attempt to interpret
what is inside
-`customRules`. The rules are stored, inherited down the hierarchy, and
returned to any client that
-asks.
-
-The UI creates custom policies only. A built-in policy is created over REST
with its own content
-shape.
-
-### What Can Carry a Policy
-
-A metadata object is identified by a type and a name, with each level below
the catalog separated by
-a dot. Eight object types can carry a policy.
-
-| Object type | Name form |
-|-------------|-----------------------------------------------|
-| `CATALOG` | `{catalog_name}` |
-| `SCHEMA` | `{catalog_name}.{schema_name}` |
-| `TABLE` | `{catalog_name}.{schema_name}.{table_name}` |
-| `FILESET` | `{catalog_name}.{schema_name}.{fileset_name}` |
-| `TOPIC` | `{catalog_name}.{schema_name}.{topic_name}` |
-| `MODEL` | `{catalog_name}.{schema_name}.{model_name}` |
-| `VIEW` | `{catalog_name}.{schema_name}.{view_name}` |
-| `FUNCTION` | `{catalog_name}.{schema_name}.{function_name}`|
-
-Columns cannot carry a policy, which is narrower than
-[tags](./tags.md). A metalake cannot carry one either, so to reach every object
-in a catalog, attach the policy to the catalog.
-
-Each policy also declares its own `supportedObjectTypes`, which narrows the
list further for that
-policy.
-
-### Content
-
-Policy content has three parts: the `supportedObjectTypes` list, the rules,
and properties.
-
-`supportedObjectTypes` is fixed when the policy is created and cannot be
changed afterward, so a
-policy meant for tables only stays that way for its lifetime.
-
-The rules are what a consumer evaluates. For a custom policy they live under
`customRules` as a map
-you define, where the name is yours and the value is any JSON value.
-
-```json
-"customRules": {
- "retentionDays": 30,
- "maxTableSizeGb": 500,
- "requiresApproval": true
-}
-```
-
-Gravitino does not interpret those names or values. Whatever consumes the
policy decides what
-`retentionDays` means and what to do about it.
-
-A built-in policy has a rule set Gravitino defines, and the service that
consumes it documents how
-those rules are applied. The compaction policy carries `minDataFileMse`,
`minDeleteFileNumber`,
-`dataFileMseWeight`, `deleteFileNumberWeight`, `max-partition-num`, and a
trigger and score
-expression, plus any `job.options.` entries passed through to the job. Those
names and their
-meanings are covered in [Iceberg compaction
policy](./iceberg-compaction-policy.md).
-
-Properties describe the policy itself rather than the behavior it asks for.
Rules change as you
-adjust thresholds, and properties stay stable. The compaction policy uses
properties for its
-strategy type and job template name, which tell the table maintenance service
what to run, and those
-are set by Gravitino rather than by you. For a custom policy, properties are
yours, and suit facts
-such as which team owns the policy, which system consumes it, or which version
of a rule set it
-represents. Anything evaluated against an object belongs in rules instead.
-
-Properties sit on the policy rather than on an attachment, so every object
carrying the policy sees
-the same values.
-
-### The Enabled Flag
-
-The `enabled` flag marks a policy as active or inactive for readers. Gravitino
does not act on it,
-so disabling a policy does not detach it or change what a consumer receives.
Treat it as a signal to
-whoever reads the policy, useful for holding a policy through review without
deleting it.
-
-### Inheritance
-
-An object shows the policies attached to it plus the policies attached to each
of its ancestors, so
-a policy on a catalog applies to every schema, table, fileset, topic, model,
view, and function beneath it. For
-catalogs that support multi-level schemas, the intermediate schemas are
ancestors too.
-
-Each policy appears once, whether it reaches the object through one ancestor
or several. A policy
-attached directly to the object counts as direct even when an ancestor carries
it too.
-
-Direct and inherited attachments are distinguishable. In the UI an inherited
policy is marked with a
-lock icon. Over REST, a policy listing requested with `details=true` carries
an `inherited` field on
-each policy, which a plain listing of names does not.
-
-A policy that reaches an object only by inheritance cannot be removed there.
Detach it from the
-ancestor that carries it, which affects every other object beneath that
ancestor as well.
-
-Inheritance is resolved when the object is read rather than stored on the
object, so attaching a
-policy to a catalog takes effect immediately for tables created afterward.
-
-## Working With Policies in the UI
-
-### Managing the Policy Set
-
-The policy list holds every policy in the metalake and can be searched. A
policy can be renamed, its
-comment and rules edited, and its enabled flag switched from there. Policies
created over REST,
-including built-in ones, appear in the list alongside the rest.
-
-Deleting a policy removes it from every object it was attached to, with no
warning about how many
-objects that affects and no way to recover the attachments.
-
-### Attaching and Detaching
-
-Policies attach from the object rather than from the policy, so open the
object and use the policy
-control there. Inherited policies carry no remove control. Detaching removes
the direct attachment
-only, so an object still shows a policy it inherits from an ancestor.
-
-### Finding Where a Policy Is Used
-
-Selecting a policy name opens a view listing the objects the policy is
attached to directly.
-Inherited reach is not included, so a policy attached to one catalog lists
that catalog rather than
-the tables under it.
-
-## Permissions
-
-Policy permissions are held on the policy, and apply in addition to
permissions on the objects being
-governed.
-
-| Privilege | Grantable on | What it allows
|
-|-----------------|------------------------------|------------------------------------------------|
-| `CREATE_POLICY` | Metalake | Creating policies in the
metalake |
-| `APPLY_POLICY` | Metalake, or a single policy | Reading a policy and
attaching or detaching it |
-
-Altering and deleting a policy are reserved for the metalake owner and the
policy owner. Attaching a
-policy also requires access to the object being governed. Policy listings show
only the policies
-that user is allowed to read.
-
-## Using the API
-
-Policies can be created, attached, and read over REST and through the Java
client. Endpoints, payload
-shapes, and worked examples are in [Manage
Policies](./manage-policies-in-gravitino.md).
+| `custom` | A free-form map that you define | A
system that you provide |
+
+A custom policy's rules live in `customRules`. Gravitino stores them and
returns them to clients;
+it does not interpret their names or values. Built-in types have a defined
content shape. See
+[Iceberg compaction policy](./iceberg-compaction-policy.md) for the compaction
rules and
+[Table maintenance service](./table-maintenance-service/optimizer.md) for a
worked example.
+
+Policy content also has `properties` and `supportedObjectTypes`. Properties
describe the policy
+itself, such as its owner or consumer. `supportedObjectTypes` is required when
creating a custom
+policy. A custom policy content update replaces the whole content and can
change this field; for a
+built-in policy, the field cannot be changed after creation. Object policy
lookup does not filter
+by this field; each consumer decides whether a policy type applies to the
object it is processing.
+
+### Policy-to-Tag Associations
+
+Each association connects one policy to one tag and stores a selector. The
selector determines
+whether that association contributes the policy to an object's lookup result.
+
+| Selector | When it matches
|
+|--------------|--------------------------------------------------------------------------|
+| `ALL_VALUES` | The effective tag is present, including an assignment without
a value. |
+| `TAG_VALUE` | One of the effective tag assignment values equals the
specified value. |
+
+A policy may be associated with multiple tags. Association listings show those
direct relations
+and their selectors, even if no object currently matches them. An object
policy lookup returns each
+matching policy once.
+
+A selector cannot be changed in place. Remove the policy-to-tag association
and add it again with
+the new selector. Removing an association leaves the policy, tag, and tag
assignments intact.
+Deleting a policy removes its associations.
+
+Object policy lookup covers `CATALOG`, `SCHEMA`, `TABLE`, `VIEW`, `COLUMN`,
`FILESET`, `TOPIC`,
+`MODEL`, `MODEL_VERSION`, and `FUNCTION`. A model version cannot carry a tag
directly, but it can
+inherit tags from its model and higher ancestors, so their policies appear in
model version lookups.
+This also includes columns, which did not support direct policy associations.
+A tag assigned to a catalog or schema can therefore make its policies appear
in descendant column
+lookups. Because lookup does not filter by `supportedObjectTypes`, a policy
whose content lists
+only `TABLE` can still appear in a column lookup; consumers must enforce the
intended scope.
+
+### Effective Tags and Inheritance
+
+An object receives tags assigned directly to it and tags inherited from its
metadata object
+ancestors. Resolution starts at the object and walks upward, so the nearest
assignment of a tag
+name wins, including its assignment values. A direct assignment therefore
overrides every
+ancestor, and a schema assignment overrides the same tag assigned on its
catalog for the schema's
+descendants. Tag names themselves are flat; tags do not inherit from other
tags.
+
+For example, a catalog with `data_domain=finance` gives its tables that
effective assignment.
+A table assigned `data_domain=risk` instead uses `risk`, so a policy
associated with
+`TAG_VALUE("finance")` no longer matches the table. `ALL_VALUES` still matches
because the
+tag is present.
+
+Object policy lookup is read-only. To change its result, update the policy or
its enabled state,
+change a policy-to-tag association, or change a tag assignment on the object
or an ancestor.
+With `details=true`, the lookup includes an `inherited` field. It is `true`
when the policy
+matches only through an inherited tag.
+
+### Enabled State
+
+Disabling a policy preserves the policy and its tag associations, but excludes
it from object
+policy lookup. Policy and association listings still show it. Enabling it
makes matching object
+lookups include it again.
+
+## Managing Policies
+
+The UI supports policy lifecycle operations such as creating custom policies,
editing their
+content, and changing their enabled state. Use the REST API or Java client to
manage
+policy-to-tag associations and to read the resulting object policies. The
+[Manage Policies](./manage-policies-in-gravitino.md) guide has requests and
examples.
+For existing direct object policy associations, see
+[Migration Guide](./migration-guide.md).
+
+To add or remove an association, a user must own the metalake or have the
required access to both
+the tag and the policy (`APPLY_TAG` and `APPLY_POLICY`, or ownership of each).
Object policy
+reads also respect the caller's access to the returned policies. `VIEW_TAG`
and `VIEW_POLICY`
+grant read-only access to tags and policies; `APPLY_TAG` and `APPLY_POLICY`
also allow reads.
diff --git a/docs/security/access-control.md b/docs/security/access-control.md
index deb942227f..99f23ac2df 100755
--- a/docs/security/access-control.md
+++ b/docs/security/access-control.md
@@ -224,9 +224,11 @@ they will be removed in a future release. Use the current
names in new roles.
| `CREATE_ROLE` | Metalake
| Create roles |
| `MANAGE_GRANTS` | Metalake, Catalog, Schema, Table, View, Topic,
Fileset, Model, Function | Grant and revoke privileges on any object in scope |
| `CREATE_TAG` | Metalake
| Create tags |
+| `VIEW_TAG` | Metalake, Tag
| Read tag metadata |
| `APPLY_TAG` | Metalake, Tag
| Attach tags to metadata objects |
| `CREATE_POLICY` | Metalake
| Create policies |
-| `APPLY_POLICY` | Metalake, Policy
| Attach policies to metadata objects |
+| `VIEW_POLICY` | Metalake, Policy
| Read policy metadata |
+| `APPLY_POLICY` | Metalake, Policy
| Associate policies with tags |
| `VIEW_SECRET_PROVIDERS` | Metalake
| List configured secrets providers |
| `REGISTER_JOB_TEMPLATE` | Metalake
| Register job templates |
| `USE_JOB_TEMPLATE` | Metalake, JobTemplate
| Run jobs from a job template |
@@ -238,12 +240,17 @@ 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.
+acts on. Granting `APPLY_POLICY` on the policy `pii_masking` lets the holder
associate that
+policy with tags, provided they also have `APPLY_TAG` on each tag. Granting it
on the metalake
+covers any policy in that 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.
+Assigning a tag to a metadata object requires `APPLY_TAG` on the tag and
access to the object.
+Associating a policy with a tag requires access to both: `APPLY_POLICY` on the
policy and
+`APPLY_TAG` on the tag. Ownership can satisfy either check.
+
+Reading a tag requires `VIEW_TAG` or `APPLY_TAG`; reading a policy requires
`VIEW_POLICY` or
+`APPLY_POLICY`. The view privileges do not allow tag assignment or
policy-to-tag association.
+List results include only tags and policies the caller can read.
### Required Privileges
@@ -297,17 +304,17 @@ owner-only; it does not accept a 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 | |
-| Secret providers | | Owner or
`VIEW_SECRET_PROVIDERS` | |
|
+| 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` | `VIEW_TAG` or `APPLY_TAG`
| Owner | Assign: `APPLY_TAG` and access to the object |
+| Policy | `CREATE_POLICY` | `VIEW_POLICY` or `APPLY_POLICY`
| Owner | Associate with tag: `APPLY_POLICY` and `APPLY_TAG` |
+| Job template | `REGISTER_JOB_TEMPLATE` | `USE_JOB_TEMPLATE`
| Owner | Run a job: `RUN_JOB` and `USE_JOB_TEMPLATE` |
+| Job | | Owner
| Owner | |
+| Secret providers | | Owner or
`VIEW_SECRET_PROVIDERS` | |
|
The secrets-provider registry is process-global server configuration; the
metalake path only scopes
authorization. Listing providers does not return secret material.
diff --git a/docs/table-maintenance-service/optimizer-configuration.md
b/docs/table-maintenance-service/optimizer-configuration.md
index f356f97cf7..5ce59539e6 100644
--- a/docs/table-maintenance-service/optimizer-configuration.md
+++ b/docs/table-maintenance-service/optimizer-configuration.md
@@ -125,7 +125,7 @@ spark.hadoop.fs.defaultFS=file:///
Four things are worth confirming before assuming a configuration problem is a
code problem.
- `builtin-iceberg-update-stats` and `builtin-iceberg-rewrite-data-files`
appear in the job template list.
-- The policy is attached to the target table, not merely created.
+- The policy is associated with a tag assigned to the target table or one of
its ancestors.
- `submit-strategy-jobs` prints `SUBMIT` lines rather than nothing.
- The rewrite log shows `Rewritten data files: N` with `N` greater than zero
for a non-empty table.
diff --git a/docs/table-maintenance-service/optimizer.md
b/docs/table-maintenance-service/optimizer.md
index 774591d55a..193f7f8c9b 100644
--- a/docs/table-maintenance-service/optimizer.md
+++ b/docs/table-maintenance-service/optimizer.md
@@ -11,7 +11,7 @@ license: "This software is licensed under the Apache License
version 2."
## Overview
-The table maintenance service keeps tables healthy without anyone watching
them. You attach a policy to a catalog, schema, or table; the service collects
statistics, evaluates them against that policy, and submits a job when the
policy says work is needed.
+The table maintenance service keeps tables healthy without anyone watching
them. Associate a policy with a tag and assign that tag to a table or one of
its ancestors. The service collects statistics, evaluates them against the
table's derived policy, and submits a job when the policy says work is needed.
The framework is generic. Metrics collection, policy evaluation, and job
submission are not tied to any particular table format, and each is a Java
ServiceLoader extension point. What ships built in is deliberately narrower,
and in alpha that means Iceberg data file compaction on identity-partitioned
tables.
@@ -41,7 +41,7 @@ Maintenance runs as four steps. Each is a separate command,
so you can stop afte
There are two ways in, and they differ in where the numbers come from rather
than in what they do.
-The built-in workflow drives everything through the Gravitino server and its
job templates, using the policy attached to a table to decide what runs. Use it
for server-side operational runs.
+The built-in workflow drives everything through the Gravitino server and its
job templates, using the policy derived from a table's effective tags to decide
what runs. Use it for server-side operational runs.
The local calculator reads a JSONL file you supply and updates statistics and
metrics directly from it. Use it for testing and batch scripts, where you
already have the numbers and want to feed them in without the server computing
them.
@@ -72,7 +72,7 @@ for the per-job options.
## Walkthrough
-This takes one Iceberg table through the whole workflow: create it, fill it
with small files, attach a compaction policy, collect statistics, and let the
service decide to compact it. It runs against a local Spark and takes about
fifteen minutes.
+This takes one Iceberg table through the whole workflow: create it, fill it
with small files, apply a compaction policy through a tag, collect statistics,
and let the service decide to compact it. It runs against a local Spark and
takes about fifteen minutes.
Each step ends with a check. If a check fails, stop there, since every step
depends on the one before it.
diff --git a/docs/tables-and-views.md b/docs/tables-and-views.md
index f2f3b44a72..108a4431e0 100644
--- a/docs/tables-and-views.md
+++ b/docs/tables-and-views.md
@@ -184,8 +184,8 @@ Views can carry tags, and appear in listings alongside
tables.
Opening a schema lists its tables and views. Selecting a table shows its
columns with their types,
its properties, and its tags.
-Tags attach from the table row and from individual column rows, which is the
fastest way to classify
-a specific field rather than a whole table. Policies attach at the table level.
+Assign tags from the table row and from individual column rows to classify a
whole table or
+a specific field. Object policies are derived from those tag assignments.
## Permissions
diff --git a/docs/topics.md b/docs/topics.md
index 53dfa9f926..185fb644b3 100644
--- a/docs/topics.md
+++ b/docs/topics.md
@@ -57,13 +57,14 @@ Leaving either unset takes the broker's own default, from
`num.partition` and
### What Gravitino Stores and What It Does Not
-Gravitino stores the topic's place in the hierarchy and anything attached to
it, including tags,
-policies, and ownership. Message content, offsets, consumer groups, and lag
stay entirely in the
-cluster.
+Gravitino stores the topic's place in the hierarchy, its tag assignments, and
ownership. Policies
+are derived from effective tags when they are read. Message content, offsets,
consumer groups, and
+lag stay entirely in the cluster.
Message schemas are also outside the catalog. Gravitino does not integrate
with a schema registry,
so the structure of the messages in a topic is not described here and cannot
be classified per field
-the way table columns can. Tags and policies attach to the topic as a whole.
+the way table columns can. Tags are assigned to the topic as a whole, and
matching policies
+are derived from those tags.
## Working With Topics in the UI
diff --git a/docs/webui-v2.md b/docs/webui-v2.md
index 5b82863979..73d4842925 100644
--- a/docs/webui-v2.md
+++ b/docs/webui-v2.md
@@ -125,9 +125,9 @@ Overview for Catalog in the Web V2.
On the catalogs page, use the catalog type selector at the top-left to switch
between `relational`, `messaging`, `fileset`, and `model`. The list updates to
show catalogs of the selected type.
-#### Tags and Policies Association
+#### Tags and Derived Policies
-The catalog list shows basic catalog information along with associated
**Tags** and **Policies**. Use **Associate Tag** and **Associate Policy** in
the list to add associations. Click the **X** on a tag to remove it.
+The catalog list shows basic catalog information along with **Tags** and
policies derived from those tags. Use **Associate Tag** to assign a tag to a
catalog. To associate a policy with a tag, use the [Manage
Policies](./manage-policies-in-gravitino.md#policy-to-tag-associations) API.
Click the **X** on a tag to remove its catalog assignment.

@@ -313,11 +313,12 @@ Click **Create Policy** to open the create form. Fill in
the required fields and

-#### Policy Metadata Objects
+To find the tags associated with a policy and inspect their selectors, use the
+[policy-to-tag association
API](./manage-policies-in-gravitino.md#list-associations).
-Click a policy tag to navigate to the **Metadata Objects** page, which lists
all metadata objects associated with the selected policy.
-
-
+The current **Metadata Objects** view and direct policy controls still call
the removed direct
+object-policy association APIs and do not work. Use the REST API or Java
client for policy-to-tag
+associations until these UI controls are updated.
### Access
@@ -367,6 +368,6 @@ Overview for Access Roles in the Web V2.
#### Create Role
-Click **Create Role** to open the create form. A role can include multiple
securable objects. Different securable object types have different available
privileges. For details, see [Securable
Objects](security/access-control.md#securable-objects) and [Privilege
Types](security/access-control.md#privilege-types).
+Click **Create Role** to open the create form. A role can include multiple
securable objects. Different securable object types have different available
privileges. For details, see [Access control](security/access-control.md).
