feat: report side-effect principal deletion from revoke#141
feat: report side-effect principal deletion from revoke#141alan-lee-12 wants to merge 4 commits into
Conversation
Some apps delete the user when their last role is revoked. Add an optional provisioning.revoke.principal_deleted_check probe that runs after the revoke queries on the same transaction; when it finds the principal gone, the revoke response carries a ResourceDeleted annotation so ConductorOne marks the account deleted immediately instead of waiting for a full sync. - config: PrincipalDeletedCheck struct/field + validation - query.go: RunRevokeProvisioning + runPrincipalDeletedCheck probe - provisioning.go: attach ResourceDeleted (combined with GrantAlreadyRevoked on the already-revoked retry path) - examples/mssql-revoke-deletes-user.yml + tests Depends on the baton-sdk ResourceDeleted annotation; the go.mod/vendor bump to the released SDK is handled separately. Refs FDE-296 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Connector PR Review: feat: report side-effect principal deletion from revokeBlocking Issues: 0 | Suggestions: 0 | Threads Resolved: 0 Review SummaryThe new commit renames Security IssuesNone found. Correctness IssuesNone found. SuggestionsNone. |
Group revoke-only probe config under revoke.revoke_options so future revoke knobs have a clear home without polluting the shared provisioning query fields. Co-authored-by: Cursor <cursoragent@cursor.com>
The probe is a positive existence query — it returns the principal when it
still exists, mirroring account_provisioning.validate. The previous name
described the downstream consequence rather than the query, which forced a
double-negative read ("write an exists query, but zero rows means deleted").
Behavior is unchanged: the exists-check returning no rows still means the
principal was deleted by the revoke, yielding a ResourceDeleted annotation.
Refs FDE-296
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
What
Some downstream apps delete the user record when their last role is revoked. Today ConductorOne doesn't learn the account is gone until the next full sync (~1h), so an immediate role change (revoke old → request new) fails downstream because the account no longer exists.
This adds an optional
provisioning.revoke.revoke_options.principal_exists_checkprobe. After the revoke queries run (same transaction), the probe checks whether the principal still exists — a positive existence query, the same shape asaccount_provisioning.validate. If it returns no rows, the principal was deleted as a side effect, and the connector attaches aResourceDeletedannotation to the revoke response so ConductorOne can mark the account deleted immediately.Changes
pkg/bsql/config.go,pkg/bsql/validate.go): a revoke-onlyRevokeEntitlementProvisioningQueriestype with arevoke_optionsblock carryingprincipal_exists_check { query }, plus validation (non-empty query + var check)pkg/bsql/query.go:RunRevokeProvisioning+runPrincipalExistsCheck— runs the probe on the same executor/tx after the revoke queries; no rows ⇒ principal deleted. Tolerates the all-zero-rows (already-revoked) case so retried revokes still report the deletion.pkg/bsql/provisioning.go:Revoke()attachesResourceDeleted{principal.Id}, combined withGrantAlreadyRevokedon the already-revoked retry path.examples/mssql-revoke-deletes-user.yml: worked example (delete-user-on-last-role revoke + probe + re-provisioning)pkg/bsql/provisioning_revoke_deleted_test.go+ config round-trip inconfig_test.goDesign note
The probe is a positive existence query (rows present = principal still exists), mirroring
account_provisioning.validate; "no rows ⇒ deleted" is the single interpretation. This keeps the SQL natural (noNOT EXISTSgymnastics) and consistent with the existing validate pattern.Dependency / merge order
Depends on the
ResourceDeletedannotation in ConductorOne/baton-sdk#1010. This branch is intentionally source-only —go.mod/go.sum/vendor/are not included, so it won't build standalone until the SDK PR is released and the dependency is bumped here (dropping the localreplacedirective used during development).Test plan (built against the local SDK via
replace)go build ./... && go test ./... -count=1— passgofmtclean;golangci-lint run ./pkg/bsql/...— clean aside from the dev-onlyreplacedirectiveRefs FDE-296
🤖 Generated with Claude Code