# Database-Level Access Control for Iceberg Catalog

info
This feature is not enabled on accounts by default. Contact Technical Support or your Customer Success representative to enable it.

This guide explains how to manage database-level permissions for the Iceberg Catalog using the Permission Management API.

## Overview

The Permission Management API controls which users can access Iceberg catalog databases when querying from [Data Workbench](https://docs.treasure.ai/products/customer-data-platform/data-workbench). Permissions are managed per-user and are scoped to databases within your TD account.

**Scope:** This access control applies only to queries executed via Data Workbench. It does not apply to access from cloud data warehouses (e.g., Databricks, Snowflake) or to Data Workbench databases.

Only **admin users** can manage permissions. Regular users can view their own permissions but cannot modify them.

## Prerequisites

- Your account must have Iceberg catalog resources provisioned (status: `active`)


## Authentication

For authentication details and API endpoints by site, see [Use Iceberg Catalog Management API](/products/customer-data-platform/composable-publish/use-iceberg-catalog-api).

## Permission Model

Each permission entry has three components:

| Field | Description | Values |
|  --- | --- | --- |
| `resource_type` | The type of resource | `DATABASE` |
| `resource_names` | List of database names | `td{account_id}_{site}_...` or `*` (wildcard) |
| `operation` | Access level granted | `FULL`, `READ`, `WRITE` |


### Operations

| Operation | SQL Commands Allowed |
|  --- | --- |
| `FULL` | All operations (SELECT, CREATE TABLE, DML, SHOW, etc.) |
| `READ` | SELECT, SHOW, information_schema access |
| `WRITE` | CREATE TABLE, CREATE TABLE AS, INSERT, UPDATE, DELETE, SHOW |


### Database Naming

- Example: `td10000_us01_export`
- Use `*` to grant access to all databases in the account


You can only assign permissions for databases that belong to your own account. Cross-account database names are rejected.

## API Endpoints

### Update Permissions (Admin Only)

```
PUT /v1/iceberg/catalog/permissions
Authorization: TD1 <admin_api_key>
Accept: application/json
Content-Type: application/json
```

This endpoint **replaces** all existing permissions for the target user. To add a permission, you must include all existing permissions in the request along with the new one.

#### curl Example

```sh
curl -X PUT "https://api-iceberg-mng.us01.treasuredata.com/v1/iceberg/catalog/permissions" \
  -H "Authorization: TD1 <admin_api_key>" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": 12345,
    "permissions": [
      {
        "resource_type": "DATABASE",
        "resource_names": ["td10000_us01_export"],
        "operation": "READ"
      }
    ]
  }'
```

#### Request Body

```json
{
  "user_id": 12345,
  "permissions": [
    {
      "resource_type": "DATABASE",
      "resource_names": ["td10000_us01_export"],
      "operation": "READ"
    }
  ]
}
```

- `user_id` (optional): Target user. Omit to update your own permissions.
  - You can find the user ID using the [Get users API](https://docs.treasure.ai/apis/td-api/openapi/users/getusers), for example:

```sh
curl -s -X GET https://api.treasuredata.com/v3/user/list -H "Authorization: TD1 <api_key>" | jq -cM '.users[] | [.id, .name]'
```
- `permissions` (required): Full list of permissions to set.


#### Response

```json
{
  "permissions": [
    {
      "resource_type": "DATABASE",
      "resource_names": ["td10000_us01_export"],
      "operation": "READ"
    }
  ]
}
```

The response shows permissions after automatic compaction (duplicate/redundant entries are merged).

### Get Permissions

```
GET /v1/iceberg/catalog/permissions
Authorization: TD1 <your_api_key>
Accept: application/json
```

#### curl Example

```sh
# Get your own permissions
curl "https://api-iceberg-mng.us01.treasuredata.com/v1/iceberg/catalog/permissions" \
  -H "Authorization: TD1 <your_api_key>" \
  -H "Accept: application/json"

# Get another user's permissions (admin only)
curl "https://api-iceberg-mng.us01.treasuredata.com/v1/iceberg/catalog/permissions?user_id=12345" \
  -H "Authorization: TD1 <admin_api_key>" \
  -H "Accept: application/json"
```

#### Query Parameters

| Parameter | Required | Description |
|  --- | --- | --- |
| `user_id` | No | Target user ID. Omit to view your own permissions. Admins can view any user. |


#### Response

```json
{
  "permissions": [
    {
      "resource_type": "DATABASE",
      "resource_names": ["td10000_us01_export"],
      "operation": "READ"
    }
  ]
}
```

Returns an empty list if the user has no permissions configured.

## Common Tasks

### Grant full access to the export database

```sh
curl -X PUT "https://api-iceberg-mng.us01.treasuredata.com/v1/iceberg/catalog/permissions" \
  -H "Authorization: TD1 <admin_api_key>" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": 12345,
    "permissions": [
      {
        "resource_type": "DATABASE",
        "resource_names": ["td10000_us01_export"],
        "operation": "FULL"
      }
    ]
  }'
```

### Grant full access using wildcard

```sh
curl -X PUT "https://api-iceberg-mng.us01.treasuredata.com/v1/iceberg/catalog/permissions" \
  -H "Authorization: TD1 <admin_api_key>" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": 12345,
    "permissions": [
      {
        "resource_type": "DATABASE",
        "resource_names": ["*"],
        "operation": "FULL"
      }
    ]
  }'
```

### Grant read-only access

```sh
curl -X PUT "https://api-iceberg-mng.us01.treasuredata.com/v1/iceberg/catalog/permissions" \
  -H "Authorization: TD1 <admin_api_key>" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": 12345,
    "permissions": [
      {
        "resource_type": "DATABASE",
        "resource_names": ["td10000_us01_export"],
        "operation": "READ"
      }
    ]
  }'
```

### Grant write-only access

```sh
curl -X PUT "https://api-iceberg-mng.us01.treasuredata.com/v1/iceberg/catalog/permissions" \
  -H "Authorization: TD1 <admin_api_key>" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": 12345,
    "permissions": [
      {
        "resource_type": "DATABASE",
        "resource_names": ["td10000_us01_export"],
        "operation": "WRITE"
      }
    ]
  }'
```

### Revoke all permissions

Send an update with an empty permissions list. This removes all access for the user.

```sh
curl -X PUT "https://api-iceberg-mng.us01.treasuredata.com/v1/iceberg/catalog/permissions" \
  -H "Authorization: TD1 <admin_api_key>" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": 12345,
    "permissions": []
  }'
```

### Add a permission without removing existing ones

1. First, GET the user's current permissions.
2. Add the new permission entry to the list.
3. PUT the combined list back.


## Automatic Permission Compaction

The API automatically merges redundant permissions on write:

| Input | Result |
|  --- | --- |
| `FULL(*)` + `READ(td10000_us01_export)` | `FULL(*)` |
| `READ(*)` + `READ(td10000_us01_export)` | `READ(*)` |


## Error Responses

| Status | Meaning |
|  --- | --- |
| 400 | Invalid database name format |
| 403 | Non-admin attempting to manage permissions, or write-only API key |
| 404 | Target user not found or does not belong to your account |
| 422 | Validation error (e.g., empty `resource_names` array) |


## Permission Evaluation

When a user runs a query, their permissions are evaluated as follows:

- Permissions are combined disjunctively (union/OR logic)
- If any permission grants access to the requested database and operation, access is allowed
- Wildcard (`*`) matches all databases in the account
- `FULL` permission implies both `READ` and `WRITE`
- Users with no permissions have no access to any database