Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 100 additions & 50 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ Below is a categorized overview of all available Permit CLI commands:
- [OpenAPI -x Extensions for Policy Configuration](#openapi--x-permit-extensions-for-policy-configuration)

- [`permit env apply openapi`](#permit-env-apply-openapi) - Create a full policy schema in Permit by reading an OpenAPI spec file and using `-x-permit` extensions, enabling the use of OpenAPI schema as a source of authorization policy configuration.
- [`permit env apply trino`](#permit-env-apply-trino) - Create a full policy schema in Permit by introspecting a Trino cluster's database schema, including catalogs, schemas, tables, and columns.

### [Custom Rego (OPA) and GitOps](#custom-rego-opa-and-gitops-1)

Expand Down Expand Up @@ -570,18 +571,99 @@ For the more complex extensions that accept objects instead of strings, here's t
}
```

#### URL Mapping
---

After creating the policy elements based on the `-x-permit` extensions, the command will automatically create URL mappings in Permit. These mappings connect API endpoints to the appropriate resources and actions for runtime authorization checks.
### `env apply trino`

For each endpoint with the required extensions, a mapping rule will be created with:
This command introspects a Trino cluster and creates a full Permit policy schema by mapping catalogs, schemas, tables, views, materialized views, columns, functions, and procedures to Permit resources and actions. It is designed to work with any Trino-connected database (e.g., PostgreSQL, MySQL) and uses a robust passthrough strategy to discover all relevant objects.

- URL path from the OpenAPI spec
- HTTP method
- Resource from `x-permit-resource`
- Action from `x-permit-action` or the HTTP method
**Arguments (Required):**

This enables Permit to perform authorization checks directly against your API endpoints.
- `--url <string>` - Trino cluster URL (e.g., http://localhost:8080)
- `--user <string>` - Trino username

**Arguments (Optional):**

- `--api-key <string>` - API key for Permit authentication
- `--password <string>` - Trino password or authentication token
- `--catalog <string>` - Restrict to a specific catalog
- `--schema <string>` - Restrict to a specific schema

**Examples:**

Connect to Trino and sync all schemas:

```bash
permit env apply trino --url http://localhost:8080 --user admin
```

Sync with authentication and a specific catalog:

```bash
permit env apply trino --url http://localhost:8080 --user admin --password secret --catalog postgresql
```

Sync a specific schema with API key:

```bash
permit env apply trino --url http://localhost:8080 --user admin --api-key permit_key --catalog postgresql --schema public
```

#### Resource Mapping

- **Catalogs** → `trino-catalog-<catalog>` (e.g., `trino-catalog-postgresql`)
- **Schemas** → `trino-schema-<catalog>-<schema>`
- **Tables** → `trino-table-<catalog>-<schema>-<table>`
- **Views** → `trino-view-<catalog>-<schema>-<view>`
- **Materialized Views** → `trino-materialized_view-<catalog>-<schema>-<mv>`
- **Columns** → `trino-column-<catalog>-<schema>-<table>-<column>`
- **Functions** → `trino-function-<catalog>-<schema>-<function>`
- **Procedures** → `trino-procedure-<catalog>-<schema>-<procedure>`
- **System Resource** → `trino_sys` (Trino System resource for system-wide actions)

Each table resource includes its columns as attributes with mapped data types:

- `varchar`, `text` → `string`
- `integer`, `bigint`, `decimal` → `number`
- `boolean` → `bool`
- `timestamp`, `date` → `time`
- `json` → `json`
- `array` → `array`

#### Actions

- **Catalog actions:** `AccessCatalog`, `CreateCatalog`, `DropCatalog`, `FilterCatalogs`
- **Schema actions:** `CreateSchema`, `DropSchema`, `RenameSchema`, `SetSchemaAuthorization`, `ShowSchemas`, `FilterSchemas`, `ShowCreateSchema`
- **Table/Column actions:** `ShowCreateTable`, `CreateTable`, `DropTable`, `RenameTable`, `SetTableProperties`, `SetTableComment`, `AddColumn`, `AlterColumn`, `DropColumn`, `RenameColumn`, `SelectFromColumns`, `InsertIntoTable`, `DeleteFromTable`, `TruncateTable`, `UpdateTableColumns`, `ShowTables`, `FilterTables`, `ShowColumns`, `FilterColumns`, `SetTableAuthorization`
- **View actions:** `CreateView`, `RenameView`, `DropView`, `SetViewAuthorization`, `SetViewComment`, `CreateViewWithSelectFromColumns`
- **Materialized View actions:** `CreateMaterializedView`, `RefreshMaterializedView`, `SetMaterializedViewProperties`, `DropMaterializedView`, `RenameMaterializedView`
- **Function actions:** `ShowFunctions`, `FilterFunctions`, `ExecuteFunction`, `CreateFunction`, `DropFunction`, `ShowCreateFunction`, `CreateViewWithExecuteFunction`
- **Procedure actions:** `ExecuteProcedure`, `ExecuteTableProcedure`
- **System actions (on `trino_sys`):**
`ImpersonateUser`, `ExecuteQuery`, `ViewQueryOwnedBy`, `FilterViewQueryOwnedBy`, `KillQueryOwnedBy`, `ReadSystemInformation`, `WriteSystemInformation`, `SetSystemSessionProperty`, `GetRowFilters`, `GetColumnMask`

#### Discovery Strategy

- **Tables, Views, Materialized Views:**
Detected using a combination of Trino metadata, table comments, `SHOW CREATE TABLE`, and naming conventions.
- **Functions and Procedures:**
Discovered using Trino's passthrough feature (`TABLE(catalog.system.query(...))`) to query the underlying database's system tables. **Function and procedure discovery is only supported for PostgreSQL and MySQL (via passthrough); Trino UDFs and other database types are not supported.** For example, `pg_catalog.pg_proc` for PostgreSQL and `information_schema.routines` for MySQL.
- **System/Admin resources:**
By default, only user/business data resources are included. System/admin/internal catalogs and schemas are excluded unless explicitly requested.

#### System Resource

A special resource named `Trino System` with key `trino_sys` is always created, representing system-wide Trino actions.

#### Testing

Use the provided Docker Compose setup in `tests/trino/` for end-to-end testing:

```bash
cd tests/trino
docker-compose up -d
permit env apply trino --url http://localhost:8080 --user test
```

---

Expand Down Expand Up @@ -1367,13 +1449,15 @@ paths:
# ...
```

A more detailed example [is available here](https://github.com/daveads/openapispec)
Check this repo for a good [example](https://github.com/daveads/openapispec)

#### Complex Extension Objects

For the more complex extensions that accept objects instead of strings, here's the expected structure:

- Object Structure: `x-permit-relation`
##### `x-permit-relation` Object Structure

```
```json
{
"subject_resource": "string", // Required: The source resource in the relation
"object_resource": "string", // Required: The target resource in the relation
Expand All @@ -1382,9 +1466,9 @@ For the more complex extensions that accept objects instead of strings, here's t
}
```

- Object Structure: `x-permit-derived-role`
##### `x-permit-derived-role` Object Structure

```
```json
{
"key": "string", // Optional: Unique identifier for the derived role
"name": "string", // Optional: Human-readable name for the derived role
Expand All @@ -1394,47 +1478,13 @@ For the more complex extensions that accept objects instead of strings, here's t
}
```

## Custom Rego (OPA) and GitOps

Extend and customize authorization policies with GitOps flows and custom Rego logic.

### Sync policies to Git repositories

Export, version, and manage authorization policies as code: all through CLI commands

#### `permit gitops create github`

This command will configure your Permit environment to use the GitOps flow with GitHub. This is useful when you want to manage your policies in your own Git repository and extend them with custom policy code.

**Arguments (Required)**

- `--inactive <boolean>` - set the environment to inactive after configuring GitOps (`default:false`)

**Example:**

```
gitops create github --inactive true
```

---

#### `permit gitops env clone`

This clones the environment or the complete project from the active GitOps repository.

**Arguments (Optional)**

- `--api-key <string>` - The API key to select the project. The API Key is of the scope `Project`.
- `--dry-run` - Instead of executing the code, it displays the command to be executed.
- `--project` - Instead of selecting an environment branch to clone, it performs the standard clone operation.

### Extend Predefined Policies with Custom Rego (Open Policy Agent)

Use the CLI to modify and fine-tune Open Policy Agent (OPA) Rego policies while maintaining system stability.
### `opa`

---
This collection of commands aims to create new experiences for developers working with Open Policy Agent (OPA) in their projects.

#### `permit opa policy`
### `opa policy`

This command will print the available policies of an active OPA instance. This is useful when you want to see the policies in your OPA instance without fetching them from the OPA server.

Expand Down
67 changes: 67 additions & 0 deletions source/commands/env/apply/trino.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React from 'react';
import { option } from 'pastel';
import zod from 'zod';
import { AuthProvider } from '../../../components/AuthProvider.js';
import TrinoComponent from '../../../components/env/trino/TrinoComponent.js';
import type { TrinoOptions } from '../../../components/env/trino/types.js';

export const description =
'Apply permissions policy from a Trino schema, creating resources from catalogs, schemas, tables, columns.';

export const options = zod.object({
apiKey: zod
.string()
.optional()
.describe(
option({
description: 'API key for Permit authentication',
alias: 'k',
}),
),
url: zod.string().describe(
option({
description: 'Trino cluster URL (e.g., http://localhost:8080)',
alias: 'u',
}),
),
user: zod.string().describe(
option({
description: 'Trino username',
}),
),
password: zod
.string()
.optional()
.describe(
option({
description: 'Trino password or authentication token',
alias: 'p',
}),
),
catalog: zod
.string()
.optional()
.describe(
option({
description: 'Restrict to a specific catalog',
alias: 'c',
}),
),
schema: zod
.string()
.optional()
.describe(
option({
description: 'Restrict to a specific schema',
alias: 's',
}),
),
});

export default function Trino({ options }: { options: TrinoOptions }) {
return (
<AuthProvider permit_key={options.apiKey} scope="environment">
<TrinoComponent {...options} />
</AuthProvider>
);
}
100 changes: 100 additions & 0 deletions source/components/env/trino/TrinoComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import React, { useEffect, useState } from 'react';
import { Text } from 'ink';
import type { TrinoOptions, PermitResource } from './types.js';
import { useTrinoProcessor } from '../../../hooks/trino/useTrinoProcessor.js';
import { mapTrinoSchemaToPermitResources } from '../../../utils/trinoUtils.js';

export default function TrinoComponent(
props: TrinoOptions,
): React.ReactElement {
const { processTrinoSchema, status, errorMessage } = useTrinoProcessor();
const [createdResources, setCreatedResources] = useState<PermitResource[]>(
[],
);

useEffect(() => {
(async () => {
const client = await import('../../../utils/trinoUtils.js');
const { connectToTrino, fetchTrinoSchema } = client;
const trinoClient = connectToTrino(props);
const trinoSchema = await fetchTrinoSchema(trinoClient, {
catalog: props.catalog,
schema: props.schema,
});
const permitResources = mapTrinoSchemaToPermitResources(trinoSchema);
setCreatedResources(permitResources);
await processTrinoSchema(props);
})();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

if (status === 'processing') {
return <Text>Processing Trino schema and syncing with Permit...</Text>;
}
if (status === 'error') {
return <Text color="red">Error: {errorMessage}</Text>;
}
if (status === 'done') {
// Group resources by type
const grouped: Record<string, string[]> = {};
for (const r of createdResources) {
let type = '';
if (r.key === 'trino_sys') {
type = 'System';
} else {
const match = r.key.match(/^trino-([a-z_]+)/);
if (match && match[1]) {
type = match[1]
.replace(/_/g, ' ')
.replace(/\b\w/g, l => l.toUpperCase());
} else {
type = 'Other';
}
}
if (!grouped[type]) {
grouped[type] = [];
}
const arr = grouped[type]!;
arr.push(r.name);
}
// Sort types in a preferred order
const typeOrder = [
'Catalog',
'Schema',
'Table',
'View',
'Materialized View',
'Column',
'Function',
'Procedure',
'System',
'Other',
];
const sortedTypes = Object.keys(grouped).sort((a, b) => {
const ia = typeOrder.indexOf(a);
const ib = typeOrder.indexOf(b);
if (ia === -1 && ib === -1) return a.localeCompare(b);
if (ia === -1) return 1;
if (ib === -1) return -1;
return ia - ib;
});
return (
<>
<Text>Trino schema successfully synced with Permit!</Text>
{sortedTypes.map(type => {
const items = grouped[type] ?? [];
return (
<Text key={type}>
{type + 's'} ({items.length})
{items
.sort((a, b) => a.localeCompare(b))
.map(name => `\n - ${name}`)
.join('')}
</Text>
);
})}
</>
);
}
return <Text>Ready to process Trino schema...</Text>;
}
29 changes: 29 additions & 0 deletions source/components/env/trino/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
export type TrinoOptions = {
apiKey?: string;
url: string;
user: string;
password?: string;
catalog?: string;
schema?: string;
};

export interface PermitResource {
key: string;
name: string;
description?: string;
actions: string[];
attributes?: {
[key: string]: {
type:
| 'string'
| 'number'
| 'object'
| 'json'
| 'time'
| 'bool'
| 'array'
| 'object_array';
description?: string;
};
};
}
Loading