Raw
 1package auditcatalog
 2
 3import (
 4	"errors"
 5	"fmt"
 6	"slices"
 7	"strings"
 8)
 9
10type Cause uint8
11
12const (
13	Request Cause = iota + 1
14	System
15	Either
16)
17
18type Actor string
19
20const (
21	ActorAdmin  Actor = "admin"
22	ActorUser   Actor = "user"
23	ActorSystem Actor = "system"
24)
25
26type Subject string
27
28const (
29	SubjectUser       Subject = "user"
30	SubjectGroup      Subject = "group"
31	SubjectGroupName  Subject = "group-name"
32	SubjectInvitation Subject = "invitation"
33	SubjectSession    Subject = "session"
34	SubjectLiteral    Subject = "literal"
35)
36
37const SentencePrefix = "audit.action."
38
39type Entry struct {
40	Name     string
41	Actors   []Actor
42	Subjects []Subject
43	Detail   []string
44	Cause    Cause
45	Sentence string
46}
47
48var registry = map[string]Entry{}
49
50func Register(entries ...Entry) bool {
51	fresh := map[string]bool{}
52	for _, e := range entries {
53		if _, ok := registry[e.Name]; ok || fresh[e.Name] {
54			panic(fmt.Sprintf("audit action %q registered twice", e.Name))
55		}
56		if err := complete(e); err != nil {
57			panic(fmt.Sprintf("audit action %q: %v", e.Name, err))
58		}
59		fresh[e.Name] = true
60	}
61	for _, e := range entries {
62		registry[e.Name] = e
63	}
64	return true
65}
66
67func complete(e Entry) error {
68	switch {
69	case e.Name == "":
70		return errors.New("no name")
71	case len(e.Actors) == 0:
72		return errors.New("no actor kind")
73	case len(e.Subjects) == 0:
74		return errors.New("no subject kind")
75	case e.Cause < Request || e.Cause > Either:
76		return errors.New("no cause")
77	case e.Sentence != SentencePrefix+e.Name:
78		return fmt.Errorf("sentence key %q is not %q", e.Sentence, SentencePrefix+e.Name)
79	}
80	return nil
81}
82
83func Lookup(name string) (Entry, bool) {
84	e, ok := registry[name]
85	return e, ok
86}
87
88func Entries() []Entry {
89	out := make([]Entry, 0, len(registry))
90	for _, e := range registry {
91		out = append(out, e)
92	}
93	slices.SortFunc(out, func(a, b Entry) int { return strings.Compare(a.Name, b.Name) })
94	return out
95}