Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Permissions

Permission-based access control using a resource:action vocabulary with wildcard support.


Permission Vocabulary

17 permissions organized by resource:

Tasks

PermissionDescriptionEndpoints
tasks:createCreate new tasksPOST /v1/tasks
tasks:readRead task detailsGET /v1/tasks/{uuid}
tasks:listList tasksGET /v1/tasks
tasks:cancelCancel running tasksDELETE /v1/tasks/{uuid}
tasks:context_readRead task context dataGET /v1/tasks/{uuid}/context

Steps

PermissionDescriptionEndpoints
steps:readRead workflow step detailsGET /v1/tasks/{uuid}/workflow_steps, GET /v1/tasks/{uuid}/workflow_steps/{step_uuid}, GET /v1/tasks/{uuid}/workflow_steps/{step_uuid}/audit
steps:resolveManually resolve stepsPATCH /v1/tasks/{uuid}/workflow_steps/{step_uuid}

Dead Letter Queue

PermissionDescriptionEndpoints
dlq:readRead DLQ entriesGET /v1/dlq, GET /v1/dlq/task/{task_uuid}, GET /v1/dlq/investigation-queue, GET /v1/dlq/staleness
dlq:updateUpdate DLQ investigationsPATCH /v1/dlq/entry/{dlq_entry_uuid}
dlq:statsView DLQ statisticsGET /v1/dlq/stats

Templates

PermissionDescriptionEndpoints
templates:readRead task templatesOrchestration: GET /v1/templates, GET /v1/templates/{namespace}/{name}/{version}
templates:validateValidate templatesWorker: POST /v1/templates/{namespace}/{name}/{version}/validate

System (Orchestration)

PermissionDescriptionEndpoints
system:config_readRead system configurationGET /config
system:handlers_readRead handler registryGET /v1/handlers, GET /v1/handlers/{namespace}, GET /v1/handlers/{namespace}/{name}
system:analytics_readRead analytics dataGET /v1/analytics/performance, GET /v1/analytics/bottlenecks

Worker

PermissionDescriptionEndpoints
worker:config_readRead worker configurationWorker: GET /config
worker:templates_readRead worker templatesWorker: GET /v1/templates, GET /v1/templates/{namespace}/{name}/{version}

Wildcards

Resource-level wildcards allow broad access within a resource domain:

PatternMatches
tasks:*All task permissions
steps:*All step permissions
dlq:*All DLQ permissions
templates:*All template permissions
system:*All system permissions
worker:*All worker permissions

Note: Global wildcards (*) are NOT supported. Use explicit resource wildcards for broad access (e.g., tasks:*, system:*). This follows AWS IAM-style resource-level granularity.

Wildcard matching is implemented in permission_matches():

  • resource:* → matches if required permission’s resource component equals the prefix
  • Exact string → matches if strings are identical

Role Patterns

Common permission sets for different service roles:

Read-Only Operator

["tasks:read", "tasks:list", "steps:read", "dlq:read", "dlq:stats"]

Suitable for dashboards, monitoring services, and read-only admin UIs.

Task Submitter

["tasks:create", "tasks:read", "tasks:list"]

Services that submit work to Tasker and track their submissions.

Ops Admin

["tasks:*", "steps:*", "dlq:*", "system:*"]

Full operational access including step resolution, DLQ investigation, and system observability.

Worker Service

["worker:config_read", "worker:templates_read"]

Worker processes that need to read their configuration and available templates.

Full Access (Admin)

["tasks:*", "steps:*", "dlq:*", "templates:*", "system:*", "worker:*"]

Full access to all resources via resource wildcards. Use sparingly.


Strict Validation

When strict_validation = true (default), tokens containing permission strings not in the vocabulary are rejected with 401:

Unknown permissions: custom:action, tasks:delete

Set strict_validation = false if your identity provider includes additional scopes that are not part of Tasker’s vocabulary. Use log_unknown_permissions = true to still log unrecognized permissions for monitoring.


Permission Check Implementation

Resource-Based Authorization

Permissions are enforced declaratively at the route level using authorize() wrappers. This ensures authorization happens before body deserialization:

#![allow(unused)]
fn main() {
// In routes.rs
use tasker_shared::web::authorize;
use tasker_shared::types::resources::{Resource, Action};

Router::new()
    .route("/tasks", post(authorize(Resource::Tasks, Action::Create, create_task)))
    .route("/tasks", get(authorize(Resource::Tasks, Action::List, list_tasks)))
    .route("/tasks/{uuid}", get(authorize(Resource::Tasks, Action::Read, get_task)))
}

The authorize() wrapper:

  1. Extracts SecurityContext from request extensions (set by auth middleware)
  2. If resource is public (Health/Metrics/Docs) → proceeds to handler
  3. If auth disabled (AuthMethod::Disabled) → proceeds to handler
  4. Checks has_permission(required) → if yes, proceeds; if no, returns 403

Resource → Permission Mapping

The ResourceAction type maps resource+action combinations to permissions:

ResourceActionPermission
TasksCreatetasks:create
TasksReadtasks:read
TasksListtasks:list
TasksCanceltasks:cancel
TasksContextReadtasks:context_read
StepsRead/Liststeps:read
StepsResolvesteps:resolve
DlqRead/Listdlq:read
DlqUpdatedlq:update
DlqStatsdlq:stats
TemplatesRead/Listtemplates:read
TemplatesValidatetemplates:validate
SystemConfigReadsystem:config_read
SystemHandlersReadsystem:handlers_read
SystemAnalyticsReadsystem:analytics_read
WorkerConfigReadworker:config_read
WorkerRead/Listworker:templates_read

Public Resources

These resources don’t require authentication:

  • Resource::Health - Health check endpoints
  • Resource::Metrics - Prometheus metrics
  • Resource::Docs - OpenAPI/Swagger documentation

Legacy Handler-Level Check (Still Available)

For cases where you need permission checks inside handler logic:

#![allow(unused)]
fn main() {
use tasker_shared::services::require_permission;
use tasker_shared::types::Permission;

fn my_handler(ctx: SecurityContext) -> Result<(), ApiError> {
    require_permission(&ctx, Permission::TasksCreate)?;
    // ... handler logic
}
}

Source: tasker-shared/src/web/authorize.rs, tasker-shared/src/types/resources.rs


OpenAPI Documentation

Permission Extensions

Each protected endpoint in the OpenAPI spec includes an x-required-permission extension that documents the exact permission required:

{
  "paths": {
    "/v1/tasks": {
      "post": {
        "security": [
          { "bearer_auth": [] },
          { "api_key_auth": [] }
        ],
        "x-required-permission": "tasks:create",
        ...
      }
    }
  }
}

Why Extensions Instead of OAuth2 Scopes?

OpenAPI 3.x only formally supports scopes for OAuth2 and OpenID Connect security schemes—not for HTTP Bearer or API Key authentication. Since Tasker uses JWT Bearer tokens with JWKS validation (not OAuth2 flows), we use vendor extensions (x-required-permission) to document permissions in a standards-compliant way.

This approach:

  • Is OpenAPI compliant (tools ignore unknown x- fields gracefully)
  • Doesn’t misrepresent our authentication mechanism
  • Is machine-readable for SDK generators and tooling
  • Is visible in generated documentation

Viewing Permissions in Swagger UI

Each operation’s description includes a Required Permission line:

**Required Permission:** `tasks:create`

This provides human-readable permission information directly in the Swagger UI.

Programmatic Access

To extract permission requirements from the OpenAPI spec:

import json

spec = json.load(open("orchestration-openapi.json"))
for path, methods in spec["paths"].items():
    for method, operation in methods.items():
        if "x-required-permission" in operation:
            print(f"{method.upper()} {path}: {operation['x-required-permission']}")

CLI: List Permissions

cargo run --bin tasker-ctl -- auth show-permissions

Outputs all 17 permissions with their resource grouping.