@atproto/oauth-scopes 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/LICENSE.txt +7 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/did.d.ts +3 -0
- package/dist/lib/did.d.ts.map +1 -0
- package/dist/lib/did.js +6 -0
- package/dist/lib/did.js.map +1 -0
- package/dist/lib/mime.d.ts +7 -0
- package/dist/lib/mime.d.ts.map +1 -0
- package/dist/lib/mime.js +65 -0
- package/dist/lib/mime.js.map +1 -0
- package/dist/lib/nsid.d.ts +3 -0
- package/dist/lib/nsid.d.ts.map +1 -0
- package/dist/lib/nsid.js +9 -0
- package/dist/lib/nsid.js.map +1 -0
- package/dist/lib/util.d.ts +3 -0
- package/dist/lib/util.d.ts.map +1 -0
- package/dist/lib/util.js +24 -0
- package/dist/lib/util.js.map +1 -0
- package/dist/parser.d.ts +31 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +118 -0
- package/dist/parser.js.map +1 -0
- package/dist/permission-set-transition.d.ts +15 -0
- package/dist/permission-set-transition.d.ts.map +1 -0
- package/dist/permission-set-transition.js +52 -0
- package/dist/permission-set-transition.js.map +1 -0
- package/dist/permission-set.d.ts +22 -0
- package/dist/permission-set.d.ts.map +1 -0
- package/dist/permission-set.js +68 -0
- package/dist/permission-set.js.map +1 -0
- package/dist/resources/account-scope.d.ts +35 -0
- package/dist/resources/account-scope.d.ts.map +1 -0
- package/dist/resources/account-scope.js +60 -0
- package/dist/resources/account-scope.js.map +1 -0
- package/dist/resources/blob-scope.d.ts +25 -0
- package/dist/resources/blob-scope.d.ts.map +1 -0
- package/dist/resources/blob-scope.js +74 -0
- package/dist/resources/blob-scope.js.map +1 -0
- package/dist/resources/identity-scope.d.ts +25 -0
- package/dist/resources/identity-scope.d.ts.map +1 -0
- package/dist/resources/identity-scope.js +46 -0
- package/dist/resources/identity-scope.js.map +1 -0
- package/dist/resources/repo-scope.d.ts +37 -0
- package/dist/resources/repo-scope.d.ts.map +1 -0
- package/dist/resources/repo-scope.js +92 -0
- package/dist/resources/repo-scope.js.map +1 -0
- package/dist/resources/rpc-scope.d.ts +31 -0
- package/dist/resources/rpc-scope.d.ts.map +1 -0
- package/dist/resources/rpc-scope.js +74 -0
- package/dist/resources/rpc-scope.js.map +1 -0
- package/dist/scope-missing-error.d.ts +9 -0
- package/dist/scope-missing-error.d.ts.map +1 -0
- package/dist/scope-missing-error.js +39 -0
- package/dist/scope-missing-error.js.map +1 -0
- package/dist/scopes-set.d.ts +21 -0
- package/dist/scopes-set.d.ts.map +1 -0
- package/dist/scopes-set.js +55 -0
- package/dist/scopes-set.js.map +1 -0
- package/dist/syntax.d.ts +76 -0
- package/dist/syntax.d.ts.map +1 -0
- package/dist/syntax.js +249 -0
- package/dist/syntax.js.map +1 -0
- package/dist/utilities.d.ts +17 -0
- package/dist/utilities.d.ts.map +1 -0
- package/dist/utilities.js +108 -0
- package/dist/utilities.js.map +1 -0
- package/jest.config.js +5 -0
- package/package.json +36 -0
- package/src/index.ts +17 -0
- package/src/lib/did.ts +3 -0
- package/src/lib/mime.test.ts +98 -0
- package/src/lib/mime.ts +70 -0
- package/src/lib/nsid.ts +6 -0
- package/src/lib/util.ts +19 -0
- package/src/parser.ts +150 -0
- package/src/permission-set-transition.test.ts +109 -0
- package/src/permission-set-transition.ts +67 -0
- package/src/permission-set.test.ts +225 -0
- package/src/permission-set.ts +78 -0
- package/src/resources/account-scope.test.ts +175 -0
- package/src/resources/account-scope.ts +66 -0
- package/src/resources/blob-scope.test.ts +118 -0
- package/src/resources/blob-scope.ts +86 -0
- package/src/resources/identity-scope.test.ts +80 -0
- package/src/resources/identity-scope.ts +49 -0
- package/src/resources/repo-scope.test.ts +255 -0
- package/src/resources/repo-scope.ts +101 -0
- package/src/resources/rpc-scope.test.ts +280 -0
- package/src/resources/rpc-scope.ts +77 -0
- package/src/scope-missing-error.ts +15 -0
- package/src/scopes-set.test.ts +47 -0
- package/src/scopes-set.ts +60 -0
- package/src/syntax.test.ts +203 -0
- package/src/syntax.ts +325 -0
- package/src/utilities.ts +109 -0
- package/tsconfig.build.json +9 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.json +7 -0
- package/tsconfig.tests.json +7 -0
- package/tsconfig.tests.tsbuildinfo +1 -0
package/src/syntax.ts
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { minIdx, toRecord } from './lib/util'
|
|
2
|
+
|
|
3
|
+
export type NeArray<T> = [T, ...T[]]
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Non-empty readonly array
|
|
7
|
+
*/
|
|
8
|
+
export type NeRoArray<T> = readonly [T, ...T[]]
|
|
9
|
+
|
|
10
|
+
export type ScopeForResource<R extends string> =
|
|
11
|
+
| R
|
|
12
|
+
| `${R}:${string}`
|
|
13
|
+
| `${R}?${string}`
|
|
14
|
+
|
|
15
|
+
export type ResourceSyntaxJson<R extends string = string> = {
|
|
16
|
+
resource: R
|
|
17
|
+
positional?: string
|
|
18
|
+
params?: Record<string, undefined | string | NeRoArray<string>>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Allows to quickly check if a scope is for a specific resource.
|
|
23
|
+
*/
|
|
24
|
+
export function isScopeForResource<R extends string>(
|
|
25
|
+
scope: string,
|
|
26
|
+
resource: R,
|
|
27
|
+
): scope is ScopeForResource<R> {
|
|
28
|
+
if (!scope.startsWith(resource)) return false
|
|
29
|
+
if (scope.length === resource.length) return true
|
|
30
|
+
|
|
31
|
+
const nextCharCode = scope.charCodeAt(resource.length)
|
|
32
|
+
return nextCharCode === 0x3a /* : */ || nextCharCode === 0x3f /* ? */
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Utility class to parse and interpret the resource scope syntax used in
|
|
37
|
+
* atproto oauth scopes.
|
|
38
|
+
* The syntax is defined as follows:
|
|
39
|
+
* ```nbf
|
|
40
|
+
* scope := resource [':' positional] ['?' params]
|
|
41
|
+
* params := param ['&' param]*
|
|
42
|
+
* param := name '=' value
|
|
43
|
+
* ```
|
|
44
|
+
* Where "positional" can be used as short-hand (i.e. not used in combination
|
|
45
|
+
* with) for a specific parameter.
|
|
46
|
+
*/
|
|
47
|
+
export class ResourceSyntax<R extends string = string> {
|
|
48
|
+
constructor(
|
|
49
|
+
public readonly resource: R,
|
|
50
|
+
public readonly positional?: string,
|
|
51
|
+
public readonly params?: Readonly<URLSearchParams>,
|
|
52
|
+
) {}
|
|
53
|
+
|
|
54
|
+
is<T extends string>(resource: T): this is ResourceSyntax<T> {
|
|
55
|
+
return this.resource === (resource as string)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
containsParamsOtherThan(allowedParam: readonly string[]): boolean {
|
|
59
|
+
const { params } = this
|
|
60
|
+
if (params) {
|
|
61
|
+
for (const key of params.keys()) {
|
|
62
|
+
if (!allowedParam.includes(key)) return true
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return false
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Retrieve the value of a parameter that only allows a single value. If the
|
|
71
|
+
* parameter is not found, it will return `undefined`. If the syntax is
|
|
72
|
+
* incorrect (i.e. the parameter has multiple values), it will return `null`.
|
|
73
|
+
*/
|
|
74
|
+
getSingle(name: string, isPositional = false): string | undefined | null {
|
|
75
|
+
const { params } = this
|
|
76
|
+
const values =
|
|
77
|
+
params != null && params.has(name)
|
|
78
|
+
? (params.getAll(name) as [string, ...string[]])
|
|
79
|
+
: undefined
|
|
80
|
+
|
|
81
|
+
if (!values) {
|
|
82
|
+
// No named parameter found, use positional parameter
|
|
83
|
+
if (isPositional) return this.positional
|
|
84
|
+
|
|
85
|
+
return undefined
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (values.length !== 1) {
|
|
89
|
+
// Single value expected
|
|
90
|
+
return null
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (isPositional && this.positional !== undefined) {
|
|
94
|
+
// Positional parameter cannot be used with named parameters
|
|
95
|
+
return null
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return values[0]
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Retrieve the values of a parameter that allows multiple values. If the
|
|
103
|
+
* parameter is not found, it will return `undefined`. If the syntax is
|
|
104
|
+
* incorrect (i.e. there is bot a positional and named parameter), it will
|
|
105
|
+
* return `null`.
|
|
106
|
+
*/
|
|
107
|
+
getMulti(name: string, isPositional?: false): NeRoArray<string> | undefined
|
|
108
|
+
getMulti(
|
|
109
|
+
name: string,
|
|
110
|
+
isPositional: boolean, // Only if this arg is true, will this method return null
|
|
111
|
+
): NeRoArray<string> | null | undefined
|
|
112
|
+
getMulti(
|
|
113
|
+
name: string,
|
|
114
|
+
isPositional = false,
|
|
115
|
+
): NeRoArray<string> | null | undefined {
|
|
116
|
+
const { params } = this
|
|
117
|
+
const values =
|
|
118
|
+
params != null && params.has(name)
|
|
119
|
+
? (params.getAll(name) as [string, ...string[]])
|
|
120
|
+
: undefined
|
|
121
|
+
|
|
122
|
+
if (!values) {
|
|
123
|
+
// No named parameter found, use positional parameter
|
|
124
|
+
|
|
125
|
+
if (isPositional && this.positional !== undefined) {
|
|
126
|
+
return [this.positional]
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return undefined
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (isPositional && this.positional !== undefined) {
|
|
133
|
+
// @NOTE we *could* return [this.positional, ...values] here but the
|
|
134
|
+
// atproto scope syntax forbids use of both positional and named
|
|
135
|
+
// parameters.
|
|
136
|
+
return null
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return values
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
toString(): ScopeForResource<R> {
|
|
143
|
+
return encodeScope(this.resource, this.positional, this.params)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
toJSON(): ResourceSyntaxJson<R> {
|
|
147
|
+
return {
|
|
148
|
+
resource: this.resource,
|
|
149
|
+
positional: this.positional,
|
|
150
|
+
params: this.params?.size ? toRecord(this.params) : undefined,
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
static fromString<R extends string>(
|
|
155
|
+
scope: R | `${R}:${string}` | `${R}?${string}`,
|
|
156
|
+
): ResourceSyntax<R> {
|
|
157
|
+
const paramIdx = scope.indexOf('?')
|
|
158
|
+
const colonIdx = scope.indexOf(':')
|
|
159
|
+
|
|
160
|
+
const resourceEnd = minIdx(paramIdx, colonIdx)
|
|
161
|
+
|
|
162
|
+
const resource = (
|
|
163
|
+
resourceEnd !== -1 ? scope.slice(0, resourceEnd) : scope
|
|
164
|
+
) as R
|
|
165
|
+
|
|
166
|
+
const positional =
|
|
167
|
+
colonIdx !== -1
|
|
168
|
+
? // There is a positional parameter, extract it
|
|
169
|
+
paramIdx === -1
|
|
170
|
+
? decodeURIComponent(scope.slice(colonIdx + 1))
|
|
171
|
+
: colonIdx < paramIdx
|
|
172
|
+
? decodeURIComponent(scope.slice(colonIdx + 1, paramIdx))
|
|
173
|
+
: undefined
|
|
174
|
+
: undefined
|
|
175
|
+
|
|
176
|
+
const params =
|
|
177
|
+
paramIdx !== -1 // There is a query string
|
|
178
|
+
? paramIdx === scope.length - 1
|
|
179
|
+
? undefined // The query string is empty
|
|
180
|
+
: new URLSearchParams(scope.slice(paramIdx + 1))
|
|
181
|
+
: undefined
|
|
182
|
+
|
|
183
|
+
return new ResourceSyntax(resource, positional, params)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Format a scope string for a resource with parameters
|
|
189
|
+
* as a positional parameter, if possible (if it has only one value).
|
|
190
|
+
* @param resource - The resource name (e.g. `rpc`, `repo`, etc.)
|
|
191
|
+
* @param inputParams - The list of parameters.
|
|
192
|
+
* @param positionalName - The name of the parameter that should be used as
|
|
193
|
+
* positional parameter.
|
|
194
|
+
*/
|
|
195
|
+
export function formatScope<R extends string>(
|
|
196
|
+
resource: R,
|
|
197
|
+
inputParams: Iterable<
|
|
198
|
+
[name: string, value: undefined | string | NeRoArray<string>]
|
|
199
|
+
>,
|
|
200
|
+
positionalName?: string,
|
|
201
|
+
): ScopeForResource<R> {
|
|
202
|
+
const queryParams = new URLSearchParams()
|
|
203
|
+
|
|
204
|
+
let positionalValue: string | undefined = undefined
|
|
205
|
+
|
|
206
|
+
for (const [name, value] of inputParams) {
|
|
207
|
+
if (value === undefined) continue
|
|
208
|
+
|
|
209
|
+
const setPositional =
|
|
210
|
+
name === positionalName && positionalValue === undefined
|
|
211
|
+
|
|
212
|
+
if (typeof value === 'string') {
|
|
213
|
+
if (setPositional) {
|
|
214
|
+
positionalValue = value
|
|
215
|
+
} else {
|
|
216
|
+
queryParams.append(name, value)
|
|
217
|
+
}
|
|
218
|
+
} else {
|
|
219
|
+
// value is "readonly [string, ...string[]]"
|
|
220
|
+
if (value.length === 0) {
|
|
221
|
+
// This should never happen (because "value" is supposed to be a
|
|
222
|
+
// non-empty array). Because some scope default to "*" (allow
|
|
223
|
+
// everything) when a parameter is not specified, we'd rather be safe
|
|
224
|
+
// here.
|
|
225
|
+
throw new Error(
|
|
226
|
+
`Invalid scope: parameter "${name}" cannot be an empty array`,
|
|
227
|
+
)
|
|
228
|
+
} else if (setPositional && value.length === 1) {
|
|
229
|
+
positionalValue = value[0]!
|
|
230
|
+
} else {
|
|
231
|
+
for (const v of value) {
|
|
232
|
+
queryParams.append(name, v)
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Fool-proof: If the input iterable defines multiple times the same
|
|
239
|
+
// positional parameter (name), and it ended up being used as both positional
|
|
240
|
+
// and query param, move the positional value to the query params.
|
|
241
|
+
if (positionalValue !== undefined && queryParams.has(positionalName!)) {
|
|
242
|
+
queryParams.append(positionalName!, positionalValue)
|
|
243
|
+
positionalValue = undefined
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return encodeScope(resource, positionalValue, queryParams)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export function encodeScope<R extends string>(
|
|
250
|
+
resource: R,
|
|
251
|
+
positional?: string,
|
|
252
|
+
params?: Readonly<URLSearchParams>,
|
|
253
|
+
): ScopeForResource<R> {
|
|
254
|
+
let scope: string = resource
|
|
255
|
+
|
|
256
|
+
if (positional !== undefined) {
|
|
257
|
+
scope += `:${encodeScopeComponent(positional)}`
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (params?.size) {
|
|
261
|
+
scope += `?${normalizeScopeComponent(params.toString())}`
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return scope as ScopeForResource<R>
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Set of characters that are allowed in scope components without encoding. This
|
|
269
|
+
* is used to normalize scope components.
|
|
270
|
+
*/
|
|
271
|
+
export const ALLOWED_SCOPE_CHARS = new Set(
|
|
272
|
+
// @NOTE This list must not contain "?" or "&" as it would interfere with
|
|
273
|
+
// query string parsing.
|
|
274
|
+
[':', '/', '+', ',', '@', '%'],
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
export function encodeScopeComponent(value: string): string {
|
|
278
|
+
return normalizeScopeComponent(encodeURIComponent(value))
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const NORMALIZABLE_CHARS_MAP = new Map(
|
|
282
|
+
Array.from(
|
|
283
|
+
ALLOWED_SCOPE_CHARS,
|
|
284
|
+
(c) => [encodeURIComponent(c), c] as const,
|
|
285
|
+
).filter(
|
|
286
|
+
([encoded, c]) =>
|
|
287
|
+
// Make sure that any char added to ALLOWED_SCOPE_CHARS that is a char
|
|
288
|
+
// that indeed needs encoding. Also, the normalizeScopeComponent only
|
|
289
|
+
// supports three-character percent-encoded sequences.
|
|
290
|
+
encoded !== c && encoded.length === 3 && encoded.startsWith('%'),
|
|
291
|
+
),
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Assumes a properly url-encoded string.
|
|
296
|
+
*/
|
|
297
|
+
export function normalizeScopeComponent(value: string): string {
|
|
298
|
+
// No need to read the last two characters since percent encoded characters
|
|
299
|
+
// are always three characters long.
|
|
300
|
+
let end = value.length - 2
|
|
301
|
+
|
|
302
|
+
for (let i = 0; i < end; i++) {
|
|
303
|
+
// Check if the character is a percent-encoded character
|
|
304
|
+
if (value.charCodeAt(i) === 0x25 /* % */) {
|
|
305
|
+
// Read the next encoded char. Current version only supports
|
|
306
|
+
// three-character percent-encoded sequences.
|
|
307
|
+
const encodedChar = value.slice(i, i + 3)
|
|
308
|
+
|
|
309
|
+
// Check if the encoded character is in the normalization map
|
|
310
|
+
const normalizedChar = NORMALIZABLE_CHARS_MAP.get(encodedChar)
|
|
311
|
+
if (normalizedChar) {
|
|
312
|
+
// Replace the encoded character with its normalized version
|
|
313
|
+
value = `${value.slice(0, i)}${normalizedChar}${value.slice(i + encodedChar.length)}`
|
|
314
|
+
|
|
315
|
+
// Adjust index to account for the length change
|
|
316
|
+
i += normalizedChar.length - 1
|
|
317
|
+
|
|
318
|
+
// Adjust end index since we replaced encoded char with normalized char
|
|
319
|
+
end -= encodedChar.length - normalizedChar.length
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return value
|
|
325
|
+
}
|
package/src/utilities.ts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { AccountScope, AccountScopeMatch } from './resources/account-scope.js'
|
|
2
|
+
import { BlobScope, BlobScopeMatch } from './resources/blob-scope.js'
|
|
3
|
+
import {
|
|
4
|
+
IdentityScope,
|
|
5
|
+
IdentityScopeMatch,
|
|
6
|
+
} from './resources/identity-scope.js'
|
|
7
|
+
import { RepoScope, RepoScopeMatch } from './resources/repo-scope.js'
|
|
8
|
+
import { RpcScope, RpcScopeMatch } from './resources/rpc-scope.js'
|
|
9
|
+
import { ResourceSyntax, isScopeForResource } from './syntax.js'
|
|
10
|
+
|
|
11
|
+
export type ScopeMatchingOptionsByResource = {
|
|
12
|
+
account: AccountScopeMatch
|
|
13
|
+
identity: IdentityScopeMatch
|
|
14
|
+
repo: RepoScopeMatch
|
|
15
|
+
rpc: RpcScopeMatch
|
|
16
|
+
blob: BlobScopeMatch
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function isValidAtprotoOauthScope(value: string) {
|
|
20
|
+
if (value === 'atproto') return true
|
|
21
|
+
if (value === 'transition:email') return true
|
|
22
|
+
if (value === 'transition:generic') return true
|
|
23
|
+
if (value === 'transition:chat.bsky') return true
|
|
24
|
+
|
|
25
|
+
const syntax = ResourceSyntax.fromString(value)
|
|
26
|
+
if (syntax.resource === 'repo') {
|
|
27
|
+
return RepoScope.fromSyntax(syntax) != null
|
|
28
|
+
} else if (syntax.resource === 'rpc') {
|
|
29
|
+
return RpcScope.fromSyntax(syntax) != null
|
|
30
|
+
} else if (syntax.resource === 'account') {
|
|
31
|
+
return AccountScope.fromSyntax(syntax) != null
|
|
32
|
+
} else if (syntax.resource === 'identity') {
|
|
33
|
+
return IdentityScope.fromSyntax(syntax) != null
|
|
34
|
+
} else if (syntax.resource === 'blob') {
|
|
35
|
+
return BlobScope.fromSyntax(syntax) != null
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return false
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function parseScope(string: string) {
|
|
42
|
+
const syntax = ResourceSyntax.fromString(string)
|
|
43
|
+
if (syntax.is('account')) return AccountScope.fromSyntax(syntax)
|
|
44
|
+
if (syntax.is('identity')) return IdentityScope.fromSyntax(syntax)
|
|
45
|
+
if (syntax.is('repo')) return RepoScope.fromSyntax(syntax)
|
|
46
|
+
if (syntax.is('rpc')) return RpcScope.fromSyntax(syntax)
|
|
47
|
+
if (syntax.is('blob')) return BlobScope.fromSyntax(syntax)
|
|
48
|
+
return null
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function scopeNeededFor<R extends keyof ScopeMatchingOptionsByResource>(
|
|
52
|
+
resource: R,
|
|
53
|
+
options: ScopeMatchingOptionsByResource[R],
|
|
54
|
+
): string {
|
|
55
|
+
switch (resource) {
|
|
56
|
+
case 'account':
|
|
57
|
+
return AccountScope.scopeNeededFor(options as AccountScopeMatch)
|
|
58
|
+
case 'identity':
|
|
59
|
+
return IdentityScope.scopeNeededFor(options as IdentityScopeMatch)
|
|
60
|
+
case 'repo':
|
|
61
|
+
return RepoScope.scopeNeededFor(options as RepoScopeMatch)
|
|
62
|
+
case 'rpc':
|
|
63
|
+
return RpcScope.scopeNeededFor(options as RpcScopeMatch)
|
|
64
|
+
case 'blob':
|
|
65
|
+
return BlobScope.scopeNeededFor(options as BlobScopeMatch)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function scopeMatches<R extends keyof ScopeMatchingOptionsByResource>(
|
|
70
|
+
scope: string,
|
|
71
|
+
resource: R,
|
|
72
|
+
options: ScopeMatchingOptionsByResource[R],
|
|
73
|
+
): boolean {
|
|
74
|
+
// Optimization: Do not try parsing the scope if it does not match the
|
|
75
|
+
// resource prefix.
|
|
76
|
+
if (!isScopeForResource(scope, resource)) return false
|
|
77
|
+
|
|
78
|
+
// @NOTE we might want to cache the parsed scopes though, in practice, a
|
|
79
|
+
// single scope is unlikely to be parsed multiple times during a single
|
|
80
|
+
// request.
|
|
81
|
+
if (resource === 'rpc') {
|
|
82
|
+
const rpcScope = RpcScope.fromString(scope)
|
|
83
|
+
if (rpcScope?.matches(options as RpcScopeMatch)) {
|
|
84
|
+
return true
|
|
85
|
+
}
|
|
86
|
+
} else if (resource === 'account') {
|
|
87
|
+
const accountScope = AccountScope.fromString(scope)
|
|
88
|
+
if (accountScope?.matches(options as AccountScopeMatch)) {
|
|
89
|
+
return true
|
|
90
|
+
}
|
|
91
|
+
} else if (resource === 'identity') {
|
|
92
|
+
const identityScope = IdentityScope.fromString(scope)
|
|
93
|
+
if (identityScope?.matches(options as IdentityScopeMatch)) {
|
|
94
|
+
return true
|
|
95
|
+
}
|
|
96
|
+
} else if (resource === 'repo') {
|
|
97
|
+
const repoScope = RepoScope.fromString(scope)
|
|
98
|
+
if (repoScope?.matches(options as RepoScopeMatch)) {
|
|
99
|
+
return true
|
|
100
|
+
}
|
|
101
|
+
} else if (resource === 'blob') {
|
|
102
|
+
const blobScope = BlobScope.fromString(scope)
|
|
103
|
+
if (blobScope?.matches(options as BlobScopeMatch)) {
|
|
104
|
+
return true
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return false
|
|
109
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["./src/index.ts","./src/parser.ts","./src/permission-set-transition.ts","./src/permission-set.ts","./src/scope-missing-error.ts","./src/scopes-set.ts","./src/syntax.ts","./src/utilities.ts","./src/lib/did.ts","./src/lib/mime.ts","./src/lib/nsid.ts","./src/lib/util.ts","./src/resources/account-scope.ts","./src/resources/blob-scope.ts","./src/resources/identity-scope.ts","./src/resources/repo-scope.ts","./src/resources/rpc-scope.ts"],"version":"5.8.3"}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"root":["./src/index.ts","./src/parser.ts","./src/permission-set-transition.test.ts","./src/permission-set-transition.ts","./src/permission-set.test.ts","./src/permission-set.ts","./src/scope-missing-error.ts","./src/scopes-set.test.ts","./src/scopes-set.ts","./src/syntax.test.ts","./src/syntax.ts","./src/utilities.ts","./src/lib/did.ts","./src/lib/mime.test.ts","./src/lib/mime.ts","./src/lib/nsid.ts","./src/lib/util.ts","./src/resources/account-scope.test.ts","./src/resources/account-scope.ts","./src/resources/blob-scope.test.ts","./src/resources/blob-scope.ts","./src/resources/identity-scope.test.ts","./src/resources/identity-scope.ts","./src/resources/repo-scope.test.ts","./src/resources/repo-scope.ts","./src/resources/rpc-scope.test.ts","./src/resources/rpc-scope.ts"],"version":"5.8.3"}
|