Add log export for audit logs and SCIM events

Route audit-log and SCIM-event exports through export_jobs with typed
arguments, an iam BuildAndUploadExport/SendExportEmail implementation,
and a concurrent export-job worker with stale recovery. Stream JSONL via
page.WalkAll into S3, and expose the request flow on console, connect,
MCP, and CLI.

Co-authored-by: Bryan Frimin <bryan@getprobo.com>
Signed-off-by: Sacha Al Himdani <sacha@probo.com>
This commit is contained in:
Sacha Al Himdani
2026-07-29 16:12:23 +02:00
parent b2e2d15582
commit cd6c46212a
45 changed files with 2340 additions and 153 deletions

View File

@@ -259,6 +259,74 @@ func TestAuditLog_RBAC(t *testing.T) {
})
}
func TestAuditLog_Export(t *testing.T) {
t.Parallel()
owner := testutil.NewClient(t, testutil.RoleOwner)
const mutation = `
mutation($input: RequestAuditLogExportInput!) {
requestAuditLogExport(input: $input) {
exportJobId
}
}
`
t.Run("owner can request export", func(t *testing.T) {
t.Parallel()
var result struct {
RequestAuditLogExport struct {
ExportJobID string `json:"exportJobId"`
} `json:"requestAuditLogExport"`
}
err := owner.Execute(mutation, map[string]any{
"input": map[string]any{
"organizationId": owner.GetOrganizationID().String(),
"fromTime": "2026-01-01T00:00:00Z",
"toTime": "2026-03-24T00:00:00Z",
},
}, &result)
require.NoError(t, err)
assert.NotEmpty(t, result.RequestAuditLogExport.ExportJobID)
})
t.Run("admin can request export", func(t *testing.T) {
t.Parallel()
admin := testutil.NewClientInOrg(t, testutil.RoleAdmin, owner)
var result struct {
RequestAuditLogExport struct {
ExportJobID string `json:"exportJobId"`
} `json:"requestAuditLogExport"`
}
err := admin.Execute(mutation, map[string]any{
"input": map[string]any{
"organizationId": admin.GetOrganizationID().String(),
"fromTime": "2026-01-01T00:00:00Z",
"toTime": "2026-03-24T00:00:00Z",
},
}, &result)
require.NoError(t, err)
assert.NotEmpty(t, result.RequestAuditLogExport.ExportJobID)
})
t.Run("viewer cannot request export", func(t *testing.T) {
t.Parallel()
viewer := testutil.NewClientInOrg(t, testutil.RoleViewer, owner)
_, err := viewer.Do(mutation, map[string]any{
"input": map[string]any{
"organizationId": viewer.GetOrganizationID().String(),
"fromTime": "2026-01-01T00:00:00Z",
"toTime": "2026-03-24T00:00:00Z",
},
})
testutil.RequireForbiddenError(t, err, "viewer cannot request audit log export")
})
}
func TestAuditLog_TenantIsolation(t *testing.T) {
t.Parallel()