tangled
alpha
login
or
join now
bnewbold.net
/
cobalt
13
fork
atom
go scratch code for atproto
13
fork
atom
overview
issues
pulls
pipelines
split out some permission parsing code
bnewbold.net
3 months ago
37226a95
d2c62f8d
+104
-75
2 changed files
expand all
collapse all
unified
split
permissions
permission.go
util.go
+9
-75
permissions/permission.go
···
4
4
"errors"
5
5
"fmt"
6
6
"net/url"
7
7
-
"strings"
8
8
-
"unicode"
9
7
10
8
"github.com/bluesky-social/indigo/atproto/syntax"
11
9
)
···
16
14
ErrUnknownResource = errors.New("unknown permission resource")
17
15
)
18
16
19
19
-
type GenericPermission struct {
20
20
-
Resource string `json:"resource"`
21
21
-
Positional string `json:"positional"`
22
22
-
Params url.Values `json:"params"`
23
23
-
}
24
24
-
17
17
+
// Parsed components of an AT permission, as currently specified.
18
18
+
//
19
19
+
// This type is somewhat redundant with the "SchemaPermission" type in the indigo lexicon package, but it can represent all possible permissions, not just those found in permission sets.
25
20
type Permission struct {
26
21
Type string `json:"type,omitempty"`
27
22
Resource string `json:"resource"`
···
37
32
NSID string `json:"nsid,omitempty"`
38
33
}
39
34
35
35
+
// Renders a permission as a permission scope string.
36
36
+
//
37
37
+
// If the permission contains information which only makes sense in the context of a permission-set (eg, the inheritAud flag), it will be silently dropped.
40
38
func (p *Permission) ScopeString() string {
41
39
42
40
positional := ""
···
99
97
return scope
100
98
}
101
99
102
102
-
func ParseGenericScope(scope string) (*GenericPermission, error) {
103
103
-
104
104
-
if !isASCII(scope) {
105
105
-
return nil, ErrInvalidPermissionSyntax
106
106
-
}
107
107
-
108
108
-
front, query, _ := strings.Cut(scope, "?")
109
109
-
resource, positional, _ := strings.Cut(front, ":")
110
110
-
111
111
-
// XXX: more charset restrictions
112
112
-
113
113
-
params, err := url.ParseQuery(query)
114
114
-
if err != nil {
115
115
-
return nil, fmt.Errorf("%w: %w", ErrInvalidPermissionSyntax, err)
116
116
-
}
117
117
-
118
118
-
p := GenericPermission{
119
119
-
Resource: resource,
120
120
-
Positional: positional,
121
121
-
Params: params,
122
122
-
}
123
123
-
return &p, nil
124
124
-
}
125
125
-
126
126
-
// TODO: improve this function, add tests
127
127
-
func validBlobAccept(accept string) bool {
128
128
-
if accept == "*/*" {
129
129
-
return true
130
130
-
}
131
131
-
parts := strings.SplitN(accept, "/", 3)
132
132
-
if len(parts) != 2 {
133
133
-
return false
134
134
-
}
135
135
-
if parts[0] == "*" {
136
136
-
return false
137
137
-
}
138
138
-
if parts[1] == "**" {
139
139
-
return false
140
140
-
}
141
141
-
return true
142
142
-
}
143
143
-
144
144
-
// TODO: improve this function, add tests
145
145
-
func validServiceRef(accept string) bool {
146
146
-
parts := strings.SplitN(accept, "#", 3)
147
147
-
if len(parts) != 2 {
148
148
-
return false
149
149
-
}
150
150
-
_, err := syntax.ParseDID(parts[0])
151
151
-
if err != nil {
152
152
-
return false
153
153
-
}
154
154
-
if len(parts[1]) == 0 {
155
155
-
return false
156
156
-
}
157
157
-
return true
158
158
-
}
159
159
-
100
100
+
// Parses a permission scope string (as would be found as a component of an OAuth scope string) into a [Permission].
101
101
+
//
102
102
+
// This function is strict: it is case sensitive, verifies field syntax, and will throw an error on unknown parameters/fields. Note that calling code is usually supposed to simply skip any permission which cause such errors, not reject entire requests.
160
103
func ParsePermissionString(scope string) (*Permission, error) {
161
104
g, err := ParseGenericScope(scope)
162
105
if err != nil {
···
343
286
}
344
287
return &p, nil
345
288
}
346
346
-
347
347
-
func isASCII(s string) bool {
348
348
-
for i := 0; i < len(s); i++ {
349
349
-
if s[i] > unicode.MaxASCII {
350
350
-
return false
351
351
-
}
352
352
-
}
353
353
-
return true
354
354
-
}
+95
permissions/util.go
···
1
1
+
package permissions
2
2
+
3
3
+
import (
4
4
+
"fmt"
5
5
+
"net/url"
6
6
+
"strings"
7
7
+
"unicode"
8
8
+
9
9
+
"github.com/bluesky-social/indigo/atproto/syntax"
10
10
+
)
11
11
+
12
12
+
// Parsed components of a generic AT scope string. This is for internal or low-level use; most code should use [ParsePermissionString] instead.
13
13
+
type GenericPermission struct {
14
14
+
Resource string `json:"resource"`
15
15
+
Positional string `json:"positional"`
16
16
+
Params url.Values `json:"params"`
17
17
+
}
18
18
+
19
19
+
func ParseGenericScope(scope string) (*GenericPermission, error) {
20
20
+
21
21
+
if !isASCII(scope) {
22
22
+
return nil, ErrInvalidPermissionSyntax
23
23
+
}
24
24
+
25
25
+
front, query, _ := strings.Cut(scope, "?")
26
26
+
resource, positional, _ := strings.Cut(front, ":")
27
27
+
28
28
+
// XXX: more charset restrictions
29
29
+
30
30
+
params, err := url.ParseQuery(query)
31
31
+
if err != nil {
32
32
+
return nil, fmt.Errorf("%w: %w", ErrInvalidPermissionSyntax, err)
33
33
+
}
34
34
+
35
35
+
p := GenericPermission{
36
36
+
Resource: resource,
37
37
+
Positional: positional,
38
38
+
Params: params,
39
39
+
}
40
40
+
return &p, nil
41
41
+
}
42
42
+
43
43
+
func (p *GenericPermission) ScopeString() string {
44
44
+
scope := p.Resource
45
45
+
if p.Positional != "" {
46
46
+
scope = scope + ":" + p.Positional
47
47
+
}
48
48
+
if len(p.Params) > 0 {
49
49
+
scope = scope + "?" + p.Params.Encode()
50
50
+
}
51
51
+
return scope
52
52
+
}
53
53
+
54
54
+
// TODO: replace with helper in syntax pkg
55
55
+
func validBlobAccept(accept string) bool {
56
56
+
if accept == "*/*" {
57
57
+
return true
58
58
+
}
59
59
+
parts := strings.SplitN(accept, "/", 3)
60
60
+
if len(parts) != 2 {
61
61
+
return false
62
62
+
}
63
63
+
if parts[0] == "*" {
64
64
+
return false
65
65
+
}
66
66
+
if parts[1] == "**" {
67
67
+
return false
68
68
+
}
69
69
+
return true
70
70
+
}
71
71
+
72
72
+
// TODO: replace with helper in syntax pkg
73
73
+
func validServiceRef(accept string) bool {
74
74
+
parts := strings.SplitN(accept, "#", 3)
75
75
+
if len(parts) != 2 {
76
76
+
return false
77
77
+
}
78
78
+
_, err := syntax.ParseDID(parts[0])
79
79
+
if err != nil {
80
80
+
return false
81
81
+
}
82
82
+
if len(parts[1]) == 0 {
83
83
+
return false
84
84
+
}
85
85
+
return true
86
86
+
}
87
87
+
88
88
+
func isASCII(s string) bool {
89
89
+
for i := 0; i < len(s); i++ {
90
90
+
if s[i] > unicode.MaxASCII {
91
91
+
return false
92
92
+
}
93
93
+
}
94
94
+
return true
95
95
+
}