From 35976d3a6a0edc7f4441ea1401857a9f239dea1e Mon Sep 17 00:00:00 2001 From: Bibash Shrestha Date: Fri, 25 Aug 2023 19:07:31 +0545 Subject: [PATCH] logic update for getCredentialsFromFilterList to support multiiple fields --- .vscode/launch.json | 8 +-- lib/credentials/cubit/credentials_cubit.dart | 5 +- .../cubit/credential_manifest_pick_cubit.dart | 18 +++---- .../get_credentials_from_filter_list.dart | 46 +++++++---------- ...dentials_from_presentation_definition.dart | 34 ++++++++++--- ...l_manifest_credential_offer_pick_page.dart | 50 +++++++++++++++---- .../view/credentials_receive_page.dart | 1 + .../cubit/qr_code_scan_cubit.dart | 2 - .../lib/src/models/input_descriptor.dart | 2 + .../src/models/presentation_definition.dart | 4 ++ .../src/models/submission_requirement.dart | 25 ++++++++++ 11 files changed, 127 insertions(+), 68 deletions(-) create mode 100644 packages/credential_manifest/lib/src/models/submission_requirement.dart diff --git a/.vscode/launch.json b/.vscode/launch.json index 0ae10e8fa..b4e33cecb 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -28,13 +28,7 @@ "request": "launch", "type": "dart", "program": "lib/main_production.dart", - "args": [ - "--flavor", - "production", - "--target", - "lib/main_production.dart", - "--release" - ] + "args": ["--flavor", "production", "--target", "lib/main_production.dart"] } ] } diff --git a/lib/credentials/cubit/credentials_cubit.dart b/lib/credentials/cubit/credentials_cubit.dart index bb079cd28..6b85a7b88 100644 --- a/lib/credentials/cubit/credentials_cubit.dart +++ b/lib/credentials/cubit/credentials_cubit.dart @@ -418,14 +418,15 @@ class CredentialsCubit extends Cubit { // need to update code final filteredCredentialList = getCredentialsFromFilterList( - [ + filterList: [ Field(path: [r'$..type'], filter: blockchainType.filter), Field( path: [r'$..associatedAddress'], filter: Filter('String', cryptoAccountData.walletAddress), ), ], - oldCredentialList, + credentialList: oldCredentialList, + isJwtVpInJwtVCRequired: null, ); /// update or create AssociatedAddres credential with new name diff --git a/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/cubit/credential_manifest_pick_cubit.dart b/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/cubit/credential_manifest_pick_cubit.dart index 0d8d78e3d..cd16dd25f 100644 --- a/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/cubit/credential_manifest_pick_cubit.dart +++ b/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/cubit/credential_manifest_pick_cubit.dart @@ -1,4 +1,5 @@ import 'package:altme/dashboard/dashboard.dart'; +import 'package:credential_manifest/credential_manifest.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -10,10 +11,10 @@ part 'credential_manifest_pick_cubit.g.dart'; /// This Cubit provide list of Credentials as required by issuer class CredentialManifestPickCubit extends Cubit { CredentialManifestPickCubit({ - List credentialList = const [], - Map presentationDefinition = const {}, + required List credentialList, + required PresentationDefinition presentationDefinition, required int inputDescriptorIndex, - bool? isJwtVpInJwtVCRequired, + required bool? isJwtVpInJwtVCRequired, }) : super(const CredentialManifestPickState(filteredCredentialList: [])) { filterList( credentialList: credentialList, @@ -25,23 +26,18 @@ class CredentialManifestPickCubit extends Cubit { void filterList({ required List credentialList, - required Map presentationDefinition, + required PresentationDefinition presentationDefinition, required int inputDescriptorIndex, - bool? isJwtVpInJwtVCRequired, + required bool? isJwtVpInJwtVCRequired, }) { /// Get instruction to filter credentials of the wallet final filteredCredentialList = getCredentialsFromPresentationDefinition( presentationDefinition: presentationDefinition, credentialList: List.from(credentialList), inputDescriptorIndex: inputDescriptorIndex, + isJwtVpInJwtVCRequired: isJwtVpInJwtVCRequired, ); - if (isJwtVpInJwtVCRequired != null && isJwtVpInJwtVCRequired) { - filteredCredentialList.removeWhere( - (CredentialModel credentialModel) => credentialModel.jwt == null, - ); - } - emit(state.copyWith(filteredCredentialList: filteredCredentialList)); } diff --git a/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/helpers/get_credentials_from_filter_list.dart b/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/helpers/get_credentials_from_filter_list.dart index 5be15adea..e06ec4e18 100644 --- a/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/helpers/get_credentials_from_filter_list.dart +++ b/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/helpers/get_credentials_from_filter_list.dart @@ -1,25 +1,22 @@ import 'package:altme/dashboard/home/tab_bar/credentials/credential.dart'; import 'package:credential_manifest/credential_manifest.dart'; -List getCredentialsFromFilterList( - List filterList, - List credentialList, -) { +List getCredentialsFromFilterList({ + required List filterList, + required List credentialList, + required bool? isJwtVpInJwtVCRequired, +}) { /// If we have some instructions we filter the wallet's /// crendential list whith it if (filterList.isNotEmpty) { - /// Filter the list of credentials - credentialList.removeWhere((credential) { - /// A credential must satisfy each field to be candidate for presentation - var isPresentationCandidate = true; - for (final field in filterList) { - /// A credential must statisfy at least one path and - /// match pattern to be selected - var isFieldCandidate = false; + final selectedCredential = []; + + for (final field in filterList) { + for (final credential in credentialList) { for (final path in field.path) { final searchList = getTextsFromCredential(path, credential.data); if (searchList.isNotEmpty) { - /// I remove credential not + /// remove unmatched credential searchList.removeWhere( (element) { if (element == field.filter?.pattern || @@ -29,27 +26,18 @@ List getCredentialsFromFilterList( return true; }, ); - - /// if [searchList] is not empty we mark this credential as - /// a valid candidate - if (searchList.isNotEmpty) { - isFieldCandidate = true; - } } - } - /// A credential must satisfy each field to be candidate - /// for presentation - /// So, if one field condition is not satisfied - /// the current credential is not a candidate for presentation - if (isFieldCandidate == false) { - isPresentationCandidate = false; + /// if [searchList] is not empty we mark this credential as + /// a valid candidate + if (searchList.isNotEmpty) { + selectedCredential.add(credential); + } } } + } - /// Remove non candidate credential from the list - return !isPresentationCandidate; - }); + return selectedCredential; } return credentialList; } diff --git a/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/helpers/get_credentials_from_presentation_definition.dart b/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/helpers/get_credentials_from_presentation_definition.dart index 98d9ed18a..d1f73012a 100644 --- a/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/helpers/get_credentials_from_presentation_definition.dart +++ b/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/helpers/get_credentials_from_presentation_definition.dart @@ -2,19 +2,37 @@ import 'package:altme/dashboard/home/tab_bar/credentials/credential.dart'; import 'package:credential_manifest/credential_manifest.dart'; List getCredentialsFromPresentationDefinition({ - required Map presentationDefinition, + required PresentationDefinition presentationDefinition, required List credentialList, required int inputDescriptorIndex, + required bool? isJwtVpInJwtVCRequired, }) { - /// Get instruction to filter credentials of the wallet - final claims = PresentationDefinition.fromJson(presentationDefinition); - final filterList = - claims.inputDescriptors[inputDescriptorIndex].constraints?.fields ?? - []; + final allInputDescriptorConsidered = + presentationDefinition.submissionRequirements != null; + + var filterList = []; + + if (allInputDescriptorConsidered) { + for (final descriptor in presentationDefinition.inputDescriptors) { + if (descriptor.constraints != null && + descriptor.constraints!.fields != null) { + for (final field in descriptor.constraints!.fields!) { + filterList.add(field); + } + } + } + } else { + filterList = presentationDefinition + .inputDescriptors[inputDescriptorIndex].constraints?.fields ?? + []; + } /// If we have some instructions we filter the wallet's /// crendential list whith it - final filteredCredentialList = - getCredentialsFromFilterList(filterList, credentialList); + final filteredCredentialList = getCredentialsFromFilterList( + filterList: filterList, + credentialList: credentialList, + isJwtVpInJwtVCRequired: isJwtVpInJwtVCRequired, + ); return filteredCredentialList; } diff --git a/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/view/credential_manifest_credential_offer_pick_page.dart b/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/view/credential_manifest_credential_offer_pick_page.dart index af15a30cd..a95688516 100644 --- a/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/view/credential_manifest_credential_offer_pick_page.dart +++ b/lib/dashboard/home/tab_bar/credentials/present/pick/credential_manifest/view/credential_manifest_credential_offer_pick_page.dart @@ -34,7 +34,7 @@ class CredentialManifestOfferPickPage extends StatelessWidget { required Issuer issuer, required int inputDescriptorIndex, required List credentialsToBePresented, - bool? isJwtVpInJwtVCRequired, + required bool? isJwtVpInJwtVCRequired, }) { return MaterialPageRoute( builder: (context) => CredentialManifestOfferPickPage( @@ -56,7 +56,7 @@ class CredentialManifestOfferPickPage extends StatelessWidget { final presentationDefinition = credential.credentialManifest!.presentationDefinition!; return CredentialManifestPickCubit( - presentationDefinition: presentationDefinition.toJson(), + presentationDefinition: presentationDefinition, credentialList: context.read().state.credentials, inputDescriptorIndex: inputDescriptorIndex, isJwtVpInJwtVCRequired: isJwtVpInJwtVCRequired, @@ -104,8 +104,17 @@ class CredentialManifestOfferPickView extends StatelessWidget { return BlocBuilder( builder: (context, credentialManifestState) { - final purpose = presentationDefinition - .inputDescriptors[inputDescriptorIndex].purpose; + final allInputDescriptorConsidered = + presentationDefinition.submissionRequirements != null; + + final purpose = allInputDescriptorConsidered + ? presentationDefinition.purpose + : presentationDefinition + .inputDescriptors[inputDescriptorIndex].purpose; + + final status = allInputDescriptorConsidered + ? '1/1' + : '${inputDescriptorIndex + 1}/${presentationDefinition.inputDescriptors.length}'; return BlocListener( listener: (context, scanState) { @@ -134,7 +143,7 @@ class CredentialManifestOfferPickView extends StatelessWidget { body: Column( children: [ Text( - '${inputDescriptorIndex + 1}/${presentationDefinition.inputDescriptors.length}', + status, style: Theme.of(context).textTheme.credentialSteps, ), const SizedBox(height: 10), @@ -155,6 +164,20 @@ class CredentialManifestOfferPickView extends StatelessWidget { final credentialModel = credentialManifestState .filteredCredentialList[index]; + if (allInputDescriptorConsidered) { + final atMost = presentationDefinition + .submissionRequirements![0].count; + final atLeast = presentationDefinition + .submissionRequirements![0].min; + if (atMost != null) { + // + } else if (atLeast != null) { + // + } else { + throw Exception(); + } + } + return CredentialsListPageItem( credentialModel: credentialModel, selected: credentialManifestState.selected @@ -181,18 +204,23 @@ class CredentialManifestOfferPickView extends StatelessWidget { .inputDescriptors[ inputDescriptorIndex]; - final isOptional = inputDescriptor + bool isOptional = inputDescriptor .constraints ?.fields ?.first .optional ?? false; - final bool isOngoingStep = + bool isOngoingStep = inputDescriptorIndex + 1 != presentationDefinition .inputDescriptors.length; + if (allInputDescriptorConsidered) { + isOptional = false; + isOngoingStep = false; + } + if (isOptional) { return MyGradientButton( onPressed: () => present( @@ -270,8 +298,12 @@ class CredentialManifestOfferPickView extends StatelessWidget { getLogger('present') .i('credential to presented - ${updatedCredentials.length}'); - if (inputDescriptorIndex + 1 != - presentationDefinition.inputDescriptors.length) { + final allInputDescriptorConsidered = + presentationDefinition.submissionRequirements != null; + + if (!allInputDescriptorConsidered && + inputDescriptorIndex + 1 != + presentationDefinition.inputDescriptors.length) { await Navigator.of(context).pushReplacement( CredentialManifestOfferPickPage.route( uri: uri, diff --git a/lib/dashboard/home/tab_bar/credentials/receive/view/credentials_receive_page.dart b/lib/dashboard/home/tab_bar/credentials/receive/view/credentials_receive_page.dart index 7c5bf1468..8a7146049 100644 --- a/lib/dashboard/home/tab_bar/credentials/receive/view/credentials_receive_page.dart +++ b/lib/dashboard/home/tab_bar/credentials/receive/view/credentials_receive_page.dart @@ -135,6 +135,7 @@ class _CredentialsReceivePageState extends State { issuer: widget.issuer, inputDescriptorIndex: 0, credentialsToBePresented: [], + isJwtVpInJwtVCRequired: null, ), ); } else { diff --git a/lib/dashboard/qr_code/qr_code_scan/cubit/qr_code_scan_cubit.dart b/lib/dashboard/qr_code/qr_code_scan/cubit/qr_code_scan_cubit.dart index 8c042893d..116e5c914 100644 --- a/lib/dashboard/qr_code/qr_code_scan/cubit/qr_code_scan_cubit.dart +++ b/lib/dashboard/qr_code/qr_code_scan/cubit/qr_code_scan_cubit.dart @@ -597,8 +597,6 @@ class QRCodeScanCubit extends Cubit { } Future launchOIDC4VPAndSIOPV2Flow() async { - final OIDC4VCType currentOIIDC4VCType = - profileCubit.state.model.oidc4vcType; final keys = []; state.uri?.queryParameters.forEach((key, value) => keys.add(key)); if (isUriAsValueValid(keys)) { diff --git a/packages/credential_manifest/lib/src/models/input_descriptor.dart b/packages/credential_manifest/lib/src/models/input_descriptor.dart index d34703a98..73acc168f 100644 --- a/packages/credential_manifest/lib/src/models/input_descriptor.dart +++ b/packages/credential_manifest/lib/src/models/input_descriptor.dart @@ -7,6 +7,7 @@ part 'input_descriptor.g.dart'; class InputDescriptor { InputDescriptor({ this.id, + this.group, this.constraints, this.purpose, this.name, @@ -16,6 +17,7 @@ class InputDescriptor { _$InputDescriptorFromJson(json); final Constraints? constraints; + final List? group; final String? purpose; final String? id; final String? name; diff --git a/packages/credential_manifest/lib/src/models/presentation_definition.dart b/packages/credential_manifest/lib/src/models/presentation_definition.dart index f8c72810a..e190e0ea2 100644 --- a/packages/credential_manifest/lib/src/models/presentation_definition.dart +++ b/packages/credential_manifest/lib/src/models/presentation_definition.dart @@ -1,5 +1,6 @@ import 'package:credential_manifest/src/models/format.dart'; import 'package:credential_manifest/src/models/input_descriptor.dart'; +import 'package:credential_manifest/src/models/submission_requirement.dart'; import 'package:json_annotation/json_annotation.dart'; part 'presentation_definition.g.dart'; @@ -11,6 +12,7 @@ class PresentationDefinition { this.id, this.name, this.purpose, + this.submissionRequirements, this.format, }); @@ -20,6 +22,8 @@ class PresentationDefinition { final String? id; @JsonKey(name: 'input_descriptors') final List inputDescriptors; + @JsonKey(name: 'submission_requirements') + List? submissionRequirements; String? name; String? purpose; Format? format; diff --git a/packages/credential_manifest/lib/src/models/submission_requirement.dart b/packages/credential_manifest/lib/src/models/submission_requirement.dart new file mode 100644 index 000000000..084084b1b --- /dev/null +++ b/packages/credential_manifest/lib/src/models/submission_requirement.dart @@ -0,0 +1,25 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'submission_requirement.g.dart'; + +@JsonSerializable(explicitToJson: true) +class SubmissionRequirement { + SubmissionRequirement({ + this.name, + this.rule, + this.count, + this.min, + this.from, + }); + + factory SubmissionRequirement.fromJson(Map json) => + _$SubmissionRequirementFromJson(json); + + String? name; + String? rule; + int? count; + int? min; + String? from; + + Map toJson() => _$SubmissionRequirementToJson(this); +}