-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Expand file tree
/
Copy pathtypes.go
More file actions
371 lines (321 loc) · 9.78 KB
/
types.go
File metadata and controls
371 lines (321 loc) · 9.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
package database
import (
"database/sql/driver"
"encoding/json"
"fmt"
"net"
"strings"
"time"
"github.com/google/uuid"
"github.com/lib/pq"
"github.com/sqlc-dev/pqtype"
"golang.org/x/xerrors"
"github.com/coder/coder/v2/coderd/rbac"
"github.com/coder/coder/v2/coderd/rbac/policy"
)
// AuditOAuthConvertState is never stored in the database. It is stored in a cookie
// clientside as a JWT. This type is provided for audit logging purposes.
type AuditOAuthConvertState struct {
CreatedAt time.Time `db:"created_at" json:"created_at"`
// The time at which the state string expires, a merge request times out if the user does not perform it quick enough.
ExpiresAt time.Time `db:"expires_at" json:"expires_at"`
FromLoginType LoginType `db:"from_login_type" json:"from_login_type"`
// The login type the user is converting to. Should be github or oidc.
ToLoginType LoginType `db:"to_login_type" json:"to_login_type"`
UserID uuid.UUID `db:"user_id" json:"user_id"`
}
type HealthSettings struct {
ID uuid.UUID `db:"id" json:"id"`
DismissedHealthchecks []string `db:"dismissed_healthchecks" json:"dismissed_healthchecks"`
}
type NotificationsSettings struct {
ID uuid.UUID `db:"id" json:"id"`
NotifierPaused bool `db:"notifier_paused" json:"notifier_paused"`
}
type PrebuildsSettings struct {
ID uuid.UUID `db:"id" json:"id"`
ReconciliationPaused bool `db:"reconciliation_paused" json:"reconciliation_paused"`
}
type Actions []policy.Action
func (a *Actions) Scan(src interface{}) error {
switch v := src.(type) {
case string:
return json.Unmarshal([]byte(v), &a)
case []byte:
return json.Unmarshal(v, &a)
}
return xerrors.Errorf("unexpected type %T", src)
}
func (a *Actions) Value() (driver.Value, error) {
return json.Marshal(a)
}
// TemplateACL is a map of ids to permissions.
type TemplateACL map[string][]policy.Action
func (t *TemplateACL) Scan(src interface{}) error {
switch v := src.(type) {
case string:
return json.Unmarshal([]byte(v), &t)
case []byte:
return json.Unmarshal(v, &t)
case json.RawMessage:
return json.Unmarshal(v, &t)
}
return xerrors.Errorf("unexpected type %T", src)
}
func (t TemplateACL) Value() (driver.Value, error) {
return json.Marshal(t)
}
type WorkspaceACL map[string]WorkspaceACLEntry
func (t *WorkspaceACL) Scan(src interface{}) error {
switch v := src.(type) {
case string:
return json.Unmarshal([]byte(v), &t)
case []byte:
return json.Unmarshal(v, &t)
case json.RawMessage:
return json.Unmarshal(v, &t)
}
return xerrors.Errorf("unexpected type %T", src)
}
//nolint:revive
func (w WorkspaceACL) RBACACL() map[string][]policy.Action {
// Convert WorkspaceACL to a map of string to []policy.Action.
// This is used for RBAC checks.
rbacACL := make(map[string][]policy.Action, len(w))
for id, entry := range w {
rbacACL[id] = entry.Permissions
}
return rbacACL
}
func (t WorkspaceACL) Value() (driver.Value, error) {
return json.Marshal(t)
}
type WorkspaceACLEntry struct {
Permissions []policy.Action `json:"permissions"`
}
// WorkspaceACLDisplayInfo supplements workspace ACLs with the actors'
// display info. Key is string rather than uuid.UUID as this aligns
// with how RBAC represents actor IDs.
type WorkspaceACLDisplayInfo map[string]struct {
Name string `json:"name"`
AvatarURL string `json:"avatar_url"`
}
// WorkspaceACLDisplayInfo is only used to read from the DB.
func (w *WorkspaceACLDisplayInfo) Scan(src interface{}) error {
switch v := src.(type) {
case string:
return json.Unmarshal([]byte(v), w)
case []byte:
return json.Unmarshal(v, w)
case json.RawMessage:
return json.Unmarshal(v, w)
}
return xerrors.Errorf("unexpected type %T", src)
}
type ExternalAuthProvider struct {
ID string `json:"id"`
Optional bool `json:"optional,omitempty"`
}
type StringMap map[string]string
func (m *StringMap) Scan(src interface{}) error {
if src == nil {
return nil
}
switch src := src.(type) {
case []byte:
err := json.Unmarshal(src, m)
if err != nil {
return err
}
default:
return xerrors.Errorf("unsupported Scan, storing driver.Value type %T into type %T", src, m)
}
return nil
}
func (m StringMap) Value() (driver.Value, error) {
return json.Marshal(m)
}
type StringMapOfInt map[string]int64
func (m *StringMapOfInt) Scan(src interface{}) error {
if src == nil {
return nil
}
switch src := src.(type) {
case []byte:
err := json.Unmarshal(src, m)
if err != nil {
return err
}
default:
return xerrors.Errorf("unsupported Scan, storing driver.Value type %T into type %T", src, m)
}
return nil
}
func (m StringMapOfInt) Value() (driver.Value, error) {
return json.Marshal(m)
}
type CustomRolePermissions []CustomRolePermission
func (s *APIKeyScopes) Scan(src any) error {
var arr []string
if err := pq.Array(&arr).Scan(src); err != nil {
return err
}
out := make(APIKeyScopes, len(arr))
for i, v := range arr {
out[i] = APIKeyScope(v)
}
*s = out
return nil
}
func (s APIKeyScopes) Value() (driver.Value, error) {
arr := make([]string, len(s))
for i, v := range s {
arr[i] = string(v)
}
return pq.Array(arr).Value()
}
func (a *CustomRolePermissions) Scan(src interface{}) error {
switch v := src.(type) {
case string:
return json.Unmarshal([]byte(v), &a)
case []byte:
return json.Unmarshal(v, &a)
}
return xerrors.Errorf("unexpected type %T", src)
}
func (a CustomRolePermissions) Value() (driver.Value, error) {
return json.Marshal(a)
}
type CustomRolePermission struct {
Negate bool `json:"negate"`
ResourceType string `json:"resource_type"`
Action policy.Action `json:"action"`
}
func (a CustomRolePermission) String() string {
str := a.ResourceType + "." + string(a.Action)
if a.Negate {
return "-" + str
}
return str
}
// NameOrganizationPair is used as a lookup tuple for custom role rows.
type NameOrganizationPair struct {
Name string `db:"name" json:"name"`
// OrganizationID if unset will assume a null column value
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
}
func (*NameOrganizationPair) Scan(_ interface{}) error {
return xerrors.Errorf("this should never happen, type 'NameOrganizationPair' should only be used as a parameter")
}
// Value returns the tuple **literal**
// To get the literal value to return, you can use the expression syntax in a psql
// shell.
//
// SELECT ('customrole'::text,'ece79dac-926e-44ca-9790-2ff7c5eb6e0c'::uuid);
// To see 'null' option. Using the nil uuid as null to avoid empty string literals for null.
// SELECT ('customrole',00000000-0000-0000-0000-000000000000);
//
// This value is usually used as an array, NameOrganizationPair[]. You can see
// what that literal is as well, with proper quoting.
//
// SELECT ARRAY[('customrole'::text,'ece79dac-926e-44ca-9790-2ff7c5eb6e0c'::uuid)];
func (a NameOrganizationPair) Value() (driver.Value, error) {
return fmt.Sprintf(`(%s,%s)`, a.Name, a.OrganizationID.String()), nil
}
// AgentIDNamePair is used as a result tuple for workspace and agent rows.
type AgentIDNamePair struct {
ID uuid.UUID `db:"id" json:"id"`
Name string `db:"name" json:"name"`
}
func (p *AgentIDNamePair) Scan(src interface{}) error {
var v string
switch a := src.(type) {
case []byte:
v = string(a)
case string:
v = a
default:
return xerrors.Errorf("unexpected type %T", src)
}
parts := strings.Split(strings.Trim(v, "()"), ",")
if len(parts) != 2 {
return xerrors.New("invalid format for AgentIDNamePair")
}
id, err := uuid.Parse(strings.TrimSpace(parts[0]))
if err != nil {
return err
}
p.ID, p.Name = id, strings.TrimSpace(parts[1])
return nil
}
func (p AgentIDNamePair) Value() (driver.Value, error) {
return fmt.Sprintf(`(%s,%s)`, p.ID.String(), p.Name), nil
}
// UserLinkClaims is the returned IDP claims for a given user link.
// These claims are fetched at login time. These are the claims that were
// used for IDP sync.
type UserLinkClaims struct {
IDTokenClaims map[string]interface{} `json:"id_token_claims"`
UserInfoClaims map[string]interface{} `json:"user_info_claims"`
// MergeClaims are computed in Golang. It is the result of merging
// the IDTokenClaims and UserInfoClaims. UserInfoClaims take precedence.
MergedClaims map[string]interface{} `json:"merged_claims"`
}
func (a *UserLinkClaims) Scan(src interface{}) error {
switch v := src.(type) {
case string:
return json.Unmarshal([]byte(v), &a)
case []byte:
return json.Unmarshal(v, &a)
}
return xerrors.Errorf("unexpected type %T", src)
}
func (a UserLinkClaims) Value() (driver.Value, error) {
return json.Marshal(a)
}
func ParseIP(ipStr string) pqtype.Inet {
ip := net.ParseIP(ipStr)
ipNet := net.IPNet{}
if ip != nil {
ipNet = net.IPNet{
IP: ip,
Mask: net.CIDRMask(len(ip)*8, len(ip)*8),
}
}
return pqtype.Inet{
IPNet: ipNet,
Valid: ip != nil,
}
}
// AllowList is a typed wrapper around a list of AllowListTarget entries.
// It implements sql.Scanner and driver.Valuer so it can be stored in and
// loaded from a Postgres text[] column that stores each entry in the
// canonical form "type:id".
type AllowList []rbac.AllowListElement
// Scan implements sql.Scanner. It supports inputs that pq.Array can decode
// into []string, and then converts each element to an AllowListTarget.
func (a *AllowList) Scan(src any) error {
var raw []string
if err := pq.Array(&raw).Scan(src); err != nil {
return err
}
out := make([]rbac.AllowListElement, len(raw))
for i, s := range raw {
e, err := rbac.ParseAllowListEntry(s)
if err != nil {
return err
}
out[i] = e
}
*a = out
return nil
}
// Value implements driver.Valuer by converting the list to []string using the
// canonical "type:id" form and delegating to pq.Array for encoding.
func (a AllowList) Value() (driver.Value, error) {
raw := make([]string, len(a))
for i, t := range a {
raw[i] = t.String()
}
return pq.Array(raw).Value()
}