@atproto/oauth-scopes 0.3.2 → 0.4.0
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 +18 -0
- package/dist/atproto-oauth-scope.js +28 -35
- package/dist/atproto-oauth-scope.js.map +1 -1
- package/dist/index.js +11 -27
- package/dist/index.js.map +1 -1
- package/dist/lib/lexicon.js +1 -2
- package/dist/lib/mime.js +4 -10
- package/dist/lib/mime.js.map +1 -1
- package/dist/lib/nsid.js +2 -6
- package/dist/lib/nsid.js.map +1 -1
- package/dist/lib/parser.js +6 -31
- package/dist/lib/parser.js.map +1 -1
- package/dist/lib/resource-permission.js +1 -2
- package/dist/lib/syntax-lexicon.js +2 -11
- package/dist/lib/syntax-lexicon.js.map +1 -1
- package/dist/lib/syntax-string.js +6 -25
- package/dist/lib/syntax-string.js.map +1 -1
- package/dist/lib/syntax.js +2 -6
- package/dist/lib/syntax.js.map +1 -1
- package/dist/lib/util.js +3 -8
- package/dist/lib/util.js.map +1 -1
- package/dist/scope-missing-error.js +5 -29
- package/dist/scope-missing-error.js.map +1 -1
- package/dist/scope-permissions-transition.js +2 -6
- package/dist/scope-permissions-transition.js.map +1 -1
- package/dist/scope-permissions.js +19 -29
- package/dist/scope-permissions.js.map +1 -1
- package/dist/scopes/account-permission.js +24 -43
- package/dist/scopes/account-permission.js.map +1 -1
- package/dist/scopes/blob-permission.js +26 -40
- package/dist/scopes/blob-permission.js.map +1 -1
- package/dist/scopes/identity-permission.js +16 -30
- package/dist/scopes/identity-permission.js.map +1 -1
- package/dist/scopes/include-scope.js +35 -54
- package/dist/scopes/include-scope.js.map +1 -1
- package/dist/scopes/repo-permission.js +39 -59
- package/dist/scopes/repo-permission.js.map +1 -1
- package/dist/scopes/rpc-permission.js +28 -50
- package/dist/scopes/rpc-permission.js.map +1 -1
- package/dist/scopes-set.js +19 -23
- package/dist/scopes-set.js.map +1 -1
- package/jest.config.cjs +14 -0
- package/package.json +10 -9
- package/src/scopes/include-scope.test.ts +5 -5
- package/tsconfig.build.tsbuildinfo +1 -1
- package/jest.config.js +0 -5
|
@@ -1,34 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const syntax_js_1 = require("../lib/syntax.js");
|
|
11
|
-
const repo_permission_js_1 = require("./repo-permission.js");
|
|
12
|
-
const rpc_permission_js_1 = require("./rpc-permission.js");
|
|
1
|
+
import { isAtprotoAudience } from '@atproto/did';
|
|
2
|
+
import { isNsid } from '../lib/nsid.js';
|
|
3
|
+
import { Parser } from '../lib/parser.js';
|
|
4
|
+
import { LexPermissionSyntax } from '../lib/syntax-lexicon.js';
|
|
5
|
+
import { ScopeStringSyntax } from '../lib/syntax-string.js';
|
|
6
|
+
import { isScopeStringFor, isScopeSyntaxFor, } from '../lib/syntax.js';
|
|
7
|
+
import { RepoPermission } from './repo-permission.js';
|
|
8
|
+
import { RpcPermission } from './rpc-permission.js';
|
|
9
|
+
export { isNsid };
|
|
13
10
|
/**
|
|
14
11
|
* This is used to handle "include:" oauth scope values, used to include
|
|
15
12
|
* permissions from a lexicon defined permission set. Not being a resource
|
|
16
13
|
* permission, it does not implement `Matchable`.
|
|
17
14
|
*/
|
|
18
|
-
class IncludeScope {
|
|
15
|
+
export class IncludeScope {
|
|
19
16
|
constructor(nsid, aud = undefined) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
configurable: true,
|
|
23
|
-
writable: true,
|
|
24
|
-
value: nsid
|
|
25
|
-
});
|
|
26
|
-
Object.defineProperty(this, "aud", {
|
|
27
|
-
enumerable: true,
|
|
28
|
-
configurable: true,
|
|
29
|
-
writable: true,
|
|
30
|
-
value: aud
|
|
31
|
-
});
|
|
17
|
+
this.nsid = nsid;
|
|
18
|
+
this.aud = aud;
|
|
32
19
|
}
|
|
33
20
|
toString() {
|
|
34
21
|
return IncludeScope.parser.format(this);
|
|
@@ -61,7 +48,7 @@ class IncludeScope {
|
|
|
61
48
|
// their respective ScopeSyntax representations, handling special cases as
|
|
62
49
|
// needed.
|
|
63
50
|
if (isLexPermissionForResource(permission, 'repo')) {
|
|
64
|
-
return new
|
|
51
|
+
return new LexPermissionSyntax(permission);
|
|
65
52
|
}
|
|
66
53
|
if (isLexPermissionForResource(permission, 'rpc')) {
|
|
67
54
|
// "rpc" permissions with a defined audience are not allowed in permission
|
|
@@ -76,9 +63,9 @@ class IncludeScope {
|
|
|
76
63
|
permission.aud === undefined &&
|
|
77
64
|
this.aud !== undefined) {
|
|
78
65
|
const { inheritAud, ...rest } = permission;
|
|
79
|
-
return new
|
|
66
|
+
return new LexPermissionSyntax({ aud: this.aud, ...rest });
|
|
80
67
|
}
|
|
81
|
-
return new
|
|
68
|
+
return new LexPermissionSyntax(permission);
|
|
82
69
|
}
|
|
83
70
|
return null;
|
|
84
71
|
}
|
|
@@ -89,10 +76,10 @@ class IncludeScope {
|
|
|
89
76
|
* and that it only contains "repo:", "rpc:", or "blob:" permissions.
|
|
90
77
|
*/
|
|
91
78
|
isAllowedPermission(permission) {
|
|
92
|
-
if (permission instanceof
|
|
79
|
+
if (permission instanceof RpcPermission) {
|
|
93
80
|
return permission.lxm.every(this.isParentAuthorityOf, this);
|
|
94
81
|
}
|
|
95
|
-
if (permission instanceof
|
|
82
|
+
if (permission instanceof RepoPermission) {
|
|
96
83
|
return permission.collection.every(this.isParentAuthorityOf, this);
|
|
97
84
|
}
|
|
98
85
|
throw new TypeError(`Unexpected permission ${permission}`);
|
|
@@ -127,10 +114,22 @@ class IncludeScope {
|
|
|
127
114
|
}
|
|
128
115
|
return true;
|
|
129
116
|
}
|
|
117
|
+
static { this.parser = new Parser('include', {
|
|
118
|
+
nsid: {
|
|
119
|
+
multiple: false,
|
|
120
|
+
required: true,
|
|
121
|
+
validate: isNsid,
|
|
122
|
+
},
|
|
123
|
+
aud: {
|
|
124
|
+
multiple: false,
|
|
125
|
+
required: false,
|
|
126
|
+
validate: isAtprotoAudience,
|
|
127
|
+
},
|
|
128
|
+
}, 'nsid'); }
|
|
130
129
|
static fromString(scope) {
|
|
131
|
-
if (!
|
|
130
|
+
if (!isScopeStringFor(scope, 'include'))
|
|
132
131
|
return null;
|
|
133
|
-
const syntax =
|
|
132
|
+
const syntax = ScopeStringSyntax.fromString(scope);
|
|
134
133
|
return IncludeScope.fromSyntax(syntax);
|
|
135
134
|
}
|
|
136
135
|
static fromSyntax(syntax) {
|
|
@@ -140,30 +139,12 @@ class IncludeScope {
|
|
|
140
139
|
return new IncludeScope(result.nsid, result.aud);
|
|
141
140
|
}
|
|
142
141
|
}
|
|
143
|
-
exports.IncludeScope = IncludeScope;
|
|
144
|
-
Object.defineProperty(IncludeScope, "parser", {
|
|
145
|
-
enumerable: true,
|
|
146
|
-
configurable: true,
|
|
147
|
-
writable: true,
|
|
148
|
-
value: new parser_js_1.Parser('include', {
|
|
149
|
-
nsid: {
|
|
150
|
-
multiple: false,
|
|
151
|
-
required: true,
|
|
152
|
-
validate: nsid_js_1.isNsid,
|
|
153
|
-
},
|
|
154
|
-
aud: {
|
|
155
|
-
multiple: false,
|
|
156
|
-
required: false,
|
|
157
|
-
validate: did_1.isAtprotoAudience,
|
|
158
|
-
},
|
|
159
|
-
}, 'nsid')
|
|
160
|
-
});
|
|
161
142
|
function toResourcePermission(syntax) {
|
|
162
|
-
if (
|
|
163
|
-
return
|
|
143
|
+
if (isScopeSyntaxFor(syntax, 'repo')) {
|
|
144
|
+
return RepoPermission.fromSyntax(syntax);
|
|
164
145
|
}
|
|
165
|
-
if (
|
|
166
|
-
return
|
|
146
|
+
if (isScopeSyntaxFor(syntax, 'rpc')) {
|
|
147
|
+
return RpcPermission.fromSyntax(syntax);
|
|
167
148
|
}
|
|
168
149
|
return null;
|
|
169
150
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"include-scope.js","sourceRoot":"","sources":["../../src/scopes/include-scope.ts"],"names":[],"mappings":";;;AAAA,sCAAiE;AAEjE,4CAA6C;AAa0B,uFAbxD,gBAAM,OAawD;AAZ7E,gDAAyC;AACzC,gEAA8D;AAC9D,8DAA2D;AAC3D,gDAKyB;AACzB,6DAAqD;AACrD,2DAAmD;AAInD;;;;GAIG;AACH,MAAa,YAAY;IACvB,YACkB,IAAU,EACV,MAAmC,SAAS;QAD5D;;;;mBAAgB,IAAI;WAAM;QAC1B;;;;mBAAgB,GAAG;WAAyC;IAC3D,CAAC;IAEJ,QAAQ;QACN,OAAO,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IACzC,CAAC;IAED,aAAa,CACX,aAAmC;QAEnC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC,CAAA;IACzD,CAAC;IAED,QAAQ,CACN,aAAmC;QAEnC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAA;IAC9E,CAAC;IAED;;;OAGG;IACH,CAAC,gBAAgB,CACf,aAAmC;QAEnC,KAAK,MAAM,aAAa,IAAI,aAAa,CAAC,WAAW,EAAE,CAAC;YACtD,MAAM,MAAM,GAAG,IAAI,CAAC,kBAAkB,CAAC,aAAa,CAAC,CAAA;YACrD,IAAI,CAAC,MAAM;gBAAE,SAAQ;YAErB,MAAM,kBAAkB,GAAG,oBAAoB,CAAC,MAAM,CAAC,CAAA;YACvD,IAAI,CAAC,kBAAkB;gBAAE,SAAQ;YAEjC,IAAI,IAAI,CAAC,mBAAmB,CAAC,kBAAkB,CAAC,EAAE,CAAC;gBACjD,MAAM,kBAAkB,CAAA;YAC1B,CAAC;QACH,CAAC;IACH,CAAC;IAES,kBAAkB,CAC1B,UAA6B;QAE7B,uEAAuE;QACvE,0EAA0E;QAC1E,UAAU;QAEV,IAAI,0BAA0B,CAAC,UAAU,EAAE,MAAM,CAAC,EAAE,CAAC;YACnD,OAAO,IAAI,uCAAmB,CAAC,UAAU,CAAC,CAAA;QAC5C,CAAC;QAED,IAAI,0BAA0B,CAAC,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC;YAClD,0EAA0E;YAC1E,OAAO;YACP,IAAI,UAAU,CAAC,GAAG,KAAK,SAAS,IAAI,UAAU,CAAC,GAAG,KAAK,GAAG,EAAE,CAAC;gBAC3D,OAAO,IAAI,CAAA;YACb,CAAC;YAED,0EAA0E;YAC1E,sEAAsE;YACtE,eAAe;YACf,IACE,UAAU,CAAC,UAAU,KAAK,IAAI;gBAC9B,UAAU,CAAC,GAAG,KAAK,SAAS;gBAC5B,IAAI,CAAC,GAAG,KAAK,SAAS,EACtB,CAAC;gBACD,MAAM,EAAE,UAAU,EAAE,GAAG,IAAI,EAAE,GAAG,UAAU,CAAA;gBAC1C,OAAO,IAAI,uCAAmB,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,EAAE,CAAC,CAAA;YAC5D,CAAC;YAED,OAAO,IAAI,uCAAmB,CAAC,UAAU,CAAC,CAAA;QAC5C,CAAC;QAED,OAAO,IAAI,CAAA;IACb,CAAC;IAED;;;;;OAKG;IACO,mBAAmB,CAC3B,UAA0C;QAE1C,IAAI,UAAU,YAAY,iCAAa,EAAE,CAAC;YACxC,OAAO,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,mBAAmB,EAAE,IAAI,CAAC,CAAA;QAC7D,CAAC;QAED,IAAI,UAAU,YAAY,mCAAc,EAAE,CAAC;YACzC,OAAO,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,mBAAmB,EAAE,IAAI,CAAC,CAAA;QACpE,CAAC;QAED,MAAM,IAAI,SAAS,CAAC,yBAAyB,UAAU,EAAE,CAAC,CAAA;IAC5D,CAAC;IAED;;;;OAIG;IACI,mBAAmB,CAAC,SAAqB;QAC9C,IAAI,SAAS,KAAK,GAAG,EAAE,CAAC;YACtB,OAAO,KAAK,CAAA;QACd,CAAC;QAED,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAA;QAE7B,MAAM,cAAc,GAAG,WAAW,CAAC,WAAW,CAAC,GAAG,CAAC,CAAA;QAEnD,4EAA4E;QAC5E,sBAAsB;QACtB,IAAI,cAAc,KAAK,CAAC,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,SAAS,CAAC,+CAA+C,CAAC,CAAA;QACtE,CAAC;QAED,qEAAqE;QACrE,IAAI,cAAc,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3C,OAAO,KAAK,CAAA;QACd,CAAC;QAED,2EAA2E;QAC3E,wEAAwE;QACxE,wBAAwB;QACxB,KAAK,IAAI,CAAC,GAAG,cAAc,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YACzC,IAAI,WAAW,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC1D,OAAO,KAAK,CAAA;YACd,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAA;IACb,CAAC;IAmBD,MAAM,CAAC,UAAU,CAAC,KAAa;QAC7B,IAAI,CAAC,IAAA,4BAAgB,EAAC,KAAK,EAAE,SAAS,CAAC;YAAE,OAAO,IAAI,CAAA;QACpD,MAAM,MAAM,GAAG,oCAAiB,CAAC,UAAU,CAAC,KAAK,CAAC,CAAA;QAClD,OAAO,YAAY,CAAC,UAAU,CAAC,MAAM,CAAC,CAAA;IACxC,CAAC;IAED,MAAM,CAAC,UAAU,CAAC,MAA8B;QAC9C,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;QAChD,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAA;QACxB,OAAO,IAAI,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CAAA;IAClD,CAAC;;AAlKH,oCAmKC;AA5B2B;;;;WAAS,IAAI,kBAAM,CAC3C,SAAS,EACT;QACE,IAAI,EAAE;YACJ,QAAQ,EAAE,KAAK;YACf,QAAQ,EAAE,IAAI;YACd,QAAQ,EAAE,gBAAM;SACjB;QACD,GAAG,EAAE;YACH,QAAQ,EAAE,KAAK;YACf,QAAQ,EAAE,KAAK;YACf,QAAQ,EAAE,uBAAiB;SAC5B;KACF,EACD,MAAM,CACP;EAf+B,CAe/B;AAeH,SAAS,oBAAoB,CAC3B,MAAmC;IAEnC,IAAI,IAAA,4BAAgB,EAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;QACrC,OAAO,mCAAc,CAAC,UAAU,CAAC,MAAM,CAAC,CAAA;IAC1C,CAAC;IACD,IAAI,IAAA,4BAAgB,EAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC;QACpC,OAAO,iCAAa,CAAC,UAAU,CAAC,MAAM,CAAC,CAAA;IACzC,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,SAAS,0BAA0B,CAGjC,UAAa,EAAE,IAAO;IACtB,OAAO,UAAU,CAAC,QAAQ,KAAK,IAAI,CAAA;AACrC,CAAC","sourcesContent":["import { AtprotoAudience, isAtprotoAudience } from '@atproto/did'\nimport { LexiconPermission, LexiconPermissionSet } from '../lib/lexicon.js'\nimport { Nsid, isNsid } from '../lib/nsid.js'\nimport { Parser } from '../lib/parser.js'\nimport { LexPermissionSyntax } from '../lib/syntax-lexicon.js'\nimport { ScopeStringSyntax } from '../lib/syntax-string.js'\nimport {\n ScopeStringFor,\n ScopeSyntax,\n isScopeStringFor,\n isScopeSyntaxFor,\n} from '../lib/syntax.js'\nimport { RepoPermission } from './repo-permission.js'\nimport { RpcPermission } from './rpc-permission.js'\n\nexport { type LexiconPermission, type LexiconPermissionSet, type Nsid, isNsid }\n\n/**\n * This is used to handle \"include:\" oauth scope values, used to include\n * permissions from a lexicon defined permission set. Not being a resource\n * permission, it does not implement `Matchable`.\n */\nexport class IncludeScope {\n constructor(\n public readonly nsid: Nsid,\n public readonly aud: undefined | AtprotoAudience = undefined,\n ) {}\n\n toString() {\n return IncludeScope.parser.format(this)\n }\n\n toPermissions(\n permissionSet: LexiconPermissionSet,\n ): Array<RepoPermission | RpcPermission> {\n return Array.from(this.buildPermissions(permissionSet))\n }\n\n toScopes(\n permissionSet: LexiconPermissionSet,\n ): Array<ScopeStringFor<'repo' | 'rpc'>> {\n return Array.from(this.buildPermissions(permissionSet), (p) => p.toString())\n }\n\n /**\n * Converts an \"include:\" to the list of permissions it includes, based on the\n * lexicon defined permission set.\n */\n *buildPermissions(\n permissionSet: LexiconPermissionSet,\n ): Generator<RepoPermission | RpcPermission, void, unknown> {\n for (const lexPermission of permissionSet.permissions) {\n const syntax = this.parseLexPermission(lexPermission)\n if (!syntax) continue\n\n const resourcePermission = toResourcePermission(syntax)\n if (!resourcePermission) continue\n\n if (this.isAllowedPermission(resourcePermission)) {\n yield resourcePermission\n }\n }\n }\n\n protected parseLexPermission(\n permission: LexiconPermission,\n ): ScopeSyntax<'repo' | 'rpc'> | null {\n // This function converts permissions listed in the permission set into\n // their respective ScopeSyntax representations, handling special cases as\n // needed.\n\n if (isLexPermissionForResource(permission, 'repo')) {\n return new LexPermissionSyntax(permission)\n }\n\n if (isLexPermissionForResource(permission, 'rpc')) {\n // \"rpc\" permissions with a defined audience are not allowed in permission\n // sets\n if (permission.aud !== undefined && permission.aud !== '*') {\n return null\n }\n\n // \"rpc\" permissions can \"inherit\" their audience from \"aud\" param defined\n // in the \"include:<nsid>?aud=<audience>\" scope the permission set was\n // loaded from.\n if (\n permission.inheritAud === true &&\n permission.aud === undefined &&\n this.aud !== undefined\n ) {\n const { inheritAud, ...rest } = permission\n return new LexPermissionSyntax({ aud: this.aud, ...rest })\n }\n\n return new LexPermissionSyntax(permission)\n }\n\n return null\n }\n\n /**\n * Verifies that a permission included through a lexicon permission set is\n * allowed in the context of the `include:` scope. This basically checks that\n * the permission is \"under\" the namespace authority of the `include:` scope,\n * and that it only contains \"repo:\", \"rpc:\", or \"blob:\" permissions.\n */\n protected isAllowedPermission(\n permission: RpcPermission | RepoPermission,\n ): boolean {\n if (permission instanceof RpcPermission) {\n return permission.lxm.every(this.isParentAuthorityOf, this)\n }\n\n if (permission instanceof RepoPermission) {\n return permission.collection.every(this.isParentAuthorityOf, this)\n }\n\n throw new TypeError(`Unexpected permission ${permission}`)\n }\n\n /**\n * Verifies that a permission item's nsid is under the same authority as the\n * nsid of the lexicon itself (which is the same as the nsid of the `include:`\n * scope).\n */\n public isParentAuthorityOf(otherNsid: '*' | Nsid) {\n if (otherNsid === '*') {\n return false\n }\n\n const lexiconNsid = this.nsid\n\n const groupPrefixEnd = lexiconNsid.lastIndexOf('.')\n\n // There should always be a dot, but since this is a security feature, let's\n // be strict about it.\n if (groupPrefixEnd === -1) {\n throw new TypeError('Dot character (\".\") missing from lexicon NSID')\n }\n\n // Make sure that otherNsid is at least as long as the \"group prefix\"\n if (groupPrefixEnd >= otherNsid.length - 1) {\n return false\n }\n\n // Make sure that the \"otherNsid\" starts with the group of the lexiconNsid,\n // up to the dot itself. We check in reverse order as nsids tend to have\n // long common prefixes.\n for (let i = groupPrefixEnd; i >= 0; i--) {\n if (lexiconNsid.charCodeAt(i) !== otherNsid.charCodeAt(i)) {\n return false\n }\n }\n\n return true\n }\n\n protected static readonly parser = new Parser(\n 'include',\n {\n nsid: {\n multiple: false,\n required: true,\n validate: isNsid,\n },\n aud: {\n multiple: false,\n required: false,\n validate: isAtprotoAudience,\n },\n },\n 'nsid',\n )\n\n static fromString(scope: string) {\n if (!isScopeStringFor(scope, 'include')) return null\n const syntax = ScopeStringSyntax.fromString(scope)\n return IncludeScope.fromSyntax(syntax)\n }\n\n static fromSyntax(syntax: ScopeSyntax<'include'>) {\n const result = IncludeScope.parser.parse(syntax)\n if (!result) return null\n return new IncludeScope(result.nsid, result.aud)\n }\n}\n\nfunction toResourcePermission(\n syntax: ScopeSyntax<'repo' | 'rpc'>,\n): RepoPermission | RpcPermission | null {\n if (isScopeSyntaxFor(syntax, 'repo')) {\n return RepoPermission.fromSyntax(syntax)\n }\n if (isScopeSyntaxFor(syntax, 'rpc')) {\n return RpcPermission.fromSyntax(syntax)\n }\n return null\n}\n\nfunction isLexPermissionForResource<\n P extends { resource: unknown },\n T extends string,\n>(permission: P, type: T): permission is P & { resource: T } {\n return permission.resource === type\n}\n"]}
|
|
1
|
+
{"version":3,"file":"include-scope.js","sourceRoot":"","sources":["../../src/scopes/include-scope.ts"],"names":[],"mappings":"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","sourcesContent":["import { AtprotoAudience, isAtprotoAudience } from '@atproto/did'\nimport { LexiconPermission, LexiconPermissionSet } from '../lib/lexicon.js'\nimport { Nsid, isNsid } from '../lib/nsid.js'\nimport { Parser } from '../lib/parser.js'\nimport { LexPermissionSyntax } from '../lib/syntax-lexicon.js'\nimport { ScopeStringSyntax } from '../lib/syntax-string.js'\nimport {\n ScopeStringFor,\n ScopeSyntax,\n isScopeStringFor,\n isScopeSyntaxFor,\n} from '../lib/syntax.js'\nimport { RepoPermission } from './repo-permission.js'\nimport { RpcPermission } from './rpc-permission.js'\n\nexport { type LexiconPermission, type LexiconPermissionSet, type Nsid, isNsid }\n\n/**\n * This is used to handle \"include:\" oauth scope values, used to include\n * permissions from a lexicon defined permission set. Not being a resource\n * permission, it does not implement `Matchable`.\n */\nexport class IncludeScope {\n constructor(\n public readonly nsid: Nsid,\n public readonly aud: undefined | AtprotoAudience = undefined,\n ) {}\n\n toString() {\n return IncludeScope.parser.format(this)\n }\n\n toPermissions(\n permissionSet: LexiconPermissionSet,\n ): Array<RepoPermission | RpcPermission> {\n return Array.from(this.buildPermissions(permissionSet))\n }\n\n toScopes(\n permissionSet: LexiconPermissionSet,\n ): Array<ScopeStringFor<'repo' | 'rpc'>> {\n return Array.from(this.buildPermissions(permissionSet), (p) => p.toString())\n }\n\n /**\n * Converts an \"include:\" to the list of permissions it includes, based on the\n * lexicon defined permission set.\n */\n *buildPermissions(\n permissionSet: LexiconPermissionSet,\n ): Generator<RepoPermission | RpcPermission, void, unknown> {\n for (const lexPermission of permissionSet.permissions) {\n const syntax = this.parseLexPermission(lexPermission)\n if (!syntax) continue\n\n const resourcePermission = toResourcePermission(syntax)\n if (!resourcePermission) continue\n\n if (this.isAllowedPermission(resourcePermission)) {\n yield resourcePermission\n }\n }\n }\n\n protected parseLexPermission(\n permission: LexiconPermission,\n ): ScopeSyntax<'repo' | 'rpc'> | null {\n // This function converts permissions listed in the permission set into\n // their respective ScopeSyntax representations, handling special cases as\n // needed.\n\n if (isLexPermissionForResource(permission, 'repo')) {\n return new LexPermissionSyntax(permission)\n }\n\n if (isLexPermissionForResource(permission, 'rpc')) {\n // \"rpc\" permissions with a defined audience are not allowed in permission\n // sets\n if (permission.aud !== undefined && permission.aud !== '*') {\n return null\n }\n\n // \"rpc\" permissions can \"inherit\" their audience from \"aud\" param defined\n // in the \"include:<nsid>?aud=<audience>\" scope the permission set was\n // loaded from.\n if (\n permission.inheritAud === true &&\n permission.aud === undefined &&\n this.aud !== undefined\n ) {\n const { inheritAud, ...rest } = permission\n return new LexPermissionSyntax({ aud: this.aud, ...rest })\n }\n\n return new LexPermissionSyntax(permission)\n }\n\n return null\n }\n\n /**\n * Verifies that a permission included through a lexicon permission set is\n * allowed in the context of the `include:` scope. This basically checks that\n * the permission is \"under\" the namespace authority of the `include:` scope,\n * and that it only contains \"repo:\", \"rpc:\", or \"blob:\" permissions.\n */\n protected isAllowedPermission(\n permission: RpcPermission | RepoPermission,\n ): boolean {\n if (permission instanceof RpcPermission) {\n return permission.lxm.every(this.isParentAuthorityOf, this)\n }\n\n if (permission instanceof RepoPermission) {\n return permission.collection.every(this.isParentAuthorityOf, this)\n }\n\n throw new TypeError(`Unexpected permission ${permission}`)\n }\n\n /**\n * Verifies that a permission item's nsid is under the same authority as the\n * nsid of the lexicon itself (which is the same as the nsid of the `include:`\n * scope).\n */\n public isParentAuthorityOf(otherNsid: '*' | Nsid) {\n if (otherNsid === '*') {\n return false\n }\n\n const lexiconNsid = this.nsid\n\n const groupPrefixEnd = lexiconNsid.lastIndexOf('.')\n\n // There should always be a dot, but since this is a security feature, let's\n // be strict about it.\n if (groupPrefixEnd === -1) {\n throw new TypeError('Dot character (\".\") missing from lexicon NSID')\n }\n\n // Make sure that otherNsid is at least as long as the \"group prefix\"\n if (groupPrefixEnd >= otherNsid.length - 1) {\n return false\n }\n\n // Make sure that the \"otherNsid\" starts with the group of the lexiconNsid,\n // up to the dot itself. We check in reverse order as nsids tend to have\n // long common prefixes.\n for (let i = groupPrefixEnd; i >= 0; i--) {\n if (lexiconNsid.charCodeAt(i) !== otherNsid.charCodeAt(i)) {\n return false\n }\n }\n\n return true\n }\n\n protected static readonly parser = new Parser(\n 'include',\n {\n nsid: {\n multiple: false,\n required: true,\n validate: isNsid,\n },\n aud: {\n multiple: false,\n required: false,\n validate: isAtprotoAudience,\n },\n },\n 'nsid',\n )\n\n static fromString(scope: string) {\n if (!isScopeStringFor(scope, 'include')) return null\n const syntax = ScopeStringSyntax.fromString(scope)\n return IncludeScope.fromSyntax(syntax)\n }\n\n static fromSyntax(syntax: ScopeSyntax<'include'>) {\n const result = IncludeScope.parser.parse(syntax)\n if (!result) return null\n return new IncludeScope(result.nsid, result.aud)\n }\n}\n\nfunction toResourcePermission(\n syntax: ScopeSyntax<'repo' | 'rpc'>,\n): RepoPermission | RpcPermission | null {\n if (isScopeSyntaxFor(syntax, 'repo')) {\n return RepoPermission.fromSyntax(syntax)\n }\n if (isScopeSyntaxFor(syntax, 'rpc')) {\n return RpcPermission.fromSyntax(syntax)\n }\n return null\n}\n\nfunction isLexPermissionForResource<\n P extends { resource: unknown },\n T extends string,\n>(permission: P, type: T): permission is P & { resource: T } {\n return permission.resource === type\n}\n"]}
|
|
@@ -1,34 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
const syntax_js_1 = require("../lib/syntax.js");
|
|
9
|
-
const util_js_1 = require("../lib/util.js");
|
|
10
|
-
exports.REPO_ACTIONS = Object.freeze([
|
|
1
|
+
import { isNsid } from '../lib/nsid.js';
|
|
2
|
+
import { Parser } from '../lib/parser.js';
|
|
3
|
+
import { ScopeStringSyntax } from '../lib/syntax-string.js';
|
|
4
|
+
import { isScopeStringFor, } from '../lib/syntax.js';
|
|
5
|
+
import { knownValuesValidator } from '../lib/util.js';
|
|
6
|
+
export { isNsid };
|
|
7
|
+
export const REPO_ACTIONS = Object.freeze([
|
|
11
8
|
'create',
|
|
12
9
|
'update',
|
|
13
10
|
'delete',
|
|
14
11
|
]);
|
|
15
|
-
|
|
16
|
-
const isCollectionParam = (value) => value === '*' ||
|
|
17
|
-
|
|
18
|
-
class RepoPermission {
|
|
12
|
+
export const isRepoAction = knownValuesValidator(REPO_ACTIONS);
|
|
13
|
+
export const isCollectionParam = (value) => value === '*' || isNsid(value);
|
|
14
|
+
export class RepoPermission {
|
|
19
15
|
constructor(collection, action) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
configurable: true,
|
|
23
|
-
writable: true,
|
|
24
|
-
value: collection
|
|
25
|
-
});
|
|
26
|
-
Object.defineProperty(this, "action", {
|
|
27
|
-
enumerable: true,
|
|
28
|
-
configurable: true,
|
|
29
|
-
writable: true,
|
|
30
|
-
value: action
|
|
31
|
-
});
|
|
16
|
+
this.collection = collection;
|
|
17
|
+
this.action = action;
|
|
32
18
|
}
|
|
33
19
|
matches({ action, collection }) {
|
|
34
20
|
return (this.action.includes(action) &&
|
|
@@ -38,35 +24,11 @@ class RepoPermission {
|
|
|
38
24
|
toString() {
|
|
39
25
|
return RepoPermission.parser.format(this);
|
|
40
26
|
}
|
|
41
|
-
static
|
|
42
|
-
if (!(0, syntax_js_1.isScopeStringFor)(scope, 'repo'))
|
|
43
|
-
return null;
|
|
44
|
-
const syntax = syntax_string_js_1.ScopeStringSyntax.fromString(scope);
|
|
45
|
-
return RepoPermission.fromSyntax(syntax);
|
|
46
|
-
}
|
|
47
|
-
static fromSyntax(syntax) {
|
|
48
|
-
const result = RepoPermission.parser.parse(syntax);
|
|
49
|
-
if (!result)
|
|
50
|
-
return null;
|
|
51
|
-
return new RepoPermission(result.collection, result.action);
|
|
52
|
-
}
|
|
53
|
-
static scopeNeededFor(options) {
|
|
54
|
-
return RepoPermission.parser.format({
|
|
55
|
-
collection: [options.collection],
|
|
56
|
-
action: [options.action],
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
exports.RepoPermission = RepoPermission;
|
|
61
|
-
Object.defineProperty(RepoPermission, "parser", {
|
|
62
|
-
enumerable: true,
|
|
63
|
-
configurable: true,
|
|
64
|
-
writable: true,
|
|
65
|
-
value: new parser_js_1.Parser('repo', {
|
|
27
|
+
static { this.parser = new Parser('repo', {
|
|
66
28
|
collection: {
|
|
67
29
|
multiple: true,
|
|
68
30
|
required: true,
|
|
69
|
-
validate:
|
|
31
|
+
validate: isCollectionParam,
|
|
70
32
|
normalize: (value) => {
|
|
71
33
|
if (value.length > 1) {
|
|
72
34
|
if (value.includes('*'))
|
|
@@ -79,16 +41,34 @@ Object.defineProperty(RepoPermission, "parser", {
|
|
|
79
41
|
action: {
|
|
80
42
|
multiple: true,
|
|
81
43
|
required: false,
|
|
82
|
-
validate:
|
|
83
|
-
default:
|
|
44
|
+
validate: isRepoAction,
|
|
45
|
+
default: REPO_ACTIONS,
|
|
84
46
|
normalize: (value) => {
|
|
85
|
-
return value ===
|
|
86
|
-
?
|
|
87
|
-
:
|
|
47
|
+
return value === REPO_ACTIONS
|
|
48
|
+
? REPO_ACTIONS // No need to filter if the default was used
|
|
49
|
+
: REPO_ACTIONS.filter(includedIn, value);
|
|
88
50
|
},
|
|
89
51
|
},
|
|
90
|
-
}, 'collection')
|
|
91
|
-
|
|
52
|
+
}, 'collection'); }
|
|
53
|
+
static fromString(scope) {
|
|
54
|
+
if (!isScopeStringFor(scope, 'repo'))
|
|
55
|
+
return null;
|
|
56
|
+
const syntax = ScopeStringSyntax.fromString(scope);
|
|
57
|
+
return RepoPermission.fromSyntax(syntax);
|
|
58
|
+
}
|
|
59
|
+
static fromSyntax(syntax) {
|
|
60
|
+
const result = RepoPermission.parser.parse(syntax);
|
|
61
|
+
if (!result)
|
|
62
|
+
return null;
|
|
63
|
+
return new RepoPermission(result.collection, result.action);
|
|
64
|
+
}
|
|
65
|
+
static scopeNeededFor(options) {
|
|
66
|
+
return RepoPermission.parser.format({
|
|
67
|
+
collection: [options.collection],
|
|
68
|
+
action: [options.action],
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
92
72
|
/**
|
|
93
73
|
* Special utility function to be used as predicate for array methods like
|
|
94
74
|
* `Array.prototype.includes`, etc. When used as predicate, it expects that
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repo-permission.js","sourceRoot":"","sources":["../../src/scopes/repo-permission.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"repo-permission.js","sourceRoot":"","sources":["../../src/scopes/repo-permission.ts"],"names":[],"mappings":"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","sourcesContent":["import { Nsid, isNsid } from '../lib/nsid.js'\nimport { Parser } from '../lib/parser.js'\nimport { ResourcePermission } from '../lib/resource-permission.js'\nimport { ScopeStringSyntax } from '../lib/syntax-string.js'\nimport {\n NeArray,\n NeRoArray,\n ScopeSyntax,\n isScopeStringFor,\n} from '../lib/syntax.js'\nimport { knownValuesValidator } from '../lib/util.js'\n\nexport { type Nsid, isNsid }\n\nexport const REPO_ACTIONS = Object.freeze([\n 'create',\n 'update',\n 'delete',\n] as const)\nexport type RepoAction = (typeof REPO_ACTIONS)[number]\nexport const isRepoAction = knownValuesValidator(REPO_ACTIONS)\n\nexport type CollectionParam = '*' | Nsid\nexport const isCollectionParam = (value: unknown): value is CollectionParam =>\n value === '*' || isNsid(value)\n\nexport type RepoPermissionMatch = {\n collection: string\n action: RepoAction\n}\n\nexport class RepoPermission\n implements ResourcePermission<'repo', RepoPermissionMatch>\n{\n constructor(\n public readonly collection: NeRoArray<'*' | Nsid>,\n public readonly action: NeRoArray<RepoAction>,\n ) {}\n\n matches({ action, collection }: RepoPermissionMatch) {\n return (\n this.action.includes(action) &&\n (this.collection.includes('*') ||\n (this.collection as readonly string[]).includes(collection))\n )\n }\n\n toString() {\n return RepoPermission.parser.format(this)\n }\n\n protected static readonly parser = new Parser(\n 'repo',\n {\n collection: {\n multiple: true,\n required: true,\n validate: isCollectionParam,\n normalize: (value) => {\n if (value.length > 1) {\n if (value.includes('*')) return ['*'] as const\n return [...new Set(value)].sort() as NeArray<Nsid>\n }\n return value as ['*' | Nsid]\n },\n },\n action: {\n multiple: true,\n required: false,\n validate: isRepoAction,\n default: REPO_ACTIONS,\n normalize: (value) => {\n return value === REPO_ACTIONS\n ? REPO_ACTIONS // No need to filter if the default was used\n : (REPO_ACTIONS.filter(includedIn, value) as NeArray<RepoAction>)\n },\n },\n },\n 'collection',\n )\n\n static fromString(scope: string): RepoPermission | null {\n if (!isScopeStringFor(scope, 'repo')) return null\n const syntax = ScopeStringSyntax.fromString(scope)\n return RepoPermission.fromSyntax(syntax)\n }\n\n static fromSyntax(syntax: ScopeSyntax<'repo'>): RepoPermission | null {\n const result = RepoPermission.parser.parse(syntax)\n if (!result) return null\n\n return new RepoPermission(result.collection, result.action)\n }\n\n static scopeNeededFor(options: RepoPermissionMatch): string {\n return RepoPermission.parser.format({\n collection: [options.collection as '*' | Nsid],\n action: [options.action],\n })\n }\n}\n\n/**\n * Special utility function to be used as predicate for array methods like\n * `Array.prototype.includes`, etc. When used as predicate, it expects that\n * the array method is called with a `thisArg` that is a readonly array of\n * the same type as the `value` parameter.\n */\nfunction includedIn<T>(this: readonly T[], value: T): boolean {\n return this.includes(value)\n}\n"]}
|
|
@@ -1,31 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
const syntax_js_1 = require("../lib/syntax.js");
|
|
11
|
-
const isLxmParam = (value) => value === '*' || (0, nsid_js_1.isNsid)(value);
|
|
12
|
-
exports.isLxmParam = isLxmParam;
|
|
13
|
-
const isAudParam = (value) => value === '*' || (0, did_1.isAtprotoAudience)(value);
|
|
14
|
-
exports.isAudParam = isAudParam;
|
|
15
|
-
class RpcPermission {
|
|
1
|
+
import { isAtprotoAudience } from '@atproto/did';
|
|
2
|
+
import { isNsid } from '../lib/nsid.js';
|
|
3
|
+
import { Parser } from '../lib/parser.js';
|
|
4
|
+
import { ScopeStringSyntax } from '../lib/syntax-string.js';
|
|
5
|
+
import { isScopeStringFor } from '../lib/syntax.js';
|
|
6
|
+
export { isAtprotoAudience, isNsid };
|
|
7
|
+
export const isLxmParam = (value) => value === '*' || isNsid(value);
|
|
8
|
+
export const isAudParam = (value) => value === '*' || isAtprotoAudience(value);
|
|
9
|
+
export class RpcPermission {
|
|
16
10
|
constructor(aud, lxm) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
configurable: true,
|
|
20
|
-
writable: true,
|
|
21
|
-
value: aud
|
|
22
|
-
});
|
|
23
|
-
Object.defineProperty(this, "lxm", {
|
|
24
|
-
enumerable: true,
|
|
25
|
-
configurable: true,
|
|
26
|
-
writable: true,
|
|
27
|
-
value: lxm
|
|
28
|
-
});
|
|
11
|
+
this.aud = aud;
|
|
12
|
+
this.lxm = lxm;
|
|
29
13
|
}
|
|
30
14
|
matches(options) {
|
|
31
15
|
const { aud, lxm } = this;
|
|
@@ -35,10 +19,25 @@ class RpcPermission {
|
|
|
35
19
|
toString() {
|
|
36
20
|
return RpcPermission.parser.format(this);
|
|
37
21
|
}
|
|
22
|
+
static { this.parser = new Parser('rpc', {
|
|
23
|
+
lxm: {
|
|
24
|
+
multiple: true,
|
|
25
|
+
required: true,
|
|
26
|
+
validate: isLxmParam,
|
|
27
|
+
normalize: (value) => value.length > 1 && value.includes('*')
|
|
28
|
+
? ['*']
|
|
29
|
+
: [...new Set(value)].sort(),
|
|
30
|
+
},
|
|
31
|
+
aud: {
|
|
32
|
+
multiple: false,
|
|
33
|
+
required: true,
|
|
34
|
+
validate: isAudParam,
|
|
35
|
+
},
|
|
36
|
+
}, 'lxm'); }
|
|
38
37
|
static fromString(scope) {
|
|
39
|
-
if (!
|
|
38
|
+
if (!isScopeStringFor(scope, 'rpc'))
|
|
40
39
|
return null;
|
|
41
|
-
const syntax =
|
|
40
|
+
const syntax = ScopeStringSyntax.fromString(scope);
|
|
42
41
|
return RpcPermission.fromSyntax(syntax);
|
|
43
42
|
}
|
|
44
43
|
static fromSyntax(syntax) {
|
|
@@ -57,25 +56,4 @@ class RpcPermission {
|
|
|
57
56
|
});
|
|
58
57
|
}
|
|
59
58
|
}
|
|
60
|
-
exports.RpcPermission = RpcPermission;
|
|
61
|
-
Object.defineProperty(RpcPermission, "parser", {
|
|
62
|
-
enumerable: true,
|
|
63
|
-
configurable: true,
|
|
64
|
-
writable: true,
|
|
65
|
-
value: new parser_js_1.Parser('rpc', {
|
|
66
|
-
lxm: {
|
|
67
|
-
multiple: true,
|
|
68
|
-
required: true,
|
|
69
|
-
validate: exports.isLxmParam,
|
|
70
|
-
normalize: (value) => value.length > 1 && value.includes('*')
|
|
71
|
-
? ['*']
|
|
72
|
-
: [...new Set(value)].sort(),
|
|
73
|
-
},
|
|
74
|
-
aud: {
|
|
75
|
-
multiple: false,
|
|
76
|
-
required: true,
|
|
77
|
-
validate: exports.isAudParam,
|
|
78
|
-
},
|
|
79
|
-
}, 'lxm')
|
|
80
|
-
});
|
|
81
59
|
//# sourceMappingURL=rpc-permission.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rpc-permission.js","sourceRoot":"","sources":["../../src/scopes/rpc-permission.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rpc-permission.js","sourceRoot":"","sources":["../../src/scopes/rpc-permission.ts"],"names":[],"mappings":"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","sourcesContent":["import { AtprotoAudience, isAtprotoAudience } from '@atproto/did'\nimport { Nsid, isNsid } from '../lib/nsid.js'\nimport { Parser } from '../lib/parser.js'\nimport { ResourcePermission } from '../lib/resource-permission.js'\nimport { ScopeStringSyntax } from '../lib/syntax-string.js'\nimport { NeRoArray, ScopeSyntax, isScopeStringFor } from '../lib/syntax.js'\n\nexport { type AtprotoAudience, type Nsid, isAtprotoAudience, isNsid }\n\nexport type LxmParam = '*' | Nsid\nexport const isLxmParam = (value: unknown): value is LxmParam =>\n value === '*' || isNsid(value)\nexport type AudParam = '*' | AtprotoAudience\nexport const isAudParam = (value: unknown): value is AudParam =>\n value === '*' || isAtprotoAudience(value)\n\nexport type RpcPermissionMatch = {\n lxm: string\n aud: string\n}\n\nexport class RpcPermission\n implements ResourcePermission<'rpc', RpcPermissionMatch>\n{\n constructor(\n public readonly aud: '*' | AtprotoAudience,\n public readonly lxm: NeRoArray<'*' | Nsid>,\n ) {}\n\n matches(options: RpcPermissionMatch) {\n const { aud, lxm } = this\n return (\n (aud === '*' || aud === options.aud) &&\n (lxm.includes('*') || (lxm as readonly string[]).includes(options.lxm))\n )\n }\n\n toString() {\n return RpcPermission.parser.format(this)\n }\n\n protected static readonly parser = new Parser(\n 'rpc',\n {\n lxm: {\n multiple: true,\n required: true,\n validate: isLxmParam,\n normalize: (value) =>\n value.length > 1 && value.includes('*')\n ? (['*'] as const)\n : ([...new Set(value)].sort() as [Nsid, ...Nsid[]]),\n },\n aud: {\n multiple: false,\n required: true,\n validate: isAudParam,\n },\n },\n 'lxm',\n )\n\n static fromString(scope: string): RpcPermission | null {\n if (!isScopeStringFor(scope, 'rpc')) return null\n const syntax = ScopeStringSyntax.fromString(scope)\n return RpcPermission.fromSyntax(syntax)\n }\n\n static fromSyntax(syntax: ScopeSyntax<'rpc'>): RpcPermission | null {\n const result = RpcPermission.parser.parse(syntax)\n if (!result) return null\n\n // rpc:*?aud=* is forbidden\n if (result.aud === '*' && result.lxm.includes('*')) return null\n\n return new RpcPermission(result.aud, result.lxm)\n }\n\n static scopeNeededFor(options: RpcPermissionMatch): string {\n return RpcPermission.parser.format({\n aud: options.aud as AtprotoAudience,\n lxm: [options.lxm as Nsid],\n })\n }\n}\n"]}
|
package/dist/scopes-set.js
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const identity_permission_js_1 = require("./scopes/identity-permission.js");
|
|
9
|
-
const repo_permission_js_1 = require("./scopes/repo-permission.js");
|
|
10
|
-
const rpc_permission_js_1 = require("./scopes/rpc-permission.js");
|
|
1
|
+
import { ScopeMissingError } from './scope-missing-error.js';
|
|
2
|
+
import { AccountPermission, } from './scopes/account-permission.js';
|
|
3
|
+
import { BlobPermission, } from './scopes/blob-permission.js';
|
|
4
|
+
import { IdentityPermission, } from './scopes/identity-permission.js';
|
|
5
|
+
import { RepoPermission, } from './scopes/repo-permission.js';
|
|
6
|
+
import { RpcPermission } from './scopes/rpc-permission.js';
|
|
7
|
+
export { ScopeMissingError };
|
|
11
8
|
/**
|
|
12
9
|
* Utility class to manage a set of scopes and check if they match specific
|
|
13
10
|
* options for a given resource.
|
|
14
11
|
*/
|
|
15
|
-
class ScopesSet extends Set {
|
|
12
|
+
export class ScopesSet extends Set {
|
|
16
13
|
/**
|
|
17
14
|
* Check if the container has a scope that matches the given options for a
|
|
18
15
|
* specific resource.
|
|
@@ -27,7 +24,7 @@ class ScopesSet extends Set {
|
|
|
27
24
|
assert(resource, options) {
|
|
28
25
|
if (!this.matches(resource, options)) {
|
|
29
26
|
const scope = scopeNeededFor(resource, options);
|
|
30
|
-
throw new
|
|
27
|
+
throw new ScopeMissingError(scope);
|
|
31
28
|
}
|
|
32
29
|
}
|
|
33
30
|
some(fn) {
|
|
@@ -55,19 +52,18 @@ class ScopesSet extends Set {
|
|
|
55
52
|
return new ScopesSet(string?.split(' '));
|
|
56
53
|
}
|
|
57
54
|
}
|
|
58
|
-
exports.ScopesSet = ScopesSet;
|
|
59
55
|
function scopeNeededFor(resource, options) {
|
|
60
56
|
switch (resource) {
|
|
61
57
|
case 'account':
|
|
62
|
-
return
|
|
58
|
+
return AccountPermission.scopeNeededFor(options);
|
|
63
59
|
case 'identity':
|
|
64
|
-
return
|
|
60
|
+
return IdentityPermission.scopeNeededFor(options);
|
|
65
61
|
case 'repo':
|
|
66
|
-
return
|
|
62
|
+
return RepoPermission.scopeNeededFor(options);
|
|
67
63
|
case 'rpc':
|
|
68
|
-
return
|
|
64
|
+
return RpcPermission.scopeNeededFor(options);
|
|
69
65
|
case 'blob':
|
|
70
|
-
return
|
|
66
|
+
return BlobPermission.scopeNeededFor(options);
|
|
71
67
|
}
|
|
72
68
|
// @ts-expect-error
|
|
73
69
|
throw new TypeError(`Unknown resource: ${resource}`);
|
|
@@ -85,15 +81,15 @@ function permissionScopeMatches(scope, resource, options) {
|
|
|
85
81
|
function parsePermissionScope(resource, scope) {
|
|
86
82
|
switch (resource) {
|
|
87
83
|
case 'account':
|
|
88
|
-
return
|
|
84
|
+
return AccountPermission.fromString(scope);
|
|
89
85
|
case 'identity':
|
|
90
|
-
return
|
|
86
|
+
return IdentityPermission.fromString(scope);
|
|
91
87
|
case 'repo':
|
|
92
|
-
return
|
|
88
|
+
return RepoPermission.fromString(scope);
|
|
93
89
|
case 'rpc':
|
|
94
|
-
return
|
|
90
|
+
return RpcPermission.fromString(scope);
|
|
95
91
|
case 'blob':
|
|
96
|
-
return
|
|
92
|
+
return BlobPermission.fromString(scope);
|
|
97
93
|
default:
|
|
98
94
|
return null;
|
|
99
95
|
}
|
package/dist/scopes-set.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scopes-set.js","sourceRoot":"","sources":["../src/scopes-set.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"scopes-set.js","sourceRoot":"","sources":["../src/scopes-set.ts"],"names":[],"mappings":"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","sourcesContent":["import { ScopeMissingError } from './scope-missing-error.js'\nimport {\n AccountPermission,\n AccountPermissionMatch,\n} from './scopes/account-permission.js'\nimport {\n BlobPermission,\n BlobPermissionMatch,\n} from './scopes/blob-permission.js'\nimport {\n IdentityPermission,\n IdentityPermissionMatch,\n} from './scopes/identity-permission.js'\nimport {\n RepoPermission,\n RepoPermissionMatch,\n} from './scopes/repo-permission.js'\nimport { RpcPermission, RpcPermissionMatch } from './scopes/rpc-permission.js'\n\nexport { ScopeMissingError }\n\nexport type ScopeMatchingOptionsByResource = {\n account: AccountPermissionMatch\n identity: IdentityPermissionMatch\n repo: RepoPermissionMatch\n rpc: RpcPermissionMatch\n blob: BlobPermissionMatch\n}\n\n/**\n * Utility class to manage a set of scopes and check if they match specific\n * options for a given resource.\n */\nexport class ScopesSet extends Set<string> {\n /**\n * Check if the container has a scope that matches the given options for a\n * specific resource.\n */\n public matches<R extends keyof ScopeMatchingOptionsByResource>(\n resource: R,\n options: ScopeMatchingOptionsByResource[R],\n ): boolean {\n for (const scope of this) {\n if (permissionScopeMatches(scope, resource, options)) return true\n }\n return false\n }\n\n public assert<R extends keyof ScopeMatchingOptionsByResource>(\n resource: R,\n options: ScopeMatchingOptionsByResource[R],\n ) {\n if (!this.matches(resource, options)) {\n const scope = scopeNeededFor(resource, options)\n throw new ScopeMissingError(scope)\n }\n }\n\n public some(fn: (scope: string) => boolean): boolean {\n for (const scope of this) if (fn(scope)) return true\n return false\n }\n\n public every(fn: (scope: string) => boolean): boolean {\n for (const scope of this) if (!fn(scope)) return false\n return true\n }\n\n public *filter(fn: (scope: string) => boolean) {\n for (const scope of this) if (fn(scope)) yield scope\n }\n\n public *map<O>(fn: (scope: string) => O) {\n for (const scope of this) yield fn(scope)\n }\n\n static fromString(string?: string): ScopesSet {\n return new ScopesSet(string?.split(' '))\n }\n}\n\nfunction scopeNeededFor<R extends keyof ScopeMatchingOptionsByResource>(\n resource: R,\n options: ScopeMatchingOptionsByResource[R],\n): string {\n switch (resource) {\n case 'account':\n return AccountPermission.scopeNeededFor(options as AccountPermissionMatch)\n case 'identity':\n return IdentityPermission.scopeNeededFor(\n options as IdentityPermissionMatch,\n )\n case 'repo':\n return RepoPermission.scopeNeededFor(options as RepoPermissionMatch)\n case 'rpc':\n return RpcPermission.scopeNeededFor(options as RpcPermissionMatch)\n case 'blob':\n return BlobPermission.scopeNeededFor(options as BlobPermissionMatch)\n }\n // @ts-expect-error\n throw new TypeError(`Unknown resource: ${resource}`)\n}\n\nfunction permissionScopeMatches<R extends keyof ScopeMatchingOptionsByResource>(\n scope: string,\n resource: R,\n options: ScopeMatchingOptionsByResource[R],\n): boolean {\n // @NOTE we might want to cache the parsed scopes though, in practice, a\n // single scope is unlikely to be parsed multiple times during a single\n // request.\n const permission = parsePermissionScope(resource, scope)\n if (!permission) return false\n\n // @ts-expect-error\n return permission.matches(options)\n}\n\nfunction parsePermissionScope(resource: string, scope: string) {\n switch (resource) {\n case 'account':\n return AccountPermission.fromString(scope)\n case 'identity':\n return IdentityPermission.fromString(scope)\n case 'repo':\n return RepoPermission.fromString(scope)\n case 'rpc':\n return RpcPermission.fromString(scope)\n case 'blob':\n return BlobPermission.fromString(scope)\n default:\n return null\n }\n}\n"]}
|
package/jest.config.cjs
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** @type {import('jest').Config} */
|
|
2
|
+
module.exports = {
|
|
3
|
+
displayName: 'OAuth Scopes',
|
|
4
|
+
transform: {
|
|
5
|
+
'^.+\\.(t|j)s$': [
|
|
6
|
+
'@swc/jest',
|
|
7
|
+
{ jsc: { transform: {} }, module: { type: 'es6' } },
|
|
8
|
+
],
|
|
9
|
+
},
|
|
10
|
+
extensionsToTreatAsEsm: ['.ts'],
|
|
11
|
+
transformIgnorePatterns: [],
|
|
12
|
+
setupFiles: ['<rootDir>/../../../test.setup.ts'],
|
|
13
|
+
moduleNameMapper: { '^(\\.\\.?\\/.+)\\.js$': ['$1.ts', '$1.js'] },
|
|
14
|
+
}
|