@owlmeans/auth-common 0.1.18-rc.41 → 0.1.18-rc.43
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/README.md +2 -2
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/auth-common/SKILL.md +8 -2
- package/build/entity.d.ts +8 -2
- package/build/entity.d.ts.map +1 -1
- package/build/entity.js +14 -2
- package/build/entity.js.map +1 -1
- package/package.json +7 -7
- package/src/entity.ts +15 -2
- package/tests/entity.spec.ts +112 -0
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ Predefined authentication protocols, guards, and constants shared between server
|
|
|
12
12
|
## Installation
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
|
-
bun add @owlmeans/auth-common@^0.1.18-rc.
|
|
15
|
+
bun add @owlmeans/auth-common@^0.1.18-rc.43
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
## Usage
|
|
@@ -98,7 +98,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
|
|
|
98
98
|
your project's skill store (`.agents/skills/`):
|
|
99
99
|
|
|
100
100
|
```sh
|
|
101
|
-
npx @owlmeans/agent-skills@^0.1.18-rc.
|
|
101
|
+
npx @owlmeans/agent-skills@^0.1.18-rc.45
|
|
102
102
|
```
|
|
103
103
|
|
|
104
104
|
The embedded files are version-matched to this package release. Do not edit them
|
package/agent-meta/manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 2,
|
|
3
3
|
"package": "@owlmeans/auth-common",
|
|
4
|
-
"version": "0.1.18-rc.
|
|
5
|
-
"generatedAt": "2026-
|
|
4
|
+
"version": "0.1.18-rc.43",
|
|
5
|
+
"generatedAt": "2026-10-01T23:27:48.728Z",
|
|
6
6
|
"canonicalRepo": "https://github.com/owlmeans/common",
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -8,7 +8,7 @@ user-invocable: false
|
|
|
8
8
|
# @owlmeans/auth-common
|
|
9
9
|
|
|
10
10
|
**Layer:** Core
|
|
11
|
-
**Install:** `"@owlmeans/auth-common": "^0.1.18-rc.
|
|
11
|
+
**Install:** `"@owlmeans/auth-common": "^0.1.18-rc.43"` in `dependencies`
|
|
12
12
|
|
|
13
13
|
Everything a server and a browser must agree on to talk authentication: aliases, the shared
|
|
14
14
|
protocol declarations, the signature guard, and the contract for resolving the organization
|
|
@@ -83,7 +83,7 @@ import { attachEntity, entityKeyOf, requireEntityKey, requireEntity } from '@owl
|
|
|
83
83
|
|
|
84
84
|
| Helper | What it answers |
|
|
85
85
|
|--------|-----------------|
|
|
86
|
-
| `attachEntity(context, request)` | Resolve the slug on `request.auth` and set `request.entity`, canonicalizing a retired slug to the current one.
|
|
86
|
+
| `attachEntity(context, request)` | Resolve the slug on `request.auth` and set `request.entity`, canonicalizing a retired slug to the current one. Keeps an entity the guard already attached when its `slug` equals the token's exactly, and drops one that does not. Otherwise a no-op when no resolver is registered; throws `AuthenFailed('entity')` when the token names an organization that will not resolve |
|
|
87
87
|
| `entityKeyOf(req)` | The value to store and query organization-scoped records by — `req.entity?.id`, falling back to the token's slug where no resolver exists |
|
|
88
88
|
| `requireEntityKey(req)` | Same, throwing `AuthorizationError` when the request carries no organization |
|
|
89
89
|
| `requireEntity(req)` | The full `ResolvedEntity` (`id`, `slug`, `iamKey`), throwing `AuthorizationError` when nothing resolved |
|
|
@@ -93,6 +93,12 @@ any socket that authenticates after its connection is already open. A path that
|
|
|
93
93
|
without it leaves `request.entity` empty and its handlers silently compare a slug against stored
|
|
94
94
|
ids.
|
|
95
95
|
|
|
96
|
+
A guard may attach the entity itself when its authority names the organization — the OIDC guard
|
|
97
|
+
does for a session of a tenanted client (`@owlmeans/oidc`), whose relying party has no registry to
|
|
98
|
+
resolve from. `attachEntity` trusts that attachment only while its `slug` is the token's
|
|
99
|
+
`entitySlugOf(request.auth)`; on any mismatch it removes it and resolves as if nothing had been
|
|
100
|
+
attached, so a stale or foreign entity can never reach a handler.
|
|
101
|
+
|
|
96
102
|
Never build a user-facing name (a hostname, a display label) from `entityKeyOf`. Those want the
|
|
97
103
|
current slug, `req.entity?.slug`.
|
|
98
104
|
|
package/build/entity.d.ts
CHANGED
|
@@ -34,8 +34,14 @@ export declare const requireEntity: (req: AbstractRequest) => ResolvedEntity;
|
|
|
34
34
|
* its handlers quietly fall back to comparing a slug against stored ids — which surfaces as "this
|
|
35
35
|
* project does not exist" rather than as a missing resolution, and costs an afternoon to find.
|
|
36
36
|
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
37
|
+
* An entity the guard already attached is kept while its slug is exactly the token's: a guard
|
|
38
|
+
* whose authority names the organization itself (an OIDC session of a tenanted client) is the only
|
|
39
|
+
* source such a deployment has, because the registry that owns the organization is not its own.
|
|
40
|
+
* One whose slug differs is never trusted — it is dropped, and resolution proceeds as if it had
|
|
41
|
+
* not been there.
|
|
42
|
+
*
|
|
43
|
+
* Otherwise a no-op when no resolver is registered: such a deployment has no organization store,
|
|
44
|
+
* and the slug is the only identifier it has.
|
|
39
45
|
*
|
|
40
46
|
* @throws {AuthenFailed} when the token names an organization that cannot be resolved.
|
|
41
47
|
*/
|
package/build/entity.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../src/entity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAKrD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,WAAW,QAAS,eAAe,KAAG,MAAM,GAAG,SAClB,CAAA;AAE1C;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,QAAS,eAAe,KAAG,MAOvD,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,aAAa,QAAS,eAAe,KAAG,cAMpD,CAAA;AAED
|
|
1
|
+
{"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../src/entity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAKrD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,WAAW,QAAS,eAAe,KAAG,MAAM,GAAG,SAClB,CAAA;AAE1C;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,QAAS,eAAe,KAAG,MAOvD,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,aAAa,QAAS,eAAe,KAAG,cAMpD,CAAA;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,YAAY,YACd,YAAY,CAAC,GAAG,CAAC,WAAW,eAAe,KACnD,OAAO,CAAC,cAAc,GAAG,SAAS,CAwBpC,CAAA"}
|
package/build/entity.js
CHANGED
|
@@ -45,13 +45,25 @@ export const requireEntity = (req) => {
|
|
|
45
45
|
* its handlers quietly fall back to comparing a slug against stored ids — which surfaces as "this
|
|
46
46
|
* project does not exist" rather than as a missing resolution, and costs an afternoon to find.
|
|
47
47
|
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
48
|
+
* An entity the guard already attached is kept while its slug is exactly the token's: a guard
|
|
49
|
+
* whose authority names the organization itself (an OIDC session of a tenanted client) is the only
|
|
50
|
+
* source such a deployment has, because the registry that owns the organization is not its own.
|
|
51
|
+
* One whose slug differs is never trusted — it is dropped, and resolution proceeds as if it had
|
|
52
|
+
* not been there.
|
|
53
|
+
*
|
|
54
|
+
* Otherwise a no-op when no resolver is registered: such a deployment has no organization store,
|
|
55
|
+
* and the slug is the only identifier it has.
|
|
50
56
|
*
|
|
51
57
|
* @throws {AuthenFailed} when the token names an organization that cannot be resolved.
|
|
52
58
|
*/
|
|
53
59
|
export const attachEntity = async (context, request) => {
|
|
54
60
|
const slug = entitySlugOf(request.auth);
|
|
61
|
+
if (request.entity != null) {
|
|
62
|
+
if (slug != null && request.entity.slug === slug) {
|
|
63
|
+
return request.entity;
|
|
64
|
+
}
|
|
65
|
+
request.entity = undefined;
|
|
66
|
+
}
|
|
55
67
|
if (slug == null || !context.hasService(ENTITY_RESOLVER)) {
|
|
56
68
|
return undefined;
|
|
57
69
|
}
|
package/build/entity.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity.js","sourceRoot":"","sources":["../src/entity.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAE/E,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAE7C;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,GAAoB,EAAsB,EAAE,CACtE,GAAG,CAAC,MAAM,EAAE,EAAE,IAAI,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;AAE1C;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,GAAoB,EAAU,EAAE;IAC/D,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,CAAC,CAAA;IAC5B,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,KAAK,EAAE,EAAE,CAAC;QAC9B,MAAM,IAAI,kBAAkB,EAAE,CAAA;IAChC,CAAC;IAED,OAAO,GAAG,CAAA;AACZ,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,GAAoB,EAAkB,EAAE;IACpE,IAAI,GAAG,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,IAAI,kBAAkB,EAAE,CAAA;IAChC,CAAC;IAED,OAAO,GAAG,CAAC,MAAM,CAAA;AACnB,CAAC,CAAA;AAED
|
|
1
|
+
{"version":3,"file":"entity.js","sourceRoot":"","sources":["../src/entity.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAE/E,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAE7C;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,GAAoB,EAAsB,EAAE,CACtE,GAAG,CAAC,MAAM,EAAE,EAAE,IAAI,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;AAE1C;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,GAAoB,EAAU,EAAE;IAC/D,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,CAAC,CAAA;IAC5B,IAAI,GAAG,IAAI,IAAI,IAAI,GAAG,KAAK,EAAE,EAAE,CAAC;QAC9B,MAAM,IAAI,kBAAkB,EAAE,CAAA;IAChC,CAAC;IAED,OAAO,GAAG,CAAA;AACZ,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,GAAoB,EAAkB,EAAE;IACpE,IAAI,GAAG,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,IAAI,kBAAkB,EAAE,CAAA;IAChC,CAAC;IAED,OAAO,GAAG,CAAC,MAAM,CAAA;AACnB,CAAC,CAAA;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,EAC/B,OAA0B,EAAE,OAAwB,EACf,EAAE;IACvC,MAAM,IAAI,GAAG,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IACvC,IAAI,OAAO,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC;QAC3B,IAAI,IAAI,IAAI,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;YACjD,OAAO,OAAO,CAAC,MAAM,CAAA;QACvB,CAAC;QACD,OAAO,CAAC,MAAM,GAAG,SAAS,CAAA;IAC5B,CAAC;IAED,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;QACzD,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,OAAO,CAAwB,eAAe,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1F,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;QACnB,MAAM,IAAI,YAAY,CAAC,QAAQ,CAAC,CAAA;IAClC,CAAC;IAED,gGAAgG;IAChG,qFAAqF;IACrF,OAAO,CAAC,IAAK,CAAC,UAAU,GAAG,MAAM,CAAC,IAAI,CAAA;IACtC,OAAO,CAAC,MAAM,GAAG,MAAM,CAAA;IAEvB,OAAO,MAAM,CAAA;AACf,CAAC,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlmeans/auth-common",
|
|
3
|
-
"version": "0.1.18-rc.
|
|
3
|
+
"version": "0.1.18-rc.43",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -29,19 +29,19 @@
|
|
|
29
29
|
}
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@owlmeans/auth": "^0.1.18-rc.
|
|
33
|
-
"@owlmeans/auth-common": "^0.1.18-rc.
|
|
32
|
+
"@owlmeans/auth": "^0.1.18-rc.37",
|
|
33
|
+
"@owlmeans/auth-common": "^0.1.18-rc.43",
|
|
34
34
|
"@owlmeans/basic-ids": "^0.1.18-rc.34",
|
|
35
|
-
"@owlmeans/basic-keys": "^0.1.18-rc.
|
|
36
|
-
"@owlmeans/client-entrypoint": "^0.1.18-rc.
|
|
35
|
+
"@owlmeans/basic-keys": "^0.1.18-rc.44",
|
|
36
|
+
"@owlmeans/client-entrypoint": "^0.1.18-rc.43",
|
|
37
37
|
"@owlmeans/context": "^0.1.18-rc.32",
|
|
38
|
-
"@owlmeans/entrypoint": "^0.1.18-rc.
|
|
38
|
+
"@owlmeans/entrypoint": "^0.1.18-rc.38",
|
|
39
39
|
"@owlmeans/resource": "^0.1.18-rc.37",
|
|
40
40
|
"@owlmeans/route": "^0.1.18-rc.33"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@owlmeans/dep-config": "workspace:*",
|
|
44
|
-
"@owlmeans/test-auth": "^0.1.18-rc.
|
|
44
|
+
"@owlmeans/test-auth": "^0.1.18-rc.42",
|
|
45
45
|
"@types/bun": "^1.4.0",
|
|
46
46
|
"nodemon": "^3.1.14",
|
|
47
47
|
"typescript": "^7.0.2"
|
package/src/entity.ts
CHANGED
|
@@ -55,8 +55,14 @@ export const requireEntity = (req: AbstractRequest): ResolvedEntity => {
|
|
|
55
55
|
* its handlers quietly fall back to comparing a slug against stored ids — which surfaces as "this
|
|
56
56
|
* project does not exist" rather than as a missing resolution, and costs an afternoon to find.
|
|
57
57
|
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
58
|
+
* An entity the guard already attached is kept while its slug is exactly the token's: a guard
|
|
59
|
+
* whose authority names the organization itself (an OIDC session of a tenanted client) is the only
|
|
60
|
+
* source such a deployment has, because the registry that owns the organization is not its own.
|
|
61
|
+
* One whose slug differs is never trusted — it is dropped, and resolution proceeds as if it had
|
|
62
|
+
* not been there.
|
|
63
|
+
*
|
|
64
|
+
* Otherwise a no-op when no resolver is registered: such a deployment has no organization store,
|
|
65
|
+
* and the slug is the only identifier it has.
|
|
60
66
|
*
|
|
61
67
|
* @throws {AuthenFailed} when the token names an organization that cannot be resolved.
|
|
62
68
|
*/
|
|
@@ -64,6 +70,13 @@ export const attachEntity = async (
|
|
|
64
70
|
context: BasicContext<any>, request: AbstractRequest
|
|
65
71
|
): Promise<ResolvedEntity | undefined> => {
|
|
66
72
|
const slug = entitySlugOf(request.auth)
|
|
73
|
+
if (request.entity != null) {
|
|
74
|
+
if (slug != null && request.entity.slug === slug) {
|
|
75
|
+
return request.entity
|
|
76
|
+
}
|
|
77
|
+
request.entity = undefined
|
|
78
|
+
}
|
|
79
|
+
|
|
67
80
|
if (slug == null || !context.hasService(ENTITY_RESOLVER)) {
|
|
68
81
|
return undefined
|
|
69
82
|
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test'
|
|
2
|
+
import { AppType, createService, makeBasicContext } from '@owlmeans/context'
|
|
3
|
+
import type { BasicConfig, BasicContext } from '@owlmeans/context'
|
|
4
|
+
import { AuthenFailed, AuthRole } from '@owlmeans/auth'
|
|
5
|
+
import type { Auth } from '@owlmeans/auth'
|
|
6
|
+
import type { AbstractRequest, ResolvedEntity } from '@owlmeans/entrypoint'
|
|
7
|
+
import { attachEntity } from '../src/entity.js'
|
|
8
|
+
import { ENTITY_RESOLVER } from '../src/consts.js'
|
|
9
|
+
import type { EntityResolverService, OrgEntityRef } from '../src/types.js'
|
|
10
|
+
|
|
11
|
+
const ACME: OrgEntityRef = { id: '0123456789abcdef01234567', slug: 'acme', formerSlugs: ['acme-old'], iamKey: 'acme-key' }
|
|
12
|
+
|
|
13
|
+
/** A context with an in-memory registry of one organization, or none at all. */
|
|
14
|
+
const contextWith = async (registry?: OrgEntityRef[]) => {
|
|
15
|
+
const ctx = makeBasicContext<BasicConfig>({ ready: false, service: 'entity-tests', type: AppType.Backend })
|
|
16
|
+
const calls: string[] = []
|
|
17
|
+
if (registry != null) {
|
|
18
|
+
ctx.registerService(createService<EntityResolverService>(ENTITY_RESOLVER, {
|
|
19
|
+
resolve: async value => {
|
|
20
|
+
calls.push(value)
|
|
21
|
+
return registry.find(entity => entity.id === value || entity.slug === value
|
|
22
|
+
|| entity.formerSlugs?.includes(value) || entity.iamKey === value) ?? null
|
|
23
|
+
},
|
|
24
|
+
byId: async id => registry.find(entity => entity.id === id) ?? null,
|
|
25
|
+
mintSlug: async () => 'minted',
|
|
26
|
+
rename: async () => { throw new Error('not used') },
|
|
27
|
+
mintName: async () => { throw new Error('not used') },
|
|
28
|
+
}))
|
|
29
|
+
}
|
|
30
|
+
ctx.configure()
|
|
31
|
+
await ctx.init()
|
|
32
|
+
|
|
33
|
+
return { ctx: ctx as BasicContext<BasicConfig>, calls }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const requestFor = (entitySlug?: string, entity?: ResolvedEntity): AbstractRequest => ({
|
|
37
|
+
alias: 'any', path: '/', params: {}, query: {}, headers: {},
|
|
38
|
+
auth: { token: 't', type: 'oidc', userId: 'u', role: AuthRole.User, scopes: [], isUser: true,
|
|
39
|
+
createdAt: new Date(), ...(entitySlug != null ? { entitySlug } : {}) } as Auth,
|
|
40
|
+
...(entity != null ? { entity } : {}),
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
const attached: ResolvedEntity = { id: 'iam-key-tenant', slug: 'tenant', iamKey: 'iam-key-tenant' }
|
|
44
|
+
|
|
45
|
+
describe('attachEntity — an entity the guard attached', () => {
|
|
46
|
+
test('is kept when its slug is the token one, with no resolver registered', async () => {
|
|
47
|
+
const { ctx } = await contextWith()
|
|
48
|
+
const request = requestFor('tenant', attached)
|
|
49
|
+
|
|
50
|
+
expect(await attachEntity(ctx, request)).toEqual(attached)
|
|
51
|
+
expect(request.entity).toEqual(attached)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
test('is kept without asking a registered resolver', async () => {
|
|
55
|
+
const { ctx, calls } = await contextWith([ACME])
|
|
56
|
+
const request = requestFor('tenant', attached)
|
|
57
|
+
|
|
58
|
+
expect(await attachEntity(ctx, request)).toEqual(attached)
|
|
59
|
+
expect(calls).toEqual([])
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
test('with another slug falls through to the resolver', async () => {
|
|
63
|
+
const { ctx, calls } = await contextWith([ACME])
|
|
64
|
+
const request = requestFor('acme', attached)
|
|
65
|
+
|
|
66
|
+
expect(await attachEntity(ctx, request)).toEqual(ACME)
|
|
67
|
+
expect(request.entity).toEqual(ACME)
|
|
68
|
+
expect(calls).toEqual(['acme'])
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
test('with another slug and no resolver is dropped, never trusted', async () => {
|
|
72
|
+
const { ctx } = await contextWith()
|
|
73
|
+
const request = requestFor('acme', attached)
|
|
74
|
+
|
|
75
|
+
expect(await attachEntity(ctx, request)).toBeUndefined()
|
|
76
|
+
expect(request.entity).toBeUndefined()
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
test('on a token naming no organization is dropped', async () => {
|
|
80
|
+
const { ctx } = await contextWith()
|
|
81
|
+
const request = requestFor(undefined, attached)
|
|
82
|
+
|
|
83
|
+
expect(await attachEntity(ctx, request)).toBeUndefined()
|
|
84
|
+
expect(request.entity).toBeUndefined()
|
|
85
|
+
})
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
describe('attachEntity — the resolver path', () => {
|
|
89
|
+
test('resolves and canonicalizes a retired slug', async () => {
|
|
90
|
+
const { ctx } = await contextWith([ACME])
|
|
91
|
+
const request = requestFor('acme-old')
|
|
92
|
+
|
|
93
|
+
expect(await attachEntity(ctx, request)).toEqual(ACME)
|
|
94
|
+
expect(request.entity).toEqual(ACME)
|
|
95
|
+
expect(request.auth?.entitySlug).toBe('acme')
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
test('refuses a token naming an organization the registry does not know', async () => {
|
|
99
|
+
const { ctx } = await contextWith([ACME])
|
|
100
|
+
|
|
101
|
+
await expect(attachEntity(ctx, requestFor('nobody'))).rejects.toBeInstanceOf(AuthenFailed)
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
test('is a no-op with no resolver and nothing attached', async () => {
|
|
105
|
+
const { ctx } = await contextWith()
|
|
106
|
+
const request = requestFor('acme')
|
|
107
|
+
|
|
108
|
+
expect(await attachEntity(ctx, request)).toBeUndefined()
|
|
109
|
+
expect(request.entity).toBeUndefined()
|
|
110
|
+
expect(request.auth?.entitySlug).toBe('acme')
|
|
111
|
+
})
|
|
112
|
+
})
|