Skip to content

Expose typed endpoint permissions and detect permission drift - #999

Merged
sdairs merged 1 commit into
mainfrom
codex/998-permission-metadata
Oct 14, 2026
Merged

sdairs merged 1 commit into
mainfrom
codex/998-permission-metadata

Conversation

@sdairs

@sdairs sdairs commented Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator

The OpenAPI analyzer currently ignores operation security requirements, so published permission changes can leave the library and CLI with no usable permission information. Add an offline, typed operation catalog and drift checks that keep it aligned with the spec.

  • Generate all 157 descriptors under meta::operations, with exact operation ID, Rust method, HTTP method/path, and all required API-key permission IDs. Unknown operation lookup returns None; a known empty list means a valid API key with no extra named permissions.
  • Resolve operation/root security consistently for generation and comparison. Detect snapshot permission deltas, stale/missing/invalid descriptors, mismatched identities and catalog coverage/order. Unsupported security shapes remain actionable and cannot generate misleading empty requirements.
  • Upgrade the shared report to schema 10, render permission deltas in drift issues, and keep analyzer/parser dependencies outside the published runtime dependency graph.

Refs #998. CLI consumption is the separate PR #1000 in the permissions series.

Stack and merge order

This is the first PR in GitHub stack #1001, targeting main. PR #1000 builds on this branch. The previous prerequisite stack and external PR #993 have merged; both permission commits have now been replayed onto that merged main. Merge this PR before #1000.

Validation

  • cargo fmt --all --check
  • cargo clippy -p clickhouse-cloud-api -p clickhouse-openapi-analyzer --all-targets -- -D warnings
  • cargo test -p clickhouse-cloud-api -p clickhouse-openapi-analyzer
  • python3 -m unittest discover -s scripts/tests -p 'test_*.py'
  • Permission-only add/remove/replace/root-inheritance fixtures, unsupported/malformed security, snapshot-refresh mismatch, descriptor/catalog validation, exact-ID lookup, deterministic reports and generated catalog parity.

Live Cloud provisioning tests are not part of the local test run; this change adds static metadata and offline tooling, with no request-execution changes.

Rebase verification

Rebased onto merged main without conflicts; git range-diff confirms both permission patches are unchanged. Formatting, all eight permission-unit tests, three permission-help subprocess tests, the query-endpoint request test, and the inherited pagination shell-token test pass on the rebased top branch. CI and live Cloud checks must attest the new PR head.

@sdairs sdairs added the run-cloud-integration Run live Cloud integration tests for this PR head label Sep 22, 2026
Base automatically changed from codex/050-990-pagination-cli to codex/050-985-api September 22, 2026 14:29
@sdairs
sdairs force-pushed the codex/998-permission-metadata branch from 990a59f to 5770830 Compare September 22, 2026 15:18
@sdairs
sdairs changed the base branch from codex/050-985-api to main September 22, 2026 15:19
@sdairs
sdairs added this pull request to stack #1001 September 22, 2026 15:19
@sdairs sdairs added run-cloud-integration Run live Cloud integration tests for this PR head and removed run-cloud-integration Run live Cloud integration tests for this PR head labels Sep 22, 2026
@sdairs
sdairs deployed to cloud-integration September 22, 2026 15:21 — with GitHub Actions Active
@mdevils
mdevils self-requested a review September 22, 2026 18:02
@mdevils

mdevils commented Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator

I'm not really sure that we should bundle permissions. One thing is API schema, it's supposed to be backwards compatible. But permissions may change (become more gradual). That information can be easily extracted in runtime by requesting the OpenAPI schema via /v1.json (or /v1.yaml).

@sdairs

sdairs commented Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator Author

@mdevils I'm open to it, but my concern would be

  1. latency
  2. offline support

perhaps caching the OpenAPI locally could work but it would need some baked in fallbacks.

@mdevils

mdevils commented Sep 23, 2026 •

Copy link
Copy Markdown
Collaborator

@sdairs from what I see in the code, permissions are not used anywhere right now. My understanding is that they will be used in permission negotiation process, which of course requires a network connection. Is there any other use-case?
Also, for proper negotiation, the connection between CLI commands and OpenAPI operations should exist in some way. Is that rust_method field?

@sdairs

sdairs commented Sep 23, 2026

Copy link
Copy Markdown
Collaborator Author

@mdevils that's a good point. It will exclusively be used for cloud stuff, and so the offline support side of things is probably not that important. That said, the CLI still needs to work offline. Even though you won't be able to actually do any cloud operations, we can't have failures if people do want to explore cloud commands offline, so there needs to be something. Primary use case for the permissions is agent permission negotiation.

I also don't think that we necessarily need to go the dynamic route from the get-go. None of this is a single-way door, we can always go down a dynamic route in the future if we see the permissions are changing to a degree that the CLI is becoming out of sync. We have a similar thing anyway with commands being changed in the Open API that are then hard-coded in the CLI, and fields of requests changing in the Open API, so we have this problem outside of just permissions already. To date it hasn't been a problem, we move fast to react to change in the OpenAPI.

I am not entirely sure what the second question means - a user/agent doesn't need to know what OpenAPI operations are performed behind the scenes.

@mdevils

mdevils commented Sep 23, 2026

Copy link
Copy Markdown
Collaborator

@sdairs

I also don't think that we necessarily need to go the dynamic route from the get-go.

OK, fair enough, we can start this way.

I am not entirely sure what the second question means - a user/agent doesn't need to know what OpenAPI operations are performed behind the scenes.

Operations are exactly the list you've generated in this PR:

I.e.:

pub const ACTIVE_BALANCES_GET: OperationMetadata = OperationMetadata {
    operation_id: "activeBalancesGet",
    rust_method: "active_balances_get",
    method: "GET",
    path: "/v1/organizations/{organizationId}/activeBalances",
    required_permissions: &["control-plane:organization:view-billing"],
};

I see that you map rust_method to operation_id. Does it mean we know operationId for any CLI command and can get necessary permission?

If so, it would be useful to have some kind of flag:

clickhousectl cloud postgres create --show-permission

And then it shows what permission it needs to run it.

That way agent can figure out for what commands what permissions are needed.

@sdairs

sdairs commented Sep 23, 2026

Copy link
Copy Markdown
Collaborator Author

@mdevils got it - correct: #1000 this PR demonstrates consumption by the CLI. It just exposes it in help, we don't need to introduce more flags/commands atm.

@sdairs
sdairs marked this pull request as ready for review October 1, 2026 17:02
@sdairs
sdairs requested a review from iskakaushik as a code owner October 1, 2026 17:02
@sdairs
sdairs force-pushed the codex/998-permission-metadata branch from 5770830 to 10dbbbc Compare October 1, 2026 17:24
@sdairs
sdairs removed this pull request from stack #1001 October 10, 2026 08:00
@sdairs
sdairs added this pull request to stack #1075 October 10, 2026 08:01
@sdairs
sdairs merged commit 24d6789 into main Oct 14, 2026
18 of 20 checks passed
sdairs added a commit that referenced this pull request Oct 14, 2026
…1000)

Cloud command help currently does not advertise the permissions needed
by its API calls. Declare each command's operations beside its domain
definitions, then derive help from the HTTP library's operation metadata
so permission strings have one source.

- Cover all 175 executable Cloud paths, including the runnable `org
prometheus` parent. Pure grouping commands and local commands receive no
endpoint-permission lines.
- Combine permission IDs across multiple calls, deduplicate them, and
retain flag/workflow conditions for selectors, read-before-write, setup,
diagnostics, polling, cleanup and rollback.
- Use one decorated clap factory for parsing and help. Reject missing,
duplicate, stale or unknown declarations and conditional flags that do
not exist; non-OpenAPI commands require an explicit explanation.
- Keep permission information in the existing eight-line agent context.
OAuth and SQL authorization behavior are unchanged.

Closes #998 after prerequisite #999 lands.

### Stack and merge order

This is the second PR in GitHub stack #1001, targeting #999
(`codex/998-permission-metadata`). PR #999 now targets `main`; both
permission commits have been replayed onto the merged prerequisite
stack, including #993. Merge #999 before this PR.

### Validation

- `cargo fmt --all --check`
- `cargo clippy -p clickhousectl -- -D warnings`
- `cargo test -p clickhousectl`
- `cargo check -p clickhousectl --no-default-features`
- `cargo clippy -p clickhousectl --all-targets --no-default-features --
-D warnings`
- `python3 -m unittest discover -s scripts/tests -p 'test_*.py'` (96
tests; both path classifiers include the new source file)
- Whole-tree declaration and help-structure checks; negative tests for
undeclared/stale/invalid declarations; conditional permission union and
exact flag validation.
- Real-binary help without credentials/network calls,
multiple-permission endpoint coverage, and observed HTTP calls for force
deletion with managed-key cleanup and query-endpoint read-before-write.
- Independent audit of declarations against the current handlers and
shared helpers.

The enforced structural guarantee is declaration coverage. The audit and
focused request tests verify current call paths; this does not introduce
arbitrary Rust call-graph analysis or a new runtime authorization gate.


### Rebase verification

Rebased onto merged `main` without conflicts; `git range-diff` confirms
both permission patches are unchanged. Formatting, all eight
permission-unit tests, three permission-help subprocess tests, the
query-endpoint request test, and the inherited pagination shell-token
test pass on the rebased top branch. CI and live Cloud checks must
attest the new PR head.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

run-cloud-integration Run live Cloud integration tests for this PR head

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants