Files
harmony/docs/guides/harmony-auth-cli.md

377 lines
11 KiB
Markdown

# Harmony Auth CLI
> **Status: read-only commands implemented.** Mutation commands follow the
> ADR-027 group migration.
`harmony-auth` inspects and manages the relationship between Zitadel
identities and OpenBao access. It presents tenants, identities, and Harmony
permissions first. JWT roles, policy names, subject claims, and HCL remain
available through advanced output.
The CLI is the preferred interface while the web UI matures. Both interfaces
use the same `harmony_auth` operations and return the same effective access.
## Mental model
The CLI has two main views:
- `identity`: who an identity is and what it can access
- `tenant`: who can access a tenant or project
Harmony permissions are the primary authorization vocabulary:
| Permission | Intended identity | Effect |
|---|---|---|
| `tenant-admin` | Human | Read, create, change, and delete secrets in a tenant or project |
| `cd-deployer` | Service account | Read deployment secrets in one project |
| `read-only` | Human or service account | Read secrets in a tenant or project |
Existing OpenBao policies that do not correspond to a Harmony assignment are
shown as imported access. The CLI does not rename, rewrite, or hide them.
## Command tree
```text
harmony-auth
├── connection check
├── identity list
├── identity show <subject-id>
├── tenant list
└── tenant show <tenant>
```
There are no flat aliases. `harmony-auth list` is not valid.
## Connection and credentials
Every command except `--help` and `--version` requires:
| Flag | Environment | Meaning |
|---|---|---|
| `--zitadel-url` | `ZITADEL_URL` | Zitadel base URL |
| `--openbao-url` | `OPENBAO_URL` | OpenBao base URL |
| none | `ZITADEL_PAT` | Zitadel service-account PAT |
| none | `OPENBAO_TOKEN` | Temporary OpenBao administrator token |
Secrets are environment-only because command-line arguments remain in shell
history and may be visible in the process list. Secret values never appear in
help output, normal output, JSON, or logs.
Example:
```sh
export ZITADEL_URL=https://sso.example.com
export ZITADEL_PAT=...
export OPENBAO_URL=https://secrets.example.com
export OPENBAO_TOKEN=...
harmony-auth connection check
```
The CLI does not persist profiles or credentials. Browser profile storage and
session credential refresh remain web UI concerns.
`connection check` attempts both backends even when one fails. It reports each
status without printing provider response bodies:
```text
Zitadel connected
OpenBao connected
```
## Identity commands
### List identities
```sh
harmony-auth identity list
harmony-auth identity list --search folk
harmony-auth identity list --kind human
harmony-auth identity list --tenant devsights
harmony-auth identity list --tenant devsights --kind service
```
Filters combine with AND semantics. `--search` matches display name, login, or
email. `--kind` accepts `human` or `service`. Tenant matching uses parsed
assignment and imported-policy scopes, not string-prefix matching.
Imported scopes are recognized only from wildcard roots:
- `<mount>/data/<tenant>/*` is tenant-wide.
- `<mount>/data/<tenant>/<project>/*` is project-specific.
- Exact secret paths and wildcard paths below a project are custom access. They
remain visible but do not affect tenant filters or summaries.
Tenant and project components must pass the same slug validation as managed
Harmony scopes. `harmony_auth` returns both the raw paths and parsed scopes;
frontends never infer scopes themselves.
Human output keeps the tenant visible:
```text
ACTIVE HUMAN Alice Example alice@example.com
subject 241696899342475267
devsights Tenant Admin
ACTIVE SERVICE Folk CD folk-cd
subject 241697058442100739
devsights/folk-timesheet CD Deployer
```
An identity without recognized access is still listed with `No access`.
### Show an identity
```sh
harmony-auth identity show 241696899342475267
```
Default output includes identity metadata, Harmony assignments, and imported
access:
```text
Alice Example
Subject: 241696899342475267
Login: alice@example.com
Kind: Human
Status: Active
Harmony permissions
019b... Tenant Admin devsights
Imported OpenBao access
legacy-folk-reader devsights/folk-timesheet Read secrets
```
Use `--advanced` to inspect implementation details:
```sh
harmony-auth identity show 241696899342475267 --advanced
```
Advanced output adds matching JWT roles, bound subject, audiences, attached
policy names, and the exact HCL returned by OpenBao. A built-in or inaccessible
policy remains listed with `Policy body unavailable`.
## Planned group-based mutations
The first release does not grant or revoke access. Existing per-subject JWT
roles are discovery input, not a writable authorization model.
ADR-027 makes Zitadel roles named `<tenant>:owner`, `<tenant>:deployer`, and
`<tenant>:viewer` authoritative. One shared OpenBao JWT role reads the `groups`
claim, and OpenBao external groups attach policies. Mutation commands ship only
after `harmony_auth` implements that model end to end.
### Grant a permission (planned)
```sh
harmony-auth identity grant 241696899342475267 \
--permission tenant-admin \
--tenant devsights
```
Grant is review-only by default:
```text
Plan
Identity: Alice Example (241696899342475267)
Permission: Tenant Admin
Scope: devsights
Effect: Read, create, change, and delete secrets
No changes applied. Re-run with --apply to continue.
```
Apply the reviewed request explicitly:
```sh
harmony-auth identity grant 241696899342475267 \
--permission tenant-admin \
--tenant devsights \
--apply
```
`cd-deployer` requires `--project`. Other permissions accept an optional
project. Permission applicability and scope validation come from
`harmony_auth`; the CLI does not duplicate those rules.
Applying a grant changes the Zitadel role assignment. It does not create or
edit a per-subject OpenBao JWT role. Applying the same grant twice must report
`changed: false`.
### Revoke a permission (planned)
The assignment ID comes from `identity show`:
```sh
harmony-auth identity revoke 241696899342475267 019b...
```
Revoke is also review-only by default. It shows identity, permission, scope,
and the warning that already-issued OpenBao tokens remain valid until expiry or
revocation. `--apply` performs the removal:
```sh
harmony-auth identity revoke 241696899342475267 019b... --apply
```
Imported per-subject access cannot be revoked through this command because it
is outside the ADR-027 group model. Its OpenBao policy name remains visible for
manual migration.
## Tenant commands
### List tenants
```sh
harmony-auth tenant list
```
Tenants and projects are discovered from managed assignments and recognized
OpenBao policy paths:
```text
TENANT PROJECT HUMANS SERVICES
devsights All projects 2 0
devsights folk-timesheet 1 1
detexion harmony-fleet 0 2
```
### Show a tenant
```sh
harmony-auth tenant show devsights
harmony-auth tenant show devsights --project folk-timesheet
```
Output lists matching scopes and identities with their access. With
`--project`, tenant-wide access and access to that exact project are included;
other projects are excluded:
```text
devsights/folk-timesheet
Alice Example Human Read-only
Folk CD Service CD Deployer
```
This is an authorization view, not a secret-value browser. Secret listing,
creation, update, and reveal are outside the first release.
## JSON output
Every implemented command accepts `--json`. JSON is written to stdout;
diagnostics and logs are written to stderr. The envelope is versioned:
```json
{
"schema_version": 1,
"command": "identity.list",
"result": {}
}
```
The first release uses these result shapes:
| Command | `result` fields |
|---|---|
| `connection check` | `zitadel: { connected }`, `openbao: { connected }` |
| `identity list` | `identities: [{ identity, access }]` |
| `identity show` | `identity`, `access` |
| `tenant list` | `tenants: [{ scope, humans, services }]` |
| `tenant show` | `tenant`, `project`, `identities: [{ identity, access }]` |
`identity` contains `subject_id`, `kind`, `display_name`, `login_name`,
`email`, and `active`. Identity kinds are `human` and `service`; `email` is a
string or `null`.
`scope` contains `tenant` and `project`, where `project` is a string or `null`.
An assignment contains `id`, `subject_id`, `permission`, `scope`,
`policy_name`, and `created_at`. IDs are UUID strings, timestamps are RFC 3339,
and permissions are `tenant_admin`, `cd_deployer`, or `read_only`.
`access` contains:
- `assignments`: assignment objects as defined above
- `imported`: objects with `role_name`, `policy_name`, `secret_paths`,
`scopes`, and `effect`
- `roles`: objects with `name`, `subject_id`, `bound_audiences`, and `policies`
Each role policy contains `name` and `body`. Policy bodies are strings only for
`identity show --advanced`; otherwise `body` is `null`. Imported
`secret_paths`, audiences, and scopes are arrays. `effect` is the
plain-language interpretation returned by `harmony_auth`.
`connection check` always returns both status objects and exits `1` when either
`connected` value is false. A top-level error is used only when the command
cannot produce those statuses.
Domain and backend errors use stdout when `--json` is active:
```json
{
"schema_version": 1,
"command": "identity.show",
"error": {
"kind": "not_found",
"message": "identity not found"
}
}
```
Error kinds are `not_found`, `invalid`, and `backend`. Clap usage errors remain
on stderr because command parsing fails before JSON dispatch.
Planned grant and revoke results will add `applied` and `changed` when those
commands are implemented. Their JSON schema is not frozen by this release.
## Exit and error behavior
| Exit | Meaning |
|---|---|
| `0` | Query completed, plan produced, or mutation applied/no-op |
| `2` | Invalid command, missing connection value, not found, or invalid input |
| `1` | Zitadel or OpenBao request failed |
Errors name the failed operation and backend but do not print credentials,
provider response bodies, or stack traces. `RUST_LOG=info` enables operation
logs on stderr.
## Architecture boundary
```text
harmony_auth_ui ─┐
├──> harmony_auth ──> Zitadel + OpenBao
harmony_auth_cli ─┘
```
`harmony_auth` owns:
- identities, scopes, permissions, assignments, roles, policies, and plans
- Zitadel identity discovery
- OpenBao role and policy discovery
- tenant and identity access queries
- permission applicability and scope validation
- assignment discovery and current legacy assignment operations used by the UI
- provider request construction and response interpretation
`harmony_auth_cli` owns:
- Clap arguments and environment mapping
- terminal and JSON rendering
- binary exit codes and logging setup
`harmony_auth_ui` owns HTTP routes, browser sessions, cookies, forms, HTML, CSS,
and browser JavaScript. Neither frontend may parse OpenBao policies, infer
tenants, reconcile JWT roles, or implement permission rules.
## First-release limits
- Authorization discovers existing per-subject JWT roles as imported access.
- Grant and revoke wait for Zitadel role and OpenBao external-group operations.
- The CLI does not create Zitadel identities.
- The CLI does not provide a generic OpenBao policy editor.
- Tenant administrators are not yet authenticated as constrained actors; the
supplied OpenBao token determines backend authority.
- The CLI does not store profiles or credentials.