Go · 1982 bytes Raw Blame History
1 // SPDX-License-Identifier: AGPL-3.0-or-later
2
3 package pat
4
5 // Scope is a typed scope constant. The set is intentionally coarse —
6 // matches the GitHub classic-PAT model. Fine-grained per-repo scoping
7 // is a post-MVP project.
8 type Scope string
9
10 const (
11 ScopeRepoRead Scope = "repo:read"
12 ScopeRepoWrite Scope = "repo:write"
13 ScopeUserRead Scope = "user:read"
14 ScopeUserWrite Scope = "user:write"
15 ScopeAdminRead Scope = "admin:read"
16 )
17
18 // AllScopes is the canonical ordered list — drives the create-form UI
19 // and the validation list in ValidScope.
20 var AllScopes = []Scope{
21 ScopeRepoRead,
22 ScopeRepoWrite,
23 ScopeUserRead,
24 ScopeUserWrite,
25 ScopeAdminRead,
26 }
27
28 // ValidScope reports whether s is a known scope name.
29 func ValidScope(s string) bool {
30 for _, sc := range AllScopes {
31 if string(sc) == s {
32 return true
33 }
34 }
35 return false
36 }
37
38 // HasScope reports whether held grants required. Operations that don't
39 // require a scope (browser-session callers) skip this check entirely.
40 //
41 // repo:write implicitly grants repo:read; user:write implicitly grants
42 // user:read. We expand the implication here so callers don't have to.
43 func HasScope(held []string, required Scope) bool {
44 want := string(required)
45 for _, h := range held {
46 if h == want {
47 return true
48 }
49 if required == ScopeRepoRead && h == string(ScopeRepoWrite) {
50 return true
51 }
52 if required == ScopeUserRead && h == string(ScopeUserWrite) {
53 return true
54 }
55 }
56 return false
57 }
58
59 // NormalizeScopes filters input to known scopes, deduplicates, and
60 // returns them in AllScopes order. Used by the create handler to
61 // canonicalize whatever the form submitted before it lands in the DB.
62 func NormalizeScopes(in []string) []string {
63 seen := map[string]struct{}{}
64 for _, s := range in {
65 if ValidScope(s) {
66 seen[s] = struct{}{}
67 }
68 }
69 out := make([]string, 0, len(seen))
70 for _, sc := range AllScopes {
71 if _, ok := seen[string(sc)]; ok {
72 out = append(out, string(sc))
73 }
74 }
75 return out
76 }
77