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:
@@ -387,3 +387,48 @@ WHERE
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (i *Identities) LoadByIDs(
|
||||
ctx context.Context,
|
||||
conn pg.Querier,
|
||||
identityIDs []gid.GID,
|
||||
) error {
|
||||
if len(identityIDs) == 0 {
|
||||
*i = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
q := `
|
||||
SELECT
|
||||
id,
|
||||
email_address,
|
||||
full_name,
|
||||
hashed_password,
|
||||
email_address_verified,
|
||||
saml_subject,
|
||||
locale,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
identities
|
||||
WHERE
|
||||
id = ANY(@identity_ids::text[])
|
||||
`
|
||||
|
||||
args := pgx.StrictNamedArgs{"identity_ids": identityIDs}
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot query identities: %w", err)
|
||||
}
|
||||
|
||||
identities, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[Identity])
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot collect identities: %w", err)
|
||||
}
|
||||
|
||||
*i = identities
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -301,3 +301,47 @@ WHERE
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *PersonalAPIKeys) LoadByIDs(
|
||||
ctx context.Context,
|
||||
conn pg.Querier,
|
||||
apiKeyIDs []gid.GID,
|
||||
) error {
|
||||
if len(apiKeyIDs) == 0 {
|
||||
*a = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
q := `
|
||||
SELECT
|
||||
id,
|
||||
identity_id,
|
||||
name,
|
||||
expires_at,
|
||||
expire_reason,
|
||||
last_used_at,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM
|
||||
iam_personal_api_keys
|
||||
WHERE
|
||||
id = ANY(@api_key_ids::text[])
|
||||
`
|
||||
|
||||
args := pgx.StrictNamedArgs{"api_key_ids": apiKeyIDs}
|
||||
|
||||
rows, err := conn.Query(ctx, q, args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot query personal api keys: %w", err)
|
||||
}
|
||||
|
||||
apiKeys, err := pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[PersonalAPIKey])
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot collect personal api keys: %w", err)
|
||||
}
|
||||
|
||||
*a = apiKeys
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user