1
0
Форкнуть 0
* complete siop validation

* refactor, but all tests fail

* tests are passing now?

* now tests fail

* almost there

* new tests

* cleanup

* Add flag indicating whether or not InputModelAttestations populated

* getting there

* fixes

* generate a new package

* make hasAttestations a function

* revert

* bug fixes

* create  a new package

* fix

Co-authored-by: Guillermo Proano <gproano@microsoft.com>
This commit is contained in:
Guillermo Proano 2021-06-03 14:15:30 -07:00 коммит произвёл GitHub
Родитель 284738407d
Коммит 8273d2219a
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
9 изменённых файлов: 1241 добавлений и 1060 удалений

Просмотреть файл

@ -1,3 +1,10 @@
# version 0.12.1-preview.25
## Siop Validation refinements
**Type of change:** Feature
**Customer impact:** medium
- Add a flag to RulesContent signaling whether or not it contains input attestations
- Allow callers to specify tokensToValidate
# version 0.12.1-preview.17
## Complete Siop Validation
**Type of change:** bug fix

Просмотреть файл

@ -3,15 +3,15 @@
* Licensed under the MIT License. See License in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { TokenType, IExpectedSiop, ITokenValidator, ClaimToken, AuthenticationErrorCode, AuthenticationErrorDescription } from '../index';
import ErrorHelpers from '../error_handling/ErrorHelpers';
import { AuthenticationErrorCode, AuthenticationErrorDescription, ClaimToken, IExpectedSiop, ITokenValidator, TokenType } from '../index';
import { IValidationResponse } from '../input_validation/IValidationResponse';
import ValidationOptions from '../options/ValidationOptions';
import IValidatorOptions from '../options/IValidatorOptions';
import { SiopValidation } from '../input_validation/SiopValidation';
import ValidationQueue from '../input_validation/ValidationQueue';
import ValidationQueueItem from '../input_validation/ValidationQueueItem';
import { SiopValidation } from '../input_validation/SiopValidation';
import IValidatorOptions from '../options/IValidatorOptions';
import ValidationOptions from '../options/ValidationOptions';
import VerifiableCredentialConstants from '../verifiable_credential/VerifiableCredentialConstants';
import ErrorHelpers from '../error_handling/ErrorHelpers';
const errorCode = (error: number) => ErrorHelpers.errorCode('VCSDKSTVa', error);
/**
@ -35,8 +35,10 @@ export default class SiopTokenValidator implements ITokenValidator {
const options = new ValidationOptions(this.validatorOption, this.expected.type);
const validator = new SiopValidation(options, this.expected);
let validationResult = await validator.validate(queueItem.tokenToValidate);
if (validationResult.result) {
validationResult = this.getTokens(validationResult, queue);
// we might already know what token we have
validationResult = this.getTokens(validationResult, queue, queueItem.tokenToValidate.type);
}
validationResult = this.validateReplayProtection(validationResult);
@ -78,25 +80,31 @@ export default class SiopTokenValidator implements ITokenValidator {
* Get tokens from current item and add them to the queue.
* @param validationResponse The response for the requestor
* @param queue with tokens to validate
* @param knownTokenType a token type declared by the caller
*/
public getTokens(validationResponse: IValidationResponse, queue: ValidationQueue): IValidationResponse {
public getTokens(validationResponse: IValidationResponse, queue: ValidationQueue, knownTokenType?: TokenType): IValidationResponse {
// Check type of SIOP
let type: TokenType;
if (validationResponse.payloadObject[VerifiableCredentialConstants.ATTESTATIONS]) {
type = TokenType.siopPresentationAttestation;
} else if (validationResponse.payloadObject[VerifiableCredentialConstants.PRESENTATION_SUBMISSION]) {
if (validationResponse.payloadObject[VerifiableCredentialConstants.PRESENTATION_SUBMISSION]) {
type = TokenType.siopPresentationExchange;
} else if (knownTokenType === TokenType.siopIssuance || knownTokenType === TokenType.siopPresentationAttestation || validationResponse.payloadObject[VerifiableCredentialConstants.ATTESTATIONS]) {
type = TokenType.siopPresentationAttestation;
} else {
type = TokenType.siop;
}
switch (type) {
case TokenType.siopPresentationAttestation:
const attestations = validationResponse.payloadObject[VerifiableCredentialConstants.ATTESTATIONS];
if (attestations) {
// Decode tokens
try {
validationResponse.tokensToValidate = ClaimToken.getClaimTokensFromAttestations(attestations);
// caller may have alredy filled tokensToValidator, if so return that
if (!validationResponse.tokensArePopulated) {
validationResponse.tokensToValidate = ClaimToken.getClaimTokensFromAttestations(attestations);
}
} catch (err) {
console.error(err);
return {

Просмотреть файл

@ -339,7 +339,7 @@ export default class Validator {
}
private isSiop(type: TokenType | undefined) {
return type === TokenType.siopIssuance || type === TokenType.siopPresentationAttestation
return type === TokenType.siopIssuance || type === TokenType.siopPresentationAttestation;
}
private setValidationResult(queue: ValidationQueue): IValidationResult {

Просмотреть файл

@ -72,6 +72,11 @@ export interface IValidationResponse extends IResponse {
*/
tokensToValidate?: { [key: string]: ClaimToken };
/**
* flag indicating whether or not tokens have been populated
*/
tokensArePopulated?: boolean;
/**
* All claims found in input tokens
*/

Просмотреть файл

@ -3,22 +3,24 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { RulesValidationError } from '../error_handling/RulesValidationError';
import { IdTokenAttestationModel } from './IdTokenAttestationModel';
import { SelfIssuedAttestationModel } from './SelfIssuedAttestationModel';
import { VerifiablePresentationAttestationModel } from './VerifiablePresentationAttestationModel';
import { IdTokenAttestationModel } from './IdTokenAttestationModel';
import { RulesValidationError } from '../error_handling/RulesValidationError';
/**
* Model for attestations for input contract
*/
export class IssuanceAttestationsModel {
private _hasAttestations: boolean = false;
/**
*
* @param selfIssued SelfIssuedAttestationModel instance
* @param presentations an array of VerifiablePresentationModel instances
* @param idTokens an array of IdTokenAttestationModel instances
*/
constructor (
constructor(
public selfIssued?: SelfIssuedAttestationModel,
public presentations?: VerifiablePresentationAttestationModel[],
public idTokens?: IdTokenAttestationModel[]) {
@ -45,10 +47,17 @@ export class IssuanceAttestationsModel {
return allIndexClaims;
}
/**
* indicates whether or not there are attestations defined
*/
public get hasAttestations(): boolean {
return this._hasAttestations;
}
/**
* Derives an IssuanceAttestationModel for input from a Rules attestation model
*/
forInput (): IssuanceAttestationsModel {
forInput(): IssuanceAttestationsModel {
return new IssuanceAttestationsModel(
this.selfIssued === undefined ? undefined : <SelfIssuedAttestationModel>this.selfIssued.forInput(),
this.presentations === undefined ? undefined : this.presentations.map(presentation => <VerifiablePresentationAttestationModel>presentation.forInput()),
@ -59,18 +68,19 @@ export class IssuanceAttestationsModel {
* Populate an instance of IssuanceAttestationsModel from any instance
* @param input object instance to populate from
*/
populateFrom (input: any): void {
const outputAttestations = new Set<string>();
let totalOutputAttestations = 0;
populateFrom(input: any): void {
const outputClaims = new Set<string>();
let totalOutputClaims = 0;
if (input.selfIssued !== undefined) {
this._hasAttestations = true;
this.selfIssued = new SelfIssuedAttestationModel();
this.selfIssued.populateFrom(input.selfIssued);
if (this.selfIssued.mapping) {
const outputAttestationKeys = Object.keys(this.selfIssued.mapping);
totalOutputAttestations += outputAttestationKeys.length;
outputAttestationKeys.forEach(outputAttestation => outputAttestations.add(outputAttestation));
totalOutputClaims += outputAttestationKeys.length;
outputAttestationKeys.forEach(outputAttestation => outputClaims.add(outputAttestation));
}
}
@ -82,12 +92,14 @@ export class IssuanceAttestationsModel {
if (p.mapping) {
const outputAttestationKeys = Object.keys(p.mapping);
totalOutputAttestations += outputAttestationKeys.length;
outputAttestationKeys.forEach(outputAttestation => outputAttestations.add(outputAttestation));
totalOutputClaims += outputAttestationKeys.length;
outputAttestationKeys.forEach(outputAttestation => outputClaims.add(outputAttestation));
}
return p;
});
this._hasAttestations = this._hasAttestations || this.presentations.length > 0;
}
if (input.idTokens !== undefined) {
@ -98,17 +110,19 @@ export class IssuanceAttestationsModel {
if (t.mapping) {
const outputAttestationKeys = Object.keys(t.mapping);
totalOutputAttestations += outputAttestationKeys.length;
outputAttestationKeys.forEach(outputAttestation => outputAttestations.add(outputAttestation));
totalOutputClaims += outputAttestationKeys.length;
outputAttestationKeys.forEach(outputAttestation => outputClaims.add(outputAttestation));
}
return t;
});
this._hasAttestations = this._hasAttestations || this.idTokens.length > 0;
}
// Ensure uniqueness of attestation mapping keys. Non-uniqueness leads to data loss.
if (totalOutputAttestations !== outputAttestations.size) {
if (totalOutputClaims !== outputClaims.size) {
throw new RulesValidationError('Attestation mapping names must be unique.');
}
}
}
}

2191
package-lock.json сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,6 +1,6 @@
{
"name": "verifiablecredentials-verification-sdk-typescript",
"version": "0.12.1-preview.17",
"version": "0.12.1-preview.25",
"description": "Typescript SDK for verifiable credentials",
"main": "dist/lib/index.js",
"types": "dist/lib/index.d.ts",

Просмотреть файл

@ -81,25 +81,6 @@ describe('Rule processor', () => {
TokenGenerator.fetchMock.reset();
}
});
it('should process RequestAttestationsOneVcSaIdtokenResponseOk with missing attestations in SIOP', async () => {
try {
const model: any = new RequestAttestationsOneVcSaIdtokenResponseOk();
model.preSiopResponseOperations = [
{
path: '$.attestations',
operation: () => undefined
}
];
let [validator, _, responderResponse, responder] = await doRequest(model);
let response = await validator.validate(responderResponse);
expect(response.result).toBeFalsy(response.detailedError);
expect(response.code).toEqual('VCSDKSTVa05');
expect(response.status).toEqual(401);
} finally {
TokenGenerator.fetchMock.reset();
}
});
it('should process RequestAttestationsOneVcSaIdtokenResponseOk: The jti claim is missing', async () => {
try {

Просмотреть файл

@ -153,6 +153,7 @@ describe('RulesModel', () => {
expect(Object.keys(roundtripSelfIssuedMapping).length).toEqual(Object.keys(<any>RULES.attestations?.selfIssued?.mapping).length);
// when id is not specified, it's the same as the name
expect((<BaseAttestationModel>roundtripSelfIssued).id).toEqual((<BaseAttestationModel>roundtripSelfIssued).name);
expect(roundtrip.attestations?.hasAttestations).toEqual(true);
// analyze the contents of input claim
const roundtripAlias = <InputClaimModel>roundtrip.attestations?.selfIssued?.mapping?.alias;