-
Notifications
You must be signed in to change notification settings - Fork 1.2k
refactor(audit): derive the audit event vocabulary from one table #6983
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,61 +4,28 @@ export type AuditTrailVersion = '2026-07-16'; | |
| export type AuditActorType = 'user' | 'api_key' | 'system' | 'anonymous'; | ||
| export type AuditOutcome = 'success' | 'failure' | 'denied'; | ||
|
|
||
| export type AuditResource = | ||
| | 'connection' | ||
| | 'sync' | ||
| | 'function' | ||
| | 'integration' | ||
| | 'api_key' | ||
| | 'member' | ||
| | 'team' | ||
| | 'user' | ||
| | 'environment' | ||
| | 'app_auth' | ||
| | 'mfa' | ||
| | 'billing' | ||
| | 'audit_log'; | ||
| // Not exported: this package emits no JavaScript, so a const here is unusable at runtime — code needing | ||
| // the list keeps a twin checked against `AuditEventKey`, as `apiKeyScopes` does for scopes. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you not achieve the same derivation without a const that's gonna be discarded at compile time
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good point, easier.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done — |
||
| const AUDIT_EVENTS = { | ||
| connection: ['created', 'updated', 'metadata_updated', 'refreshed', 'deleted'], | ||
| sync: ['enabled', 'disabled', 'paused', 'started', 'triggered', 'cancelled', 'frequency_changed', 'variant_created', 'variant_deleted'], | ||
| function: ['deployed', 'upgraded', 'deleted'], | ||
| integration: ['created', 'updated', 'deleted'], | ||
| api_key: ['created', 'updated', 'deleted'], | ||
| member: ['invited', 'invite_accepted', 'invite_declined', 'invite_revoked', 'role_changed', 'removed'], | ||
| team: ['updated'], | ||
| user: ['updated'], | ||
| environment: ['created', 'updated', 'variables_changed', 'webhook_urls_changed', 'deleted'], | ||
| app_auth: ['login', 'logout', 'signup', 'password_changed', 'password_reset'], | ||
| mfa: ['enrolled', 'enabled', 'disabled', 'verified', 'recovery_regenerated'], | ||
| billing: ['plan_changed', 'trial_extended', 'details_changed', 'payment_method_added', 'payment_method_removed'] | ||
| } as const; | ||
|
|
||
| export type AuditAction = | ||
| | 'created' | ||
| | 'reauthorized' | ||
| | 'refreshed' | ||
| | 'updated' | ||
| | 'metadata_updated' | ||
| | 'deleted' | ||
| | 'paused' | ||
| | 'started' | ||
| | 'cancelled' | ||
| | 'enabled' | ||
| | 'disabled' | ||
| | 'frequency_changed' | ||
| | 'triggered' | ||
| | 'variant_created' | ||
| | 'variant_deleted' | ||
| | 'upgraded' | ||
| | 'deployed' | ||
| | 'invited' | ||
| | 'invite_accepted' | ||
| | 'invite_declined' | ||
| | 'invite_revoked' | ||
| | 'removed' | ||
| | 'role_changed' | ||
| | 'variables_changed' | ||
| | 'webhook_urls_changed' | ||
| | 'login' | ||
| | 'logout' | ||
| | 'signup' | ||
| | 'password_changed' | ||
| | 'password_reset' | ||
| | 'enrolled' | ||
| | 'recovery_regenerated' | ||
| | 'verified' | ||
| | 'plan_changed' | ||
| | 'trial_extended' | ||
| | 'details_changed' | ||
| | 'payment_method_added' | ||
| | 'payment_method_removed' | ||
| | 'exported'; | ||
| export type AuditResource = keyof typeof AUDIT_EVENTS; | ||
| export type AuditActionOf<R extends AuditResource> = (typeof AUDIT_EVENTS)[R][number]; | ||
| export type AuditAction = AuditActionOf<AuditResource>; | ||
|
|
||
| export type AuditEventKey = { [R in AuditResource]: `${R}.${AuditActionOf<R>}` }[AuditResource]; | ||
|
|
||
| export type AuditScope = 'account' | 'environment'; | ||
|
|
||
|
|
@@ -86,7 +53,7 @@ export interface AuditContext { | |
| // decision — a customer endpoint cannot be added without consciously opting in or out. The policy's | ||
| // resource/action/scope are captured as type parameters so the endpoint's declaration and the | ||
| // middleware spec that services it are checked against each other by the compiler. | ||
| export interface AuditPolicy<R extends AuditResource = AuditResource, A extends AuditAction = AuditAction, S extends AuditScope = AuditScope> { | ||
| export interface AuditPolicy<R extends AuditResource = AuditResource, A extends AuditActionOf<R> = AuditActionOf<R>, S extends AuditScope = AuditScope> { | ||
| kind: 'audit'; | ||
| resource: R; | ||
| action: A; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit, you can probably do it in one line
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Applied. Verified it still catches a failure in either tuple position independently, since a single assertion covering two invariants is easy to get silently half-working — an event emitted but absent from the table, and a table entry nothing emits, each fail it on their own.