Skip to content

Commit

Permalink
feat: fetch entity statement chain
Browse files Browse the repository at this point in the history
Signed-off-by: Berend Sliedrecht <[email protected]>
  • Loading branch information
berendsliedrecht committed Jul 12, 2024
1 parent 86af531 commit 77b3e3c
Show file tree
Hide file tree
Showing 10 changed files with 384 additions and 34 deletions.
1 change: 1 addition & 0 deletions biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
},
"linter": {
"enabled": true,
"ignore": ["__tests__/*.ts"],
"rules": {
"recommended": true,
"performance": {
Expand Down
66 changes: 38 additions & 28 deletions packages/core/__tests__/fetchEntityConfigurationChains.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import assert from 'node:assert/strict'
import { describe, it } from 'node:test'
import nock from 'nock'
import { fetchEntityConfigurationChains } from '../src/entityConfiguration'
import { EntityConfigurationClaimsOptions, fetchEntityConfigurationChains } from '../src/entityConfiguration'
import type { SignCallback, VerifyCallback } from '../src/utils'
import { setupConfigurationChain } from './utils/setupConfigurationChain'

Expand All @@ -13,8 +13,8 @@ describe('fetch entity configuration chains', () => {
const leafEntityId = 'https://leaf.example.org'
const trustAnchorEntityId = 'https://trust.example.org'

const scopes = []
const claims = []
const scopes: Array<nock.Scope> = []
const claims: Array<EntityConfigurationClaimsOptions> = []

const configurations = await setupConfigurationChain(
[{ entityId: leafEntityId, authorityHints: [trustAnchorEntityId] }, { entityId: trustAnchorEntityId }],
Expand All @@ -37,10 +37,10 @@ describe('fetch entity configuration chains', () => {
})

assert.strictEqual(trustChains.length, 1)
assert.strictEqual(trustChains[0].length, 2)
assert.strictEqual(trustChains[0]!.length, 2)

assert.deepStrictEqual(trustChains[0][0], claims[0])
assert.deepStrictEqual(trustChains[0][1], claims[1])
assert.deepStrictEqual(trustChains[0]![0], claims[0])
assert.deepStrictEqual(trustChains[0]![1], claims[1])

for (const scope of scopes) {
scope.done()
Expand All @@ -52,8 +52,8 @@ describe('fetch entity configuration chains', () => {
const intermediateEntityId = 'https://intermediate.example.org'
const trustAnchorEntityId = 'https://trust.example.org'

const scopes = []
const claims = []
const scopes: Array<nock.Scope> = []
const claims: Array<EntityConfigurationClaimsOptions> = []

const configurations = await setupConfigurationChain(
[
Expand Down Expand Up @@ -83,11 +83,11 @@ describe('fetch entity configuration chains', () => {
})

assert.strictEqual(trustChains.length, 1)
assert.strictEqual(trustChains[0].length, 3)
assert.strictEqual(trustChains[0]!.length, 3)

assert.deepStrictEqual(trustChains[0][0], claims[0])
assert.deepStrictEqual(trustChains[0][1], claims[1])
assert.deepStrictEqual(trustChains[0][2], claims[2])
assert.deepStrictEqual(trustChains[0]![0], claims[0])
assert.deepStrictEqual(trustChains[0]![1], claims[1])
assert.deepStrictEqual(trustChains[0]![2], claims[2])

for (const scope of scopes) {
scope.done()
Expand All @@ -99,8 +99,8 @@ describe('fetch entity configuration chains', () => {
const trustAnchorEntityId = 'https://trust.example.org'
const superiorTrustAnchorEntityId = 'https://trust.superior.example.org'

const scopes = []
const claims = []
const scopes: Array<nock.Scope> = []
const claims: Array<EntityConfigurationClaimsOptions> = []

const configurations = await setupConfigurationChain(
[
Expand Down Expand Up @@ -130,10 +130,10 @@ describe('fetch entity configuration chains', () => {
})

assert.strictEqual(trustChains.length, 1)
assert.strictEqual(trustChains[0].length, 2)
assert.strictEqual(trustChains[0]!.length, 2)

assert.deepStrictEqual(trustChains[0][0], claims[0])
assert.deepStrictEqual(trustChains[0][1], claims[1])
assert.deepStrictEqual(trustChains[0]![0], claims[0])
assert.deepStrictEqual(trustChains[0]![1], claims[1])

for (const scope of scopes) {
scope.done()
Expand All @@ -147,8 +147,8 @@ describe('fetch entity configuration chains', () => {
const trustAnchorOneEntityId = 'https://trust.one.example.org'
const trustAnchorTwoEntityId = 'https://trust.two.example.org'

const scopes = []
const claims = []
const scopes: Array<nock.Scope> = []
const claims: Array<EntityConfigurationClaimsOptions> = []

const configurations = await setupConfigurationChain(
[
Expand Down Expand Up @@ -186,8 +186,8 @@ describe('fetch entity configuration chains', () => {
})

assert.strictEqual(trustChains.length, 2)
assert.strictEqual(trustChains[0].length, 3)
assert.strictEqual(trustChains[1].length, 3)
assert.strictEqual(trustChains[0]!.length, 3)
assert.strictEqual(trustChains[1]!.length, 3)

for (const scope of scopes) {
scope.done()
Expand All @@ -201,8 +201,8 @@ describe('fetch entity configuration chains', () => {
const trustAnchorOneEntityId = 'https://trust.one.example.org'
const trustAnchorTwoEntityId = 'https://trust.two.example.org'

const scopes = []
const claims = []
const scopes: Array<nock.Scope> = []
const claims: Array<EntityConfigurationClaimsOptions> = []

const configurations = await setupConfigurationChain(
[
Expand Down Expand Up @@ -240,15 +240,15 @@ describe('fetch entity configuration chains', () => {
})

assert.strictEqual(trustChains.length, 1)
assert.strictEqual(trustChains[0].length, 3)
assert.strictEqual(trustChains[0]!.length, 3)

for (const scope of scopes) {
scope.done()
}
})

it('should not fetch an entity configuration chain when no authority_hints are found', async () => {
const scopes = []
const scopes: Array<nock.Scope> = []

const configurations = await setupConfigurationChain([{ entityId: 'https://leaf.example.org' }], signJwtCallback)

Expand All @@ -262,7 +262,7 @@ describe('fetch entity configuration chains', () => {

const trustChains = await fetchEntityConfigurationChains({
verifyJwtCallback,
leafEntityId: configurations[0].entityId,
leafEntityId: configurations[0]!.entityId,
trustAnchorEntityIds: ['https://trust.example.org'],
})

Expand All @@ -274,7 +274,7 @@ describe('fetch entity configuration chains', () => {
})

it('should not fetch an entity configuration chain when a loop is found', async () => {
const scopes = []
const scopes: Array<nock.Scope> = []

const leafEntityId = 'https://leaf.example.org'
const intermediateOneEntityId = 'https://intermediate.one.example.org'
Expand Down Expand Up @@ -306,7 +306,7 @@ describe('fetch entity configuration chains', () => {

const trustChains = await fetchEntityConfigurationChains({
verifyJwtCallback,
leafEntityId: configurations[0].entityId,
leafEntityId: configurations[0]!.entityId,
trustAnchorEntityIds: [trustAnchorEntityId],
})

Expand All @@ -316,4 +316,14 @@ describe('fetch entity configuration chains', () => {
scope.done()
}
})

it('should not fetch an entity configuration chain when no trust anchors are provided', async () => {
await assert.rejects(
fetchEntityConfigurationChains({
verifyJwtCallback,
leafEntityId: 'https://example.org',
trustAnchorEntityIds: [],
})
)
})
})
203 changes: 203 additions & 0 deletions packages/core/__tests__/fetchEntityStatementChain.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
import assert from 'node:assert/strict'
import { describe, it } from 'node:test'
import nock from 'nock'
import { type EntityConfigurationClaimsOptions, fetchEntityConfigurationChains } from '../src/entityConfiguration'
import { fetchEntityStatementChain } from '../src/entityStatement'
import type { SignCallback, VerifyCallback } from '../src/utils'
import { setupConfigurationChain } from './utils/setupConfigurationChain'

describe('fetch entity statement chain', () => {
const signJwtCallback: SignCallback = () => Promise.resolve(new Uint8Array(10).fill(42))
const verifyJwtCallback: VerifyCallback = () => Promise.resolve(true)

it('should fetch a basic entity statement chain', async () => {
const leafEntityId = 'https://leaf.example.org'
const trustAnchorEntityId = 'https://trust.example.org'

const scopes: Array<nock.Scope> = []
const claims: Array<EntityConfigurationClaimsOptions> = []

const configurations = await setupConfigurationChain(
[
{ entityId: leafEntityId, authorityHints: [trustAnchorEntityId] },
{ entityId: trustAnchorEntityId, subordinates: [leafEntityId] },
],
signJwtCallback
)

for (const { entityId, jwt, claims: configurationClaims, subordinateStatements } of configurations) {
claims.push(configurationClaims)
const scope = nock(entityId).get('/.well-known/openid-federation').reply(200, jwt, {
'content-type': 'application/entity-statement+jwt',
})

for (const { jwt, entityId } of subordinateStatements ?? []) {
scope.get('/fetch').query({ iss: configurationClaims.iss, sub: entityId }).reply(200, jwt, {
'content-type': 'application/entity-statement+jwt',
})
}

scopes.push(scope)
}

const chains = await fetchEntityConfigurationChains({
verifyJwtCallback,
leafEntityId,
trustAnchorEntityIds: [trustAnchorEntityId],
})

assert.strictEqual(chains.length, 1)
assert.strictEqual(chains[0]!.length, 2)

assert.deepStrictEqual(chains[0]![0], claims[0])
assert.deepStrictEqual(chains[0]![1], claims[1])

const statements = await fetchEntityStatementChain({
verifyJwtCallback,
entityConfigurations: chains[0]!,
})

assert.strictEqual(statements.length, 2)

assert.deepStrictEqual(statements[0]!.iss, trustAnchorEntityId)
assert.deepStrictEqual(statements[0]!.sub, leafEntityId)

assert.deepStrictEqual(statements[1]!.iss, trustAnchorEntityId)
assert.deepStrictEqual(statements[1]!.sub, trustAnchorEntityId)

for (const scope of scopes) {
scope.done()
}
})

it('should fetch a basic entity statement chain of 3 entities', async () => {
const leafEntityId = 'https://leaf.example.org'
const intermediateEntityId = 'https://intermediate.example.org'
const trustAnchorEntityId = 'https://trust.example.org'

const scopes: Array<nock.Scope> = []
const claims: Array<EntityConfigurationClaimsOptions> = []

const configurations = await setupConfigurationChain(
[
{ entityId: leafEntityId, authorityHints: [intermediateEntityId] },
{
entityId: intermediateEntityId,
authorityHints: [trustAnchorEntityId],
subordinates: [leafEntityId],
},
{ entityId: trustAnchorEntityId, subordinates: [intermediateEntityId] },
],
signJwtCallback
)

for (const { entityId, jwt, claims: configurationClaims, subordinateStatements } of configurations) {
claims.push(configurationClaims)
const scope = nock(entityId).get('/.well-known/openid-federation').reply(200, jwt, {
'content-type': 'application/entity-statement+jwt',
})

for (const { jwt, entityId } of subordinateStatements ?? []) {
scope.get('/fetch').query({ iss: configurationClaims.iss, sub: entityId }).reply(200, jwt, {
'content-type': 'application/entity-statement+jwt',
})
}

scopes.push(scope)
}

const chains = await fetchEntityConfigurationChains({
verifyJwtCallback,
leafEntityId,
trustAnchorEntityIds: [trustAnchorEntityId],
})

assert.strictEqual(chains.length, 1)
assert.strictEqual(chains[0]!.length, 3)

assert.deepStrictEqual(chains[0]![0], claims[0])
assert.deepStrictEqual(chains[0]![1], claims[1])
assert.deepStrictEqual(chains[0]![2], claims[2])

const statements = await fetchEntityStatementChain({
verifyJwtCallback,
entityConfigurations: chains[0]!,
})

assert.strictEqual(statements.length, 3)

assert.deepStrictEqual(statements[0]!.iss, intermediateEntityId)
assert.deepStrictEqual(statements[0]!.sub, leafEntityId)

assert.deepStrictEqual(statements[1]!.iss, trustAnchorEntityId)
assert.deepStrictEqual(statements[1]!.sub, intermediateEntityId)

assert.deepStrictEqual(statements[2]!.iss, trustAnchorEntityId)
assert.deepStrictEqual(statements[2]!.sub, trustAnchorEntityId)

for (const scope of scopes) {
scope.done()
}
})

it('should not fetch an entity statement chain when no source_endpoint is found', async () => {
const leafEntityId = 'https://leaf.example.org'
const trustAnchorEntityId = 'https://trust.example.org'

const scopes: Array<nock.Scope> = []
const claims: Array<EntityConfigurationClaimsOptions> = []

const configurations = await setupConfigurationChain(
[
{ entityId: leafEntityId, authorityHints: [trustAnchorEntityId] },
{
entityId: trustAnchorEntityId,
subordinates: [leafEntityId],
includeSourceEndpoint: false,
},
],
signJwtCallback
)

for (const { entityId, jwt, claims: configurationClaims } of configurations) {
claims.push(configurationClaims)
const scope = nock(entityId).get('/.well-known/openid-federation').reply(200, jwt, {
'content-type': 'application/entity-statement+jwt',
})

scopes.push(scope)
}

const chains = await fetchEntityConfigurationChains({
verifyJwtCallback,
leafEntityId,
trustAnchorEntityIds: [trustAnchorEntityId],
})

assert.strictEqual(chains.length, 1)
assert.strictEqual(chains[0]!.length, 2)

assert.deepStrictEqual(chains[0]![0], claims[0])
assert.deepStrictEqual(chains[0]![1], claims[1])

await assert.rejects(
fetchEntityStatementChain({
verifyJwtCallback,
entityConfigurations: chains[0]!,
})
)

for (const scope of scopes) {
scope.done()
}
})

it('should not fetch an entity statement chain when no entity configurations are provided', async () => {
await assert.rejects(
fetchEntityStatementChain({
verifyJwtCallback,
entityConfigurations: [],
})
)
})
})
Loading

0 comments on commit 77b3e3c

Please sign in to comment.