Skip to content

Commit

Permalink
logic update for getCredentialsFromFilterList to support multiiple fi…
Browse files Browse the repository at this point in the history
…elds
  • Loading branch information
bibash28 committed Aug 25, 2023
1 parent 936d2ab commit 35976d3
Show file tree
Hide file tree
Showing 11 changed files with 127 additions and 68 deletions.
8 changes: 1 addition & 7 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
}
]
}
5 changes: 3 additions & 2 deletions lib/credentials/cubit/credentials_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -418,14 +418,15 @@ class CredentialsCubit extends Cubit<CredentialsState> {

// 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
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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<CredentialManifestPickState> {
CredentialManifestPickCubit({
List<CredentialModel> credentialList = const <CredentialModel>[],
Map<String, dynamic> presentationDefinition = const <String, dynamic>{},
required List<CredentialModel> credentialList,
required PresentationDefinition presentationDefinition,
required int inputDescriptorIndex,
bool? isJwtVpInJwtVCRequired,
required bool? isJwtVpInJwtVCRequired,
}) : super(const CredentialManifestPickState(filteredCredentialList: [])) {
filterList(
credentialList: credentialList,
Expand All @@ -25,23 +26,18 @@ class CredentialManifestPickCubit extends Cubit<CredentialManifestPickState> {

void filterList({
required List<CredentialModel> credentialList,
required Map<String, dynamic> 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));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
import 'package:altme/dashboard/home/tab_bar/credentials/credential.dart';
import 'package:credential_manifest/credential_manifest.dart';

List<CredentialModel> getCredentialsFromFilterList(
List<Field> filterList,
List<CredentialModel> credentialList,
) {
List<CredentialModel> getCredentialsFromFilterList({
required List<Field> filterList,
required List<CredentialModel> 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 = <CredentialModel>[];

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 ||
Expand All @@ -29,27 +26,18 @@ List<CredentialModel> 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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,37 @@ import 'package:altme/dashboard/home/tab_bar/credentials/credential.dart';
import 'package:credential_manifest/credential_manifest.dart';

List<CredentialModel> getCredentialsFromPresentationDefinition({
required Map<String, dynamic> presentationDefinition,
required PresentationDefinition presentationDefinition,
required List<CredentialModel> 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 ??
<Field>[];
final allInputDescriptorConsidered =
presentationDefinition.submissionRequirements != null;

var filterList = <Field>[];

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 ??
<Field>[];
}

/// 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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class CredentialManifestOfferPickPage extends StatelessWidget {
required Issuer issuer,
required int inputDescriptorIndex,
required List<CredentialModel> credentialsToBePresented,
bool? isJwtVpInJwtVCRequired,
required bool? isJwtVpInJwtVCRequired,
}) {
return MaterialPageRoute<void>(
builder: (context) => CredentialManifestOfferPickPage(
Expand All @@ -56,7 +56,7 @@ class CredentialManifestOfferPickPage extends StatelessWidget {
final presentationDefinition =
credential.credentialManifest!.presentationDefinition!;
return CredentialManifestPickCubit(
presentationDefinition: presentationDefinition.toJson(),
presentationDefinition: presentationDefinition,
credentialList: context.read<CredentialsCubit>().state.credentials,
inputDescriptorIndex: inputDescriptorIndex,
isJwtVpInJwtVCRequired: isJwtVpInJwtVCRequired,
Expand Down Expand Up @@ -104,8 +104,17 @@ class CredentialManifestOfferPickView extends StatelessWidget {
return BlocBuilder<CredentialManifestPickCubit,
CredentialManifestPickState>(
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<ScanCubit, ScanState>(
listener: (context, scanState) {
Expand Down Expand Up @@ -134,7 +143,7 @@ class CredentialManifestOfferPickView extends StatelessWidget {
body: Column(
children: <Widget>[
Text(
'${inputDescriptorIndex + 1}/${presentationDefinition.inputDescriptors.length}',
status,
style: Theme.of(context).textTheme.credentialSteps,
),
const SizedBox(height: 10),
Expand All @@ -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
Expand All @@ -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(
Expand Down Expand Up @@ -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<void, void>(
CredentialManifestOfferPickPage.route(
uri: uri,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ class _CredentialsReceivePageState extends State<CredentialsReceivePage> {
issuer: widget.issuer,
inputDescriptorIndex: 0,
credentialsToBePresented: [],
isJwtVpInJwtVCRequired: null,
),
);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -597,8 +597,6 @@ class QRCodeScanCubit extends Cubit<QRCodeScanState> {
}

Future<void> launchOIDC4VPAndSIOPV2Flow() async {
final OIDC4VCType currentOIIDC4VCType =
profileCubit.state.model.oidc4vcType;
final keys = <String>[];
state.uri?.queryParameters.forEach((key, value) => keys.add(key));
if (isUriAsValueValid(keys)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ part 'input_descriptor.g.dart';
class InputDescriptor {
InputDescriptor({
this.id,
this.group,
this.constraints,
this.purpose,
this.name,
Expand All @@ -16,6 +17,7 @@ class InputDescriptor {
_$InputDescriptorFromJson(json);

final Constraints? constraints;
final List<String>? group;
final String? purpose;
final String? id;
final String? name;
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -11,6 +12,7 @@ class PresentationDefinition {
this.id,
this.name,
this.purpose,
this.submissionRequirements,
this.format,
});

Expand All @@ -20,6 +22,8 @@ class PresentationDefinition {
final String? id;
@JsonKey(name: 'input_descriptors')
final List<InputDescriptor> inputDescriptors;
@JsonKey(name: 'submission_requirements')
List<SubmissionRequirement>? submissionRequirements;
String? name;
String? purpose;
Format? format;
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String, dynamic> json) =>
_$SubmissionRequirementFromJson(json);

String? name;
String? rule;
int? count;
int? min;
String? from;

Map<String, dynamic> toJson() => _$SubmissionRequirementToJson(this);
}

0 comments on commit 35976d3

Please sign in to comment.