mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-22 23:21:08 +02:00
* audit: remove 'op' from error messages and do some clean up * Allow early error checking to be concerned with vault/Core vs. audit
168 lines
3.9 KiB
Go
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
|
|
}
|