Skip to main content

Permissions: Developer Reference

This page provides technical details on VAMS permission patterns, constraint templates, and JSON examples for developers, integrators, and advanced administrators who configure access control programmatically.

For an introduction to how the authorization system works, see Permissions Model. For web UI instructions on managing roles, constraints, and user assignments, see the User Guide: Permissions.


Common permission patterns

The following patterns cover the most common access configurations. VAMS provides pre-built permission templates for each of these patterns.

Database administrator

Full access to a single database including asset CRUD, pipeline and workflow management, metadata schema management, and the ability to update or delete the database itself. Cannot create new databases.

Constraint summary (13 constraints):

#ConstraintObject TypePermissionsScope
1Web routeswebGETStandard pages + /assetIngestion
2API routesapiGET, PUT, POST, DELETEAll non-admin routes (excludes /tags, /tag-types)
3API routes (tags GET)apiGETRead-only on /tags, /tag-types
4Database entitydatabaseGET, PUT, DELETEScoped to specific database (no POST = no create)
5AssetsassetGET, PUT, POST, DELETEScoped to specific database (includes permanent delete)
6Pipelines (scoped)pipelineGET, PUT, POST, DELETEScoped to specific database (full management)
7Pipelines (GLOBAL)pipelineGET, POSTdatabaseId equals GLOBAL (view + execute)
8Workflows (scoped)workflowGET, PUT, POST, DELETEScoped to specific database (full management)
9Workflows (GLOBAL)workflowGET, POSTdatabaseId equals GLOBAL (view + execute)
10Metadata schemas (scoped)metadataSchemaGET, PUT, POST, DELETEScoped to specific database (full management)
11Metadata schemas (GLOBAL)metadataSchemaGETdatabaseId equals GLOBAL (view only)
12TagstagGETGlobal (read-only)
13Tag typestagTypeGETGlobal (read-only)

Key design decisions:

  • No database creation -- The database entity constraint grants GET + PUT + DELETE but not POST, preventing new database creation even though the API route constraint allows POST on /database (needed for asset operations using /database/\{id\}/... sub-paths).
  • Scoped + GLOBAL pattern -- Two separate constraints per entity type: one scoped with full CRUD for management, one GLOBAL with GET + POST for viewing and executing shared resources.
  • Metadata schema GLOBAL = GET only -- Global schema access is read-only to prevent accidentally creating schemas in the GLOBAL scope.
  • Tags read-only -- Since tags and tag types are shared across all databases, the recommended approach is to limit database-scoped roles to GET-only access.

Example constraint: Database entity (no POST = no create)

{
"name": "my-project-admin-database",
"description": "Allow read, update, and delete of my-project-db (no create)",
"objectType": "database",
"criteriaAnd": [
{ "field": "databaseId", "id": "db1", "operator": "equals", "value": "my-project-db" }
],
"groupPermissions": [
{
"groupId": "my-project-admin",
"id": "db-get",
"permission": "GET",
"permissionType": "allow"
},
{
"groupId": "my-project-admin",
"id": "db-put",
"permission": "PUT",
"permissionType": "allow"
},
{
"groupId": "my-project-admin",
"id": "db-delete",
"permission": "DELETE",
"permissionType": "allow"
}
]
}

Example constraint: GLOBAL pipeline view + execute

Uses the GLOBAL keyword (not a wildcard) to match only shared global pipelines:

{
"name": "my-project-admin-pipelines-global",
"description": "Allow viewing and executing GLOBAL pipelines",
"objectType": "pipeline",
"criteriaAnd": [
{ "field": "databaseId", "id": "pipe-global1", "operator": "equals", "value": "GLOBAL" }
],
"groupPermissions": [
{
"groupId": "my-project-admin",
"id": "pipe-global-get",
"permission": "GET",
"permissionType": "allow"
},
{
"groupId": "my-project-admin",
"id": "pipe-global-post",
"permission": "POST",
"permissionType": "allow"
}
]
}

Database user

Standard working access within a specific database. Can view all data, create and update assets, upload files, archive (soft delete) assets, and execute workflows. Cannot permanently delete assets, create or delete pipelines/workflows/metadata schemas, modify the database itself, or use asset ingestion.

Constraint summary (15 constraints):

#ConstraintObject TypePermissionsScope
1Web routeswebGETStandard pages (excludes /assetIngestion)
2API routes (GET)apiGETBroad read access
3API routes (POST)apiPOSTAsset operations + workflow execution (excludes /ingest-asset, /metadataschema, /tags)
4API routes (PUT)apiPUTAsset updates only (excludes /pipelines, /workflows, /metadataschema, /tags)
5API routes (DELETE)apiDELETEArchive paths only (archiveAsset, archiveFile) + standard non-asset deletes
6Database entitydatabaseGETScoped to specific database (read-only)
7AssetsassetGET, PUT, POST, DELETEScoped to specific database (DELETE needed for archive; permanent delete blocked at Tier 1)
8Pipelines (scoped)pipelineGET, POSTScoped to specific database (view + execute)
9Pipelines (GLOBAL)pipelineGET, POSTdatabaseId equals GLOBAL (view + execute)
10Workflows (scoped)workflowGET, POSTScoped to specific database (view + execute)
11Workflows (GLOBAL)workflowGET, POSTdatabaseId equals GLOBAL (view + execute)
12Metadata schemas (scoped)metadataSchemaGETScoped to specific database (view only)
13Metadata schemas (GLOBAL)metadataSchemaGETdatabaseId equals GLOBAL (view only)
14TagstagGETGlobal (read-only)
15Tag typestagTypeGETGlobal (read-only)

Key design decisions:

  • Archive vs. permanent delete (two-tier enforcement) -- The asset entity constraint grants DELETE at Tier 2 because both archive and permanent delete require DELETE on the asset entity. The differentiation happens at Tier 1 API routes: the DELETE API constraint uses the contains operator to only match paths containing archiveAsset or archiveFile, blocking permanent delete paths.
  • API route method separation -- Unlike the admin (which uses a single API constraint with all methods), the user has 4 separate API constraints, one per HTTP method, each allowing different route subsets.
  • Tier 2 as a safety net -- Even though PUT on /database is allowed at Tier 1 (needed for asset operations using /database/\{id\}/assets/... sub-paths), Tier 2 blocks it because the database entity constraint only grants GET.

Example constraint: API routes DELETE (archive only)

This constraint prevents permanent asset deletion while allowing archive operations:

{
"name": "my-project-user-api-routes-delete",
"description": "Allow DELETE for archive operations only",
"objectType": "api",
"criteriaOr": [
{
"field": "route__path",
"id": "api-del1",
"operator": "contains",
"value": "archiveAsset"
},
{
"field": "route__path",
"id": "api-del2",
"operator": "contains",
"value": "archiveFile"
},
{
"field": "route__path",
"id": "api-del3",
"operator": "starts_with",
"value": "/unsubscribe"
},
{
"field": "route__path",
"id": "api-del4",
"operator": "starts_with",
"value": "/subscriptions"
},
{
"field": "route__path",
"id": "api-del5",
"operator": "starts_with",
"value": "/asset-links"
},
{
"field": "route__path",
"id": "api-del6",
"operator": "starts_with",
"value": "/comments"
}
],
"groupPermissions": [
{
"groupId": "my-project-user",
"id": "api-delete",
"permission": "DELETE",
"permissionType": "allow"
}
]
}

The contains operator on archiveAsset matches /database/\{id\}/assets/\{id\}/archiveAsset but does not match /database/\{id\}/assets/\{id\}/deleteAsset. This is the Tier 1 enforcement that distinguishes archive from permanent delete.

Example constraint: Asset entity (DELETE for archive, protected by Tier 1)

{
"name": "my-project-user-assets",
"description": "Allow create, update, and archive access to assets in my-project-db",
"objectType": "asset",
"criteriaAnd": [
{ "field": "databaseId", "id": "asset-db1", "operator": "equals", "value": "my-project-db" }
],
"groupPermissions": [
{
"groupId": "my-project-user",
"id": "asset-get",
"permission": "GET",
"permissionType": "allow"
},
{
"groupId": "my-project-user",
"id": "asset-put",
"permission": "PUT",
"permissionType": "allow"
},
{
"groupId": "my-project-user",
"id": "asset-post",
"permission": "POST",
"permissionType": "allow"
},
{
"groupId": "my-project-user",
"id": "asset-delete",
"permission": "DELETE",
"permissionType": "allow"
}
]
}
note

DELETE is granted at Tier 2 because archive operations require it. Permanent delete is blocked at Tier 1 (see the API routes DELETE constraint above).

Database Admin vs. Database User comparison

CapabilityAdminUser
View database, assets, pipelines, workflows, schemasYesYes
Create and update assetsYesYes
Upload filesYesYes
Archive (soft delete) assetsYesYes
Permanent delete assetsYesNo (Tier 1 blocks)
Update or delete the databaseYesNo
Create new databasesNoNo
Create or delete pipelines (scoped)YesNo
Create or delete workflows (scoped)YesNo
Create or delete metadata schemas (scoped)YesNo
View and execute GLOBAL pipelines and workflowsYesYes
View GLOBAL metadata schemasYesYes
Asset ingestionYesNo
Tag management (create, modify, delete)No (manage via broader role)No (manage via broader role)
Tag viewingYesYes

Database read-only

View-only access scoped to a single database. Can browse assets, view files, and read metadata but cannot modify anything.

Key constraints:

  • web -- Allow GET on viewing pages only (no /upload)
  • api -- Allow GET on all read routes; allow POST only on /auth/routes, /search, /check-subscription
  • database -- Allow GET where databaseId equals \{DATABASE_ID\}
  • asset -- Allow GET where databaseId equals \{DATABASE_ID\}

Key differences from the admin and user roles: web routes are the same set of pages, but the UI respects the lack of write permissions. API routes only allow GET method, plus POST on non-mutating operations. Data constraints have only GET permission on all object types.

Global read-only

View-only access across all databases. Same as database read-only but without the databaseId filter on entity constraints.

Multi-database access

To give a user access to multiple databases, use criteriaOr instead of criteriaAnd for the databaseId field:

{
"name": "multi-db-editor-assets",
"description": "Access to assets across finance and operations databases",
"objectType": "asset",
"criteriaOr": [
{ "field": "databaseId", "id": "db1", "operator": "equals", "value": "finance-db" },
{ "field": "databaseId", "id": "db2", "operator": "equals", "value": "operations-db" }
],
"groupPermissions": [
{
"groupId": "multi-db-editor",
"id": "asset-get",
"permission": "GET",
"permissionType": "allow"
},
{
"groupId": "multi-db-editor",
"id": "asset-put",
"permission": "PUT",
"permissionType": "allow"
},
{
"groupId": "multi-db-editor",
"id": "asset-post",
"permission": "POST",
"permissionType": "allow"
}
]
}

Alternatively, use the starts_with operator with a naming convention:

{
"criteriaAnd": [
{ "field": "databaseId", "id": "db1", "operator": "starts_with", "value": "team-alpha-" }
]
}

This matches any database whose ID starts with team-alpha- (for example, team-alpha-prod, team-alpha-staging).

warning

Do not use criteriaAnd with multiple databaseId equals conditions. A single entity can only have one databaseId value, so multiple equals conditions in criteriaAnd will never match simultaneously. Use criteriaOr instead.

Deny by tag

Block modification of assets with specific tags. This pattern uses a deny constraint to override any allow rules. The Casbin policy effect ensures deny always wins.

Example: Deny editing of tagged assets

{
"name": "my-project-admin-deny-tagged-locked",
"description": "Deny editing of assets tagged with 'locked'",
"objectType": "asset",
"criteriaAnd": [
{ "field": "tags", "id": "tag-match", "operator": "contains", "value": "locked" }
],
"groupPermissions": [
{
"groupId": "my-project-admin",
"id": "deny-put",
"permission": "PUT",
"permissionType": "deny"
},
{
"groupId": "my-project-admin",
"id": "deny-post",
"permission": "POST",
"permissionType": "deny"
},
{
"groupId": "my-project-admin",
"id": "deny-delete",
"permission": "DELETE",
"permissionType": "deny"
}
]
}

Even though the admin role has full CRUD on assets, this deny constraint matches any asset whose tags field contains "locked". When a user attempts to PUT, POST, or DELETE a locked asset, Casbin finds the deny rule and blocks the operation. GET (viewing) is still permitted.

Important notes:

  • The tags field is checked as a string match. If an asset has tags ["locked", "reviewed"], the contains operator with value locked will match.
  • You can stack multiple deny constraints for different tag values (for example, one for "locked" and another for "approved").
  • Deny constraints can be applied to any role.
  • The deny applies to the data entity operation (Tier 2). The user can still call the API endpoint (Tier 1), but the operation is denied when Casbin evaluates the asset entity.

Example: Deny archived asset deletion

{
"name": "deny-archived-asset-delete",
"description": "Prevent users from deleting assets tagged as archived",
"objectType": "asset",
"criteriaAnd": [{ "field": "tags", "id": "tag1", "operator": "contains", "value": "archived" }],
"groupPermissions": [
{
"groupId": "my-project-user",
"id": "deny-del",
"permission": "DELETE",
"permissionType": "deny"
}
]
}
tip

Deny-by-tag is useful for restricting access to sensitive assets across roles. Because deny always overrides allow, you can add this constraint to any role to block tagged assets regardless of other permissions.


Permission templates

VAMS includes pre-built permission templates that you can import to quickly set up common access patterns. Templates are JSON files with variable placeholders (such as \{\{DATABASE_ID\}\} and \{\{ROLE_NAME\}\}) that are replaced with actual values during import.

Available templates

TemplateFileVariablesDescription
Database Admindatabase-admin.jsonDATABASE_ID, ROLE_NAMEFull management of a specific database (13 constraints)
Database Userdatabase-user.jsonDATABASE_ID, ROLE_NAMEStandard user access with archive-only delete (15 constraints)
Database Read-Onlydatabase-readonly.jsonDATABASE_ID, ROLE_NAMEView-only access to a specific database (10 constraints)
Global Read-Onlyglobal-readonly.jsonROLE_NAMERead-only access across all databases (10 constraints)
Deny Tagged Assetsdeny-tagged-assets.jsonROLE_NAME, TAG_VALUEOverlay: deny editing of assets with a specific tag (1 constraint)

Templates are located in the documentation/permissionsTemplates/ directory.

Applying templates

You can apply templates using the CLI tool or the POST /auth/constraintsTemplateImport API endpoint.

Using the CLI tool:

# Apply the database-admin template with variable substitution
python tools/PermissionsSetup/apply_template.py \
--template documentation/permissionsTemplates/database-admin.json \
--role-name my-project-admin \
--variables '{"DATABASE_ID": "my-project-db"}' --dry-run

# Apply the database-user template
python tools/PermissionsSetup/apply_template.py \
--template documentation/permissionsTemplates/database-user.json \
--role-name my-project-user \
--variables '{"DATABASE_ID": "my-project-db"}' --dry-run

# Stack multiple deny constraints
python tools/PermissionsSetup/apply_template.py \
--template documentation/permissionsTemplates/deny-tagged-assets.json \
--role-name my-project-admin --var TAG_VALUE=locked

python tools/PermissionsSetup/apply_template.py \
--template documentation/permissionsTemplates/deny-tagged-assets.json \
--role-name my-project-admin --var TAG_VALUE=approved

Using the API directly:

curl -X POST https://your-api/auth/constraintsTemplateImport \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"template": {
"name": "Database Admin",
"description": "Administrative access for my-project-db",
"version": "1.0"
},
"variables": [
{"name": "DATABASE_ID", "required": true, "description": "The databaseId to scope permissions to"},
{"name": "ROLE_NAME", "required": true, "description": "The role name to create"}
],
"variableValues": {
"DATABASE_ID": "my-project-db",
"ROLE_NAME": "my-project-admin"
},
"constraints": [ ... ]
}'

The response includes the count and IDs of all created constraints:

{
"success": true,
"message": "Successfully imported 13 constraints from template 'Database Admin' for role 'my-project-admin'",
"constraintsCreated": 13,
"constraintIds": ["uuid-1", "uuid-2", "..."],
"timestamp": "2024-01-01T00:00:00.000000"
}
tip

You can post the entire contents of a JSON template file as the request body. Just add the variableValues field with your specific values.

Template JSON format

Templates are self-describing JSON files containing metadata, variable definitions, and constraint definitions:

{
"metadata": {
"name": "Database Admin",
"description": "Administrative access for a specific database",
"version": "1.0"
},
"variables": [
{
"name": "DATABASE_ID",
"required": true,
"description": "The databaseId to scope permissions to"
},
{
"name": "ROLE_NAME",
"required": true,
"description": "The role name to create"
}
],
"constraints": [
{
"name": "{{ROLE_NAME}}-web-routes",
"description": "Allow navigation to all standard pages for {{ROLE_NAME}}",
"objectType": "web",
"criteriaAnd": [],
"criteriaOr": [
{ "field": "route__path", "operator": "starts_with", "value": "/assets" }
],
"groupPermissions": [{ "action": "GET", "type": "allow" }]
}
]
}

Key differences between the template format and the constraint creation API format:

  • groupPermissions use action and type (template format) instead of permission and permissionType (API format).
  • No identifier, groupId, or permission id fields are needed -- the API generates these automatically.
  • Variable placeholders (\{\{VARIABLE\}\}) are replaced with values from variableValues.
Templates create constraints only

The template import API creates constraints but does not create roles or assign users to roles. You must create the role and assign users separately using the /roles and /user-roles API endpoints.


  • Permissions Model -- Core authorization concepts, object types, constraint fields, operators, and route references
  • User Guide: Permissions -- Web UI instructions for managing roles, constraints, and user assignments
  • CLI Permissions Commands -- Command-line interface for managing roles, constraints, and user-role assignments
  • Auth API Reference -- API endpoints for authentication, constraints, and template import