Emit audit and SCIM log exports as CSV

JSONL was awkward in spreadsheets and SIEM imports. Write
tab-separated-friendly CSV with organization name on every row,
resolve audit actors to email or API key name, and enrich SCIM rows
with profile email and display name when available.

Signed-off-by: Cursor Agent <cursoragent@cursor.com>

Co-authored-by: Bryan FRIMIN <bryan@frimin.fr>
This commit is contained in:
Cursor Agent
2026-07-29 18:14:25 +00:00
parent 0448c25d8b
commit 5cd407bd86
11 changed files with 726 additions and 108 deletions

View File

@@ -495,6 +495,88 @@ WHERE
return nil
}
func (p *MembershipProfiles) LoadByOrganizationIDAndUserNames(
ctx context.Context,
conn pg.Querier,
scope Scoper,
organizationID gid.GID,
userNames []string,
) error {
if len(userNames) == 0 {
*p = nil
return nil
}
q := `
SELECT
p.id,
p.identity_id,
p.organization_id,
i.email_address,
p.source,
p.state,
p.full_name,
p.kind,
p.additional_email_addresses,
p.position,
p.contract_start_date,
p.contract_end_date,
'' AS organization_name,
p.user_name,
p.external_id,
p.nickname,
p.locale,
p.timezone,
p.profile_url,
p.preferred_language,
p.given_name,
p.family_name,
p.formatted_name,
p.middle_name,
p.honorific_prefix,
p.honorific_suffix,
p.employee_number,
p.department,
p.cost_center,
p.enterprise_organization,
p.division,
p.manager_value,
p.created_at,
p.updated_at
FROM
iam_membership_profiles p
INNER JOIN identities i
ON i.id = p.identity_id
WHERE
p.%s
AND p.organization_id = @organization_id
AND p.user_name = ANY(@user_names::citext[])
`
q = fmt.Sprintf(q, scope.SQLFragment())
args := pgx.NamedArgs{
"organization_id": organizationID,
"user_names": userNames,
}
maps.Copy(args, scope.SQLArguments())
rows, err := conn.Query(ctx, q, args)
if err != nil {
return fmt.Errorf("cannot query profiles by user names: %w", err)
}
profiles, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[MembershipProfile])
if err != nil {
return fmt.Errorf("cannot collect profiles by user names: %w", err)
}
*p = profiles
return nil
}
func (p *MembershipProfiles) LoadByOrganizationID(
ctx context.Context,
conn pg.Querier,