Add whoami and can-i data-plane access diagnostics - #170
Conversation
Code Coverage OverviewLanguages: C# C# / code-coverage/dotnetThe overall line coverage in commit adda968 in the Show a line coverage summary of the most impacted files.
Updated |
298a962 to
ae7fc14
Compare
Adds two RBAC/access diagnostic commands that rely solely on data-plane operations (no control-plane/ARM dependency): - whoami: reports credential type and, for Entra ID connections, the principal, tenant, application id, UPN, display name, identity type, and token expiry decoded from the Cosmos access token (signature not validated, local display only). Key/emulator connections report no Entra identity. - can-i <read|query|write|manage>: probes data-plane access with safe, non-mutating requests (point read, COUNT query, delete of a random id) and reports allow/deny/indeterminate. manage is always indeterminate; master-key connections report allow. Both commands support --format (table/json/csv). Includes JwtClaims helper, credential tracking on ShellInterpreter, localization strings, focused unit tests, and docs. Addresses #163.
3351d8f to
d9a965a
Compare
…gnostics # Conflicts: # CHANGELOG.md
There was a problem hiding this comment.
Pull request overview
This PR adds two new diagnostics commands to the Cosmos DB Shell—whoami (identity introspection) and can-i (data-plane access probing)—designed to rely only on data-plane operations (no ARM dependency) and to support table/json/csv output patterns used elsewhere in the CLI.
Changes:
- Add
whoamito report credential type and (for Entra-based auth) identity details decoded from the Cosmos access token. - Add
can-i <read|query|write|manage>to probe container-scoped data-plane permissions and returnallow/deny/indeterminate. - Extend MCP tool responses to emit first-class
structuredContentwhile keeping a byte-equivalent JSON text block; update docs/localization/tests accordingly.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| README.md | Mentions the new whoami / can-i diagnostics and --format support. |
| docs/mcp.md | Documents dual text + structuredContent payload for MCP tool results. |
| docs/commands.md | Adds a new Diagnostics section describing whoami / can-i usage and semantics. |
| CosmosDBShell/lang/en.ftl | Adds localized strings for both new commands and their output fields/errors. |
| CosmosDBShell/Azure.Data.Cosmos.Shell.Util/JwtClaims.cs | Adds a helper to decode JWT payload claims without signature validation (display-only). |
| CosmosDBShell/Azure.Data.Cosmos.Shell.Mcp/McpResponseFactory.cs | Emits MCP structured content and reuses its raw JSON for the text block to keep them equivalent. |
| CosmosDBShell/Azure.Data.Cosmos.Shell.Core/ShellInterpreter.cs | Tracks active TokenCredential + credential type label at the successful connection choke point. |
| CosmosDBShell/Azure.Data.Cosmos.Shell.Commands/WhoamiCommand.cs | Implements whoami command execution, formatting, and interactive table rendering. |
| CosmosDBShell/Azure.Data.Cosmos.Shell.Commands/CanICommand.cs | Implements can-i action probing via stream APIs and maps status codes to decisions. |
| CosmosDBShell.Tests/UtilTest/JwtClaimsTests.cs | Adds unit tests for JWT payload decoding and claim selection behavior. |
| CosmosDBShell.Tests/McpResponseFactoryTests.cs | Adds tests ensuring structured content matches the JSON text block exactly. |
| CosmosDBShell.Tests/CommandTests/WhoamiCommandTests.cs | Adds offline tests for disconnected/key-auth branches and format validation/output. |
| CosmosDBShell.Tests/CommandTests/CanICommandTests.cs | Adds offline tests for validation, manage semantics, key-auth branch, and format validation/output. |
| CHANGELOG.md | Adds entries for the new diagnostics commands and MCP structured tool results. |
… with ETag, fix redirect docs
Migrate whoami and can-i to the current CommandState renderer model and align invalid-format tests with the current format parser contract.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated no new comments.
Suppressed comments (3)
Previously missed (2) — in code that hasn't changed since the last review.
CosmosDBShell/Azure.Data.Cosmos.Shell.Commands/WhoamiCommand.cs:77
- The AuthenticationFailedException wrapper drops the original exception, which makes verbose/diagnostic output lose the per-credential failure details (inner exceptions, stack trace). Preserve the inner exception when rethrowing CommandException so troubleshooting whoami token acquisition failures remains actionable.
catch (AuthenticationFailedException ex)
{
throw new CommandException("whoami", MessageService.GetArgsString("command-whoami-token-error", "message", ex.Message));
}
CosmosDBShell/Azure.Data.Cosmos.Shell.Commands/CanICommand.cs:175
- MapDecision treats HTTP 404 as "allow" for read/write probes. A 404 can also mean the target database/container does not exist, so reporting "allow" can be misleading (similar to the query probe, where 404 is already treated as indeterminate). Consider disambiguating NotFound cases (e.g., via a follow-up container metadata read, Cosmos substatus, or returning indeterminate with a targeted note) so missing resources don't look like successful authorization.
case HttpStatusCode.Forbidden:
case HttpStatusCode.Unauthorized:
return ("deny", null);
case HttpStatusCode.OK:
case HttpStatusCode.NoContent:
case HttpStatusCode.NotFound:
case HttpStatusCode.PreconditionFailed:
return ("allow", null);
CosmosDBShell/Azure.Data.Cosmos.Shell.Commands/CanICommand.cs:145
- The PR description states the
queryprobe is a COUNT query, but the implementation uses a minimalSELECT TOP 1 ...query withMaxItemCount = 1(see ProbeQueryAsync) to avoid expensive scans. Please update the PR description to match the implemented behavior so reviewers/users aren't misled.
private static async Task<HttpStatusCode> ProbeQueryAsync(Container container, CancellationToken token)
{
// A minimal TOP 1 query with a single-item page proves query authorization without
// forcing a full scan (as an aggregate like COUNT would) on large containers.
var requestOptions = new QueryRequestOptions { MaxItemCount = 1 };
using var iterator = container.GetItemQueryStreamIterator(
new QueryDefinition("SELECT TOP 1 c.id FROM c"),
requestOptions: requestOptions);
using var response = await iterator.ReadNextAsync(token);
return response.StatusCode;
Summary
Adds two RBAC / access diagnostic commands (issue #163, "G5. RBAC / access diagnostics") that rely solely on data-plane operations — no control-plane /
Azure.ResourceManager(ARM) dependency, so they survive the planned ARM removal.Fixes #163
whoamiReports the current credential type and, for Microsoft Entra ID connections, the principal id, tenant id, application id, user principal name, display name, identity type, and token expiry decoded from the Cosmos DB access token.
can-i <read|query|write|manage>Probes whether the current identity can perform an action against a container without mutating data, reporting
allow,deny, orindeterminate:read— point read of a random idquery— aCOUNTquerywrite— delete of a random, almost-certainly-nonexistent id (non-mutating;allowis a heuristic inference of write access from delete permission)manage— cannot be probed on the data plane, always reportedindeterminateAccount-key and emulator connections use a master key and report
allowwith methodkey. Entra connections use methodprobeand include the observed HTTP status code. Probes use the Cosmos stream APIs so 401/403/404 responses are handled via status codes rather than exceptions.--formatsupportBoth commands accept
--format/-f(tabledefault,json,csv), following thelscommand pattern (also honors theCOSMOSDB_SHELL_FORMATenvironment variable). The rich table renders interactively;json/csv(and redirected output) defer toPrintState.Implementation notes
ShellInterpreternow tracks the activeTokenCredentialand a credential-type label, committed atomically insideConnect()(the single success choke point) to avoid stale credentials after a failed account switch.JwtClaimshelper decodes a JWT payload (base64url) without signature validation for local introspection.McpAnnotation.Scope vs. #163 (partial)
This addresses #163 but does not fully satisfy its acceptance criteria, by design — the role/scope enumeration parts depend on the control-plane (ARM), which is planned for removal. Shipped vs. deferred:
whoamireports identity (credential type, principal, tenant, app id, UPN, display name, token expiry)can-ireturns non-destructive allow/deny for read/query/writewhoamiresolvable role assignments,can-igoverning role assignment, and a determinatemanageresultTests & docs
whoami,can-i, andJwtClaims(not-connected, key-auth, invalid action/format, manage, json/csv output). Full offline suite green (1791 tests).lang/en.ftl(passesLocalizationKeyAuditTests/HelpCommandVerificationTests).docs/commands.md(new Diagnostics section),README.md, andCHANGELOG.md.Addresses #163 (see scope note above — intentionally not auto-closing).
Sample:
