package storage import ( "context" "database/sql" "time" ) func (s *Store) inAuditedTx(ctx context.Context, operation string, audits []AppendAuditInput, write func(tx *sql.Tx) error) error { if err := s.checkOpen(operation); err != nil { return err } tx, err := s.sql.BeginTx(ctx, nil) if err != nil { return classify("begin "+operation, err) } defer tx.Rollback() if err := write(tx); err != nil { return err } at := canonicalTime(s.now()) for _, audit := range audits { if _, err := appendAudit(ctx, tx, operation+" audit", audit, at); err != nil { return err } } return classify("commit "+operation, tx.Commit()) } func (s *Store) CreateForwardSessionAudited(ctx context.Context, input CreateForwardSessionInput, audits ...AppendAuditInput) (ForwardSession, error) { var row ForwardSession err := s.inAuditedTx(ctx, "create forward session", audits, func(tx *sql.Tx) error { var err error row, err = createForwardSession(ctx, tx, input) return err }) if err != nil { return ForwardSession{}, err } return row, nil } func (s *Store) MarkReauthenticatedAudited(ctx context.Context, id string, at time.Time, audit AppendAuditInput) error { return s.inAuditedTx(ctx, "mark forward session reauthenticated", []AppendAuditInput{audit}, func(tx *sql.Tx) error { return markReauthenticated(ctx, tx, id, at) }) } func (s *Store) RecordLoginAudited(ctx context.Context, userID string, at time.Time, audit AppendAuditInput) error { const operation = "record login" return s.inAuditedTx(ctx, operation, []AppendAuditInput{audit}, func(tx *sql.Tx) error { result, err := tx.ExecContext(ctx, `UPDATE users SET last_login_at = ? WHERE id = ?`, timeToInt(at), userID) if err != nil { return classify(operation, err) } return requireChanged(operation, result) }) }