-
Notifications
You must be signed in to change notification settings - Fork 207
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
fix: issue where attributes and predicates match #640
Changes from 4 commits
3b0794a
8174b1f
0a57b2e
028f70c
daf2053
0237061
f610203
4255975
e50b32e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,240 @@ | ||||||||
import type { IndyCredentialMetadata } from '../../modules/credentials/models/CredentialInfo' | ||||||||
import type { CustomCredentialTags, CredentialPreviewAttribute, RequestCredentialMessage } from '@aries-framework/core' | ||||||||
|
||||||||
import { checkProofRequestForDuplicates } from '..' | ||||||||
import { Attachment, AttachmentData } from '../../decorators/attachment/Attachment' | ||||||||
|
||||||||
import { | ||||||||
CredentialMetadataKeys, | ||||||||
AriesFrameworkError, | ||||||||
AttributeFilter, | ||||||||
PredicateType, | ||||||||
ProofAttributeInfo, | ||||||||
ProofPredicateInfo, | ||||||||
ProofRequest, | ||||||||
CredentialState, | ||||||||
OfferCredentialMessage, | ||||||||
CredentialRecord, | ||||||||
CredentialPreview, | ||||||||
} from '@aries-framework/core' | ||||||||
|
||||||||
export const INDY_CREDENTIAL_OFFER_ATTACHMENT_ID = 'libindy-cred-offer-0' | ||||||||
|
||||||||
const credentialPreview = CredentialPreview.fromRecord({ | ||||||||
name: 'John', | ||||||||
age: '99', | ||||||||
}) | ||||||||
|
||||||||
const offerAttachment = new Attachment({ | ||||||||
id: INDY_CREDENTIAL_OFFER_ATTACHMENT_ID, | ||||||||
mimeType: 'application/json', | ||||||||
data: new AttachmentData({ | ||||||||
base64: | ||||||||
'eyJzY2hlbWFfaWQiOiJhYWEiLCJjcmVkX2RlZl9pZCI6IlRoN01wVGFSWlZSWW5QaWFiZHM4MVk6MzpDTDoxNzpUQUciLCJub25jZSI6Im5vbmNlIiwia2V5X2NvcnJlY3RuZXNzX3Byb29mIjp7fX0', | ||||||||
}), | ||||||||
}) | ||||||||
|
||||||||
const mockCredentialRecord = ({ | ||||||||
state, | ||||||||
requestMessage, | ||||||||
metadata, | ||||||||
threadId, | ||||||||
connectionId, | ||||||||
tags, | ||||||||
id, | ||||||||
credentialAttributes, | ||||||||
}: { | ||||||||
state?: CredentialState | ||||||||
requestMessage?: RequestCredentialMessage | ||||||||
metadata?: IndyCredentialMetadata & { indyRequest: Record<string, unknown> } | ||||||||
tags?: CustomCredentialTags | ||||||||
threadId?: string | ||||||||
connectionId?: string | ||||||||
id?: string | ||||||||
credentialAttributes?: CredentialPreviewAttribute[] | ||||||||
} = {}) => { | ||||||||
const offerMessage = new OfferCredentialMessage({ | ||||||||
comment: 'some comment', | ||||||||
credentialPreview: credentialPreview, | ||||||||
offerAttachments: [offerAttachment], | ||||||||
}) | ||||||||
|
||||||||
const credentialRecord = new CredentialRecord({ | ||||||||
offerMessage, | ||||||||
id, | ||||||||
credentialAttributes: credentialAttributes || credentialPreview.attributes, | ||||||||
requestMessage, | ||||||||
state: state || CredentialState.OfferSent, | ||||||||
threadId: threadId ?? offerMessage.id, | ||||||||
connectionId: connectionId ?? '123', | ||||||||
tags, | ||||||||
}) | ||||||||
|
||||||||
if (metadata?.indyRequest) { | ||||||||
credentialRecord.metadata.set(CredentialMetadataKeys.IndyRequest, { ...metadata.indyRequest }) | ||||||||
} | ||||||||
|
||||||||
if (metadata?.schemaId) { | ||||||||
credentialRecord.metadata.add(CredentialMetadataKeys.IndyCredential, { | ||||||||
schemaId: metadata.schemaId, | ||||||||
}) | ||||||||
} | ||||||||
|
||||||||
if (metadata?.credentialDefinitionId) { | ||||||||
credentialRecord.metadata.add(CredentialMetadataKeys.IndyCredential, { | ||||||||
credentialDefinitionId: metadata.credentialDefinitionId, | ||||||||
}) | ||||||||
} | ||||||||
|
||||||||
return credentialRecord | ||||||||
} | ||||||||
|
||||||||
describe('Present Proof', () => { | ||||||||
let secCredDef: CredentialRecord | ||||||||
let credDef: CredentialRecord | ||||||||
|
||||||||
beforeAll(async () => { | ||||||||
credDef = mockCredentialRecord({ state: CredentialState.OfferSent }) | ||||||||
secCredDef = mockCredentialRecord({ state: CredentialState.OfferSent }) | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because we're doing unit tests we can just create a random credential definition id (took this one from https://indyscan.io). Also because the
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we still want to test if it passes with an attribute and a predicate with different credential id's? or should I remove those tests then as well? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that could be useful, but this method doesn't test that. It never touches the credentialDefinitionId (note credentialDefinitionId is different from the credentialId. ) We should do an actual proof exchange to be able to test that (and should be done in proofs.test.ts) |
||||||||
}) | ||||||||
|
||||||||
test('attribute names match, same cred def filter', async () => { | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does not have to be async |
||||||||
const attributes = { | ||||||||
name: new ProofAttributeInfo({ | ||||||||
name: 'age', | ||||||||
restrictions: [ | ||||||||
new AttributeFilter({ | ||||||||
credentialDefinitionId: credDef.id, | ||||||||
}), | ||||||||
], | ||||||||
}), | ||||||||
age: new ProofAttributeInfo({ | ||||||||
name: 'age', | ||||||||
restrictions: [ | ||||||||
new AttributeFilter({ | ||||||||
credentialDefinitionId: credDef.id, | ||||||||
}), | ||||||||
], | ||||||||
}), | ||||||||
} | ||||||||
|
||||||||
const nonce = 'testtesttest12345' | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @TimoGlastra I created an issue for this, #646. Is this solution fine or do we need to expose the generateNonce function in a more accessible way? |
||||||||
|
||||||||
const proofRequest = new ProofRequest({ | ||||||||
name: 'proof-request', | ||||||||
version: '1.0', | ||||||||
nonce, | ||||||||
requestedAttributes: attributes, | ||||||||
}) | ||||||||
|
||||||||
expect(() => checkProofRequestForDuplicates(proofRequest)).toThrowError(AriesFrameworkError) | ||||||||
}) | ||||||||
|
||||||||
test('attribute names match with predicates name, same cred def filter', async () => { | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does not have to be async |
||||||||
const attributes = { | ||||||||
name: new ProofAttributeInfo({ | ||||||||
name: 'age', | ||||||||
restrictions: [ | ||||||||
new AttributeFilter({ | ||||||||
credentialDefinitionId: credDef.id, | ||||||||
}), | ||||||||
], | ||||||||
}), | ||||||||
} | ||||||||
|
||||||||
const predicates = { | ||||||||
age: new ProofPredicateInfo({ | ||||||||
name: 'age', | ||||||||
predicateType: PredicateType.GreaterThanOrEqualTo, | ||||||||
predicateValue: 50, | ||||||||
restrictions: [ | ||||||||
new AttributeFilter({ | ||||||||
credentialDefinitionId: credDef.id, | ||||||||
}), | ||||||||
], | ||||||||
}), | ||||||||
} | ||||||||
|
||||||||
const nonce = 'testtesttest12345' | ||||||||
|
||||||||
const proofRequest = new ProofRequest({ | ||||||||
name: 'proof-request', | ||||||||
version: '1.0', | ||||||||
nonce, | ||||||||
requestedAttributes: attributes, | ||||||||
requestedPredicates: predicates, | ||||||||
}) | ||||||||
|
||||||||
expect(() => checkProofRequestForDuplicates(proofRequest)).toThrowError(AriesFrameworkError) | ||||||||
}) | ||||||||
|
||||||||
test('attribute names match, different cred def filter', async () => { | ||||||||
const attributes = { | ||||||||
name: new ProofAttributeInfo({ | ||||||||
name: 'age', | ||||||||
restrictions: [ | ||||||||
new AttributeFilter({ | ||||||||
credentialDefinitionId: credDef.id, | ||||||||
}), | ||||||||
], | ||||||||
}), | ||||||||
age: new ProofAttributeInfo({ | ||||||||
name: 'age', | ||||||||
restrictions: [ | ||||||||
new AttributeFilter({ | ||||||||
credentialDefinitionId: secCredDef.id, | ||||||||
}), | ||||||||
], | ||||||||
}), | ||||||||
} | ||||||||
|
||||||||
const nonce = 'testtesttest12345' | ||||||||
|
||||||||
const proofRequest = new ProofRequest({ | ||||||||
name: 'proof-request', | ||||||||
version: '1.0', | ||||||||
nonce, | ||||||||
requestedAttributes: attributes, | ||||||||
}) | ||||||||
|
||||||||
expect(() => checkProofRequestForDuplicates(proofRequest)).toThrowError(AriesFrameworkError) | ||||||||
}) | ||||||||
|
||||||||
test('attribute name matches with predicate name, different cred def filter', async () => { | ||||||||
const attributes = { | ||||||||
name: new ProofAttributeInfo({ | ||||||||
name: 'age', | ||||||||
restrictions: [ | ||||||||
new AttributeFilter({ | ||||||||
credentialDefinitionId: credDef.id, | ||||||||
}), | ||||||||
], | ||||||||
}), | ||||||||
} | ||||||||
|
||||||||
const predicates = { | ||||||||
age: new ProofPredicateInfo({ | ||||||||
name: 'age', | ||||||||
predicateType: PredicateType.GreaterThanOrEqualTo, | ||||||||
predicateValue: 50, | ||||||||
restrictions: [ | ||||||||
new AttributeFilter({ | ||||||||
credentialDefinitionId: secCredDef.id, | ||||||||
}), | ||||||||
], | ||||||||
}), | ||||||||
} | ||||||||
|
||||||||
const nonce = 'testtesttest12345' | ||||||||
|
||||||||
const proofRequest = new ProofRequest({ | ||||||||
name: 'proof-request', | ||||||||
version: '1.0', | ||||||||
nonce, | ||||||||
requestedAttributes: attributes, | ||||||||
requestedPredicates: predicates, | ||||||||
}) | ||||||||
|
||||||||
expect(() => checkProofRequestForDuplicates(proofRequest)).toThrowError(AriesFrameworkError) | ||||||||
}) | ||||||||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { AriesFrameworkError } from '../error/AriesFrameworkError' | ||
|
||
export function assertNoDuplicatesInArray(arr: string[]) { | ||
const arrayLength = arr.length | ||
const uniqueArrayLength = new Set(arr).size | ||
|
||
if (arrayLength === uniqueArrayLength) return | ||
|
||
const duplicates = arr.filter((item, index) => arr.indexOf(item) != index) | ||
throw new AriesFrameworkError(`The proof request contains duplicate items: ${duplicates.toString()}`) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import type { ProofRequest } from '../modules/proofs/models/ProofRequest' | ||
|
||
import { assertNoDuplicatesInArray } from './assertNoDuplicates' | ||
|
||
export function attributesToArray(proofRequest: ProofRequest) { | ||
// Attributes can contain either a `name` string value or an `names` string array. We reduce it to a single array | ||
// containing all attribute names from the requested attributes. | ||
return Array.from(proofRequest.requestedAttributes.values()).reduce<string[]>( | ||
(names, a) => [...names, ...(a.name ? [a.name] : a.names ? a.names : [])], | ||
[] | ||
) | ||
} | ||
|
||
export function predicatesToArray(proofRequest: ProofRequest) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think |
||
return Array.from(proofRequest.requestedPredicates.values()).map((a) => a.name) | ||
} | ||
|
||
export function checkProofRequestForDuplicates(proofRequest: ProofRequest) { | ||
const attributes = attributesToArray(proofRequest) | ||
const predicates = predicatesToArray(proofRequest) | ||
assertNoDuplicatesInArray(attributes.concat(predicates)) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -664,3 +664,29 @@ export async function setupProofsTest(faberName: string, aliceName: string, auto | |
aliceReplay, | ||
} | ||
} | ||
|
||
export async function setupSecondCredential( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this still being used somewhere? |
||
faberAgent: Agent, | ||
aliceAgent: Agent, | ||
faberConnection: ConnectionRecord | ||
): Promise<string> { | ||
const { definition } = await prepareForIssuance(faberAgent, ['name', 'age']) | ||
|
||
const credentialPreview = CredentialPreview.fromRecord({ | ||
name: 'John', | ||
age: '99', | ||
}) | ||
|
||
await issueCredential({ | ||
issuerAgent: faberAgent, | ||
issuerConnectionId: faberConnection.id, | ||
holderAgent: aliceAgent, | ||
credentialTemplate: { | ||
credentialDefinitionId: definition.id, | ||
comment: 'some comment about credential', | ||
preview: credentialPreview, | ||
}, | ||
}) | ||
|
||
return definition.id | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does not have to be async