main
go 173 lines 3.41 KB
Raw
1 package policy
2
3 import (
4 "fmt"
5 "maps"
6
7 "github.com/gosuda/portal-tunnel/v2/utils"
8 )
9
10 type Mode string
11
12 const (
13 ModeAuto Mode = "auto"
14 ModeManual Mode = "manual"
15 )
16
17 type Approver struct {
18 state *utils.Snapshot[approvalState]
19 }
20
21 type approvalState struct {
22 approvedKeys map[string]struct{}
23 deniedKeys map[string]struct{}
24 approvalMode Mode
25 }
26
27 func newApprovalState() approvalState {
28 return approvalState{
29 approvalMode: ModeAuto,
30 approvedKeys: make(map[string]struct{}),
31 deniedKeys: make(map[string]struct{}),
32 }
33 }
34
35 func (state approvalState) snapshot() approvalState {
36 state.approvedKeys = maps.Clone(state.approvedKeys)
37 state.deniedKeys = maps.Clone(state.deniedKeys)
38 return state
39 }
40
41 func NewApprover() *Approver {
42 return &Approver{
43 state: utils.NewSnapshot(newApprovalState(), approvalState.snapshot),
44 }
45 }
46
47 func (a *Approver) current() approvalState {
48 if a == nil || a.state == nil {
49 return newApprovalState()
50 }
51 return a.state.Load()
52 }
53
54 func (a *Approver) Mode() Mode {
55 return a.current().approvalMode
56 }
57
58 func (a *Approver) SetMode(mode Mode) error {
59 if mode != ModeAuto && mode != ModeManual {
60 return fmt.Errorf("invalid approval mode: %q", mode)
61 }
62 if a == nil || a.state == nil {
63 return nil
64 }
65 a.state.UpdateCopy(func(state *approvalState) {
66 state.approvalMode = mode
67 })
68 return nil
69 }
70
71 func (a *Approver) IsApproved(key string) bool {
72 _, ok := a.current().approvedKeys[key]
73 return ok
74 }
75
76 func (a *Approver) Approve(key string) {
77 if a == nil || a.state == nil {
78 return
79 }
80 a.state.UpdateCopy(func(state *approvalState) {
81 if state.approvedKeys == nil {
82 state.approvedKeys = make(map[string]struct{})
83 }
84 state.approvedKeys[key] = struct{}{}
85 delete(state.deniedKeys, key)
86 })
87 }
88
89 func (a *Approver) Revoke(key string) {
90 if a == nil || a.state == nil {
91 return
92 }
93 a.state.UpdateCopy(func(state *approvalState) {
94 delete(state.approvedKeys, key)
95 })
96 }
97
98 func (a *Approver) ApprovedKeys() []string {
99 approvedKeys := a.current().approvedKeys
100 out := make([]string, 0, len(approvedKeys))
101 for key := range approvedKeys {
102 out = append(out, key)
103 }
104 return out
105 }
106
107 func (a *Approver) IsDenied(key string) bool {
108 _, ok := a.current().deniedKeys[key]
109 return ok
110 }
111
112 func (a *Approver) Deny(key string) {
113 if a == nil || a.state == nil {
114 return
115 }
116 a.state.UpdateCopy(func(state *approvalState) {
117 if state.deniedKeys == nil {
118 state.deniedKeys = make(map[string]struct{})
119 }
120 state.deniedKeys[key] = struct{}{}
121 delete(state.approvedKeys, key)
122 })
123 }
124
125 func (a *Approver) Undeny(key string) {
126 if a == nil || a.state == nil {
127 return
128 }
129 a.state.UpdateCopy(func(state *approvalState) {
130 delete(state.deniedKeys, key)
131 })
132 }
133
134 func (a *Approver) DeniedKeys() []string {
135 deniedKeys := a.current().deniedKeys
136 out := make([]string, 0, len(deniedKeys))
137 for key := range deniedKeys {
138 out = append(out, key)
139 }
140 return out
141 }
142
143 func (a *Approver) SetDecisions(approvedKeys, deniedKeys []string) {
144 if a == nil {
145 return
146 }
147
148 approved := make(map[string]struct{}, len(approvedKeys))
149 for _, key := range approvedKeys {
150 if key == "" {
151 continue
152 }
153 approved[key] = struct{}{}
154 }
155
156 denied := make(map[string]struct{}, len(deniedKeys))
157 for _, key := range deniedKeys {
158 if key == "" {
159 continue
160 }
161 delete(approved, key)
162 denied[key] = struct{}{}
163 }
164
165 if a.state == nil {
166 return
167 }
168 a.state.Update(func(state approvalState) approvalState {
169 state.approvedKeys = approved
170 state.deniedKeys = denied
171 return state
172 })
173 }