Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add validation to JSON transformer #830

Merged
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
3d91c49
feat: wip add validation to JSON transformer
Jun 2, 2022
0c350cf
feat: wip address code review feedback.
Jun 3, 2022
5de2d99
chore: merge branch 'main' into feat/json-tranform-validator
Jun 3, 2022
19a8f48
chore: wip fix validation issues
Jun 3, 2022
89e691a
chore: merge remote-tracking branch 'upstream/main' into feat/json-tr…
Jun 3, 2022
29d26f5
chore: fixing more issues
Jun 3, 2022
29f4e6d
chore: fix som emore errors
Jun 3, 2022
587d438
chore: code review feedback
Jun 6, 2022
8d235b5
chore: come more code review feedback
Jun 6, 2022
9ae21e3
chore: fix type check
Jun 6, 2022
e6f8637
chore: fix tests
Jun 6, 2022
f474eba
chore: fix TransportDecorator test
Jun 7, 2022
1eb8def
chore: enable validate for ConnectionService
Jun 7, 2022
71aca28
chore: code review feedback
Jun 9, 2022
f189eca
chore: add test for message validator
Jun 9, 2022
8721bb7
chore: merge branch 'main' into feat/json-tranform-validator
Jun 9, 2022
d13c3c9
chore: fix accDecorator tests
Jun 9, 2022
686e0fe
chore: add tests for validating error array
Jun 9, 2022
ea761dc
chore: wip this fails
Jun 9, 2022
f3e9023
chore: code review feedback
Jun 9, 2022
793117c
chore: refactor
Jun 9, 2022
502244e
chore: wip code review feedback fixing tests
Jun 10, 2022
c2c70b7
chore: fix tests
Jun 10, 2022
7824356
chore: merge remote-tracking branch 'upstream/main' into feat/json-tr…
Jun 10, 2022
3cabb43
chore: fix typo
Jun 10, 2022
50365d7
chore: fix newline in in template literal
Jun 13, 2022
9e04a1b
chore: code review feedback
Jun 13, 2022
c0e45c7
chore: code review feedback
Jun 14, 2022
7695eaa
chore: merge branch 'main' into feat/json-tranform-validator
Jun 15, 2022
992862a
chore: code review feedback
Jun 15, 2022
bdab6bc
chore: code review feedback
Jun 15, 2022
80fee1c
chore: merge remote-tracking branch 'upstream/main' into feat/json-tr…
Jun 15, 2022
cbb0f62
chore: code review feedback
Jun 15, 2022
5a4a9bf
chore: merge remote-tracking branch 'upstream/main' into feat/json-tr…
Jun 15, 2022
d5eecb9
chore: merge remote-tracking branch 'upstream/main' into feat/json-tr…
Jun 16, 2022
8504119
chore: code review feedback
Jun 16, 2022
e71fe1c
chore: merge remote-tracking branch 'upstream/main' into feat/json-tr…
Jun 16, 2022
cee5211
chore: remove unused es-lint ignore line
Jun 16, 2022
d8174fc
fix: variable declaration
Jun 17, 2022
ff330a2
Merge remote-tracking branch 'upstream/main' into feat/json-tranform-…
Jun 17, 2022
baf038b
merge remote-tracking branch 'upstream/main' into feat/json-tranform-…
Jun 20, 2022
3291848
Merge branch 'main' into feat/json-tranform-validator
TimoGlastra Jun 20, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 4 additions & 14 deletions packages/core/src/agent/MessageReceiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import type { Logger } from '../logger'
import type { ConnectionRecord } from '../modules/connections'
import type { InboundTransport } from '../transport'
import type { PlaintextMessage, EncryptedMessage } from '../types'
import type { AgentMessage } from './AgentMessage'
import type { DecryptedMessageContext } from './EnvelopeService'
import type { TransportSession } from './TransportService'

Expand All @@ -17,6 +16,7 @@ import { MessageValidator } from '../utils/MessageValidator'
import { canHandleMessageType, parseMessageType, replaceLegacyDidSovPrefixOnMessage } from '../utils/messageType'

import { AgentConfig } from './AgentConfig'
import { AgentMessage } from './AgentMessage'
import { Dispatcher } from './Dispatcher'
import { EnvelopeService } from './EnvelopeService'
import { MessageSender } from './MessageSender'
Expand Down Expand Up @@ -168,7 +168,7 @@ export class MessageReceiver {
let message: AgentMessage
try {
message = await this.transformMessage(plaintextMessage)
await this.validateMessage(message)
this.validateMessage(message)
morrieinmaas marked this conversation as resolved.
Show resolved Hide resolved
} catch (error) {
if (connection) await this.sendProblemReportMessage(error.message, connection, plaintextMessage)
throw error
Expand Down Expand Up @@ -216,18 +216,8 @@ export class MessageReceiver {
* Validate an AgentMessage instance.
* @param message agent message to validate
*/
private async validateMessage(message: AgentMessage) {
try {
await MessageValidator.validate(message)
} catch (error) {
this.logger.error(`Error validating message ${message.type}`, {
errors: error,
message: message.toJSON(),
})
throw new ProblemReportError(`Error validating message ${message.type}`, {
problemCode: ProblemReportReason.MessageParseFailure,
})
}
private validateMessage(message: AgentMessage) {
return MessageValidator.validateSync(message, AgentMessage)
}

/**
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/agent/MessageSender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import type { DidDocument, Key } from '../modules/dids'
import type { OutOfBandRecord } from '../modules/oob/repository'
import type { OutboundTransport } from '../transport/OutboundTransport'
import type { OutboundMessage, OutboundPackage, EncryptedMessage } from '../types'
import type { AgentMessage } from './AgentMessage'
import type { EnvelopeKeys } from './EnvelopeService'
import type { TransportSession } from './TransportService'

Expand All @@ -22,6 +21,7 @@ import { MessageRepository } from '../storage/MessageRepository'
import { MessageValidator } from '../utils/MessageValidator'
import { getProtocolScheme } from '../utils/uri'

import { AgentMessage } from './AgentMessage'
import { EnvelopeService } from './EnvelopeService'
import { TransportService } from './TransportService'

Expand Down Expand Up @@ -313,7 +313,7 @@ export class MessageSender {
}

try {
await MessageValidator.validate(message)
MessageValidator.validateSync(message, AgentMessage)
morrieinmaas marked this conversation as resolved.
Show resolved Hide resolved
} catch (error) {
this.logger.error(
`Aborting sending outbound message ${message.type} to ${service.serviceEndpoint}. Message validation failed`,
Expand Down
24 changes: 12 additions & 12 deletions packages/core/src/decorators/transport/TransportDecorator.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { ClassValidationError } from '../../error/ClassValidationError'
import { JsonTransformer } from '../../utils/JsonTransformer'
import { MessageValidator } from '../../utils/MessageValidator'

import { TransportDecorator, ReturnRouteTypes } from './TransportDecorator'

const validTransport = (transportJson: Record<string, unknown>) =>
MessageValidator.validate(JsonTransformer.fromJSON(transportJson, TransportDecorator))
const expectValid = (transportJson: Record<string, unknown>) =>
expect(validTransport(transportJson)).resolves.toBeUndefined()
MessageValidator.validateSync(JsonTransformer.fromJSON(transportJson, TransportDecorator), TransportDecorator)
const expectValid = (transportJson: Record<string, unknown>) => expect(validTransport(transportJson)).toBeUndefined()
const expectInvalid = (transportJson: Record<string, unknown>) =>
expect(() => validTransport(transportJson)).toThrowError('Failed to validate class.')
expect(() => validTransport(transportJson)).toThrowError(ClassValidationError)

const valid = {
all: {
Expand Down Expand Up @@ -60,18 +60,18 @@ describe('Decorators | TransportDecorator', () => {
expect(json).toEqual(transformed)
})

it('should only allow correct return_route values', async () => {
it('should only allow correct return_route values', () => {
expect.assertions(4)
await expectValid(valid.all)
await expectValid(valid.none)
await expectValid(valid.thread)
await expectInvalid(invalid.random)
expectValid(valid.all)
expectValid(valid.none)
expectValid(valid.thread)
expectInvalid(invalid.random)
})

it('should require return_route_thread when return_route is thread', async () => {
expect.assertions(3)
await expectValid(valid.thread)
await expectInvalid(invalid.invalidThreadId)
await expectInvalid(invalid.missingThreadId)
expectValid(valid.thread)
expectInvalid(invalid.invalidThreadId)
expectInvalid(invalid.missingThreadId)
})
})
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { ClassValidationError } from '../../../error/ClassValidationError'
import { MessageValidator } from '../../../utils/MessageValidator'
import { ConnectionRequestMessage } from '../messages/ConnectionRequestMessage'

describe('ConnectionRequestMessage', () => {
it('throws an error when the message does not contain a connection parameter', async () => {
expect.assertions(1)

it('throws an error when the message does not contain a connection parameter', () => {
const connectionRequest = new ConnectionRequestMessage({
did: 'did',
label: 'test-label',
Expand All @@ -14,6 +13,8 @@ describe('ConnectionRequestMessage', () => {
// @ts-ignore
delete connectionRequest.connection

return expect(MessageValidator.validate(connectionRequest)).rejects.not.toBeNull()
expect(() => MessageValidator.validateSync(connectionRequest, ConnectionRequestMessage)).toThrowError(
ClassValidationError
)
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export abstract class CredentialFormatService<CF extends CredentialFormat = Cred

// proposal methods
abstract createProposal(options: FormatCreateProposalOptions<CF>): Promise<FormatCreateProposalReturn>
abstract processProposal(options: FormatProcessOptions): Promise<void>
abstract processProposal(options: FormatProcessOptions): void
morrieinmaas marked this conversation as resolved.
Show resolved Hide resolved
abstract acceptProposal(options: FormatAcceptProposalOptions<CF>): Promise<FormatCreateOfferReturn>

// offer methods
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export class IndyCredentialFormatService extends CredentialFormatService<IndyCre
const proposal = new IndyCredPropose(indyCredentialProposal)

try {
await MessageValidator.validate(proposal)
MessageValidator.validateSync(proposal, IndyCredPropose)
} catch (error) {
throw new AriesFrameworkError(`Invalid proposal supplied: ${indyCredentialProposal} in Indy Format Service`)
}
Expand All @@ -133,15 +133,15 @@ export class IndyCredentialFormatService extends CredentialFormatService<IndyCre
return { format, attachment, previewAttributes }
}

public async processProposal({ attachment }: FormatProcessOptions): Promise<void> {
public processProposal({ attachment }: FormatProcessOptions): void {
const credProposalJson = attachment.getDataAsJson()

if (!credProposalJson) {
throw new AriesFrameworkError('Missing indy credential proposal data payload')
}

const credProposal = JsonTransformer.fromJSON(credProposalJson, IndyCredPropose)
await MessageValidator.validate(credProposal)
return MessageValidator.validateSync(credProposal, IndyCredPropose)
}

public async acceptProposal({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ export class V1CredentialService extends CredentialService<[IndyCredentialFormat
previousSentMessage: offerCredentialMessage ?? undefined,
})

await this.formatService.processProposal({
this.formatService.processProposal({
credentialRecord,
attachment: this.rfc0592ProposalAttachmentFromV1ProposeMessage(proposalMessage),
})
Expand Down
2 changes: 0 additions & 2 deletions packages/core/src/modules/dids/methods/web/WebDidResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { Resolver } from 'did-resolver'
import * as didWeb from 'web-did-resolver'

import { JsonTransformer } from '../../../../utils/JsonTransformer'
import { MessageValidator } from '../../../../utils/MessageValidator'
import { DidDocument } from '../../domain'

export class WebDidResolver implements DidResolver {
Expand All @@ -29,7 +28,6 @@ export class WebDidResolver implements DidResolver {
let didDocument = null
if (result.didDocument) {
didDocument = JsonTransformer.fromJSON(result.didDocument, DidDocument)
await MessageValidator.validate(didDocument)
}

return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ describe('ProofRequest', () => {
ProofRequest
)

expect(MessageValidator.validate(proofRequest)).resolves.not.toThrow()
expect(() => MessageValidator.validateSync(proofRequest, ProofRequest)).not.toThrow()
})

it('should throw an error if the proof request json contains an invalid structure', async () => {
Expand Down Expand Up @@ -69,12 +69,11 @@ describe('ProofRequest', () => {
}

expect(() => JsonTransformer.fromJSON(proofRequest, ProofRequest)).toThrowError(ClassValidationError)
let caughtError
try {
JsonTransformer.fromJSON(proofRequest, ProofRequest)
} catch (e) {
caughtError = e
const caughtError = e as ClassValidationError
expect(caughtError.validationErrors).toHaveLength(2)
}
expect(caughtError.validationErrors).toHaveLength(2)
})
})
7 changes: 1 addition & 6 deletions packages/core/src/utils/MessageValidator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { validateOrReject, validateSync } from 'class-validator'
import { validateSync } from 'class-validator'

import { ClassValidationError } from '../error'
import { isValidationErrorArray } from '../error/ValidationErrorUtils'
Expand All @@ -11,10 +11,6 @@ export class MessageValidator {
* @throws array of validation errors {@link ValidationError}
*/
// eslint-disable-next-line @typescript-eslint/ban-types
public static validate<T extends object>(classInstance: T) {
return validateOrReject(classInstance)
}
// eslint-disable-next-line @typescript-eslint/ban-types
public static validateSync<T extends object>(
morrieinmaas marked this conversation as resolved.
Show resolved Hide resolved
// eslint-disable-next-line @typescript-eslint/ban-types
classInstance: T & {},
morrieinmaas marked this conversation as resolved.
Show resolved Hide resolved
morrieinmaas marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -34,6 +30,5 @@ export class MessageValidator {
classType: Object.prototype.constructor(cls).name,
})
}
return errors
}
}
2 changes: 1 addition & 1 deletion packages/core/src/utils/__tests__/JsonTransformer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ describe('JsonTransformer', () => {
const didDocumentString =
'{"@context":["https://w3id.org/did/v1"],"id":"did:peer:1zQmRYBx1pL86DrsxoJ2ZD3w42d7Ng92ErPgFsCSqg8Q1h4i","controller": "nowYouAreUnderMyControl", "keyAgreement":[{"id":"#6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V", "controller": "#id", "type":"Ed25519VerificationKey2018","publicKeyBase58":"ByHnpUCFb1vAfh9CFZ8ZkmUZguURW8nSw889hy6rD8L7"}],"service":[{"id":"#service-0","type":"did-communication","serviceEndpoint":"https://example.com/endpoint","recipientKeys":["#6MkqRYqQiSgvZQdnBytw86Qbs2ZWUkGv22od935YF4s8M7V"],"routingKeys":["did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH#z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH"],"accept":["didcomm/v2","didcomm/aip2;env=rfc587"]}]}'

const didDocument = JsonTransformer.deserialize(didDocumentString, DidDocument, { validate: false })
const didDocument = JsonTransformer.deserialize(didDocumentString, DidDocument)

const keyAgreement = didDocument.keyAgreement ?? []

Expand Down
25 changes: 1 addition & 24 deletions packages/core/src/utils/__tests__/MessageValidator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,6 @@ import { ConnectionInvitationMessage } from '../../modules/connections'
import { MessageValidator } from '../MessageValidator'

describe('MessageValidator', () => {
describe('validate', () => {
it('validates a class instance of ConnectionMessage', () => {
const invitation = new ConnectionInvitationMessage({
did: 'did:sov:test1234',
id: 'afe2867e-58c3-4a8d-85b2-23370dd9c9f0',
label: 'test-label',
})

expect(MessageValidator.validate(invitation)).resolves
})
it('throws an error for an invalid class instance of ConnectionMessage', () => {
const invitation = new ConnectionInvitationMessage({
did: 'did:sov:test1234',
id: 'afe2867e-58c3-4a8d-85b2-23370dd9c9f0',
label: 'test-label',
})

invitation.did = 'hello, world!'

expect(MessageValidator.validate(invitation)).rejects
})
})

describe('validateSync', () => {
it('validates a class instance correctly', () => {
const invitation = new ConnectionInvitationMessage({
Expand All @@ -33,7 +10,7 @@ describe('MessageValidator', () => {
label: 'test-label',
})

expect(MessageValidator.validateSync(invitation, ConnectionInvitationMessage)).toEqual([])
expect(MessageValidator.validateSync(invitation, ConnectionInvitationMessage)).toBeUndefined()
})
it('throws an error for invalid class instance', () => {
const invitation = new ConnectionInvitationMessage({
Expand Down