Skip to content

Commit

Permalink
fix: better metadata policy validation
Browse files Browse the repository at this point in the history
Signed-off-by: Berend Sliedrecht <[email protected]>
  • Loading branch information
berendsliedrecht committed Jul 9, 2024
1 parent d6a2600 commit 58078c7
Show file tree
Hide file tree
Showing 9 changed files with 64 additions and 28 deletions.
22 changes: 22 additions & 0 deletions packages/core/__tests__/fixtures/metadataPolicyFigure12.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export const metadataPolicyFigure12 = {
openid_relying_party: {
grant_types: {
default: ['authorization_code'],
subset_of: ['authorization_code', 'refresh_token'],
superset_of: ['authorization_code'],
},
token_endpoint_auth_method: {
one_of: ['private_key_jwt', 'self_signed_tls_client_auth'],
essential: true,
},
token_endpoint_auth_signing_alg: {
one_of: ['PS256', 'ES256'],
},
subject_type: {
value: 'pairwise',
},
contacts: {
add: ['[email protected]'],
},
},
}
6 changes: 6 additions & 0 deletions packages/core/__tests__/schemas.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { trustMarkIssuerSchema } from '../src/trustMark'
import { trustMarkOwnerSchema } from '../src/trustMark'

import { federationEntityMetadata } from '../src/metadata'
import { metadataPolicySchema } from '../src/metadata/metadataPolicy'
import { constraintsFigure17 } from './fixtures/constraintsFigure17'
import { entityConfigurationFigure8 } from './fixtures/entityConfigurationFigure8'
import { entityConfigurationFigure9 } from './fixtures/entityConfigurationFigure9'
Expand All @@ -28,6 +29,7 @@ import { entityStatementFigure62 } from './fixtures/entityStatementFigure62'
import { entityStatementFigure70 } from './fixtures/entityStatementFigure70'
import { federationEntityMetadataFigure7 } from './fixtures/federationEntityMetadataFigure7'
import { metadataFigure63 } from './fixtures/metadataFigure63'
import { metadataPolicyFigure12 } from './fixtures/metadataPolicyFigure12'
import { trustMarkClaimsFigure19 } from './fixtures/trustMarkClaimsFigure19'
import { trustMarkClaimsFigure21 } from './fixtures/trustMarkClaimsFigure21'
import { trustMarkClaimsFigure22 } from './fixtures/trustMarkClaimsFigure22'
Expand Down Expand Up @@ -63,6 +65,10 @@ describe('zod validation schemas', () => {
assert.doesNotThrow(() => entityConfigurationSchema.parse(entityConfigurationFigure9))
})

it('should validate figure 12 -- metadata policy', () => {
assert.doesNotThrow(() => metadataPolicySchema.parse(metadataPolicyFigure12))
})

it('should validate figure 17 -- constraints', () => {
assert.doesNotThrow(() => constraintSchema.parse(constraintsFigure17))
})
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/metadata/entity/utils/createEntity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ export const createEntity = ({
return {
identifier,
schema,
policySchema: swapValidators(schema, metadataPolicySchema),
policySchema: swapValidators(schema, metadataPolicySchema.optional()),
}
}
4 changes: 2 additions & 2 deletions packages/core/src/metadata/operator/standard/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ export const addOperator: MetadataOperator = {
key: 'add',
parameterJsonValues: [
z.array(z.string()),
z.array(z.record(z.string().or(z.number())), z.unknown()),
z.array(z.record(z.string().or(z.number()), z.unknown())),
z.array(z.number()),
],
operatorJsonValues: [
z.array(z.string()),
z.array(z.record(z.string().or(z.number())), z.unknown()),
z.array(z.record(z.string().or(z.number()), z.unknown())),
z.array(z.number()),
],
canBeCombinedWith: ['default', 'subset_of', 'superset_of', 'essential'],
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/metadata/operator/standard/oneOf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const oneOfOperator: MetadataOperator = {
parameterJsonValues: [z.string(), z.record(z.string().or(z.number()), z.unknown()), z.number()],
operatorJsonValues: [
z.array(z.string()),
z.array(z.record(z.string().or(z.number())), z.unknown()),
z.array(z.record(z.string().or(z.number()), z.unknown())),
z.array(z.number()),
],
canBeCombinedWith: ['default', 'essential'],
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/metadata/operator/standard/subsetOf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ export const subsetOfOperator: MetadataOperator = {
key: 'subset_of',
parameterJsonValues: [
z.array(z.string()),
z.array(z.record(z.string().or(z.number())), z.unknown()),
z.array(z.record(z.string().or(z.number()), z.unknown())),
z.array(z.number()),
],
operatorJsonValues: [
z.array(z.string()),
z.array(z.record(z.string().or(z.number())), z.unknown()),
z.array(z.record(z.string().or(z.number()), z.unknown())),
z.array(z.number()),
],
canBeCombinedWith: ['add', 'default', 'superset_of', 'essential'],
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/metadata/operator/standard/supersetOf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ export const supersetOfOperator: MetadataOperator = {
key: 'superset_of',
parameterJsonValues: [
z.array(z.string()),
z.array(z.record(z.string().or(z.number())), z.unknown()),
z.array(z.record(z.string().or(z.number()), z.unknown())),
z.array(z.number()),
],
operatorJsonValues: [
z.array(z.string()),
z.array(z.record(z.string().or(z.number())), z.unknown()),
z.array(z.record(z.string().or(z.number()), z.unknown())),
z.array(z.number()),
],
canBeCombinedWith: ['add', 'default', 'subset_of', 'essential'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,23 @@ import type { MetadataOperator } from '../MetadataOperator'
export const createPolicyOperatorSchema = (operator: MetadataOperator) =>
z
.object({
[operator.key]: operator.operatorJsonValues.reduce((acc, schema) => acc.or(schema)),
[operator.key]: operator.operatorJsonValues.reduce((acc, schema) => acc.or(schema)).optional(),
})
.passthrough()
.superRefine((data, ctx) => {
if (!Object.keys(data).every((key) => operator.canBeCombinedWith.includes(key))) {
const dataKeys = Object.keys(data)

if (
dataKeys.includes(operator.key) &&
dataKeys.some((key) => key !== operator.key && !operator.canBeCombinedWith.includes(key))
) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `policy operator '${
operator.key
}' can only be combined with one of: [${operator.canBeCombinedWith.join(', ')}]`,
}' can only be combined with one of: [${operator.canBeCombinedWith.join(
', '
)}]. keys: [${Object.keys(data).join(',')}]`,
})
}

Expand Down
34 changes: 17 additions & 17 deletions packages/core/src/metadata/policy.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { z } from 'zod'
import { addOperator, addOperatorSchema } from './operator'
import { defaultOperator, defaultOperatorSchema } from './operator'
import { essentialOperator, essentialOperatorSchema } from './operator'
import { oneOfOperator, oneOfOperatorSchema } from './operator'
import { supersetOfOperator, supersetOfOperatorSchema } from './operator'
import { subsetOfOperator, subsetOfOperatorSchema } from './operator'
import { valueOperator, valueOperatorSchema } from './operator'
import type { z } from 'zod'
import {
addOperatorSchema,
defaultOperatorSchema,
essentialOperatorSchema,
oneOfOperatorSchema,
subsetOfOperatorSchema,
supersetOfOperatorSchema,
valueOperatorSchema,
} from './operator'

export const metadataPolicySchema = z.object({
[addOperator.key]: addOperatorSchema.optional(),
[defaultOperator.key]: defaultOperatorSchema.optional(),
[essentialOperator.key]: essentialOperatorSchema.optional(),
[oneOfOperator.key]: oneOfOperatorSchema.optional(),
[supersetOfOperator.key]: supersetOfOperatorSchema.optional(),
[subsetOfOperator.key]: subsetOfOperatorSchema.optional(),
[valueOperator.key]: valueOperatorSchema.optional(),
})
export const metadataPolicySchema = addOperatorSchema
.and(defaultOperatorSchema.optional())
.and(essentialOperatorSchema.optional())
.and(oneOfOperatorSchema.optional())
.and(supersetOfOperatorSchema.optional())
.and(subsetOfOperatorSchema.optional())
.and(valueOperatorSchema.optional())

export type MetadataPolicyOperator = z.input<typeof metadataPolicySchema>

0 comments on commit 58078c7

Please sign in to comment.