vault/audit/event.go
Peter Wilson 3dc16db87e
VAULT-24798: audit - improve error messages (#26312)
* audit: remove 'op' from error messages and do some clean up

* Allow early error checking to be concerned with vault/Core vs. audit
2024-04-11 09:09:32 +01:00

168 lines
3.9 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package audit
import (
"fmt"
"strings"
"time"
"github.com/hashicorp/vault/internal/observability/event"
"github.com/hashicorp/vault/sdk/logical"
)
// version defines the version of audit events.
const version = "v0.1"
// Audit subtypes.
const (
RequestType subtype = "AuditRequest"
ResponseType subtype = "AuditResponse"
)
// Audit formats.
const (
JSONFormat format = "json"
JSONxFormat format = "jsonx"
)
// Check AuditEvent implements the timeProvider at compile time.
var _ timeProvider = (*AuditEvent)(nil)
// AuditEvent is the audit event.
type AuditEvent struct {
ID string `json:"id"`
Version string `json:"version"`
Subtype subtype `json:"subtype"` // the subtype of the audit event.
Timestamp time.Time `json:"timestamp"`
Data *logical.LogInput `json:"data"`
}
// format defines types of format audit events support.
type format string
// subtype defines the type of audit event.
type subtype string
// NewEvent should be used to create an audit event. The subtype field is needed
// for audit events. It will generate an ID if no ID is supplied. Supported
// options: WithID, WithNow.
func NewEvent(s subtype, opt ...Option) (*AuditEvent, error) {
// Get the default options
opts, err := getOpts(opt...)
if err != nil {
return nil, err
}
if opts.withID == "" {
var err error
opts.withID, err = event.NewID(string(event.AuditType))
if err != nil {
return nil, fmt.Errorf("error creating ID for event: %w", err)
}
}
audit := &AuditEvent{
ID: opts.withID,
Timestamp: opts.withNow,
Version: version,
Subtype: s,
}
if err := audit.validate(); err != nil {
return nil, err
}
return audit, nil
}
// validate attempts to ensure the audit event in its present state is valid.
func (a *AuditEvent) validate() error {
if a == nil {
return fmt.Errorf("event is nil: %w", ErrInvalidParameter)
}
if a.ID == "" {
return fmt.Errorf("missing ID: %w", ErrInvalidParameter)
}
if a.Version != version {
return fmt.Errorf("event version unsupported: %w", ErrInvalidParameter)
}
if a.Timestamp.IsZero() {
return fmt.Errorf("event timestamp cannot be the zero time instant: %w", ErrInvalidParameter)
}
err := a.Subtype.validate()
if err != nil {
return err
}
return nil
}
// validate ensures that subtype is one of the set of allowed event subtypes.
func (t subtype) validate() error {
switch t {
case RequestType, ResponseType:
return nil
default:
return fmt.Errorf("invalid event subtype %q: %w", t, ErrInvalidParameter)
}
}
// validate ensures that format is one of the set of allowed event formats.
func (f format) validate() error {
switch f {
case JSONFormat, JSONxFormat:
return nil
default:
return fmt.Errorf("invalid format %q: %w", f, ErrInvalidParameter)
}
}
// String returns the string version of a format.
func (f format) String() string {
return string(f)
}
// MetricTag returns a tag corresponding to this subtype to include in metrics.
// If a tag cannot be found the value is returned 'as-is' in string format.
func (t subtype) MetricTag() string {
switch t {
case RequestType:
return "log_request"
case ResponseType:
return "log_response"
}
return t.String()
}
// String returns the subtype as a human-readable string.
func (t subtype) String() string {
switch t {
case RequestType:
return "request"
case ResponseType:
return "response"
}
return string(t)
}
// formattedTime returns the UTC time the AuditEvent was created in the RFC3339Nano
// format (which removes trailing zeros from the seconds field).
func (a *AuditEvent) formattedTime() string {
return a.Timestamp.UTC().Format(time.RFC3339Nano)
}
// IsValidFormat provides a means to validate whether the supplied format is valid.
// Examples of valid formats are JSON and JSONx.
func IsValidFormat(v string) bool {
err := format(strings.TrimSpace(strings.ToLower(v))).validate()
return err == nil
}