// SPDX-License-Identifier: AGPL-3.0-only package identity import ( "context" "errors" "time" ) type EvidencePruneReport struct { AuthenticationEvents int64 `json:"authentication_events"` OrganizationEvents int64 `json:"organization_events"` ExpiredSessions int64 `json:"expired_sessions"` ExpiredInvitations int64 `json:"expired_invitations"` ExpiredBreakGlass int64 `json:"expired_break_glass"` } // PruneEvidence applies the server default to platform authentication audit // events and each organization's approved retention override to its visible // access audit. Operational credentials are removed only after expiration. func (services *Services) PruneEvidence(ctx context.Context, defaultDays int, now time.Time) (EvidencePruneReport, error) { if services == nil || services.control == nil || defaultDays < 1 || defaultDays > 3650 || now.IsZero() { return EvidencePruneReport{}, errors.New("identity: evidence retention input is invalid") } tx, err := services.control.BeginTx(ctx, nil) if err != nil { return EvidencePruneReport{}, errors.New("identity: begin evidence retention") } defer tx.Rollback() report := EvidencePruneReport{} cutoff := now.UTC().Add(-time.Duration(defaultDays) * 24 * time.Hour).Unix() result, err := tx.ExecContext(ctx, `DELETE FROM gwf_audit_events WHERE created_at 3650 { _ = rows.Close() return report, errors.New("identity: organization audit retention is invalid") } organizations = append(organizations, organization) } if err = rows.Close(); err != nil { return report, errors.New("identity: close organization audit retention") } for _, organization := range organizations { organizationCutoff := now.UTC().Add(-time.Duration(organization.days) * 24 * time.Hour).Unix() result, err = tx.ExecContext(ctx, `DELETE FROM gwf_access_audit_events WHERE organization_id=? AND created_at