From 23adf8318fb9d3c5f7295dde67a9ecd40b53616a Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 08:06:19 -0800 Subject: [PATCH 001/373] Update OpenIdRawRequest.swift --- .../RawRequests/OpenIdRawRequest.swift | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/RawRequests/OpenIdRawRequest.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/RawRequests/OpenIdRawRequest.swift index a90d1f43..9cd10efc 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/RawRequests/OpenIdRawRequest.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/RawRequests/OpenIdRawRequest.swift @@ -4,7 +4,24 @@ *--------------------------------------------------------------------------------------------*/ /// Representation of a Raw Open Id Request. -protocol OpenIdRawRequest { +protocol OpenIdRawRequest: Mappable where T == VerifiedIdRequestContent { + + var type: RequestType { get } var raw: Data? { get } } + +protocol VerifiedIdRequestContent { + + var style: RequesterStyle { get } + + var requirement: Requirement { get } + + var rootOfTrust: RootOfTrust { get } + +} + +enum RequestType { + case Presentation + case Issuance +} From 1b6eec94f23bc5c06bcb9035ea86c2fc28299f55 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 08:06:43 -0800 Subject: [PATCH 002/373] Update OpenIdRawRequest dependencies --- .../Extensions/Services/PresentationService+OpenIdForVC.swift | 2 +- .../Requests/Resolvers/OpenIdURLRequestResolver.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Services/PresentationService+OpenIdForVC.swift b/WalletLibrary/WalletLibrary/Extensions/Services/PresentationService+OpenIdForVC.swift index 3d3cf496..8f4feab2 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Services/PresentationService+OpenIdForVC.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Services/PresentationService+OpenIdForVC.swift @@ -12,7 +12,7 @@ import VCServices extension PresentationService: OpenIdForVCResolver, OpenIdForVCResponder { /// Fetches and validates the presentation request. - func getRequest(url: String) async throws -> OpenIdRawRequest { + func getRequest(url: String) async throws -> any OpenIdRawRequest { return try await AsyncWrapper().wrap { () in self.getRequest(usingUrl: url) }() diff --git a/WalletLibrary/WalletLibrary/Requests/Resolvers/OpenIdURLRequestResolver.swift b/WalletLibrary/WalletLibrary/Requests/Resolvers/OpenIdURLRequestResolver.swift index b90386c2..6b64270b 100644 --- a/WalletLibrary/WalletLibrary/Requests/Resolvers/OpenIdURLRequestResolver.swift +++ b/WalletLibrary/WalletLibrary/Requests/Resolvers/OpenIdURLRequestResolver.swift @@ -50,7 +50,7 @@ struct OpenIdURLRequestResolver: RequestResolving { } /// Resolve raw request from input given. - func resolve(input: VerifiedIdRequestInput) async throws -> OpenIdRawRequest { + func resolve(input: VerifiedIdRequestInput) async throws -> any OpenIdRawRequest { guard let input = input as? VerifiedIdRequestURL else { throw OpenIdURLRequestResolverError.unsupportedVerifiedIdRequestInputWith(type: String(describing: type(of: input))) From 35980d5c03f942f82a4320ab03dbb435e0e920b4 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 08:06:50 -0800 Subject: [PATCH 003/373] Update OpenIdForVCResolver.swift --- .../Protocols/Requests/OpenIdForVC/OpenIdForVCResolver.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/OpenIdForVC/OpenIdForVCResolver.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/OpenIdForVC/OpenIdForVCResolver.swift index 1f6e711b..ce18e051 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/OpenIdForVC/OpenIdForVCResolver.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/OpenIdForVC/OpenIdForVCResolver.swift @@ -10,5 +10,5 @@ import VCEntities */ protocol OpenIdForVCResolver { /// Fetches and validates the presentation request. - func getRequest(url: String) async throws -> OpenIdRawRequest + func getRequest(url: String) async throws -> any OpenIdRawRequest } From 26400021ce0b0a881adf28f6fdd725e51fd81004 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 08:07:48 -0800 Subject: [PATCH 004/373] Add OpenIdRequestHandlerTests --- .../Handlers/OpenIdRequestHandlerTests.swift | 86 +++++++++++++++++++ .../OpenIdURLRequestResolverTests.swift | 2 +- 2 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift diff --git a/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift new file mode 100644 index 00000000..8ed46ec1 --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift @@ -0,0 +1,86 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import XCTest +@testable import WalletLibrary + +class OpenIdRequestHandlerTests: XCTestCase { + + func testHandleRequest_WithRawRequest_ReturnsVerifiedIdPresentationRequest() async throws { + + // Arrange Mocked Mapper + let expectedStyle = MockRequesterStyle(requester: "mock requester") + let expectedRequirement = MockRequirement(id: "mockRequirement324") + let expectedRootOfTrust = RootOfTrust(verified: true, source: "mock source") + let expectedContent = MockVerifiedIdRequestContent(style: expectedStyle, + requirement: expectedRequirement, + rootOfTrust: expectedRootOfTrust) + let mockMapper = MockMapper(returnedObject: expectedContent) + + // Arrange Input + let mockRawRequest = MockOpenIdRawRequest(raw: Data()) + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) + let handler = OpenIdRequestHandler(configuration: configuration) + + // Act + let actualRequest = try await handler.handleRequest(from: mockRawRequest) + + // Assert + XCTAssertEqual(actualRequest.style as? MockRequesterStyle, expectedStyle) + XCTAssertEqual(actualRequest.requirement as? MockRequirement, expectedRequirement) + XCTAssertEqual(actualRequest.rootOfTrust.source, expectedRootOfTrust.source) + + } + + func testHandleRequest_WithInvalidMapping_ThrowsError() async throws { + + // Arrange + let mockData = "test data" + let mockInput = MockInput(mockData: mockData) + let resolver = OpenIdURLRequestResolver(openIdResolver: MockOpenIdForVCResolver()) + + // Act + do { + let _ = try await resolver.resolve(input: mockInput) + XCTFail("resolver did not throw an error.") + } catch { + // Assert + XCTAssert(error is OpenIdURLRequestResolverError) + + switch (error as? OpenIdURLRequestResolverError) { + case .unsupportedVerifiedIdRequestInputWith(type: let type): + XCTAssertEqual(type, "MockInput") + default: + XCTFail("error thrown is incorrect type.") + } + } + } +} + +struct MockMapper: Mapping { + + let error: Error? + + let returnedObject: Any? + + init(error: Error? = nil, returnedObject: Any? = nil) { + self.error = error + self.returnedObject = returnedObject + } + + /// Map one object to another. + func map(_ object: T) throws -> T.T { + + if let error = error { + throw error + } + + if let returnedObject = returnedObject as? T.T { + return returnedObject + } + + return try object.map(using: self) + } +} diff --git a/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift index 07c15bf5..2a6afedc 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift @@ -106,7 +106,7 @@ class OpenIdURLRequestResolverTests: XCTestCase { func testCanResolve_WithValidRequestHandler_ReturnsTrue() throws { // Arrange - let mockHandler = OpenIdRequestHandler() + let mockHandler = OpenIdRequestHandler(configuration: LibraryConfiguration(logger: WalletLibraryLogger(), mapper: Mapper())) let resolver = OpenIdURLRequestResolver(openIdResolver: MockOpenIdForVCResolver()) // Act From 7694b18d09958d868703fc7767205535c526d1d5 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 08:05:56 -0800 Subject: [PATCH 005/373] Extend PresentationRequest --- .../PresentationRequest+OpenIdRawRequest.swift | 8 ++++++++ .../VCSDK/PresentationDescriptor+Mappable.swift | 7 ++++--- .../VCSDK/PresentationRequest+Mappable.swift | 17 +++++++++++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationRequest+Mappable.swift diff --git a/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationRequest+OpenIdRawRequest.swift b/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationRequest+OpenIdRawRequest.swift index b7e3cc00..df718542 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationRequest+OpenIdRawRequest.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationRequest+OpenIdRawRequest.swift @@ -10,6 +10,14 @@ import VCEntities */ extension VCEntities.PresentationRequest: OpenIdRawRequest { + var type: RequestType { + if content.prompt == "create" { + return .Issuance + } + + return .Presentation + } + /// The raw representation of the request. var raw: Data? { do { diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDescriptor+Mappable.swift index 3cfe4cb7..7da4eb8c 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDescriptor+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDescriptor+Mappable.swift @@ -15,11 +15,12 @@ extension VCEntities.PresentationDescriptor: Mappable { let acceptedIssuers = issuers?.compactMap { $0.iss } ?? [] - var issuanceOptions: IssuanceOptions? = nil + var issuanceOptions: [IssuanceOption] = [] if let contracts = contracts, !contracts.isEmpty { - issuanceOptions = IssuanceOptions(acceptedIssuers: acceptedIssuers, - credentialIssuerMetadata: contracts) + issuanceOptions = contracts.compactMap { + IssuanceOption(credentialIssuerMetadata: $0) + } } return VerifiedIdRequirement(encrypted: encrypted ?? false, diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationRequest+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationRequest+Mappable.swift new file mode 100644 index 00000000..f4539d2a --- /dev/null +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationRequest+Mappable.swift @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import VCEntities + +/** + * An extension of the VCEntities.PresentationRequest class to be able + * to map PresentationRequest to VerifiedIdRequestContent. + */ +extension VCEntities.PresentationRequest: Mappable { + + func map(using mapper: Mapping) throws -> VerifiedIdRequestContent { + throw VerifiedIdClientError.TODO(message: "implement") + } +} From e56382c10a26562204e7af066831d83de74c2150 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 08:20:05 -0800 Subject: [PATCH 006/373] Add library configuration --- .../WalletLibrary.xcodeproj/project.pbxproj | 20 +++++++++++++++++++ .../PresentationDescriptor+Mappable.swift | 6 ++---- .../WalletLibrary/LibraryConfiguration.swift | 16 +++++++++++++++ 3 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 WalletLibrary/WalletLibrary/LibraryConfiguration.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index b8e4d910..e440d854 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -51,6 +51,9 @@ 55A81C0D2991BF86002C259A /* MockInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81C0C2991BF86002C259A /* MockInput.swift */; }; 55A81C102991C211002C259A /* MockResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81C0F2991C211002C259A /* MockResolver.swift */; }; 55A81C132991C829002C259A /* MockHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81C122991C829002C259A /* MockHandler.swift */; }; + 55E2F06C299553300008010D /* PresentationRequest+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */; }; + 55E2F06F299553B40008010D /* OpenIdRequestHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F06E299553B40008010D /* OpenIdRequestHandlerTests.swift */; }; + 55E2F071299554110008010D /* LibraryConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F070299554110008010D /* LibraryConfiguration.swift */; }; 55E33684293E7B6600CD2ED7 /* PromiseKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55E33679293E7B5F00CD2ED7 /* PromiseKit.framework */; }; 55E336D1293FA6F400CD2ED7 /* VerifiedIdRequirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E336D0293FA6F400CD2ED7 /* VerifiedIdRequirement.swift */; }; 55E336D4293FA75300CD2ED7 /* VerifiedId.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E336D3293FA75300CD2ED7 /* VerifiedId.swift */; }; @@ -289,6 +292,9 @@ 55A81C0C2991BF86002C259A /* MockInput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockInput.swift; sourceTree = ""; }; 55A81C0F2991C211002C259A /* MockResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockResolver.swift; sourceTree = ""; }; 55A81C122991C829002C259A /* MockHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockHandler.swift; sourceTree = ""; }; + 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PresentationRequest+Mappable.swift"; sourceTree = ""; }; + 55E2F06E299553B40008010D /* OpenIdRequestHandlerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenIdRequestHandlerTests.swift; sourceTree = ""; }; + 55E2F070299554110008010D /* LibraryConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryConfiguration.swift; sourceTree = ""; }; 55E33666293E7B5000CD2ED7 /* PMKFoundation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = PMKFoundation.xcodeproj; path = Foundation/PMKFoundation.xcodeproj; sourceTree = ""; }; 55E3366F293E7B5F00CD2ED7 /* PromiseKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = PromiseKit.xcodeproj; path = PromiseKit/PromiseKit.xcodeproj; sourceTree = ""; }; 55E33685293E7B7D00CD2ED7 /* Secp256k1.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Secp256k1.xcodeproj; path = Secp256k1/Secp256k1.xcodeproj; sourceTree = ""; }; @@ -425,6 +431,7 @@ 55E3370F293FDDF400CD2ED7 /* Requests */, 55E336D2293FA74100CD2ED7 /* VerifiedId */, 55E337632943747E00CD2ED7 /* Utilities */, + 55E2F070299554110008010D /* LibraryConfiguration.swift */, 550E3208298B0EA4004E7716 /* VerifiedIdClient.swift */, 550E3207298B0EA4004E7716 /* VerifiedIdClientBuilder.swift */, 552E5095293E6AB200868F47 /* WalletLibrary.h */, @@ -520,6 +527,7 @@ 5534E676294A1FA0005D0765 /* AccessTokenDescriptor+Mappable.swift */, 5534E671294A19D5005D0765 /* IdTokenDescriptor+Mappable.swift */, 5534E64C294A0888005D0765 /* PresentationDescriptor+Mappable.swift */, + 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */, ); path = VCSDK; sourceTree = ""; @@ -553,6 +561,7 @@ 55A81BF02991BB13002C259A /* Requests */ = { isa = PBXGroup; children = ( + 55E2F06D299553B40008010D /* Handlers */, 550A9193299402450014D030 /* Resolvers */, 55A81C062991BB2C002C259A /* RequestResolverFactoryTests.swift */, 55A81C082991BB3A002C259A /* RequestHandlerFactoryTests.swift */, @@ -596,6 +605,14 @@ path = Handlers; sourceTree = ""; }; + 55E2F06D299553B40008010D /* Handlers */ = { + isa = PBXGroup; + children = ( + 55E2F06E299553B40008010D /* OpenIdRequestHandlerTests.swift */, + ); + path = Handlers; + sourceTree = ""; + }; 55E33667293E7B5000CD2ED7 /* Products */ = { isa = PBXGroup; children = ( @@ -1117,6 +1134,7 @@ 55A81BE52991B2E5002C259A /* RequestResolverFactory.swift in Sources */, 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */, 55E33714293FDEEF00CD2ED7 /* Contract.swift in Sources */, + 55E2F071299554110008010D /* LibraryConfiguration.swift in Sources */, 550E3209298B0EA4004E7716 /* VerifiedIdClientBuilder.swift in Sources */, 550A9192299400820014D030 /* OpenIdForVCResponder.swift in Sources */, 5534E672294A19D5005D0765 /* IdTokenDescriptor+Mappable.swift in Sources */, @@ -1125,6 +1143,7 @@ 550A919E29940ED70014D030 /* OpenIdRawRequest.swift in Sources */, 55E337BC2948B5FE00CD2ED7 /* PresentationService+OpenIdForVC.swift in Sources */, 55E336D4293FA75300CD2ED7 /* VerifiedId.swift in Sources */, + 55E2F06C299553300008010D /* PresentationRequest+Mappable.swift in Sources */, 55A81BCA2991AB82002C259A /* RequestResolving.swift in Sources */, 55E33708293FD25900CD2ED7 /* AccessTokenRequirement.swift in Sources */, ); @@ -1134,6 +1153,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 55E2F06F299553B40008010D /* OpenIdRequestHandlerTests.swift in Sources */, 55E3376B29478C5000CD2ED7 /* AsyncWrapperTests.swift in Sources */, 5534E6AD294AAFA6005D0765 /* PresentationDescriptorMappingTests.swift in Sources */, 55A81C132991C829002C259A /* MockHandler.swift in Sources */, diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDescriptor+Mappable.swift index 7da4eb8c..86f1c7ba 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDescriptor+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDescriptor+Mappable.swift @@ -15,12 +15,10 @@ extension VCEntities.PresentationDescriptor: Mappable { let acceptedIssuers = issuers?.compactMap { $0.iss } ?? [] - var issuanceOptions: [IssuanceOption] = [] + var issuanceOptions: IssuanceOptions? = nil if let contracts = contracts, !contracts.isEmpty { - issuanceOptions = contracts.compactMap { - IssuanceOption(credentialIssuerMetadata: $0) - } + issuanceOptions = IssuanceOptions(acceptedIssuers: acceptedIssuers, credentialIssuerMetadata: contracts) } return VerifiedIdRequirement(encrypted: encrypted ?? false, diff --git a/WalletLibrary/WalletLibrary/LibraryConfiguration.swift b/WalletLibrary/WalletLibrary/LibraryConfiguration.swift new file mode 100644 index 00000000..40980e4a --- /dev/null +++ b/WalletLibrary/WalletLibrary/LibraryConfiguration.swift @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +class LibraryConfiguration { + + let logger: WalletLibraryLogger + + let mapper: Mapping + + init(logger: WalletLibraryLogger, mapper: Mapping) { + self.logger = logger + self.mapper = mapper + } +} From ceb9c58524a195bd3aa1ac5c9941f0b5effeda02 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 08:05:01 -0800 Subject: [PATCH 007/373] Update OpenIdRequestHandler.swift --- .../Handlers/OpenIdRequestHandler.swift | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift index 30061bed..2cf6e335 100644 --- a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift +++ b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift @@ -8,9 +8,37 @@ */ struct OpenIdRequestHandler: RequestHandling { + private let configuration: LibraryConfiguration + + init(configuration: LibraryConfiguration) { + self.configuration = configuration + } + /// Create a VeriifiedIdRequest based on the Open Id raw request given. /// TODO: post private preview, input needs to be more generic to support multiple profiles of Open Id. - func handleRequest(from: OpenIdRawRequest) async throws -> any VerifiedIdRequest { + func handleRequest(from request: any OpenIdRawRequest) async throws -> any VerifiedIdRequest { + + if request.type == .Issuance { + return try await handleIssuanceRequest(from: request) + } + + return try await handlePresentationRequest(from: request) + } + + private func handleIssuanceRequest(from request: any OpenIdRawRequest) async throws -> any VerifiedIdIssuanceRequest { throw VerifiedIdClientError.TODO(message: "implement") } + + private func handlePresentationRequest(from request: any OpenIdRawRequest) async throws -> any VerifiedIdPresentationRequest { + let content = try configuration.mapper.map(request) + return OpenIdPresentationRequest(content: content, configuration: configuration) + } +} + +protocol VerifiedIdIssuanceRequest: VerifiedIdRequest where T == any VerifiedIdRequest { + +} + +protocol VerifiedIdPresentationRequest: VerifiedIdRequest where T == Void { + } From 823313b96aa813cb620b1a2c9aba45bb72de87da Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 08:07:09 -0800 Subject: [PATCH 008/373] Add mocks --- .../MockVerifiedIdRequestContent.swift | 21 ++++++++++++++ .../OpenIdForVC/MockOpenIdRawRequest.swift | 9 +++++- .../OpenIdForVC/MockOpenIdforVCResolver.swift | 6 ++-- .../Requirements/MockRequirement.swift | 29 +++++++++++++++++++ .../Requests/Styles/MockRequesterStyle.swift | 15 ++++++++++ 5 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiedIdRequestContent.swift create mode 100644 WalletLibrary/WalletLibraryTests/Mocks/Requests/Requirements/MockRequirement.swift create mode 100644 WalletLibrary/WalletLibraryTests/Mocks/Requests/Styles/MockRequesterStyle.swift diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiedIdRequestContent.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiedIdRequestContent.swift new file mode 100644 index 00000000..d4606403 --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiedIdRequestContent.swift @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +@testable import WalletLibrary + +struct MockVerifiedIdRequestContent: VerifiedIdRequestContent { + + var style: WalletLibrary.RequesterStyle + + var requirement: WalletLibrary.Requirement + + var rootOfTrust: WalletLibrary.RootOfTrust + + init(style: RequesterStyle, requirement: Requirement, rootOfTrust: RootOfTrust) { + self.style = style + self.requirement = requirement + self.rootOfTrust = rootOfTrust + } +} diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/OpenIdForVC/MockOpenIdRawRequest.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/OpenIdForVC/MockOpenIdRawRequest.swift index 373dd283..25a4e26b 100644 --- a/WalletLibrary/WalletLibraryTests/Mocks/Requests/OpenIdForVC/MockOpenIdRawRequest.swift +++ b/WalletLibrary/WalletLibraryTests/Mocks/Requests/OpenIdForVC/MockOpenIdRawRequest.swift @@ -7,9 +7,16 @@ struct MockOpenIdRawRequest: OpenIdRawRequest, Equatable { + var type: RequestType + var raw: Data? - init(raw: Data?) { + init(raw: Data?, type: RequestType = .Presentation) { self.raw = raw + self.type = type + } + + func map(using mapper: Mapping) throws -> VerifiedIdRequestContent { + throw VerifiedIdClientError.TODO(message: "not implemented") } } diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/OpenIdForVC/MockOpenIdforVCResolver.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/OpenIdForVC/MockOpenIdforVCResolver.swift index 3294cb98..7524ca35 100644 --- a/WalletLibrary/WalletLibraryTests/Mocks/Requests/OpenIdForVC/MockOpenIdforVCResolver.swift +++ b/WalletLibrary/WalletLibraryTests/Mocks/Requests/OpenIdForVC/MockOpenIdforVCResolver.swift @@ -12,13 +12,13 @@ struct MockOpenIdForVCResolver: OpenIdForVCResolver { case nilCallback } - private let mockGetRequestCallback: ((String) -> OpenIdRawRequest)? + private let mockGetRequestCallback: ((String) -> any OpenIdRawRequest)? - init(mockGetRequestCallback: ((String) -> OpenIdRawRequest)? = nil) { + init(mockGetRequestCallback: ((String) -> any OpenIdRawRequest)? = nil) { self.mockGetRequestCallback = mockGetRequestCallback } - func getRequest(url: String) async throws -> OpenIdRawRequest { + func getRequest(url: String) async throws -> any OpenIdRawRequest { guard let mockGetRequestCallback = mockGetRequestCallback else { throw MockOpenIdForVCResolverError.nilCallback diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/Requirements/MockRequirement.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/Requirements/MockRequirement.swift new file mode 100644 index 00000000..7e5970c9 --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Mocks/Requests/Requirements/MockRequirement.swift @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +@testable import WalletLibrary + +struct MockRequirement: Requirement, Equatable { + + let required: Bool + + let id: String + + let mockValidateCallback: (() throws -> Void)? + + init(id: String, required: Bool = true, mockValidateCallback: (() throws -> Void)? = nil) { + self.id = id + self.required = required + self.mockValidateCallback = mockValidateCallback + } + + func validate() throws { + try mockValidateCallback?() + } + + static func == (lhs: MockRequirement, rhs: MockRequirement) -> Bool { + return lhs.id == rhs.id + } +} diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/Styles/MockRequesterStyle.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/Styles/MockRequesterStyle.swift new file mode 100644 index 00000000..91aa8ad0 --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Mocks/Requests/Styles/MockRequesterStyle.swift @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +@testable import WalletLibrary + +struct MockRequesterStyle: RequesterStyle, Equatable { + + let requester: String + + init(requester: String) { + self.requester = requester + } +} From 8146ebdf011bf2132e95eeb2e8b351bf239d74a4 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 08:06:56 -0800 Subject: [PATCH 009/373] Create OpenIdPresentationRequest.swift --- .../OpenId/OpenIdPresentationRequest.swift | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 WalletLibrary/WalletLibrary/Requests/Protocols/OpenId/OpenIdPresentationRequest.swift diff --git a/WalletLibrary/WalletLibrary/Requests/Protocols/OpenId/OpenIdPresentationRequest.swift b/WalletLibrary/WalletLibrary/Requests/Protocols/OpenId/OpenIdPresentationRequest.swift new file mode 100644 index 00000000..dbf541b9 --- /dev/null +++ b/WalletLibrary/WalletLibrary/Requests/Protocols/OpenId/OpenIdPresentationRequest.swift @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +class OpenIdPresentationRequest: VerifiedIdPresentationRequest { + + let style: RequesterStyle + + let requirement: Requirement + + let rootOfTrust: RootOfTrust + + private let configuration: LibraryConfiguration + + init(content: VerifiedIdRequestContent, configuration: LibraryConfiguration) { + self.style = content.style + self.requirement = content.requirement + self.rootOfTrust = content.rootOfTrust + self.configuration = configuration + } + + func isSatisfied() -> Bool { + return false + } + + func complete() async -> Result<(), Error> { + return Result.failure(VerifiedIdClientError.TODO(message: "implement")) + } + + func cancel(message: String?) -> Result { + return Result.failure(VerifiedIdClientError.TODO(message: "implement")) + } +} From 424d998fcb613d4b5add074f9cb1d15025ea3165 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 08:27:24 -0800 Subject: [PATCH 010/373] Update folder naming for request protocols --- .../WalletLibrary.xcodeproj/project.pbxproj | 50 ++++++++++++++++++- .../OpenId/OpenIdPresentationRequest.swift | 0 2 files changed, 49 insertions(+), 1 deletion(-) rename WalletLibrary/WalletLibrary/Requests/{Protocols => RequestProtocols}/OpenId/OpenIdPresentationRequest.swift (100%) diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index e440d854..c8ead99d 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -54,6 +54,10 @@ 55E2F06C299553300008010D /* PresentationRequest+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */; }; 55E2F06F299553B40008010D /* OpenIdRequestHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F06E299553B40008010D /* OpenIdRequestHandlerTests.swift */; }; 55E2F071299554110008010D /* LibraryConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F070299554110008010D /* LibraryConfiguration.swift */; }; + 55E2F076299555280008010D /* MockRequirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F073299555280008010D /* MockRequirement.swift */; }; + 55E2F077299555280008010D /* MockRequesterStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F075299555280008010D /* MockRequesterStyle.swift */; }; + 55E2F0792995553E0008010D /* MockVerifiedIdRequestContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F0782995553E0008010D /* MockVerifiedIdRequestContent.swift */; }; + 55E2F07D2995561E0008010D /* OpenIdPresentationRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F07C2995561E0008010D /* OpenIdPresentationRequest.swift */; }; 55E33684293E7B6600CD2ED7 /* PromiseKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55E33679293E7B5F00CD2ED7 /* PromiseKit.framework */; }; 55E336D1293FA6F400CD2ED7 /* VerifiedIdRequirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E336D0293FA6F400CD2ED7 /* VerifiedIdRequirement.swift */; }; 55E336D4293FA75300CD2ED7 /* VerifiedId.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E336D3293FA75300CD2ED7 /* VerifiedId.swift */; }; @@ -295,6 +299,10 @@ 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PresentationRequest+Mappable.swift"; sourceTree = ""; }; 55E2F06E299553B40008010D /* OpenIdRequestHandlerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenIdRequestHandlerTests.swift; sourceTree = ""; }; 55E2F070299554110008010D /* LibraryConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryConfiguration.swift; sourceTree = ""; }; + 55E2F073299555280008010D /* MockRequirement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockRequirement.swift; sourceTree = ""; }; + 55E2F075299555280008010D /* MockRequesterStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockRequesterStyle.swift; sourceTree = ""; }; + 55E2F0782995553E0008010D /* MockVerifiedIdRequestContent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockVerifiedIdRequestContent.swift; sourceTree = ""; }; + 55E2F07C2995561E0008010D /* OpenIdPresentationRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenIdPresentationRequest.swift; sourceTree = ""; }; 55E33666293E7B5000CD2ED7 /* PMKFoundation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = PMKFoundation.xcodeproj; path = Foundation/PMKFoundation.xcodeproj; sourceTree = ""; }; 55E3366F293E7B5F00CD2ED7 /* PromiseKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = PromiseKit.xcodeproj; path = PromiseKit/PromiseKit.xcodeproj; sourceTree = ""; }; 55E33685293E7B7D00CD2ED7 /* Secp256k1.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Secp256k1.xcodeproj; path = Secp256k1/Secp256k1.xcodeproj; sourceTree = ""; }; @@ -580,11 +588,14 @@ 55A81C0B2991BF73002C259A /* Requests */ = { isa = PBXGroup; children = ( - 550A919629940D810014D030 /* OpenIdForVC */, 55A81C112991C81A002C259A /* Handlers */, + 550A919629940D810014D030 /* OpenIdForVC */, + 55E2F072299555280008010D /* Requirements */, 55A81C0E2991C202002C259A /* Resolvers */, + 55E2F074299555280008010D /* Styles */, 55A81C0C2991BF86002C259A /* MockInput.swift */, 550A913C29930D970014D030 /* MockRawRequest.swift */, + 55E2F0782995553E0008010D /* MockVerifiedIdRequestContent.swift */, ); path = Requests; sourceTree = ""; @@ -613,6 +624,38 @@ path = Handlers; sourceTree = ""; }; + 55E2F072299555280008010D /* Requirements */ = { + isa = PBXGroup; + children = ( + 55E2F073299555280008010D /* MockRequirement.swift */, + ); + path = Requirements; + sourceTree = ""; + }; + 55E2F074299555280008010D /* Styles */ = { + isa = PBXGroup; + children = ( + 55E2F075299555280008010D /* MockRequesterStyle.swift */, + ); + path = Styles; + sourceTree = ""; + }; + 55E2F07A2995561E0008010D /* RequestProtocols */ = { + isa = PBXGroup; + children = ( + 55E2F07B2995561E0008010D /* OpenId */, + ); + path = RequestProtocols; + sourceTree = ""; + }; + 55E2F07B2995561E0008010D /* OpenId */ = { + isa = PBXGroup; + children = ( + 55E2F07C2995561E0008010D /* OpenIdPresentationRequest.swift */, + ); + path = OpenId; + sourceTree = ""; + }; 55E33667293E7B5000CD2ED7 /* Products */ = { isa = PBXGroup; children = ( @@ -716,6 +759,7 @@ 55E3370F293FDDF400CD2ED7 /* Requests */ = { isa = PBXGroup; children = ( + 55E2F07A2995561E0008010D /* RequestProtocols */, 550A91712993117B0014D030 /* Input */, 550A9156299310D40014D030 /* Handlers */, 550A916D299310D40014D030 /* Resolvers */, @@ -1146,6 +1190,7 @@ 55E2F06C299553300008010D /* PresentationRequest+Mappable.swift in Sources */, 55A81BCA2991AB82002C259A /* RequestResolving.swift in Sources */, 55E33708293FD25900CD2ED7 /* AccessTokenRequirement.swift in Sources */, + 55E2F07D2995561E0008010D /* OpenIdPresentationRequest.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1153,6 +1198,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 55E2F0792995553E0008010D /* MockVerifiedIdRequestContent.swift in Sources */, 55E2F06F299553B40008010D /* OpenIdRequestHandlerTests.swift in Sources */, 55E3376B29478C5000CD2ED7 /* AsyncWrapperTests.swift in Sources */, 5534E6AD294AAFA6005D0765 /* PresentationDescriptorMappingTests.swift in Sources */, @@ -1164,7 +1210,9 @@ 55A81C0D2991BF86002C259A /* MockInput.swift in Sources */, 550A919829940DCE0014D030 /* MockOpenIdforVCResolver.swift in Sources */, 550A91A0299412790014D030 /* MockOpenIdRawRequest.swift in Sources */, + 55E2F076299555280008010D /* MockRequirement.swift in Sources */, 5534E68C294A8B8F005D0765 /* AccessTokenDescriptorMappingTests.swift in Sources */, + 55E2F077299555280008010D /* MockRequesterStyle.swift in Sources */, 552E50A0293E6AB200868F47 /* VerifiedIdClientTests.swift in Sources */, 5534E688294A5F16005D0765 /* IdTokenDescriptorMappingTests.swift in Sources */, 550A913D29930D970014D030 /* MockRawRequest.swift in Sources */, diff --git a/WalletLibrary/WalletLibrary/Requests/Protocols/OpenId/OpenIdPresentationRequest.swift b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdPresentationRequest.swift similarity index 100% rename from WalletLibrary/WalletLibrary/Requests/Protocols/OpenId/OpenIdPresentationRequest.swift rename to WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdPresentationRequest.swift From d00ff6eb1413424aa1f10906068952ff45469c02 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 08:35:27 -0800 Subject: [PATCH 011/373] Move VerifiedIdRequest to different folder --- .../WalletLibrary.xcodeproj/project.pbxproj | 4 ++-- .../{ => Protocols}/Requests/VerifiedIdRequest.swift | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) rename WalletLibrary/WalletLibrary/{ => Protocols}/Requests/VerifiedIdRequest.swift (77%) diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index c8ead99d..c5b1a970 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -767,7 +767,6 @@ 55E336CF293FA6D900CD2ED7 /* Requirements */, 55E3370D293FD84700CD2ED7 /* IssuanceRequest.swift */, 5534E5B42948FEB5005D0765 /* RootOfTrust.swift */, - 550E320F298B113C004E7716 /* VerifiedIdRequest.swift */, 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */, 55A81BE62991B2F3002C259A /* RequestHandlerFactory.swift */, ); @@ -828,9 +827,10 @@ 55E337B72948B50800CD2ED7 /* Handlers */, 55E3376629437E1000CD2ED7 /* OpenIdForVC */, 55E33710293FDE1400CD2ED7 /* Request.swift */, - 550E3211298B1150004E7716 /* VerifiedIdRequestInput.swift */, 550E3213298B11A5004E7716 /* Requirement.swift */, 55A81BC92991AB82002C259A /* RequestResolving.swift */, + 550E320F298B113C004E7716 /* VerifiedIdRequest.swift */, + 550E3211298B1150004E7716 /* VerifiedIdRequestInput.swift */, ); path = Requests; sourceTree = ""; diff --git a/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequest.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequest.swift similarity index 77% rename from WalletLibrary/WalletLibrary/Requests/VerifiedIdRequest.swift rename to WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequest.swift index 1d45c53a..d63488a1 100644 --- a/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequest.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequest.swift @@ -28,3 +28,15 @@ public protocol VerifiedIdRequest { /// Cancel the request with an optional message. func cancel(message: String?) -> Result } + +/** + * Internal Protocol that represents an Issuance Request. + * TODO: add VerifiedId Style + */ +protocol VerifiedIdIssuanceRequest: VerifiedIdRequest where T == any VerifiedIdRequest { } + +/** + * Internal Protocol that represents a Presentation Request. + * TODO: add VerifiedId Style + */ +protocol VerifiedIdPresentationRequest: VerifiedIdRequest where T == Void { } From c10d378199b9bafa9787d0dfa189fe8af446e655 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 08:35:40 -0800 Subject: [PATCH 012/373] Revert PresentationDescriptor Mapping --- .../Mappings/VCSDK/PresentationDescriptor+Mappable.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDescriptor+Mappable.swift index 86f1c7ba..3cfe4cb7 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDescriptor+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDescriptor+Mappable.swift @@ -18,7 +18,8 @@ extension VCEntities.PresentationDescriptor: Mappable { var issuanceOptions: IssuanceOptions? = nil if let contracts = contracts, !contracts.isEmpty { - issuanceOptions = IssuanceOptions(acceptedIssuers: acceptedIssuers, credentialIssuerMetadata: contracts) + issuanceOptions = IssuanceOptions(acceptedIssuers: acceptedIssuers, + credentialIssuerMetadata: contracts) } return VerifiedIdRequirement(encrypted: encrypted ?? false, From beb4c4dedbebbd074b1f84f85631b4e61deb6629 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 08:36:08 -0800 Subject: [PATCH 013/373] Move VerifiedIdIssuanceRequest and PresentationRequest out of OpenIdRequestHandler --- .../Requests/Handlers/OpenIdRequestHandler.swift | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift index 2cf6e335..8b8137ea 100644 --- a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift +++ b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift @@ -5,6 +5,7 @@ /** * Handles a raw Open Id request and configures a VeriifedIdRequest object. + * Post Private Preview TODO: add processors to support multiple profiles of open id. */ struct OpenIdRequestHandler: RequestHandling { @@ -15,7 +16,6 @@ struct OpenIdRequestHandler: RequestHandling { } /// Create a VeriifiedIdRequest based on the Open Id raw request given. - /// TODO: post private preview, input needs to be more generic to support multiple profiles of Open Id. func handleRequest(from request: any OpenIdRawRequest) async throws -> any VerifiedIdRequest { if request.type == .Issuance { @@ -34,11 +34,3 @@ struct OpenIdRequestHandler: RequestHandling { return OpenIdPresentationRequest(content: content, configuration: configuration) } } - -protocol VerifiedIdIssuanceRequest: VerifiedIdRequest where T == any VerifiedIdRequest { - -} - -protocol VerifiedIdPresentationRequest: VerifiedIdRequest where T == Void { - -} From de63ffc4dca087011e516bc7ce15a7c6ba338a91 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 08:46:49 -0800 Subject: [PATCH 014/373] Remove old data models. --- .../WalletLibrary.xcodeproj/project.pbxproj | 16 +++++----- .../Protocols/Requests/Request.swift | 14 --------- .../Requests/IssuanceRequest.swift | 31 ------------------- 3 files changed, 8 insertions(+), 53 deletions(-) delete mode 100644 WalletLibrary/WalletLibrary/Protocols/Requests/Request.swift delete mode 100644 WalletLibrary/WalletLibrary/Requests/IssuanceRequest.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index c5b1a970..c4e57994 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -58,6 +58,8 @@ 55E2F077299555280008010D /* MockRequesterStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F075299555280008010D /* MockRequesterStyle.swift */; }; 55E2F0792995553E0008010D /* MockVerifiedIdRequestContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F0782995553E0008010D /* MockVerifiedIdRequestContent.swift */; }; 55E2F07D2995561E0008010D /* OpenIdPresentationRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F07C2995561E0008010D /* OpenIdPresentationRequest.swift */; }; + 55E2F07F29955A3B0008010D /* VerifiedIdRequestContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F07E29955A3B0008010D /* VerifiedIdRequestContent.swift */; }; + 55E2F08129955A960008010D /* RequestType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F08029955A960008010D /* RequestType.swift */; }; 55E33684293E7B6600CD2ED7 /* PromiseKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55E33679293E7B5F00CD2ED7 /* PromiseKit.framework */; }; 55E336D1293FA6F400CD2ED7 /* VerifiedIdRequirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E336D0293FA6F400CD2ED7 /* VerifiedIdRequirement.swift */; }; 55E336D4293FA75300CD2ED7 /* VerifiedId.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E336D3293FA75300CD2ED7 /* VerifiedId.swift */; }; @@ -66,8 +68,6 @@ 55E33708293FD25900CD2ED7 /* AccessTokenRequirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E33707293FD25900CD2ED7 /* AccessTokenRequirement.swift */; }; 55E3370A293FD3E000CD2ED7 /* IdTokenRequirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E33709293FD3E000CD2ED7 /* IdTokenRequirement.swift */; }; 55E3370C293FD61E00CD2ED7 /* SelfAttestedClaimRequirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E3370B293FD61E00CD2ED7 /* SelfAttestedClaimRequirement.swift */; }; - 55E3370E293FD84700CD2ED7 /* IssuanceRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E3370D293FD84700CD2ED7 /* IssuanceRequest.swift */; }; - 55E33711293FDE1400CD2ED7 /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E33710293FDE1400CD2ED7 /* Request.swift */; }; 55E33714293FDEEF00CD2ED7 /* Contract.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E33713293FDEEF00CD2ED7 /* Contract.swift */; }; 55E337652943749600CD2ED7 /* AsyncWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E337642943749600CD2ED7 /* AsyncWrapper.swift */; }; 55E3376829437E3500CD2ED7 /* OpenIdForVCResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E3376729437E3500CD2ED7 /* OpenIdForVCResolver.swift */; }; @@ -303,6 +303,8 @@ 55E2F075299555280008010D /* MockRequesterStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockRequesterStyle.swift; sourceTree = ""; }; 55E2F0782995553E0008010D /* MockVerifiedIdRequestContent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockVerifiedIdRequestContent.swift; sourceTree = ""; }; 55E2F07C2995561E0008010D /* OpenIdPresentationRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenIdPresentationRequest.swift; sourceTree = ""; }; + 55E2F07E29955A3B0008010D /* VerifiedIdRequestContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdRequestContent.swift; sourceTree = ""; }; + 55E2F08029955A960008010D /* RequestType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestType.swift; sourceTree = ""; }; 55E33666293E7B5000CD2ED7 /* PMKFoundation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = PMKFoundation.xcodeproj; path = Foundation/PMKFoundation.xcodeproj; sourceTree = ""; }; 55E3366F293E7B5F00CD2ED7 /* PromiseKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = PromiseKit.xcodeproj; path = PromiseKit/PromiseKit.xcodeproj; sourceTree = ""; }; 55E33685293E7B7D00CD2ED7 /* Secp256k1.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Secp256k1.xcodeproj; path = Secp256k1/Secp256k1.xcodeproj; sourceTree = ""; }; @@ -313,8 +315,6 @@ 55E33707293FD25900CD2ED7 /* AccessTokenRequirement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessTokenRequirement.swift; sourceTree = ""; }; 55E33709293FD3E000CD2ED7 /* IdTokenRequirement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdTokenRequirement.swift; sourceTree = ""; }; 55E3370B293FD61E00CD2ED7 /* SelfAttestedClaimRequirement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelfAttestedClaimRequirement.swift; sourceTree = ""; }; - 55E3370D293FD84700CD2ED7 /* IssuanceRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssuanceRequest.swift; sourceTree = ""; }; - 55E33710293FDE1400CD2ED7 /* Request.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Request.swift; sourceTree = ""; }; 55E33713293FDEEF00CD2ED7 /* Contract.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Contract.swift; sourceTree = ""; }; 55E337642943749600CD2ED7 /* AsyncWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncWrapper.swift; sourceTree = ""; }; 55E3376729437E3500CD2ED7 /* OpenIdForVCResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenIdForVCResolver.swift; sourceTree = ""; }; @@ -765,7 +765,6 @@ 550A916D299310D40014D030 /* Resolvers */, 55E33712293FDEDF00CD2ED7 /* Contract */, 55E336CF293FA6D900CD2ED7 /* Requirements */, - 55E3370D293FD84700CD2ED7 /* IssuanceRequest.swift */, 5534E5B42948FEB5005D0765 /* RootOfTrust.swift */, 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */, 55A81BE62991B2F3002C259A /* RequestHandlerFactory.swift */, @@ -826,10 +825,11 @@ 550A919C29940ECA0014D030 /* RawRequests */, 55E337B72948B50800CD2ED7 /* Handlers */, 55E3376629437E1000CD2ED7 /* OpenIdForVC */, - 55E33710293FDE1400CD2ED7 /* Request.swift */, 550E3213298B11A5004E7716 /* Requirement.swift */, 55A81BC92991AB82002C259A /* RequestResolving.swift */, + 55E2F08029955A960008010D /* RequestType.swift */, 550E320F298B113C004E7716 /* VerifiedIdRequest.swift */, + 55E2F07E29955A3B0008010D /* VerifiedIdRequestContent.swift */, 550E3211298B1150004E7716 /* VerifiedIdRequestInput.swift */, ); path = Requests; @@ -1151,9 +1151,10 @@ 550E3212298B1150004E7716 /* VerifiedIdRequestInput.swift in Sources */, 550A919B29940E9E0014D030 /* PresentationRequest+OpenIdRawRequest.swift in Sources */, 5534E667294A0B6C005D0765 /* Mappable.swift in Sources */, + 55E2F08129955A960008010D /* RequestType.swift in Sources */, 55A81BE72991B2F3002C259A /* RequestHandlerFactory.swift in Sources */, + 55E2F07F29955A3B0008010D /* VerifiedIdRequestContent.swift in Sources */, 550A916F299310D40014D030 /* OpenIdRequestHandler.swift in Sources */, - 55E3370E293FD84700CD2ED7 /* IssuanceRequest.swift in Sources */, 55E3370A293FD3E000CD2ED7 /* IdTokenRequirement.swift in Sources */, 55E336D8293FCE0500CD2ED7 /* IssuanceOptions.swift in Sources */, 550E320C298B0F30004E7716 /* WalletLibraryLogConsumer.swift in Sources */, @@ -1182,7 +1183,6 @@ 550E3209298B0EA4004E7716 /* VerifiedIdClientBuilder.swift in Sources */, 550A9192299400820014D030 /* OpenIdForVCResponder.swift in Sources */, 5534E672294A19D5005D0765 /* IdTokenDescriptor+Mappable.swift in Sources */, - 55E33711293FDE1400CD2ED7 /* Request.swift in Sources */, 5534E677294A1FA0005D0765 /* AccessTokenDescriptor+Mappable.swift in Sources */, 550A919E29940ED70014D030 /* OpenIdRawRequest.swift in Sources */, 55E337BC2948B5FE00CD2ED7 /* PresentationService+OpenIdForVC.swift in Sources */, diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/Request.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/Request.swift deleted file mode 100644 index b7e1ece4..00000000 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/Request.swift +++ /dev/null @@ -1,14 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Microsoft Corporation. All rights reserved. -* Licensed under the MIT License. See License.txt in the project root for license information. -*--------------------------------------------------------------------------------------------*/ - -/** - * Request Protocol that all Verified Id Requests will conform to. - */ -public protocol Request { - - /// A string to describe the requester. - var requester: String { get } - -} diff --git a/WalletLibrary/WalletLibrary/Requests/IssuanceRequest.swift b/WalletLibrary/WalletLibrary/Requests/IssuanceRequest.swift deleted file mode 100644 index f116f6d2..00000000 --- a/WalletLibrary/WalletLibrary/Requests/IssuanceRequest.swift +++ /dev/null @@ -1,31 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Microsoft Corporation. All rights reserved. -* Licensed under the MIT License. See License.txt in the project root for license information. -*--------------------------------------------------------------------------------------------*/ - -/** - * Issuance Request object containing information needed to get a Verified Id. - */ -public struct IssuanceRequest: Request { - - /// URL of the customer who is the issuer. - public let requester: String - - /// Information such as a list of contract URLs to describe where to get contract. - public let credentialIssuerMetadata: [String] - - /// A list of contracts that can be used to issue the verified id. - public internal(set) var contracts: [Contract] - - /// The optional pin requirements needed to display a pin for the request. - public let pinRequirements: PinRequirement? - - /// The credential format data that describes requested verified id accepted formats. - let credentialFormats: [CredentialFormat]? - - /// The state that is sent back with issuance completion response. - let state: String - - /// The optional raw representation of the request. - let raw: String? -} From 507a8de4029194b9c7633eb68a11e70282b90052 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 08:46:59 -0800 Subject: [PATCH 015/373] Create RequestType.swift --- .../Protocols/Requests/RequestType.swift | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 WalletLibrary/WalletLibrary/Protocols/Requests/RequestType.swift diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/RequestType.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/RequestType.swift new file mode 100644 index 00000000..4e7b59f5 --- /dev/null +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/RequestType.swift @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +/** + * The request types that the library supports. + */ +enum RequestType { + case Presentation + case Issuance +} From 3d1899dc04333c88ebc24464fe21ce96a4b235d0 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 08:47:02 -0800 Subject: [PATCH 016/373] Create VerifiedIdRequestContent.swift --- .../Requests/VerifiedIdRequestContent.swift | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequestContent.swift diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequestContent.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequestContent.swift new file mode 100644 index 00000000..b2175eef --- /dev/null +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequestContent.swift @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +protocol VerifiedIdRequestContent { + + var style: RequesterStyle { get } + + var requirement: Requirement { get } + + var rootOfTrust: RootOfTrust { get } + +} From 8e8f579942de3627123bcdafdd232e84cc4acbb2 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 08:47:17 -0800 Subject: [PATCH 017/373] Add comment to configuration --- WalletLibrary/WalletLibrary/LibraryConfiguration.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/WalletLibrary/WalletLibrary/LibraryConfiguration.swift b/WalletLibrary/WalletLibrary/LibraryConfiguration.swift index 40980e4a..2743ce1a 100644 --- a/WalletLibrary/WalletLibrary/LibraryConfiguration.swift +++ b/WalletLibrary/WalletLibrary/LibraryConfiguration.swift @@ -3,6 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/** + * Utilities such as logger, mapper, httpclient (post private preview) that are configured in builder and + * all of library will use. + */ class LibraryConfiguration { let logger: WalletLibraryLogger From aa07d8250a05bf3201e6f05263aeeffa3f7ac992 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 08:47:23 -0800 Subject: [PATCH 018/373] Update PresentationRequest+OpenIdRawRequest.swift --- .../Entities/PresentationRequest+OpenIdRawRequest.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationRequest+OpenIdRawRequest.swift b/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationRequest+OpenIdRawRequest.swift index df718542..d3000cd8 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationRequest+OpenIdRawRequest.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationRequest+OpenIdRawRequest.swift @@ -10,8 +10,13 @@ import VCEntities */ extension VCEntities.PresentationRequest: OpenIdRawRequest { + /// If prompt equals create, the requet is an issuance request. + private var promptValueForIssuance: String { + "create" + } + var type: RequestType { - if content.prompt == "create" { + if content.prompt == promptValueForIssuance { return .Issuance } From 2a77e3c7264d071cd34f754800d248d92d0d1249 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 08:47:27 -0800 Subject: [PATCH 019/373] Update OpenIdRawRequest.swift --- .../RawRequests/OpenIdRawRequest.swift | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/RawRequests/OpenIdRawRequest.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/RawRequests/OpenIdRawRequest.swift index 9cd10efc..0c274831 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/RawRequests/OpenIdRawRequest.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/RawRequests/OpenIdRawRequest.swift @@ -3,25 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/// Representation of a Raw Open Id Request. +/** + * Representation of a Raw Open Id Request. + * Object that conforms to this protocol must be able to map to VerifiedIdRequestContent. + */ protocol OpenIdRawRequest: Mappable where T == VerifiedIdRequestContent { var type: RequestType { get } var raw: Data? { get } } - -protocol VerifiedIdRequestContent { - - var style: RequesterStyle { get } - - var requirement: Requirement { get } - - var rootOfTrust: RootOfTrust { get } - -} - -enum RequestType { - case Presentation - case Issuance -} From 77e76fb7912084b44aedc4000491cca564b70f45 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 08:56:04 -0800 Subject: [PATCH 020/373] Fix type in presentation request extension. --- .../Entities/PresentationRequest+OpenIdRawRequest.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationRequest+OpenIdRawRequest.swift b/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationRequest+OpenIdRawRequest.swift index d3000cd8..33373b6d 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationRequest+OpenIdRawRequest.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationRequest+OpenIdRawRequest.swift @@ -10,7 +10,7 @@ import VCEntities */ extension VCEntities.PresentationRequest: OpenIdRawRequest { - /// If prompt equals create, the requet is an issuance request. + /// If prompt equals create, the request is an issuance request. private var promptValueForIssuance: String { "create" } From 99b2e18ce5b0cb37b62f97baa1c998cc50f561af Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 08:56:20 -0800 Subject: [PATCH 021/373] Add comments to Requests. --- .../Protocols/Requests/VerifiedIdRequestContent.swift | 4 ++++ .../RequestProtocols/OpenId/OpenIdPresentationRequest.swift | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequestContent.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequestContent.swift index b2175eef..34b087f3 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequestContent.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequestContent.swift @@ -3,6 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/** + * Contents in a Verified Id Request. + * This protocol is used to map protocol specific requests to common request object. + */ protocol VerifiedIdRequestContent { var style: RequesterStyle { get } diff --git a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdPresentationRequest.swift b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdPresentationRequest.swift index dbf541b9..4268002f 100644 --- a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdPresentationRequest.swift +++ b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdPresentationRequest.swift @@ -3,6 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/** + * Presentation Requst that is Open Id specific. + * TODO: we will need open id specific data to implement complete and cancel. + */ class OpenIdPresentationRequest: VerifiedIdPresentationRequest { let style: RequesterStyle From 5d8eb686b55d015b6f6bfc1e7cd17d4005022140 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 08:56:27 -0800 Subject: [PATCH 022/373] Update MockVerifiedIdRequestContent.swift --- .../Mocks/Requests/MockVerifiedIdRequestContent.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiedIdRequestContent.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiedIdRequestContent.swift index d4606403..3c64bc16 100644 --- a/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiedIdRequestContent.swift +++ b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiedIdRequestContent.swift @@ -7,11 +7,11 @@ struct MockVerifiedIdRequestContent: VerifiedIdRequestContent { - var style: WalletLibrary.RequesterStyle + var style: RequesterStyle - var requirement: WalletLibrary.Requirement + var requirement: Requirement - var rootOfTrust: WalletLibrary.RootOfTrust + var rootOfTrust: RootOfTrust init(style: RequesterStyle, requirement: Requirement, rootOfTrust: RootOfTrust) { self.style = style From 786cd5dc8c91cdbc793e6e87b726303af38fc07b Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 09:08:03 -0800 Subject: [PATCH 023/373] Add MockMapper --- .../WalletLibrary.xcodeproj/project.pbxproj | 12 +++++++ .../Mocks/Utilities/MockMapper.swift | 32 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 WalletLibrary/WalletLibraryTests/Mocks/Utilities/MockMapper.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index c4e57994..9940658e 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -60,6 +60,7 @@ 55E2F07D2995561E0008010D /* OpenIdPresentationRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F07C2995561E0008010D /* OpenIdPresentationRequest.swift */; }; 55E2F07F29955A3B0008010D /* VerifiedIdRequestContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F07E29955A3B0008010D /* VerifiedIdRequestContent.swift */; }; 55E2F08129955A960008010D /* RequestType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F08029955A960008010D /* RequestType.swift */; }; + 55E2F08429955E8A0008010D /* MockMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F08329955E8A0008010D /* MockMapper.swift */; }; 55E33684293E7B6600CD2ED7 /* PromiseKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55E33679293E7B5F00CD2ED7 /* PromiseKit.framework */; }; 55E336D1293FA6F400CD2ED7 /* VerifiedIdRequirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E336D0293FA6F400CD2ED7 /* VerifiedIdRequirement.swift */; }; 55E336D4293FA75300CD2ED7 /* VerifiedId.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E336D3293FA75300CD2ED7 /* VerifiedId.swift */; }; @@ -305,6 +306,7 @@ 55E2F07C2995561E0008010D /* OpenIdPresentationRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenIdPresentationRequest.swift; sourceTree = ""; }; 55E2F07E29955A3B0008010D /* VerifiedIdRequestContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdRequestContent.swift; sourceTree = ""; }; 55E2F08029955A960008010D /* RequestType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestType.swift; sourceTree = ""; }; + 55E2F08329955E8A0008010D /* MockMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockMapper.swift; sourceTree = ""; }; 55E33666293E7B5000CD2ED7 /* PMKFoundation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = PMKFoundation.xcodeproj; path = Foundation/PMKFoundation.xcodeproj; sourceTree = ""; }; 55E3366F293E7B5F00CD2ED7 /* PromiseKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = PromiseKit.xcodeproj; path = PromiseKit/PromiseKit.xcodeproj; sourceTree = ""; }; 55E33685293E7B7D00CD2ED7 /* Secp256k1.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Secp256k1.xcodeproj; path = Secp256k1/Secp256k1.xcodeproj; sourceTree = ""; }; @@ -580,6 +582,7 @@ 55A81C0A2991BF6C002C259A /* Mocks */ = { isa = PBXGroup; children = ( + 55E2F08229955E7B0008010D /* Utilities */, 55A81C0B2991BF73002C259A /* Requests */, ); path = Mocks; @@ -656,6 +659,14 @@ path = OpenId; sourceTree = ""; }; + 55E2F08229955E7B0008010D /* Utilities */ = { + isa = PBXGroup; + children = ( + 55E2F08329955E8A0008010D /* MockMapper.swift */, + ); + path = Utilities; + sourceTree = ""; + }; 55E33667293E7B5000CD2ED7 /* Products */ = { isa = PBXGroup; children = ( @@ -1199,6 +1210,7 @@ buildActionMask = 2147483647; files = ( 55E2F0792995553E0008010D /* MockVerifiedIdRequestContent.swift in Sources */, + 55E2F08429955E8A0008010D /* MockMapper.swift in Sources */, 55E2F06F299553B40008010D /* OpenIdRequestHandlerTests.swift in Sources */, 55E3376B29478C5000CD2ED7 /* AsyncWrapperTests.swift in Sources */, 5534E6AD294AAFA6005D0765 /* PresentationDescriptorMappingTests.swift in Sources */, diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Utilities/MockMapper.swift b/WalletLibrary/WalletLibraryTests/Mocks/Utilities/MockMapper.swift new file mode 100644 index 00000000..63ba57c9 --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Mocks/Utilities/MockMapper.swift @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +@testable import WalletLibrary + +struct MockMapper: Mapping { + + let error: Error? + + let returnedObject: Any? + + init(error: Error? = nil, returnedObject: Any? = nil) { + self.error = error + self.returnedObject = returnedObject + } + + /// Map one object to another. + func map(_ object: T) throws -> T.T { + + if let error = error { + throw error + } + + if let returnedObject = returnedObject as? T.T { + return returnedObject + } + + return try object.map(using: self) + } +} From 74a31d302996c82f7fd4668f535fd3c62f619852 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 09:08:35 -0800 Subject: [PATCH 024/373] Remove await from private method for presentation request. --- .../Requests/Handlers/OpenIdRequestHandler.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift index 8b8137ea..e0a8e2cd 100644 --- a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift +++ b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift @@ -22,14 +22,14 @@ struct OpenIdRequestHandler: RequestHandling { return try await handleIssuanceRequest(from: request) } - return try await handlePresentationRequest(from: request) + return try handlePresentationRequest(from: request) } private func handleIssuanceRequest(from request: any OpenIdRawRequest) async throws -> any VerifiedIdIssuanceRequest { throw VerifiedIdClientError.TODO(message: "implement") } - private func handlePresentationRequest(from request: any OpenIdRawRequest) async throws -> any VerifiedIdPresentationRequest { + private func handlePresentationRequest(from request: any OpenIdRawRequest) throws -> any VerifiedIdPresentationRequest { let content = try configuration.mapper.map(request) return OpenIdPresentationRequest(content: content, configuration: configuration) } From 6b26e49fc7d04322ace606559faa566701316f96 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 09:08:41 -0800 Subject: [PATCH 025/373] Update OpenIdRequestHandlerTests.swift --- .../Handlers/OpenIdRequestHandlerTests.swift | 59 +++++-------------- 1 file changed, 16 insertions(+), 43 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift index 8ed46ec1..a57dbc4f 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift @@ -8,7 +8,11 @@ import XCTest class OpenIdRequestHandlerTests: XCTestCase { - func testHandleRequest_WithRawRequest_ReturnsVerifiedIdPresentationRequest() async throws { + enum ExpectedError: Error, Equatable { + case expectedToBeThrown + } + + func testHandleRequest_WithRawPresentationRequest_ReturnsVerifiedIdRequest() async throws { // Arrange Mocked Mapper let expectedStyle = MockRequesterStyle(requester: "mock requester") @@ -31,56 +35,25 @@ class OpenIdRequestHandlerTests: XCTestCase { XCTAssertEqual(actualRequest.style as? MockRequesterStyle, expectedStyle) XCTAssertEqual(actualRequest.requirement as? MockRequirement, expectedRequirement) XCTAssertEqual(actualRequest.rootOfTrust.source, expectedRootOfTrust.source) - } - func testHandleRequest_WithInvalidMapping_ThrowsError() async throws { + func testHandleRequest_WithPresentationRequestInvalidMapping_ThrowsError() async throws { - // Arrange - let mockData = "test data" - let mockInput = MockInput(mockData: mockData) - let resolver = OpenIdURLRequestResolver(openIdResolver: MockOpenIdForVCResolver()) + let mockMapper = MockMapper(error: ExpectedError.expectedToBeThrown) + + // Arrange Input + let mockRawRequest = MockOpenIdRawRequest(raw: Data()) + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) + let handler = OpenIdRequestHandler(configuration: configuration) // Act do { - let _ = try await resolver.resolve(input: mockInput) - XCTFail("resolver did not throw an error.") + let _ = try await handler.handleRequest(from: mockRawRequest) + XCTFail("handler did not throw an error.") } catch { // Assert - XCTAssert(error is OpenIdURLRequestResolverError) - - switch (error as? OpenIdURLRequestResolverError) { - case .unsupportedVerifiedIdRequestInputWith(type: let type): - XCTAssertEqual(type, "MockInput") - default: - XCTFail("error thrown is incorrect type.") - } + XCTAssert(error is ExpectedError) + XCTAssertEqual(error as? ExpectedError, ExpectedError.expectedToBeThrown) } } } - -struct MockMapper: Mapping { - - let error: Error? - - let returnedObject: Any? - - init(error: Error? = nil, returnedObject: Any? = nil) { - self.error = error - self.returnedObject = returnedObject - } - - /// Map one object to another. - func map(_ object: T) throws -> T.T { - - if let error = error { - throw error - } - - if let returnedObject = returnedObject as? T.T { - return returnedObject - } - - return try object.map(using: self) - } -} From 7b04a54c5e7484c25857da16fbab06851c07e694 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 09:13:35 -0800 Subject: [PATCH 026/373] Fix nit before pr. --- .../WalletLibrary/Protocols/Requests/VerifiedIdRequest.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequest.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequest.swift index d63488a1..86518cb6 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequest.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequest.swift @@ -37,6 +37,5 @@ protocol VerifiedIdIssuanceRequest: VerifiedIdRequest where T == any VerifiedIdR /** * Internal Protocol that represents a Presentation Request. - * TODO: add VerifiedId Style */ protocol VerifiedIdPresentationRequest: VerifiedIdRequest where T == Void { } From 25430aeb46355efe923d54c1714934543852b021 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 10:51:55 -0800 Subject: [PATCH 027/373] Add LinkedDomainResult mapper --- .../WalletLibrary.xcodeproj/project.pbxproj | 16 ++++++++++++ .../LinkedDomainResult+RootOfTrust.swift | 25 +++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/LinkedDomainResult+RootOfTrust.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 9940658e..1d13e3af 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -61,6 +61,10 @@ 55E2F07F29955A3B0008010D /* VerifiedIdRequestContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F07E29955A3B0008010D /* VerifiedIdRequestContent.swift */; }; 55E2F08129955A960008010D /* RequestType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F08029955A960008010D /* RequestType.swift */; }; 55E2F08429955E8A0008010D /* MockMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F08329955E8A0008010D /* MockMapper.swift */; }; + 55E2F086299573C30008010D /* OpenIdRequesterStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F085299573C30008010D /* OpenIdRequesterStyle.swift */; }; + 55E2F0882995743D0008010D /* LinkedDomainResult+RootOfTrust.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F0872995743D0008010D /* LinkedDomainResult+RootOfTrust.swift */; }; + 55E2F08A299576350008010D /* PresentationDefinition+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F089299576350008010D /* PresentationDefinition+Mappable.swift */; }; + 55E2F08C299576730008010D /* GroupRequirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F08B299576730008010D /* GroupRequirement.swift */; }; 55E33684293E7B6600CD2ED7 /* PromiseKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55E33679293E7B5F00CD2ED7 /* PromiseKit.framework */; }; 55E336D1293FA6F400CD2ED7 /* VerifiedIdRequirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E336D0293FA6F400CD2ED7 /* VerifiedIdRequirement.swift */; }; 55E336D4293FA75300CD2ED7 /* VerifiedId.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E336D3293FA75300CD2ED7 /* VerifiedId.swift */; }; @@ -307,6 +311,10 @@ 55E2F07E29955A3B0008010D /* VerifiedIdRequestContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdRequestContent.swift; sourceTree = ""; }; 55E2F08029955A960008010D /* RequestType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestType.swift; sourceTree = ""; }; 55E2F08329955E8A0008010D /* MockMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockMapper.swift; sourceTree = ""; }; + 55E2F085299573C30008010D /* OpenIdRequesterStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenIdRequesterStyle.swift; sourceTree = ""; }; + 55E2F0872995743D0008010D /* LinkedDomainResult+RootOfTrust.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LinkedDomainResult+RootOfTrust.swift"; sourceTree = ""; }; + 55E2F089299576350008010D /* PresentationDefinition+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationDefinition+Mappable.swift"; sourceTree = ""; }; + 55E2F08B299576730008010D /* GroupRequirement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupRequirement.swift; sourceTree = ""; }; 55E33666293E7B5000CD2ED7 /* PMKFoundation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = PMKFoundation.xcodeproj; path = Foundation/PMKFoundation.xcodeproj; sourceTree = ""; }; 55E3366F293E7B5F00CD2ED7 /* PromiseKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = PromiseKit.xcodeproj; path = PromiseKit/PromiseKit.xcodeproj; sourceTree = ""; }; 55E33685293E7B7D00CD2ED7 /* Secp256k1.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Secp256k1.xcodeproj; path = Secp256k1/Secp256k1.xcodeproj; sourceTree = ""; }; @@ -535,8 +543,10 @@ isa = PBXGroup; children = ( 5534E676294A1FA0005D0765 /* AccessTokenDescriptor+Mappable.swift */, + 55E2F0872995743D0008010D /* LinkedDomainResult+RootOfTrust.swift */, 5534E671294A19D5005D0765 /* IdTokenDescriptor+Mappable.swift */, 5534E64C294A0888005D0765 /* PresentationDescriptor+Mappable.swift */, + 55E2F089299576350008010D /* PresentationDefinition+Mappable.swift */, 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */, ); path = VCSDK; @@ -655,6 +665,7 @@ isa = PBXGroup; children = ( 55E2F07C2995561E0008010D /* OpenIdPresentationRequest.swift */, + 55E2F085299573C30008010D /* OpenIdRequesterStyle.swift */, ); path = OpenId; sourceTree = ""; @@ -702,6 +713,7 @@ children = ( 55E33707293FD25900CD2ED7 /* AccessTokenRequirement.swift */, 55E3379D2948B4B000CD2ED7 /* CredentialFormat.swift */, + 55E2F08B299576730008010D /* GroupRequirement.swift */, 55E336D7293FCE0500CD2ED7 /* IssuanceOptions.swift */, 55E33709293FD3E000CD2ED7 /* IdTokenRequirement.swift */, 55E337B32948B4B000CD2ED7 /* PinRequirement.swift */, @@ -1159,6 +1171,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 55E2F08C299576730008010D /* GroupRequirement.swift in Sources */, 550E3212298B1150004E7716 /* VerifiedIdRequestInput.swift in Sources */, 550A919B29940E9E0014D030 /* PresentationRequest+OpenIdRawRequest.swift in Sources */, 5534E667294A0B6C005D0765 /* Mappable.swift in Sources */, @@ -1188,6 +1201,7 @@ 55E3370C293FD61E00CD2ED7 /* SelfAttestedClaimRequirement.swift in Sources */, 55E3376829437E3500CD2ED7 /* OpenIdForVCResolver.swift in Sources */, 55A81BE52991B2E5002C259A /* RequestResolverFactory.swift in Sources */, + 55E2F086299573C30008010D /* OpenIdRequesterStyle.swift in Sources */, 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */, 55E33714293FDEEF00CD2ED7 /* Contract.swift in Sources */, 55E2F071299554110008010D /* LibraryConfiguration.swift in Sources */, @@ -1195,11 +1209,13 @@ 550A9192299400820014D030 /* OpenIdForVCResponder.swift in Sources */, 5534E672294A19D5005D0765 /* IdTokenDescriptor+Mappable.swift in Sources */, 5534E677294A1FA0005D0765 /* AccessTokenDescriptor+Mappable.swift in Sources */, + 55E2F08A299576350008010D /* PresentationDefinition+Mappable.swift in Sources */, 550A919E29940ED70014D030 /* OpenIdRawRequest.swift in Sources */, 55E337BC2948B5FE00CD2ED7 /* PresentationService+OpenIdForVC.swift in Sources */, 55E336D4293FA75300CD2ED7 /* VerifiedId.swift in Sources */, 55E2F06C299553300008010D /* PresentationRequest+Mappable.swift in Sources */, 55A81BCA2991AB82002C259A /* RequestResolving.swift in Sources */, + 55E2F0882995743D0008010D /* LinkedDomainResult+RootOfTrust.swift in Sources */, 55E33708293FD25900CD2ED7 /* AccessTokenRequirement.swift in Sources */, 55E2F07D2995561E0008010D /* OpenIdPresentationRequest.swift in Sources */, ); diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/LinkedDomainResult+RootOfTrust.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/LinkedDomainResult+RootOfTrust.swift new file mode 100644 index 00000000..848351eb --- /dev/null +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/LinkedDomainResult+RootOfTrust.swift @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import VCEntities + +/** + * An extension of the VCEntities.IdTokenDescriptor class to be able + * to map IdTokenDescriptor to IdTokenRequirement. + */ +extension VCEntities.LinkedDomainResult: Mappable { + + /// Map the id token descriptor to the id token requirement. + func map(using mapper: Mapping) throws -> RootOfTrust { + switch (self) { + case LinkedDomainResult.linkedDomainMissing: + return RootOfTrust(verified: false, source: nil) + case LinkedDomainResult.linkedDomainUnverified(domainUrl: let source): + return RootOfTrust(verified: false, source: source) + case LinkedDomainResult.linkedDomainVerified(domainUrl: let source): + return RootOfTrust(verified: true, source: source) + } + } +} From f82b188c7968b38b92d8ae6efe8183f2a69c7669 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 10:52:02 -0800 Subject: [PATCH 028/373] Create PresentationDefinition+Mappable.swift --- .../VCSDK/PresentationDefinition+Mappable.swift | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift new file mode 100644 index 00000000..c0c314f1 --- /dev/null +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import VCEntities + +/** + * An extension of the VCEntities.PresentationDescriptor class to be able + * to map PresentationDescriptor to VerifiedIdRequirement. + */ +extension VCEntities.PresentationDefinition: Mappable { + + func map(using mapper: Mapping) throws -> Requirement { + throw VerifiedIdClientError.TODO(message: "implement") + } +} From b5074557d5aa9bb382cf75386b40cd46d09ebd6a Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 10:52:17 -0800 Subject: [PATCH 029/373] Implement Presentation Request mapper. --- .../VCSDK/PresentationRequest+Mappable.swift | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationRequest+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationRequest+Mappable.swift index f4539d2a..3507c243 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationRequest+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationRequest+Mappable.swift @@ -5,6 +5,10 @@ import VCEntities +enum PresentationRequestMappingError: Error { + case presentationDefinitionMissingInRequest +} + /** * An extension of the VCEntities.PresentationRequest class to be able * to map PresentationRequest to VerifiedIdRequestContent. @@ -12,6 +16,19 @@ import VCEntities extension VCEntities.PresentationRequest: Mappable { func map(using mapper: Mapping) throws -> VerifiedIdRequestContent { - throw VerifiedIdClientError.TODO(message: "implement") + + guard let presentationDefinition = content.claims?.vpToken?.presentationDefinition else { + throw PresentationRequestMappingError.presentationDefinitionMissingInRequest + } + + let requirement = try mapper.map(presentationDefinition) + let requesterStyle = OpenIdRequesterStyle(requester: "test") + let rootOfTrust = try mapper.map(self.linkedDomainResult) + + let content = VerifiedIdRequestContent(style: requesterStyle, + requirement: requirement, + rootOfTrust: rootOfTrust) + + return content } } From bf8d029f5226f47443279710265f59a746cf77d3 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 10:52:32 -0800 Subject: [PATCH 030/373] Make VerifiedIdRequestContent a struct. --- .../Protocols/Requests/VerifiedIdRequestContent.swift | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequestContent.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequestContent.swift index 34b087f3..d4934d8a 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequestContent.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequestContent.swift @@ -7,12 +7,11 @@ * Contents in a Verified Id Request. * This protocol is used to map protocol specific requests to common request object. */ -protocol VerifiedIdRequestContent { +struct VerifiedIdRequestContent { - var style: RequesterStyle { get } + let style: RequesterStyle - var requirement: Requirement { get } - - var rootOfTrust: RootOfTrust { get } + let requirement: Requirement + let rootOfTrust: RootOfTrust } From 2df0ff7a1b0a9d9a3b24e160a741955f68c4f9a1 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 10:52:38 -0800 Subject: [PATCH 031/373] Create OpenIdRequesterStyle.swift --- .../OpenId/OpenIdRequesterStyle.swift | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdRequesterStyle.swift diff --git a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdRequesterStyle.swift b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdRequesterStyle.swift new file mode 100644 index 00000000..dbe03663 --- /dev/null +++ b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdRequesterStyle.swift @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +/** + * Requester Style that is Open Id specific. + */ +struct OpenIdRequesterStyle: RequesterStyle { + let requester: String +} From 7d7d46f570e71c26381b848db3470c269a9b4e2b Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 10:52:44 -0800 Subject: [PATCH 032/373] Create GroupRequirement.swift --- .../Requirements/GroupRequirement.swift | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift diff --git a/WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift b/WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift new file mode 100644 index 00000000..6a8b6429 --- /dev/null +++ b/WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +/** + * Information to describe a group of requirements. + */ +public class GroupRequirement: Requirement { + + /// If the requirement is required or not. + public let required: Bool + + public let requirements: [Requirement] + + public let requirementsOperator: Operator + + init(required: Bool, + requirements: [Requirement], + requirementsOperator: Operator) { + self.required = required + self.requirements = requirements + } + + public func validate() throws { + throw VerifiedIdClientError.TODO(message: "implement") + } +} + +public enum Operator { + case ANY + case ALL +} From 3a7602f8769bccdfcc852abf6fab3ae8a446fa67 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 13:49:11 -0800 Subject: [PATCH 033/373] Implement presentation definition mapper --- .../PresentationDefinition+Mappable.swift | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift index c0c314f1..5c624de4 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift @@ -12,6 +12,23 @@ import VCEntities extension VCEntities.PresentationDefinition: Mappable { func map(using mapper: Mapping) throws -> Requirement { - throw VerifiedIdClientError.TODO(message: "implement") + + guard let inputDescriptors = self.inputDescriptors else { + throw VerifiedIdClientError.TODO(message: "add error") + } + + if inputDescriptors.capacity == 1, + let onlyDescriptor = inputDescriptors.first { + return try mapper.map(onlyDescriptor) + } + + let requirements = try inputDescriptors.compactMap { + try mapper.map($0) + } + + /// VC SDK only supports any operator for now. + return GroupRequirement(required: true, + requirements: requirements, + requirementsOperator: .ANY) } } From 708f14519138adc18650932a862aa0a1a84729a9 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 13:49:46 -0800 Subject: [PATCH 034/373] Add PresentationInputDescriptor mapper --- .../WalletLibrary.xcodeproj/project.pbxproj | 12 +++---- ...PresentationInputDescriptor+Mappable.swift | 36 +++++++++++++++++++ 2 files changed, 40 insertions(+), 8 deletions(-) create mode 100644 WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationInputDescriptor+Mappable.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 1d13e3af..d2a4e901 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -42,6 +42,7 @@ 5534E688294A5F16005D0765 /* IdTokenDescriptorMappingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5534E687294A5F16005D0765 /* IdTokenDescriptorMappingTests.swift */; }; 5534E68C294A8B8F005D0765 /* AccessTokenDescriptorMappingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5534E68B294A8B8F005D0765 /* AccessTokenDescriptorMappingTests.swift */; }; 5534E6AD294AAFA6005D0765 /* PresentationDescriptorMappingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5534E6AC294AAFA6005D0765 /* PresentationDescriptorMappingTests.swift */; }; + 559BD3032995993700BD61AC /* PresentationInputDescriptor+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */; }; 55A81BCA2991AB82002C259A /* RequestResolving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BC92991AB82002C259A /* RequestResolving.swift */; }; 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE02991AB96002C259A /* RequestHandling.swift */; }; 55A81BE52991B2E5002C259A /* RequestResolverFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */; }; @@ -56,7 +57,6 @@ 55E2F071299554110008010D /* LibraryConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F070299554110008010D /* LibraryConfiguration.swift */; }; 55E2F076299555280008010D /* MockRequirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F073299555280008010D /* MockRequirement.swift */; }; 55E2F077299555280008010D /* MockRequesterStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F075299555280008010D /* MockRequesterStyle.swift */; }; - 55E2F0792995553E0008010D /* MockVerifiedIdRequestContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F0782995553E0008010D /* MockVerifiedIdRequestContent.swift */; }; 55E2F07D2995561E0008010D /* OpenIdPresentationRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F07C2995561E0008010D /* OpenIdPresentationRequest.swift */; }; 55E2F07F29955A3B0008010D /* VerifiedIdRequestContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F07E29955A3B0008010D /* VerifiedIdRequestContent.swift */; }; 55E2F08129955A960008010D /* RequestType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F08029955A960008010D /* RequestType.swift */; }; @@ -69,7 +69,6 @@ 55E336D1293FA6F400CD2ED7 /* VerifiedIdRequirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E336D0293FA6F400CD2ED7 /* VerifiedIdRequirement.swift */; }; 55E336D4293FA75300CD2ED7 /* VerifiedId.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E336D3293FA75300CD2ED7 /* VerifiedId.swift */; }; 55E336D6293FA78C00CD2ED7 /* VerifiedIdClaim.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E336D5293FA78C00CD2ED7 /* VerifiedIdClaim.swift */; }; - 55E336D8293FCE0500CD2ED7 /* IssuanceOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E336D7293FCE0500CD2ED7 /* IssuanceOptions.swift */; }; 55E33708293FD25900CD2ED7 /* AccessTokenRequirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E33707293FD25900CD2ED7 /* AccessTokenRequirement.swift */; }; 55E3370A293FD3E000CD2ED7 /* IdTokenRequirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E33709293FD3E000CD2ED7 /* IdTokenRequirement.swift */; }; 55E3370C293FD61E00CD2ED7 /* SelfAttestedClaimRequirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E3370B293FD61E00CD2ED7 /* SelfAttestedClaimRequirement.swift */; }; @@ -292,6 +291,7 @@ 5534E687294A5F16005D0765 /* IdTokenDescriptorMappingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdTokenDescriptorMappingTests.swift; sourceTree = ""; }; 5534E68B294A8B8F005D0765 /* AccessTokenDescriptorMappingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessTokenDescriptorMappingTests.swift; sourceTree = ""; }; 5534E6AC294AAFA6005D0765 /* PresentationDescriptorMappingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentationDescriptorMappingTests.swift; sourceTree = ""; }; + 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationInputDescriptor+Mappable.swift"; sourceTree = ""; }; 55A81BC92991AB82002C259A /* RequestResolving.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolving.swift; sourceTree = ""; }; 55A81BE02991AB96002C259A /* RequestHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestHandling.swift; sourceTree = ""; }; 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolverFactory.swift; sourceTree = ""; }; @@ -306,7 +306,6 @@ 55E2F070299554110008010D /* LibraryConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryConfiguration.swift; sourceTree = ""; }; 55E2F073299555280008010D /* MockRequirement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockRequirement.swift; sourceTree = ""; }; 55E2F075299555280008010D /* MockRequesterStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockRequesterStyle.swift; sourceTree = ""; }; - 55E2F0782995553E0008010D /* MockVerifiedIdRequestContent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockVerifiedIdRequestContent.swift; sourceTree = ""; }; 55E2F07C2995561E0008010D /* OpenIdPresentationRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenIdPresentationRequest.swift; sourceTree = ""; }; 55E2F07E29955A3B0008010D /* VerifiedIdRequestContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdRequestContent.swift; sourceTree = ""; }; 55E2F08029955A960008010D /* RequestType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestType.swift; sourceTree = ""; }; @@ -321,7 +320,6 @@ 55E336D0293FA6F400CD2ED7 /* VerifiedIdRequirement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdRequirement.swift; sourceTree = ""; }; 55E336D3293FA75300CD2ED7 /* VerifiedId.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedId.swift; sourceTree = ""; }; 55E336D5293FA78C00CD2ED7 /* VerifiedIdClaim.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdClaim.swift; sourceTree = ""; }; - 55E336D7293FCE0500CD2ED7 /* IssuanceOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssuanceOptions.swift; sourceTree = ""; }; 55E33707293FD25900CD2ED7 /* AccessTokenRequirement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessTokenRequirement.swift; sourceTree = ""; }; 55E33709293FD3E000CD2ED7 /* IdTokenRequirement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdTokenRequirement.swift; sourceTree = ""; }; 55E3370B293FD61E00CD2ED7 /* SelfAttestedClaimRequirement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelfAttestedClaimRequirement.swift; sourceTree = ""; }; @@ -548,6 +546,7 @@ 5534E64C294A0888005D0765 /* PresentationDescriptor+Mappable.swift */, 55E2F089299576350008010D /* PresentationDefinition+Mappable.swift */, 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */, + 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */, ); path = VCSDK; sourceTree = ""; @@ -608,7 +607,6 @@ 55E2F074299555280008010D /* Styles */, 55A81C0C2991BF86002C259A /* MockInput.swift */, 550A913C29930D970014D030 /* MockRawRequest.swift */, - 55E2F0782995553E0008010D /* MockVerifiedIdRequestContent.swift */, ); path = Requests; sourceTree = ""; @@ -714,7 +712,6 @@ 55E33707293FD25900CD2ED7 /* AccessTokenRequirement.swift */, 55E3379D2948B4B000CD2ED7 /* CredentialFormat.swift */, 55E2F08B299576730008010D /* GroupRequirement.swift */, - 55E336D7293FCE0500CD2ED7 /* IssuanceOptions.swift */, 55E33709293FD3E000CD2ED7 /* IdTokenRequirement.swift */, 55E337B32948B4B000CD2ED7 /* PinRequirement.swift */, 55E3370B293FD61E00CD2ED7 /* SelfAttestedClaimRequirement.swift */, @@ -1180,7 +1177,6 @@ 55E2F07F29955A3B0008010D /* VerifiedIdRequestContent.swift in Sources */, 550A916F299310D40014D030 /* OpenIdRequestHandler.swift in Sources */, 55E3370A293FD3E000CD2ED7 /* IdTokenRequirement.swift in Sources */, - 55E336D8293FCE0500CD2ED7 /* IssuanceOptions.swift in Sources */, 550E320C298B0F30004E7716 /* WalletLibraryLogConsumer.swift in Sources */, 550E3217298B1236004E7716 /* RequesterStyle.swift in Sources */, 55E336D1293FA6F400CD2ED7 /* VerifiedIdRequirement.swift in Sources */, @@ -1192,6 +1188,7 @@ 550E3210298B113C004E7716 /* VerifiedIdRequest.swift in Sources */, 5534E5B52948FEB5005D0765 /* RootOfTrust.swift in Sources */, 550E320E298B0FAC004E7716 /* WalletLibraryLogger.swift in Sources */, + 559BD3032995993700BD61AC /* PresentationInputDescriptor+Mappable.swift in Sources */, 55E337B52948B4B000CD2ED7 /* PinRequirement.swift in Sources */, 550A9170299310D40014D030 /* OpenIdURLRequestResolver.swift in Sources */, 5534E669294A0C3F005D0765 /* Mapper.swift in Sources */, @@ -1225,7 +1222,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 55E2F0792995553E0008010D /* MockVerifiedIdRequestContent.swift in Sources */, 55E2F08429955E8A0008010D /* MockMapper.swift in Sources */, 55E2F06F299553B40008010D /* OpenIdRequestHandlerTests.swift in Sources */, 55E3376B29478C5000CD2ED7 /* AsyncWrapperTests.swift in Sources */, diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationInputDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationInputDescriptor+Mappable.swift new file mode 100644 index 00000000..17365686 --- /dev/null +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationInputDescriptor+Mappable.swift @@ -0,0 +1,36 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import VCEntities + +enum PresentationInputDescriptorMappingError: Error { + case noVerifiedIdRequirementTypePresent +} + +extension VCEntities.PresentationInputDescriptor: Mappable { + + func map(using mapper: Mapping) throws -> VerifiedIdRequirement { + + guard let types = schema?.compactMap({ $0.uri }), + !types.isEmpty else { + throw PresentationRequestMappingError.presentationDefinitionMissingInRequest + } + + let issuanceOptions = self.issuanceMetadata?.compactMap { + if let contract = $0.contract, + let url = URL(string: contract) { + return VerifiedIdRequestURL(url: url) + } + + return nil + } + + return VerifiedIdRequirement(encrypted: false, + required: true, + types: types, + purpose: nil, + issuanceOptions: issuanceOptions ?? []) + } +} From f05cf2b6a1acc3a85663c414e05dac57978c6f6d Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 13:49:49 -0800 Subject: [PATCH 035/373] Update PresentationDescriptor+Mappable.swift --- .../VCSDK/PresentationDescriptor+Mappable.swift | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDescriptor+Mappable.swift index 3cfe4cb7..4939ed84 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDescriptor+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDescriptor+Mappable.swift @@ -13,20 +13,19 @@ extension VCEntities.PresentationDescriptor: Mappable { func map(using mapper: Mapping) throws -> VerifiedIdRequirement { - let acceptedIssuers = issuers?.compactMap { $0.iss } ?? [] - - var issuanceOptions: IssuanceOptions? = nil - if let contracts = contracts, - !contracts.isEmpty { - issuanceOptions = IssuanceOptions(acceptedIssuers: acceptedIssuers, - credentialIssuerMetadata: contracts) + let issuanceOptions = contracts?.compactMap { + + if let contract = URL(string: $0) { + return VerifiedIdRequestURL(url: contract) + } + + return nil } return VerifiedIdRequirement(encrypted: encrypted ?? false, required: presentationRequired ?? false, types: [credentialType], - acceptedIssuers: acceptedIssuers, purpose: nil, - issuanceOptions: issuanceOptions) + issuanceOptions: issuanceOptions ?? []) } } From 53afc6130b1ba1b8014f05a5480e8bec67e234a1 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 13:50:08 -0800 Subject: [PATCH 036/373] Remove old data models --- .../Requirements/IssuanceOptions.swift | 17 --------------- .../MockVerifiedIdRequestContent.swift | 21 ------------------- 2 files changed, 38 deletions(-) delete mode 100644 WalletLibrary/WalletLibrary/Requests/Requirements/IssuanceOptions.swift delete mode 100644 WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiedIdRequestContent.swift diff --git a/WalletLibrary/WalletLibrary/Requests/Requirements/IssuanceOptions.swift b/WalletLibrary/WalletLibrary/Requests/Requirements/IssuanceOptions.swift deleted file mode 100644 index 70bf5cd5..00000000 --- a/WalletLibrary/WalletLibrary/Requests/Requirements/IssuanceOptions.swift +++ /dev/null @@ -1,17 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Microsoft Corporation. All rights reserved. -* Licensed under the MIT License. See License.txt in the project root for license information. -*--------------------------------------------------------------------------------------------*/ - -/** - * Parameters used for an Issuance during Presentation flow - * to define information needed for the issuance of the requested Verified Id. - */ -public struct IssuanceOptions: Equatable { - - /// A list of issuers that are accepted. - public let acceptedIssuers: [String] - - /// Information such as a contract URL to describe where to get contract. - public let credentialIssuerMetadata: [String] -} diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiedIdRequestContent.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiedIdRequestContent.swift deleted file mode 100644 index 3c64bc16..00000000 --- a/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiedIdRequestContent.swift +++ /dev/null @@ -1,21 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Microsoft Corporation. All rights reserved. -* Licensed under the MIT License. See License.txt in the project root for license information. -*--------------------------------------------------------------------------------------------*/ - -@testable import WalletLibrary - -struct MockVerifiedIdRequestContent: VerifiedIdRequestContent { - - var style: RequesterStyle - - var requirement: Requirement - - var rootOfTrust: RootOfTrust - - init(style: RequesterStyle, requirement: Requirement, rootOfTrust: RootOfTrust) { - self.style = style - self.requirement = requirement - self.rootOfTrust = rootOfTrust - } -} From 811a8f4aa24c6582754e53ea13dbb7138679f488 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 13:50:19 -0800 Subject: [PATCH 037/373] Update PresentationRequest+Mappable.swift --- .../Mappings/VCSDK/PresentationRequest+Mappable.swift | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationRequest+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationRequest+Mappable.swift index 7ded7c25..e88e4428 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationRequest+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationRequest+Mappable.swift @@ -18,14 +18,19 @@ extension VCEntities.PresentationRequest: Mappable { func map(using mapper: Mapping) throws -> VerifiedIdRequestContent { guard let presentationDefinition = content.claims?.vpToken?.presentationDefinition else { + throw PresentationRequestMappingError.presentationDefinitionMissingInRequest } let requirement = try mapper.map(presentationDefinition) - let requesterStyle = OpenIdRequesterStyle(requester: "test") - let rootOfTrust = try mapper.map(self.linkedDomainResult) + let rootOfTrust = try mapper.map(linkedDomainResult) + + let clientName = content.registration?.clientName ?? "" + let requesterStyle = OpenIdRequesterStyle(requester: clientName) let content = VerifiedIdRequestContent(style: requesterStyle, requirement: requirement, rootOfTrust: rootOfTrust) return content + } +} From 21f36887c2405737ae10bb740d51f6409eedb82c Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 13:50:47 -0800 Subject: [PATCH 038/373] Update GroupRequirement.swift --- .../WalletLibrary/Requests/Requirements/GroupRequirement.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift b/WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift index 6a8b6429..7bccef53 100644 --- a/WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift +++ b/WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift @@ -20,6 +20,7 @@ public class GroupRequirement: Requirement { requirementsOperator: Operator) { self.required = required self.requirements = requirements + self.requirementsOperator = requirementsOperator } public func validate() throws { From de61aace5bacd547e46275cf71b61b88fb2fee8e Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 13:50:51 -0800 Subject: [PATCH 039/373] Update VerifiedIdRequirement.swift --- .../Requests/Requirements/VerifiedIdRequirement.swift | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Requirements/VerifiedIdRequirement.swift b/WalletLibrary/WalletLibrary/Requests/Requirements/VerifiedIdRequirement.swift index c0b7b21f..96b97c3c 100644 --- a/WalletLibrary/WalletLibrary/Requests/Requirements/VerifiedIdRequirement.swift +++ b/WalletLibrary/WalletLibrary/Requests/Requirements/VerifiedIdRequirement.swift @@ -17,14 +17,11 @@ public class VerifiedIdRequirement: Requirement { /// The type of the verified Id required. public let types: [String] - /// The accepted issuers for verified Id required. - public let acceptedIssuers: [String] - /// The purpose for the verified Id, developer can display to the user if desired. public let purpose: String? /// An optional property for information needed for issuance during presentation flow. - public let issuanceOptions: IssuanceOptions? + public let issuanceOptions: [VerifiedIdRequestInput] /// TODO: helper method that returns verified id that match the requirement from a list of verified ids. public func getMatches(verifiedIds: [VerifiedId]) -> [VerifiedId] { @@ -34,13 +31,11 @@ public class VerifiedIdRequirement: Requirement { init(encrypted: Bool, required: Bool, types: [String], - acceptedIssuers: [String], purpose: String?, - issuanceOptions: IssuanceOptions?) { + issuanceOptions: [VerifiedIdRequestInput]) { self.encrypted = encrypted self.required = required self.types = types - self.acceptedIssuers = acceptedIssuers self.purpose = purpose self.issuanceOptions = issuanceOptions } From 1ea114a01c8f46f4f932ab545fe6d3fe90b73dad Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 13:51:01 -0800 Subject: [PATCH 040/373] Update OpenIdRequestHandlerTests.swift --- .../Requests/Handlers/OpenIdRequestHandlerTests.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift index b3c56eff..e2982aea 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift @@ -18,9 +18,9 @@ class OpenIdRequestHandlerTests: XCTestCase { let expectedStyle = MockRequesterStyle(requester: "mock requester") let expectedRequirement = MockRequirement(id: "mockRequirement324") let expectedRootOfTrust = RootOfTrust(verified: true, source: "mock source") - let expectedContent = MockVerifiedIdRequestContent(style: expectedStyle, - requirement: expectedRequirement, - rootOfTrust: expectedRootOfTrust) + let expectedContent = VerifiedIdRequestContent(style: expectedStyle, + requirement: expectedRequirement, + rootOfTrust: expectedRootOfTrust) let mockMapper = MockMapper(returnedObject: expectedContent) let mockRawRequest = MockOpenIdRawRequest(raw: Data()) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) From 6ae618419f8be323f00ab9a2eaf8e1256cc5ad68 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 14:07:18 -0800 Subject: [PATCH 041/373] Fix comment on LinkedDomainResult mapper. --- .../Mappings/VCSDK/LinkedDomainResult+RootOfTrust.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/LinkedDomainResult+RootOfTrust.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/LinkedDomainResult+RootOfTrust.swift index 848351eb..35780c81 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/LinkedDomainResult+RootOfTrust.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/LinkedDomainResult+RootOfTrust.swift @@ -6,12 +6,11 @@ import VCEntities /** - * An extension of the VCEntities.IdTokenDescriptor class to be able - * to map IdTokenDescriptor to IdTokenRequirement. + * An extension of the VCEntities.LinkedDomainResult class to be able + * to map LinkedDomainResult to RootOfTrust. */ extension VCEntities.LinkedDomainResult: Mappable { - /// Map the id token descriptor to the id token requirement. func map(using mapper: Mapping) throws -> RootOfTrust { switch (self) { case LinkedDomainResult.linkedDomainMissing: From a77516e268156fb9dafd4977124b62ae47a06264 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 14:07:33 -0800 Subject: [PATCH 042/373] fix comment on presentation definition mapper --- .../Mappings/VCSDK/PresentationDefinition+Mappable.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift index 5c624de4..677fdbeb 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift @@ -6,8 +6,8 @@ import VCEntities /** - * An extension of the VCEntities.PresentationDescriptor class to be able - * to map PresentationDescriptor to VerifiedIdRequirement. + * An extension of the VCEntities.PresentationDefinition class to be able + * to map PresentationDefinition to a Requirement. */ extension VCEntities.PresentationDefinition: Mappable { From 4feda6b5c769ffa111b22a5d150321c3e7638e87 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 14:07:38 -0800 Subject: [PATCH 043/373] Update PresentationInputDescriptor+Mappable.swift --- .../Mappings/VCSDK/PresentationInputDescriptor+Mappable.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationInputDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationInputDescriptor+Mappable.swift index 17365686..e5acd407 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationInputDescriptor+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationInputDescriptor+Mappable.swift @@ -9,6 +9,10 @@ enum PresentationInputDescriptorMappingError: Error { case noVerifiedIdRequirementTypePresent } +/** + * An extension of the VCEntities.PresentationInputDescriptor class to be able + * to map PresentationInputDescriptor to VerifiedIdRequirement. + */ extension VCEntities.PresentationInputDescriptor: Mappable { func map(using mapper: Mapping) throws -> VerifiedIdRequirement { From 4557ae05db7fd7a1277d5af4359fcd220bcf179f Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 14:08:02 -0800 Subject: [PATCH 044/373] Make VerifiedIdRequestContent a struct. --- WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj | 2 +- .../{Protocols => }/Requests/VerifiedIdRequestContent.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename WalletLibrary/WalletLibrary/{Protocols => }/Requests/VerifiedIdRequestContent.swift (86%) diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index d2a4e901..49d7a5ba 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -788,6 +788,7 @@ 5534E5B42948FEB5005D0765 /* RootOfTrust.swift */, 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */, 55A81BE62991B2F3002C259A /* RequestHandlerFactory.swift */, + 55E2F07E29955A3B0008010D /* VerifiedIdRequestContent.swift */, ); path = Requests; sourceTree = ""; @@ -849,7 +850,6 @@ 55A81BC92991AB82002C259A /* RequestResolving.swift */, 55E2F08029955A960008010D /* RequestType.swift */, 550E320F298B113C004E7716 /* VerifiedIdRequest.swift */, - 55E2F07E29955A3B0008010D /* VerifiedIdRequestContent.swift */, 550E3211298B1150004E7716 /* VerifiedIdRequestInput.swift */, ); path = Requests; diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequestContent.swift b/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift similarity index 86% rename from WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequestContent.swift rename to WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift index d4934d8a..d4e822ed 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequestContent.swift +++ b/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift @@ -5,7 +5,7 @@ /** * Contents in a Verified Id Request. - * This protocol is used to map protocol specific requests to common request object. + * This object is used to map protocol specific requests to common request object. */ struct VerifiedIdRequestContent { From cfab7a09e4dac03702d8e8f01e3f67de74a9acff Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 14:08:14 -0800 Subject: [PATCH 045/373] Add comments to GroupRequirement --- .../Requirements/GroupRequirement.swift | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift b/WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift index 7bccef53..31e84285 100644 --- a/WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift +++ b/WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift @@ -3,6 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/** + * This value tells the user if they must get all of the requirements in the list or just one of them. + */ +public enum GroupRequirementOperator { + case ANY + case ALL +} + /** * Information to describe a group of requirements. */ @@ -13,22 +21,17 @@ public class GroupRequirement: Requirement { public let requirements: [Requirement] - public let requirementsOperator: Operator + public let requirementOperator: GroupRequirementOperator init(required: Bool, requirements: [Requirement], - requirementsOperator: Operator) { + requirementOperator: GroupRequirementOperator) { self.required = required self.requirements = requirements - self.requirementsOperator = requirementsOperator + self.requirementOperator = requirementOperator } public func validate() throws { throw VerifiedIdClientError.TODO(message: "implement") } } - -public enum Operator { - case ANY - case ALL -} From 5da718a80586a0e287b44c16f4c074e833605d31 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 14:27:23 -0800 Subject: [PATCH 046/373] Update VerifiedIdRequestURL.swift --- .../WalletLibrary/Requests/Input/VerifiedIdRequestURL.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Input/VerifiedIdRequestURL.swift b/WalletLibrary/WalletLibrary/Requests/Input/VerifiedIdRequestURL.swift index 24ee478a..cc39e90a 100644 --- a/WalletLibrary/WalletLibrary/Requests/Input/VerifiedIdRequestURL.swift +++ b/WalletLibrary/WalletLibrary/Requests/Input/VerifiedIdRequestURL.swift @@ -6,7 +6,7 @@ /** * A URL used to initiate a Verified Id Request. For example, an Open Id reference url or a Contract url. */ -public struct VerifiedIdRequestURL: VerifiedIdRequestInput { +public struct VerifiedIdRequestURL: VerifiedIdRequestInput, Equatable { let url: URL From 9a81161e27135b46ab33e07b868aa38c737816b3 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 14:27:35 -0800 Subject: [PATCH 047/373] Remove CredentialFormat class. --- .../WalletLibrary.xcodeproj/project.pbxproj | 4 ---- .../Requests/Requirements/CredentialFormat.swift | 16 ---------------- 2 files changed, 20 deletions(-) delete mode 100644 WalletLibrary/WalletLibrary/Requests/Requirements/CredentialFormat.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 49d7a5ba..f1a3efde 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -76,7 +76,6 @@ 55E337652943749600CD2ED7 /* AsyncWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E337642943749600CD2ED7 /* AsyncWrapper.swift */; }; 55E3376829437E3500CD2ED7 /* OpenIdForVCResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E3376729437E3500CD2ED7 /* OpenIdForVCResolver.swift */; }; 55E3376B29478C5000CD2ED7 /* AsyncWrapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E3376A29478C5000CD2ED7 /* AsyncWrapperTests.swift */; }; - 55E337B42948B4B000CD2ED7 /* CredentialFormat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E3379D2948B4B000CD2ED7 /* CredentialFormat.swift */; }; 55E337B52948B4B000CD2ED7 /* PinRequirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E337B32948B4B000CD2ED7 /* PinRequirement.swift */; }; 55E337BC2948B5FE00CD2ED7 /* PresentationService+OpenIdForVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E337BB2948B5FE00CD2ED7 /* PresentationService+OpenIdForVC.swift */; }; /* End PBXBuildFile section */ @@ -327,7 +326,6 @@ 55E337642943749600CD2ED7 /* AsyncWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncWrapper.swift; sourceTree = ""; }; 55E3376729437E3500CD2ED7 /* OpenIdForVCResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenIdForVCResolver.swift; sourceTree = ""; }; 55E3376A29478C5000CD2ED7 /* AsyncWrapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncWrapperTests.swift; sourceTree = ""; }; - 55E3379D2948B4B000CD2ED7 /* CredentialFormat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CredentialFormat.swift; path = WalletLibrary/Requests/Requirements/CredentialFormat.swift; sourceTree = SOURCE_ROOT; }; 55E337B32948B4B000CD2ED7 /* PinRequirement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PinRequirement.swift; path = WalletLibrary/Requests/Requirements/PinRequirement.swift; sourceTree = SOURCE_ROOT; }; 55E337BB2948B5FE00CD2ED7 /* PresentationService+OpenIdForVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationService+OpenIdForVC.swift"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -710,7 +708,6 @@ isa = PBXGroup; children = ( 55E33707293FD25900CD2ED7 /* AccessTokenRequirement.swift */, - 55E3379D2948B4B000CD2ED7 /* CredentialFormat.swift */, 55E2F08B299576730008010D /* GroupRequirement.swift */, 55E33709293FD3E000CD2ED7 /* IdTokenRequirement.swift */, 55E337B32948B4B000CD2ED7 /* PinRequirement.swift */, @@ -1194,7 +1191,6 @@ 5534E669294A0C3F005D0765 /* Mapper.swift in Sources */, 550E320A298B0EA4004E7716 /* VerifiedIdClient.swift in Sources */, 5534E64D294A0888005D0765 /* PresentationDescriptor+Mappable.swift in Sources */, - 55E337B42948B4B000CD2ED7 /* CredentialFormat.swift in Sources */, 55E3370C293FD61E00CD2ED7 /* SelfAttestedClaimRequirement.swift in Sources */, 55E3376829437E3500CD2ED7 /* OpenIdForVCResolver.swift in Sources */, 55A81BE52991B2E5002C259A /* RequestResolverFactory.swift in Sources */, diff --git a/WalletLibrary/WalletLibrary/Requests/Requirements/CredentialFormat.swift b/WalletLibrary/WalletLibrary/Requests/Requirements/CredentialFormat.swift deleted file mode 100644 index d345fbd0..00000000 --- a/WalletLibrary/WalletLibrary/Requests/Requirements/CredentialFormat.swift +++ /dev/null @@ -1,16 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Microsoft Corporation. All rights reserved. -* Licensed under the MIT License. See License.txt in the project root for license information. -*--------------------------------------------------------------------------------------------*/ - -/** - * Data that describes requested verified id formats. - */ -public struct CredentialFormat { - - /// The format of the verified Id requested such as jwt-vc. - public let format: String - - /// The types of the requested verified Id. - public let types: [String] -} From d3fc5dd1be8c422cae9f3e5b61237375bbae288e Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 14:27:40 -0800 Subject: [PATCH 048/373] Update PresentationDefinition+Mappable.swift --- .../Mappings/VCSDK/PresentationDefinition+Mappable.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift index 677fdbeb..9b1d2a26 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift @@ -29,6 +29,6 @@ extension VCEntities.PresentationDefinition: Mappable { /// VC SDK only supports any operator for now. return GroupRequirement(required: true, requirements: requirements, - requirementsOperator: .ANY) + requirementOperator: .ANY) } } From f70de4c27632dccd870d0777a294f2c081f6ffdd Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 14:27:58 -0800 Subject: [PATCH 049/373] Update PresentationDescriptorMappingTests.swift --- .../PresentationDescriptorMappingTests.swift | 127 +++++++++--------- 1 file changed, 61 insertions(+), 66 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDescriptorMappingTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDescriptorMappingTests.swift index a0648420..dd6b7fa8 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDescriptorMappingTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDescriptorMappingTests.swift @@ -8,116 +8,111 @@ import VCEntities @testable import WalletLibrary class PresentationDescriptorMappingTests: XCTestCase { - + let mapper = Mapper() - - let encoder = JSONEncoder() - - let decoder = JSONDecoder() - - let exceptedCredentialType = "credentialType4235" - - func testSuccessfulMapping() throws { + + func testMap_WhenWithAllDefaultInput_ReturnsVerifiedIdRequirement() throws { + // Arrange let (input, expectedResult) = try setUpInput(encrypted: false, required: false) + + // Act let actualResult = try mapper.map(input) + + // Assert assertEqual(actualResult, expectedResult) } - - func testMappingWithEncryptedAsTrue() throws { + + func testMap_WhenEncyptedIsTrue_ReturnsVerifiedIdRequirement() throws { + // Arrange let (input, expectedResult) = try setUpInput(encrypted: true, required: false) + + // Act let actualResult = try mapper.map(input) + + // Assert assertEqual(actualResult, expectedResult) } - - func testMappingWithRequiredAsTrue() throws { + + func testMap_WhenRequiredIsTrue_ReturnsVerifiedIdRequirement() throws { + // Arrange let (input, expectedResult) = try setUpInput(encrypted: false, required: true) + + // Act let actualResult = try mapper.map(input) + + // Assert assertEqual(actualResult, expectedResult) } - - func testMappingWithOneNilIssuerValue() throws { - let (input, expectedResult) = try setUpInput(issuers: [nil]) - let actualResult = try mapper.map(input) - assertEqual(actualResult, expectedResult) - } - - func testMappingWithOneIssuerValue() throws { - let (input, expectedResult) = try setUpInput(issuers: ["issuer235"]) - let actualResult = try mapper.map(input) - assertEqual(actualResult, expectedResult) - } - - func testMappingWithThreeIssuerValue() throws { - let (input, expectedResult) = try setUpInput(issuers: ["issuer235", - "issuer7345", - "issuer9083"]) - let actualResult = try mapper.map(input) - assertEqual(actualResult, expectedResult) - } - - func testMappingWithNilIssuerValue() throws { - let (input, expectedResult) = try setUpInput(issuers: nil) - let actualResult = try mapper.map(input) - assertEqual(actualResult, expectedResult) - } - - func testMappingWithNilContractValue() throws { + + func testMap_WhenContractIsNil_ReturnsVerifiedIdRequiremen() throws { + // Arrange let (input, expectedResult) = try setUpInput(contracts: nil) + + // Act let actualResult = try mapper.map(input) + + // Assert assertEqual(actualResult, expectedResult) } - - func testMappingWithOneContractValue() throws { + + func testMap_WithOneContract_ReturnsVerifiedIdRequiremen() throws { + // Arrange let (input, expectedResult) = try setUpInput(contracts: ["contract2645"]) + + // Act let actualResult = try mapper.map(input) + + // Assert assertEqual(actualResult, expectedResult) } - - func testMappingWithThreeContractValue() throws { + + func testMap_WithThreeContracts_ReturnsVerifiedIdRequiremen() throws { + // Arrange let (input, expectedResult) = try setUpInput(contracts: ["contract2645", "contract0394", "contract2343"]) + + // Act let actualResult = try mapper.map(input) + + // Assert assertEqual(actualResult, expectedResult) } - + private func assertEqual(_ actual: VerifiedIdRequirement, _ expected: VerifiedIdRequirement) { XCTAssertEqual(actual.encrypted, expected.encrypted) XCTAssertEqual(actual.required, expected.required) XCTAssertEqual(actual.types, expected.types) - XCTAssertEqual(actual.acceptedIssuers, expected.acceptedIssuers) XCTAssertEqual(actual.purpose, expected.purpose) - XCTAssertEqual(actual.issuanceOptions, expected.issuanceOptions) + XCTAssertEqual(actual.issuanceOptions as? [VerifiedIdRequestURL], expected.issuanceOptions as? [VerifiedIdRequestURL]) } - + private func setUpInput(encrypted: Bool? = false, required: Bool? = false, - issuers: [String?]? = [], + credentialType: String = "credentialType", contracts: [String]? = []) throws -> (PresentationDescriptor, VerifiedIdRequirement) { - - let issuersInput = issuers?.compactMap { IssuerDescriptor(iss: $0) } + let input = PresentationDescriptor(encrypted: encrypted, claims: [], presentationRequired: required, - credentialType: exceptedCredentialType, - issuers: issuersInput, + credentialType: credentialType, + issuers: nil, contracts: contracts) - - let expectedIssuers = issuers?.compactMap { $0 } ?? [] - - var expectedIssuanceOptions: IssuanceOptions? = nil - if let expectedContracts = contracts, - !expectedContracts.isEmpty { - expectedIssuanceOptions = IssuanceOptions(acceptedIssuers: expectedIssuers, - credentialIssuerMetadata: expectedContracts) + + let expectedIssuanceOptions = contracts?.compactMap { + + if let contract = URL(string: $0) { + return VerifiedIdRequestURL(url: contract) + } + + return nil } - + let expectedResult = VerifiedIdRequirement(encrypted: encrypted ?? false, required: required ?? false, - types: [exceptedCredentialType], - acceptedIssuers: expectedIssuers, + types: [credentialType], purpose: nil, - issuanceOptions: expectedIssuanceOptions) + issuanceOptions: expectedIssuanceOptions ?? []) return (input, expectedResult) } } From 51eedab612851979c023f23fd8bdbac6a34954f9 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 15:04:18 -0800 Subject: [PATCH 050/373] Add tests for LinkedDomainResult mapper. --- .../WalletLibrary.xcodeproj/project.pbxproj | 52 ++++++++++++------- ...wift => LinkedDomainResult+Mappable.swift} | 0 .../WalletLibrary/Requests/RootOfTrust.swift | 2 +- .../LinkedDomainResult+MappableTests.swift | 51 ++++++++++++++++++ 4 files changed, 86 insertions(+), 19 deletions(-) rename WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/{LinkedDomainResult+RootOfTrust.swift => LinkedDomainResult+Mappable.swift} (100%) create mode 100644 WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/LinkedDomainResult+MappableTests.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index f1a3efde..23a1f43a 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -39,10 +39,14 @@ 5534E66E294A0F8F005D0765 /* Mapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5534E66D294A0F8F005D0765 /* Mapping.swift */; }; 5534E672294A19D5005D0765 /* IdTokenDescriptor+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5534E671294A19D5005D0765 /* IdTokenDescriptor+Mappable.swift */; }; 5534E677294A1FA0005D0765 /* AccessTokenDescriptor+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5534E676294A1FA0005D0765 /* AccessTokenDescriptor+Mappable.swift */; }; - 5534E688294A5F16005D0765 /* IdTokenDescriptorMappingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5534E687294A5F16005D0765 /* IdTokenDescriptorMappingTests.swift */; }; - 5534E68C294A8B8F005D0765 /* AccessTokenDescriptorMappingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5534E68B294A8B8F005D0765 /* AccessTokenDescriptorMappingTests.swift */; }; - 5534E6AD294AAFA6005D0765 /* PresentationDescriptorMappingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5534E6AC294AAFA6005D0765 /* PresentationDescriptorMappingTests.swift */; }; + 5534E688294A5F16005D0765 /* IdTokenDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5534E687294A5F16005D0765 /* IdTokenDescriptor+MappableTests.swift */; }; + 5534E68C294A8B8F005D0765 /* AccessTokenDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5534E68B294A8B8F005D0765 /* AccessTokenDescriptor+MappableTests.swift */; }; + 5534E6AD294AAFA6005D0765 /* PresentationDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5534E6AC294AAFA6005D0765 /* PresentationDescriptor+MappableTests.swift */; }; 559BD3032995993700BD61AC /* PresentationInputDescriptor+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */; }; + 559BD3052995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3042995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift */; }; + 559BD3072995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3062995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift */; }; + 559BD3092995ABE800BD61AC /* PresentationRequest+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3082995ABE800BD61AC /* PresentationRequest+MappableTests.swift */; }; + 559BD30B2995B1B500BD61AC /* LinkedDomainResult+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD30A2995B1B500BD61AC /* LinkedDomainResult+MappableTests.swift */; }; 55A81BCA2991AB82002C259A /* RequestResolving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BC92991AB82002C259A /* RequestResolving.swift */; }; 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE02991AB96002C259A /* RequestHandling.swift */; }; 55A81BE52991B2E5002C259A /* RequestResolverFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */; }; @@ -62,7 +66,7 @@ 55E2F08129955A960008010D /* RequestType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F08029955A960008010D /* RequestType.swift */; }; 55E2F08429955E8A0008010D /* MockMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F08329955E8A0008010D /* MockMapper.swift */; }; 55E2F086299573C30008010D /* OpenIdRequesterStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F085299573C30008010D /* OpenIdRequesterStyle.swift */; }; - 55E2F0882995743D0008010D /* LinkedDomainResult+RootOfTrust.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F0872995743D0008010D /* LinkedDomainResult+RootOfTrust.swift */; }; + 55E2F0882995743D0008010D /* LinkedDomainResult+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F0872995743D0008010D /* LinkedDomainResult+Mappable.swift */; }; 55E2F08A299576350008010D /* PresentationDefinition+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F089299576350008010D /* PresentationDefinition+Mappable.swift */; }; 55E2F08C299576730008010D /* GroupRequirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F08B299576730008010D /* GroupRequirement.swift */; }; 55E33684293E7B6600CD2ED7 /* PromiseKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55E33679293E7B5F00CD2ED7 /* PromiseKit.framework */; }; @@ -287,10 +291,14 @@ 5534E66D294A0F8F005D0765 /* Mapping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Mapping.swift; sourceTree = ""; }; 5534E671294A19D5005D0765 /* IdTokenDescriptor+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IdTokenDescriptor+Mappable.swift"; sourceTree = ""; }; 5534E676294A1FA0005D0765 /* AccessTokenDescriptor+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AccessTokenDescriptor+Mappable.swift"; sourceTree = ""; }; - 5534E687294A5F16005D0765 /* IdTokenDescriptorMappingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdTokenDescriptorMappingTests.swift; sourceTree = ""; }; - 5534E68B294A8B8F005D0765 /* AccessTokenDescriptorMappingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessTokenDescriptorMappingTests.swift; sourceTree = ""; }; - 5534E6AC294AAFA6005D0765 /* PresentationDescriptorMappingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentationDescriptorMappingTests.swift; sourceTree = ""; }; + 5534E687294A5F16005D0765 /* IdTokenDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IdTokenDescriptor+MappableTests.swift"; sourceTree = ""; }; + 5534E68B294A8B8F005D0765 /* AccessTokenDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AccessTokenDescriptor+MappableTests.swift"; sourceTree = ""; }; + 5534E6AC294AAFA6005D0765 /* PresentationDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationDescriptor+MappableTests.swift"; sourceTree = ""; }; 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationInputDescriptor+Mappable.swift"; sourceTree = ""; }; + 559BD3042995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationDefinition+MappableTests.swift"; sourceTree = ""; }; + 559BD3062995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationInputDescriptor+MappableTests.swift"; sourceTree = ""; }; + 559BD3082995ABE800BD61AC /* PresentationRequest+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationRequest+MappableTests.swift"; sourceTree = ""; }; + 559BD30A2995B1B500BD61AC /* LinkedDomainResult+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LinkedDomainResult+MappableTests.swift"; sourceTree = ""; }; 55A81BC92991AB82002C259A /* RequestResolving.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolving.swift; sourceTree = ""; }; 55A81BE02991AB96002C259A /* RequestHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestHandling.swift; sourceTree = ""; }; 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolverFactory.swift; sourceTree = ""; }; @@ -310,7 +318,7 @@ 55E2F08029955A960008010D /* RequestType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestType.swift; sourceTree = ""; }; 55E2F08329955E8A0008010D /* MockMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockMapper.swift; sourceTree = ""; }; 55E2F085299573C30008010D /* OpenIdRequesterStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenIdRequesterStyle.swift; sourceTree = ""; }; - 55E2F0872995743D0008010D /* LinkedDomainResult+RootOfTrust.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LinkedDomainResult+RootOfTrust.swift"; sourceTree = ""; }; + 55E2F0872995743D0008010D /* LinkedDomainResult+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LinkedDomainResult+Mappable.swift"; sourceTree = ""; }; 55E2F089299576350008010D /* PresentationDefinition+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationDefinition+Mappable.swift"; sourceTree = ""; }; 55E2F08B299576730008010D /* GroupRequirement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupRequirement.swift; sourceTree = ""; }; 55E33666293E7B5000CD2ED7 /* PMKFoundation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = PMKFoundation.xcodeproj; path = Foundation/PMKFoundation.xcodeproj; sourceTree = ""; }; @@ -539,12 +547,12 @@ isa = PBXGroup; children = ( 5534E676294A1FA0005D0765 /* AccessTokenDescriptor+Mappable.swift */, - 55E2F0872995743D0008010D /* LinkedDomainResult+RootOfTrust.swift */, + 55E2F0872995743D0008010D /* LinkedDomainResult+Mappable.swift */, 5534E671294A19D5005D0765 /* IdTokenDescriptor+Mappable.swift */, - 5534E64C294A0888005D0765 /* PresentationDescriptor+Mappable.swift */, 55E2F089299576350008010D /* PresentationDefinition+Mappable.swift */, - 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */, + 5534E64C294A0888005D0765 /* PresentationDescriptor+Mappable.swift */, 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */, + 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */, ); path = VCSDK; sourceTree = ""; @@ -568,9 +576,13 @@ 5534E67E294A3943005D0765 /* VCSDK */ = { isa = PBXGroup; children = ( - 5534E68B294A8B8F005D0765 /* AccessTokenDescriptorMappingTests.swift */, - 5534E687294A5F16005D0765 /* IdTokenDescriptorMappingTests.swift */, - 5534E6AC294AAFA6005D0765 /* PresentationDescriptorMappingTests.swift */, + 5534E68B294A8B8F005D0765 /* AccessTokenDescriptor+MappableTests.swift */, + 5534E687294A5F16005D0765 /* IdTokenDescriptor+MappableTests.swift */, + 559BD3042995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift */, + 5534E6AC294AAFA6005D0765 /* PresentationDescriptor+MappableTests.swift */, + 559BD3062995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift */, + 559BD3082995ABE800BD61AC /* PresentationRequest+MappableTests.swift */, + 559BD30A2995B1B500BD61AC /* LinkedDomainResult+MappableTests.swift */, ); path = VCSDK; sourceTree = ""; @@ -1208,7 +1220,7 @@ 55E336D4293FA75300CD2ED7 /* VerifiedId.swift in Sources */, 55E2F06C299553300008010D /* PresentationRequest+Mappable.swift in Sources */, 55A81BCA2991AB82002C259A /* RequestResolving.swift in Sources */, - 55E2F0882995743D0008010D /* LinkedDomainResult+RootOfTrust.swift in Sources */, + 55E2F0882995743D0008010D /* LinkedDomainResult+Mappable.swift in Sources */, 55E33708293FD25900CD2ED7 /* AccessTokenRequirement.swift in Sources */, 55E2F07D2995561E0008010D /* OpenIdPresentationRequest.swift in Sources */, ); @@ -1221,21 +1233,25 @@ 55E2F08429955E8A0008010D /* MockMapper.swift in Sources */, 55E2F06F299553B40008010D /* OpenIdRequestHandlerTests.swift in Sources */, 55E3376B29478C5000CD2ED7 /* AsyncWrapperTests.swift in Sources */, - 5534E6AD294AAFA6005D0765 /* PresentationDescriptorMappingTests.swift in Sources */, + 5534E6AD294AAFA6005D0765 /* PresentationDescriptor+MappableTests.swift in Sources */, 55A81C132991C829002C259A /* MockHandler.swift in Sources */, + 559BD3072995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift in Sources */, 55A81C072991BB2C002C259A /* RequestResolverFactoryTests.swift in Sources */, 55A81C102991C211002C259A /* MockResolver.swift in Sources */, 55A81C092991BB3A002C259A /* RequestHandlerFactoryTests.swift in Sources */, 550A91952994025C0014D030 /* OpenIdURLRequestResolverTests.swift in Sources */, + 559BD3052995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift in Sources */, 55A81C0D2991BF86002C259A /* MockInput.swift in Sources */, 550A919829940DCE0014D030 /* MockOpenIdforVCResolver.swift in Sources */, + 559BD3092995ABE800BD61AC /* PresentationRequest+MappableTests.swift in Sources */, 550A91A0299412790014D030 /* MockOpenIdRawRequest.swift in Sources */, 55E2F076299555280008010D /* MockRequirement.swift in Sources */, - 5534E68C294A8B8F005D0765 /* AccessTokenDescriptorMappingTests.swift in Sources */, + 5534E68C294A8B8F005D0765 /* AccessTokenDescriptor+MappableTests.swift in Sources */, 55E2F077299555280008010D /* MockRequesterStyle.swift in Sources */, 552E50A0293E6AB200868F47 /* VerifiedIdClientTests.swift in Sources */, - 5534E688294A5F16005D0765 /* IdTokenDescriptorMappingTests.swift in Sources */, + 5534E688294A5F16005D0765 /* IdTokenDescriptor+MappableTests.swift in Sources */, 550A913D29930D970014D030 /* MockRawRequest.swift in Sources */, + 559BD30B2995B1B500BD61AC /* LinkedDomainResult+MappableTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/LinkedDomainResult+RootOfTrust.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/LinkedDomainResult+Mappable.swift similarity index 100% rename from WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/LinkedDomainResult+RootOfTrust.swift rename to WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/LinkedDomainResult+Mappable.swift diff --git a/WalletLibrary/WalletLibrary/Requests/RootOfTrust.swift b/WalletLibrary/WalletLibrary/Requests/RootOfTrust.swift index 84aee13f..f0804019 100644 --- a/WalletLibrary/WalletLibrary/Requests/RootOfTrust.swift +++ b/WalletLibrary/WalletLibrary/Requests/RootOfTrust.swift @@ -6,7 +6,7 @@ /** * Root of Trust such as Linked Domain Verified for the request. */ -public struct RootOfTrust { +public struct RootOfTrust: Equatable { /// Whether root of trust is verified or not (for example, if the linked domain check succeeded. public let verified: Bool diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/LinkedDomainResult+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/LinkedDomainResult+MappableTests.swift new file mode 100644 index 00000000..4a18788d --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/LinkedDomainResult+MappableTests.swift @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import XCTest +import VCEntities +@testable import WalletLibrary + +class LinkedDomainResultMappingTests: XCTestCase { + + let mapper = Mapper() + + func testMap_WithLinkedDomainMissing_ReturnsRootOfTrust() throws { + // Arrange + let linkedDomainResult = LinkedDomainResult.linkedDomainMissing + let expectedResult = RootOfTrust(verified: false, source: nil) + + // Act + let actualResult = try mapper.map(linkedDomainResult) + + // Assert + XCTAssertEqual(actualResult, expectedResult) + } + + func testMap_WithLinkedDomainVerified_ReturnsRootOfTrust() throws { + // Arrange + let mockDomain = "verifiedDomain342" + let linkedDomainResult = LinkedDomainResult.linkedDomainVerified(domainUrl: mockDomain) + let expectedResult = RootOfTrust(verified: true, source: mockDomain) + + // Act + let actualResult = try mapper.map(linkedDomainResult) + + // Assert + XCTAssertEqual(actualResult, expectedResult) + } + + func testMap_WithLinkedDomainUnverified_ReturnsRootOfTrust() throws { + // Arrange + let mockDomain = "unverifiedDomain235" + let linkedDomainResult = LinkedDomainResult.linkedDomainUnverified(domainUrl: mockDomain) + let expectedResult = RootOfTrust(verified: false, source: mockDomain) + + // Act + let actualResult = try mapper.map(linkedDomainResult) + + // Assert + XCTAssertEqual(actualResult, expectedResult) + } +} From 952bbb230c5fbf8769dac7873b8692060b622276 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 15:04:46 -0800 Subject: [PATCH 051/373] Rename AccessTokenDescriptorMappingTests --- ...ppingTests.swift => AccessTokenDescriptor+MappableTests.swift} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/{AccessTokenDescriptorMappingTests.swift => AccessTokenDescriptor+MappableTests.swift} (100%) diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/AccessTokenDescriptorMappingTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/AccessTokenDescriptor+MappableTests.swift similarity index 100% rename from WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/AccessTokenDescriptorMappingTests.swift rename to WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/AccessTokenDescriptor+MappableTests.swift From 0d0c7e4adc1da597b1df680839e95eab832dd5d1 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 15:05:08 -0800 Subject: [PATCH 052/373] Rename IdTokenDescriptorMappingTests. --- ...orMappingTests.swift => IdTokenDescriptor+MappableTests.swift} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/{IdTokenDescriptorMappingTests.swift => IdTokenDescriptor+MappableTests.swift} (100%) diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/IdTokenDescriptorMappingTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/IdTokenDescriptor+MappableTests.swift similarity index 100% rename from WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/IdTokenDescriptorMappingTests.swift rename to WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/IdTokenDescriptor+MappableTests.swift From 7db61236611392d923afbcd96cf3a65050a09d39 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 15:05:40 -0800 Subject: [PATCH 053/373] Rename PresentationDescriptorMappingTests --- ...pingTests.swift => PresentationDescriptor+MappableTests.swift} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/{PresentationDescriptorMappingTests.swift => PresentationDescriptor+MappableTests.swift} (100%) diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDescriptorMappingTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDescriptor+MappableTests.swift similarity index 100% rename from WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDescriptorMappingTests.swift rename to WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDescriptor+MappableTests.swift From 2afc5eb3d0303af5b9e14f5d2b6828e1089b26e5 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 16:41:44 -0800 Subject: [PATCH 054/373] Update MockMapper.swift --- .../Mocks/Utilities/MockMapper.swift | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Utilities/MockMapper.swift b/WalletLibrary/WalletLibraryTests/Mocks/Utilities/MockMapper.swift index 63ba57c9..7630ad1b 100644 --- a/WalletLibrary/WalletLibraryTests/Mocks/Utilities/MockMapper.swift +++ b/WalletLibrary/WalletLibraryTests/Mocks/Utilities/MockMapper.swift @@ -7,24 +7,20 @@ struct MockMapper: Mapping { - let error: Error? + let callback: ((Any) throws -> (Any?))? - let returnedObject: Any? - - init(error: Error? = nil, returnedObject: Any? = nil) { - self.error = error - self.returnedObject = returnedObject + init(callback: ((Any) throws -> (Any?))? = nil) { + self.callback = callback } /// Map one object to another. func map(_ object: T) throws -> T.T { - if let error = error { - throw error - } - - if let returnedObject = returnedObject as? T.T { - return returnedObject + if let callback = callback { + let result = try callback(object) + if let result = result as? T.T { + return result + } } return try object.map(using: self) From 1bdc40e01304bc94be4fb31970104b995e537192 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 16:41:49 -0800 Subject: [PATCH 055/373] Update OpenIdRequestHandlerTests.swift --- .../Handlers/OpenIdRequestHandlerTests.swift | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift index e2982aea..b5727be6 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import XCTest +import VCEntities @testable import WalletLibrary class OpenIdRequestHandlerTests: XCTestCase { @@ -21,7 +22,16 @@ class OpenIdRequestHandlerTests: XCTestCase { let expectedContent = VerifiedIdRequestContent(style: expectedStyle, requirement: expectedRequirement, rootOfTrust: expectedRootOfTrust) - let mockMapper = MockMapper(returnedObject: expectedContent) + + func callback(object: Any) throws -> Any? { + if object is MockOpenIdRawRequest { + return expectedContent + } + + return nil + } + + let mockMapper = MockMapper(callback: callback) let mockRawRequest = MockOpenIdRawRequest(raw: Data()) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration) @@ -38,7 +48,15 @@ class OpenIdRequestHandlerTests: XCTestCase { func testHandleRequest_WithPresentationRequestInvalidMapping_ThrowsError() async throws { // Arrange - let mockMapper = MockMapper(error: ExpectedError.expectedToBeThrown) + func callback(object: Any) throws -> Any? { + if object is MockOpenIdRawRequest { + throw ExpectedError.expectedToBeThrown + } + + return nil + } + + let mockMapper = MockMapper(callback: callback) let mockRawRequest = MockOpenIdRawRequest(raw: Data()) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration) From e5480d74361589d9699027006ce6da01937b1238 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 16:42:04 -0800 Subject: [PATCH 056/373] Create PresentationDefinition+MappableTests.swift --- ...PresentationDefinition+MappableTests.swift | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDefinition+MappableTests.swift diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDefinition+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDefinition+MappableTests.swift new file mode 100644 index 00000000..db27ffef --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDefinition+MappableTests.swift @@ -0,0 +1,72 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import XCTest +import VCEntities +@testable import WalletLibrary + +class PresentationDefinitionMappingTests: XCTestCase { + + func testMap_WithNilInputDescriptors_ThrowsError() throws { + + // Arrange + let mockMapper = MockMapper() + let presentationDefinition = PresentationDefinition(id: nil, + inputDescriptors: nil, + issuance: nil) + + // Act + XCTAssertThrowsError(try mockMapper.map(presentationDefinition)) { error in + // Assert + XCTAssert(error is PresentationDefinitionMappingError) + XCTAssertEqual(error as? PresentationDefinitionMappingError, .nilInputDescriptors) + } + } + + func testMap_WithOneInputDescriptorPresent_ReturnsVerifiedIdRequirement() throws { + + // Arrange + let mockVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, + required: false, + types: [], + purpose: nil, + issuanceOptions: []) + let inputDescriptor = PresentationInputDescriptor(id: nil, + schema: nil, + issuanceMetadata: nil, + name: nil, + purpose: nil, + constraints: nil) + let presentationDefinition = PresentationDefinition(id: nil, + inputDescriptors: [inputDescriptor], + issuance: nil) + + func callback(type: Any) throws -> Any? { + if type is PresentationInputDescriptor { + return mockVerifiedIdRequirement + } + + return nil + } + + let mockMapper = MockMapper(callback: callback) + + let actualResult = try mockMapper.map(presentationDefinition) + + // Assert + XCTAssertIdentical(actualResult as AnyObject, mockVerifiedIdRequirement as AnyObject) + } + + func testMap_WithMultipleInputDescriptorPresent_ReturnsGroupRequirement() throws { + // Arrange +// let (input, expectedResult) = try setUpInput(encrypted: false, required: false) + + // Act +// let actualResult = try mapper.map(input) + + // Assert +// assertEqual(actualResult, expectedResult) + } +} From 02e39b907a2d71c77bfa1cd8b51527934343aeb1 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 16:42:15 -0800 Subject: [PATCH 057/373] Update PresentationDefinition+Mappable.swift --- .../Mappings/VCSDK/PresentationDefinition+Mappable.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift index 9b1d2a26..49cd2f1b 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift @@ -5,6 +5,10 @@ import VCEntities +enum PresentationDefinitionMappingError: Error { + case nilInputDescriptors +} + /** * An extension of the VCEntities.PresentationDefinition class to be able * to map PresentationDefinition to a Requirement. @@ -14,7 +18,7 @@ extension VCEntities.PresentationDefinition: Mappable { func map(using mapper: Mapping) throws -> Requirement { guard let inputDescriptors = self.inputDescriptors else { - throw VerifiedIdClientError.TODO(message: "add error") + throw PresentationDefinitionMappingError.nilInputDescriptors } if inputDescriptors.capacity == 1, From d6e49ce4367a9d7fdb5f4021694c9a8e87b2376b Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 16:42:18 -0800 Subject: [PATCH 058/373] Create PresentationInputDescriptor+MappableTests.swift --- ...ntationInputDescriptor+MappableTests.swift | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationInputDescriptor+MappableTests.swift diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationInputDescriptor+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationInputDescriptor+MappableTests.swift new file mode 100644 index 00000000..b10e0fc9 --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationInputDescriptor+MappableTests.swift @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import XCTest +import VCEntities +@testable import WalletLibrary + +class PresentationInputDescriptorMappingTests: XCTestCase { + + let mapper = Mapper() + + func testMap_WhenWithAllDefaultInput_ReturnsVerifiedIdRequirement() throws { + // Arrange +// let (input, expectedResult) = try setUpInput(encrypted: false, required: false) + + // Act +// let actualResult = try mapper.map(input) + + // Assert +// assertEqual(actualResult, expectedResult) + } + +} From a0c0362ed9f5d959e0bacc03b4fb539cbb462e94 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 16:42:21 -0800 Subject: [PATCH 059/373] Create PresentationRequest+MappableTests.swift --- .../PresentationRequest+MappableTests.swift | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationRequest+MappableTests.swift diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationRequest+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationRequest+MappableTests.swift new file mode 100644 index 00000000..a5b6071e --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationRequest+MappableTests.swift @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import XCTest +import VCEntities +@testable import WalletLibrary + +class PresentationRequestMappingTests: XCTestCase { + + let mapper = Mapper() + + func testMap_WithNoPresentationDefinitionPresent_ThrowsError() throws { +// // Arrange +// let presentationRequestToken = PresentationRequestToken(headers: Header(), +// content: PresentationRequestClaims() + + // Act +// let actualResult = try mapper.map(input) + + // Assert +// assertEqual(actualResult, expectedResult) + } + + func testMap_WithInvalidPresentationDefinition_ThrowsError() throws { + // Arrange +// let (input, expectedResult) = try setUpInput(encrypted: false, required: false) + + // Act +// let actualResult = try mapper.map(input) + + // Assert +// assertEqual(actualResult, expectedResult) + } + + func testMap_WithInvalidRootOfTrust_ThrowsError() throws { + // Arrange +// let (input, expectedResult) = try setUpInput(encrypted: false, required: false) + + // Act +// let actualResult = try mapper.map(input) + + // Assert +// assertEqual(actualResult, expectedResult) + } + + func testMap_WithNoClientNamePresent_ReturnVerifiedIdRequestContent() throws { + // Arrange +// let (input, expectedResult) = try setUpInput(encrypted: false, required: false) + + // Act +// let actualResult = try mapper.map(input) + + // Assert +// assertEqual(actualResult, expectedResult) + } + + func testMap_WithClientNamePresent_ReturnVerifiedIdRequestContent() throws { + // Arrange +// let (input, expectedResult) = try setUpInput(encrypted: false, required: false) + + // Act +// let actualResult = try mapper.map(input) + + // Assert +// assertEqual(actualResult, expectedResult) + } + +} From 6aa4eea419ba30b14b35abda556fb6aff1377095 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 18:05:50 -0800 Subject: [PATCH 060/373] Complete test cases for PresentationDefinition mapper. --- ...PresentationDefinition+MappableTests.swift | 57 ++++++++++++++++--- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDefinition+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDefinition+MappableTests.swift index db27ffef..bab1b4a4 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDefinition+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDefinition+MappableTests.swift @@ -28,7 +28,7 @@ class PresentationDefinitionMappingTests: XCTestCase { func testMap_WithOneInputDescriptorPresent_ReturnsVerifiedIdRequirement() throws { // Arrange - let mockVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, + let expectedVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, required: false, types: [], purpose: nil, @@ -45,7 +45,7 @@ class PresentationDefinitionMappingTests: XCTestCase { func callback(type: Any) throws -> Any? { if type is PresentationInputDescriptor { - return mockVerifiedIdRequirement + return expectedVerifiedIdRequirement } return nil @@ -56,17 +56,60 @@ class PresentationDefinitionMappingTests: XCTestCase { let actualResult = try mockMapper.map(presentationDefinition) // Assert - XCTAssertIdentical(actualResult as AnyObject, mockVerifiedIdRequirement as AnyObject) + XCTAssert(actualResult is VerifiedIdRequirement) + XCTAssertIdentical(actualResult as AnyObject, expectedVerifiedIdRequirement as AnyObject) } func testMap_WithMultipleInputDescriptorPresent_ReturnsGroupRequirement() throws { + // Arrange -// let (input, expectedResult) = try setUpInput(encrypted: false, required: false) + let mockVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, + required: false, + types: [], + purpose: nil, + issuanceOptions: []) + let firstMockInputDescriptor = PresentationInputDescriptor(id: nil, + schema: nil, + issuanceMetadata: nil, + name: nil, + purpose: nil, + constraints: nil) + let secondMockInputDescriptor = PresentationInputDescriptor(id: nil, + schema: nil, + issuanceMetadata: nil, + name: nil, + purpose: nil, + constraints: nil) + let thirdMockInputDescriptor = PresentationInputDescriptor(id: nil, + schema: nil, + issuanceMetadata: nil, + name: nil, + purpose: nil, + constraints: nil) + let inputDescriptors = [firstMockInputDescriptor, secondMockInputDescriptor, thirdMockInputDescriptor] + let presentationDefinition = PresentationDefinition(id: nil, + inputDescriptors: inputDescriptors, + issuance: nil) - // Act -// let actualResult = try mapper.map(input) + func callback(type: Any) throws -> Any? { + if type is PresentationInputDescriptor { + return mockVerifiedIdRequirement + } + + return nil + } + + let mockMapper = MockMapper(callback: callback) + + let actualResult = try mockMapper.map(presentationDefinition) // Assert -// assertEqual(actualResult, expectedResult) + XCTAssert(actualResult is GroupRequirement) + XCTAssertEqual((actualResult as? GroupRequirement)?.requirements.count, 3) + XCTAssertIdentical((actualResult as? GroupRequirement)?.requirements.first as AnyObject, mockVerifiedIdRequirement as AnyObject) + XCTAssertIdentical((actualResult as? GroupRequirement)?.requirements[1] as AnyObject, mockVerifiedIdRequirement as AnyObject) + XCTAssertIdentical((actualResult as? GroupRequirement)?.requirements[2] as AnyObject, mockVerifiedIdRequirement as AnyObject) + XCTAssert((actualResult as? GroupRequirement)?.required ?? false) + XCTAssertEqual((actualResult as? GroupRequirement)?.requirementOperator, .ANY) } } From a2c0498d311ce3b28eee91375fca29a0784f9611 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 18:14:08 -0800 Subject: [PATCH 061/373] Update PresentationInputDescriptor+Mappable.swift --- .../VCSDK/PresentationInputDescriptor+Mappable.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationInputDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationInputDescriptor+Mappable.swift index e5acd407..2b11316d 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationInputDescriptor+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationInputDescriptor+Mappable.swift @@ -6,7 +6,7 @@ import VCEntities enum PresentationInputDescriptorMappingError: Error { - case noVerifiedIdRequirementTypePresent + case noVerifiedIdTypeInPresentationInputDescriptor } /** @@ -19,10 +19,11 @@ extension VCEntities.PresentationInputDescriptor: Mappable { guard let types = schema?.compactMap({ $0.uri }), !types.isEmpty else { - throw PresentationRequestMappingError.presentationDefinitionMissingInRequest + throw PresentationInputDescriptorMappingError.noVerifiedIdTypeInPresentationInputDescriptor } let issuanceOptions = self.issuanceMetadata?.compactMap { + if let contract = $0.contract, let url = URL(string: contract) { return VerifiedIdRequestURL(url: url) @@ -34,7 +35,7 @@ extension VCEntities.PresentationInputDescriptor: Mappable { return VerifiedIdRequirement(encrypted: false, required: true, types: types, - purpose: nil, + purpose: purpose, issuanceOptions: issuanceOptions ?? []) } } From 3003f15ea6d64d16a573d8f505d7fd99f5b7f961 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 18:14:32 -0800 Subject: [PATCH 062/373] Define test cases for PresentationInputDescriptor mapper --- ...ntationInputDescriptor+MappableTests.swift | 89 ++++++++++++++++++- 1 file changed, 88 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationInputDescriptor+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationInputDescriptor+MappableTests.swift index b10e0fc9..544b2565 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationInputDescriptor+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationInputDescriptor+MappableTests.swift @@ -11,7 +11,7 @@ class PresentationInputDescriptorMappingTests: XCTestCase { let mapper = Mapper() - func testMap_WhenWithAllDefaultInput_ReturnsVerifiedIdRequirement() throws { + func testMap_WithNilSchema_ThrowsError() throws { // Arrange // let (input, expectedResult) = try setUpInput(encrypted: false, required: false) @@ -22,4 +22,91 @@ class PresentationInputDescriptorMappingTests: XCTestCase { // assertEqual(actualResult, expectedResult) } + func testMap_WithNoVerifiedIdTypesPresent_ThrowsError() throws { + // Arrange +// let (input, expectedResult) = try setUpInput(encrypted: false, required: false) + + // Act +// let actualResult = try mapper.map(input) + + // Assert +// assertEqual(actualResult, expectedResult) + } + + func testMap_WithOneTypePresent_ReturnsVerifiedIdRequirement() throws { + // Arrange +// let (input, expectedResult) = try setUpInput(encrypted: false, required: false) + + // Act +// let actualResult = try mapper.map(input) + + // Assert +// assertEqual(actualResult, expectedResult) + } + + func testMap_WithMultipleTypesPresent_ReturnsVerifiedIdRequirement() throws { + // Arrange +// let (input, expectedResult) = try setUpInput(encrypted: false, required: false) + + // Act +// let actualResult = try mapper.map(input) + + // Assert +// assertEqual(actualResult, expectedResult) + } + + func testMap_WithNoIssuanceMetadataPresent_ReturnsVerifiedIdRequirement() throws { + // Arrange +// let (input, expectedResult) = try setUpInput(encrypted: false, required: false) + + // Act +// let actualResult = try mapper.map(input) + + // Assert +// assertEqual(actualResult, expectedResult) + } + + func testMap_WithInvalidContractFormatInIssuanceMetadata_ReturnsVerifiedIdRequirement() throws { + // Arrange +// let (input, expectedResult) = try setUpInput(encrypted: false, required: false) + + // Act +// let actualResult = try mapper.map(input) + + // Assert +// assertEqual(actualResult, expectedResult) + } + + func testMap_WithOneContractPresent_ReturnsVerifiedIdRequirement() throws { + // Arrange +// let (input, expectedResult) = try setUpInput(encrypted: false, required: false) + + // Act +// let actualResult = try mapper.map(input) + + // Assert +// assertEqual(actualResult, expectedResult) + } + + func testMap_WithMultipleContractsPresent_ReturnsVerifiedIdRequirement() throws { + // Arrange +// let (input, expectedResult) = try setUpInput(encrypted: false, required: false) + + // Act +// let actualResult = try mapper.map(input) + + // Assert +// assertEqual(actualResult, expectedResult) + } + + func testMap_WithNoPurposePresent_ReturnsVerifiedIdRequirement() throws { + // Arrange +// let (input, expectedResult) = try setUpInput(encrypted: false, required: false) + + // Act +// let actualResult = try mapper.map(input) + + // Assert +// assertEqual(actualResult, expectedResult) + } } From 97fd96d7aac2f640fb6384997fefa4dd0c1d6528 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 18:14:44 -0800 Subject: [PATCH 063/373] Define test cases for PresentationRequest mapper. --- .../Translations/VCSDK/PresentationRequest+MappableTests.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationRequest+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationRequest+MappableTests.swift index a5b6071e..cca936fe 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationRequest+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationRequest+MappableTests.swift @@ -66,5 +66,4 @@ class PresentationRequestMappingTests: XCTestCase { // Assert // assertEqual(actualResult, expectedResult) } - } From bd7ed9f61300d6139e08227b8eb595025f8c4ab3 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 18:16:45 -0800 Subject: [PATCH 064/373] Update VerifiableCredential-SDK-iOS --- WalletLibrary/Submodules/VerifiableCredential-SDK-iOS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS b/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS index adc058da..c38314e3 160000 --- a/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS +++ b/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS @@ -1 +1 @@ -Subproject commit adc058da72b76d01aa6f137c67e17a55967141ab +Subproject commit c38314e3bf52c94d7d21ec3beaacb7680f33b387 From a1169b42ec62b88c5147211bfaa76fb9029259b2 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 19:02:07 -0800 Subject: [PATCH 065/373] complete test cases for presentation input descriptor mapper. --- ...ntationInputDescriptor+MappableTests.swift | 221 ++++++++++++++---- 1 file changed, 179 insertions(+), 42 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationInputDescriptor+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationInputDescriptor+MappableTests.swift index 544b2565..08d00649 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationInputDescriptor+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationInputDescriptor+MappableTests.swift @@ -9,104 +9,241 @@ import VCEntities class PresentationInputDescriptorMappingTests: XCTestCase { - let mapper = Mapper() - func testMap_WithNilSchema_ThrowsError() throws { // Arrange -// let (input, expectedResult) = try setUpInput(encrypted: false, required: false) + let mockMapper = MockMapper() + let presentationInputDescriptor = PresentationInputDescriptor(id: nil, + schema: nil, + issuanceMetadata: nil, + name: nil, + purpose: nil, + constraints: nil) // Act -// let actualResult = try mapper.map(input) + XCTAssertThrowsError(try mockMapper.map(presentationInputDescriptor)) { error in + // Assert + XCTAssert(error is PresentationInputDescriptorMappingError) + XCTAssertEqual(error as? PresentationInputDescriptorMappingError, .noVerifiedIdTypeInPresentationInputDescriptor) + } + } + + func testMap_WithNilVerifiedIdTypePresent_ThrowsError() throws { + // Arrange + let mockMapper = MockMapper() + let schema = InputDescriptorSchema(uri: nil) + let presentationInputDescriptor = PresentationInputDescriptor(id: nil, + schema: [schema], + issuanceMetadata: nil, + name: nil, + purpose: nil, + constraints: nil) - // Assert -// assertEqual(actualResult, expectedResult) + // Act + XCTAssertThrowsError(try mockMapper.map(presentationInputDescriptor)) { error in + // Assert + XCTAssert(error is PresentationInputDescriptorMappingError) + XCTAssertEqual(error as? PresentationInputDescriptorMappingError, .noVerifiedIdTypeInPresentationInputDescriptor) + } } func testMap_WithNoVerifiedIdTypesPresent_ThrowsError() throws { // Arrange -// let (input, expectedResult) = try setUpInput(encrypted: false, required: false) + let mockMapper = MockMapper() + let presentationInputDescriptor = PresentationInputDescriptor(id: nil, + schema: [], + issuanceMetadata: nil, + name: nil, + purpose: nil, + constraints: nil) // Act -// let actualResult = try mapper.map(input) - - // Assert -// assertEqual(actualResult, expectedResult) + XCTAssertThrowsError(try mockMapper.map(presentationInputDescriptor)) { error in + // Assert + XCTAssert(error is PresentationInputDescriptorMappingError) + XCTAssertEqual(error as? PresentationInputDescriptorMappingError, .noVerifiedIdTypeInPresentationInputDescriptor) + } } func testMap_WithOneTypePresent_ReturnsVerifiedIdRequirement() throws { // Arrange -// let (input, expectedResult) = try setUpInput(encrypted: false, required: false) + let mockMapper = MockMapper() + let mockSchema = InputDescriptorSchema(uri: "mockType") + let expectedVerifiedIdRequest = VerifiedIdRequirement(encrypted: false, + required: true, + types: ["mockType"], + purpose: nil, + issuanceOptions: []) + let presentationInputDescriptor = PresentationInputDescriptor(id: nil, + schema: [mockSchema], + issuanceMetadata: nil, + name: nil, + purpose: nil, + constraints: nil) // Act -// let actualResult = try mapper.map(input) + let actualResult = try mockMapper.map(presentationInputDescriptor) // Assert -// assertEqual(actualResult, expectedResult) + XCTAssertEqual(actualResult.encrypted, expectedVerifiedIdRequest.encrypted) + XCTAssertEqual(actualResult.required, expectedVerifiedIdRequest.required) + XCTAssertEqual(actualResult.types, expectedVerifiedIdRequest.types) + XCTAssertEqual(actualResult.issuanceOptions as? [VerifiedIdRequestURL], []) + XCTAssertEqual(actualResult.purpose, expectedVerifiedIdRequest.purpose) } func testMap_WithMultipleTypesPresent_ReturnsVerifiedIdRequirement() throws { // Arrange -// let (input, expectedResult) = try setUpInput(encrypted: false, required: false) + let mockMapper = MockMapper() + let firstType = "firstType" + let secondType = "secondType" + let thirdType = "thirdType" + let firstSchema = InputDescriptorSchema(uri: firstType) + let secondSchema = InputDescriptorSchema(uri: secondType) + let thirdSchema = InputDescriptorSchema(uri: thirdType) + let expectedVerifiedIdRequest = VerifiedIdRequirement(encrypted: false, + required: true, + types: [firstType, secondType, thirdType], + purpose: nil, + issuanceOptions: []) + let presentationInputDescriptor = PresentationInputDescriptor(id: nil, + schema: [firstSchema, secondSchema, thirdSchema], + issuanceMetadata: nil, + name: nil, + purpose: nil, + constraints: nil) // Act -// let actualResult = try mapper.map(input) + let actualResult = try mockMapper.map(presentationInputDescriptor) // Assert -// assertEqual(actualResult, expectedResult) + XCTAssertEqual(actualResult.encrypted, expectedVerifiedIdRequest.encrypted) + XCTAssertEqual(actualResult.required, expectedVerifiedIdRequest.required) + XCTAssertEqual(actualResult.types, expectedVerifiedIdRequest.types) + XCTAssertEqual(actualResult.issuanceOptions as? [VerifiedIdRequestURL], []) + XCTAssertEqual(actualResult.purpose, expectedVerifiedIdRequest.purpose) } func testMap_WithNoIssuanceMetadataPresent_ReturnsVerifiedIdRequirement() throws { // Arrange -// let (input, expectedResult) = try setUpInput(encrypted: false, required: false) + let mockMapper = MockMapper() + let mockSchema = InputDescriptorSchema(uri: "mockType") + let expectedVerifiedIdRequest = VerifiedIdRequirement(encrypted: false, + required: true, + types: ["mockType"], + purpose: nil, + issuanceOptions: []) + let presentationInputDescriptor = PresentationInputDescriptor(id: nil, + schema: [mockSchema], + issuanceMetadata: nil, + name: nil, + purpose: nil, + constraints: nil) // Act -// let actualResult = try mapper.map(input) + let actualResult = try mockMapper.map(presentationInputDescriptor) // Assert -// assertEqual(actualResult, expectedResult) + XCTAssertEqual(actualResult.encrypted, expectedVerifiedIdRequest.encrypted) + XCTAssertEqual(actualResult.required, expectedVerifiedIdRequest.required) + XCTAssertEqual(actualResult.types, expectedVerifiedIdRequest.types) + XCTAssertEqual(actualResult.issuanceOptions as? [VerifiedIdRequestURL], []) + XCTAssertEqual(actualResult.purpose, expectedVerifiedIdRequest.purpose) } func testMap_WithInvalidContractFormatInIssuanceMetadata_ReturnsVerifiedIdRequirement() throws { // Arrange -// let (input, expectedResult) = try setUpInput(encrypted: false, required: false) - - // Act -// let actualResult = try mapper.map(input) - - // Assert -// assertEqual(actualResult, expectedResult) - } - - func testMap_WithOneContractPresent_ReturnsVerifiedIdRequirement() throws { - // Arrange -// let (input, expectedResult) = try setUpInput(encrypted: false, required: false) + let mockMapper = MockMapper() + let mockSchema = InputDescriptorSchema(uri: "mockType") + let invalidContract = "//|\\" + let invalidIssuanceMetadata = IssuanceMetadata(contract: invalidContract, issuerDid: nil) + let expectedVerifiedIdRequest = VerifiedIdRequirement(encrypted: false, + required: true, + types: ["mockType"], + purpose: nil, + issuanceOptions: []) + let presentationInputDescriptor = PresentationInputDescriptor(id: nil, + schema: [mockSchema], + issuanceMetadata: [invalidIssuanceMetadata], + name: nil, + purpose: nil, + constraints: nil) // Act -// let actualResult = try mapper.map(input) + let actualResult = try mockMapper.map(presentationInputDescriptor) // Assert -// assertEqual(actualResult, expectedResult) + XCTAssertEqual(actualResult.encrypted, expectedVerifiedIdRequest.encrypted) + XCTAssertEqual(actualResult.required, expectedVerifiedIdRequest.required) + XCTAssertEqual(actualResult.types, expectedVerifiedIdRequest.types) + XCTAssertEqual(actualResult.issuanceOptions as? [VerifiedIdRequestURL], []) + XCTAssertEqual(actualResult.purpose, expectedVerifiedIdRequest.purpose) } - func testMap_WithMultipleContractsPresent_ReturnsVerifiedIdRequirement() throws { + func testMap_WithOneInvalidContractAndTwoValidContractsPresent_ReturnsVerifiedIdRequirement() throws { // Arrange -// let (input, expectedResult) = try setUpInput(encrypted: false, required: false) + let mockMapper = MockMapper() + let mockSchema = InputDescriptorSchema(uri: "mockType") + let invalidContract = "//|\\" + let firstValidContract = "https://mockcontract1.com" + let secondValidContract = "https://mockcontract2.com" + + let invalidIssuanceMetadata = IssuanceMetadata(contract: invalidContract, issuerDid: nil) + let firstValidIssuanceMetadata = IssuanceMetadata(contract: firstValidContract, issuerDid: nil) + let secondValidIssuanceMetadata = IssuanceMetadata(contract: secondValidContract, issuerDid: nil) + + let firstVerifiedIdRequestURL = VerifiedIdRequestURL(url: URL(string: firstValidContract)!) + let secondVerifiedIdRequestURL = VerifiedIdRequestURL(url: URL(string: secondValidContract)!) + + let expectedVerifiedIdRequest = VerifiedIdRequirement(encrypted: false, + required: true, + types: ["mockType"], + purpose: nil, + issuanceOptions: [firstVerifiedIdRequestURL, secondVerifiedIdRequestURL]) + + let issuanceMetadatas = [firstValidIssuanceMetadata, invalidIssuanceMetadata, secondValidIssuanceMetadata] + let presentationInputDescriptor = PresentationInputDescriptor(id: nil, + schema: [mockSchema], + issuanceMetadata: issuanceMetadatas, + name: nil, + purpose: nil, + constraints: nil) // Act -// let actualResult = try mapper.map(input) + let actualResult = try mockMapper.map(presentationInputDescriptor) // Assert -// assertEqual(actualResult, expectedResult) + XCTAssertEqual(actualResult.encrypted, expectedVerifiedIdRequest.encrypted) + XCTAssertEqual(actualResult.required, expectedVerifiedIdRequest.required) + XCTAssertEqual(actualResult.types, expectedVerifiedIdRequest.types) + XCTAssertEqual(actualResult.issuanceOptions as? [VerifiedIdRequestURL], [firstVerifiedIdRequestURL, secondVerifiedIdRequestURL]) + XCTAssertEqual(actualResult.purpose, expectedVerifiedIdRequest.purpose) } - func testMap_WithNoPurposePresent_ReturnsVerifiedIdRequirement() throws { + func testMap_WithPurposePresent_ReturnsVerifiedIdRequirement() throws { // Arrange -// let (input, expectedResult) = try setUpInput(encrypted: false, required: false) + let mockMapper = MockMapper() + let mockSchema = InputDescriptorSchema(uri: "mockType") + let purpose = "purpose of the requirement." + let expectedVerifiedIdRequest = VerifiedIdRequirement(encrypted: false, + required: true, + types: ["mockType"], + purpose: purpose, + issuanceOptions: []) + let presentationInputDescriptor = PresentationInputDescriptor(id: nil, + schema: [mockSchema], + issuanceMetadata: nil, + name: nil, + purpose: purpose, + constraints: nil) // Act -// let actualResult = try mapper.map(input) + let actualResult = try mockMapper.map(presentationInputDescriptor) // Assert -// assertEqual(actualResult, expectedResult) + XCTAssertEqual(actualResult.encrypted, expectedVerifiedIdRequest.encrypted) + XCTAssertEqual(actualResult.required, expectedVerifiedIdRequest.required) + XCTAssertEqual(actualResult.types, expectedVerifiedIdRequest.types) + XCTAssertEqual(actualResult.issuanceOptions as? [VerifiedIdRequestURL], []) + XCTAssertEqual(actualResult.purpose, expectedVerifiedIdRequest.purpose) } } From 9c3bba3c06c83ae8826989a9923728b8c840a14a Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Feb 2023 19:02:49 -0800 Subject: [PATCH 066/373] Update VerifiableCredential-SDK-iOS --- WalletLibrary/Submodules/VerifiableCredential-SDK-iOS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS b/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS index c38314e3..3dfed92c 160000 --- a/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS +++ b/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS @@ -1 +1 @@ -Subproject commit c38314e3bf52c94d7d21ec3beaacb7680f33b387 +Subproject commit 3dfed92cec48530ef98f939ebf8ea3951924346c From b8e70afe971ae9feed5efda0a49cf6927830393f Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 10 Feb 2023 08:08:11 -0800 Subject: [PATCH 067/373] Fix comments in mapper classes. --- .../Mappings/VCSDK/PresentationDefinition+Mappable.swift | 9 ++++++--- .../VCSDK/PresentationInputDescriptor+Mappable.swift | 3 +++ .../Mappings/VCSDK/PresentationRequest+Mappable.swift | 3 +++ .../Requests/Requirements/GroupRequirement.swift | 2 +- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift index 49cd2f1b..bd5dd993 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift @@ -5,6 +5,9 @@ import VCEntities +/** + * Errors thrown in Presentation Definition Mappable extension. + */ enum PresentationDefinitionMappingError: Error { case nilInputDescriptors } @@ -22,15 +25,15 @@ extension VCEntities.PresentationDefinition: Mappable { } if inputDescriptors.capacity == 1, - let onlyDescriptor = inputDescriptors.first { - return try mapper.map(onlyDescriptor) + let onlyPresentationInputDescriptor = inputDescriptors.first { + return try mapper.map(onlyPresentationInputDescriptor) } let requirements = try inputDescriptors.compactMap { try mapper.map($0) } - /// VC SDK only supports any operator for now. + /// VC SDK only supports ANY operator for now. return GroupRequirement(required: true, requirements: requirements, requirementOperator: .ANY) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationInputDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationInputDescriptor+Mappable.swift index 2b11316d..ead5a6c2 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationInputDescriptor+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationInputDescriptor+Mappable.swift @@ -5,6 +5,9 @@ import VCEntities +/** + * Errors thrown in Presentation Input Descriptor Mappable extension. + */ enum PresentationInputDescriptorMappingError: Error { case noVerifiedIdTypeInPresentationInputDescriptor } diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationRequest+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationRequest+Mappable.swift index e88e4428..16030a09 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationRequest+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationRequest+Mappable.swift @@ -5,6 +5,9 @@ import VCEntities +/** + * Errors thrown in Presentation Request Mappable extension. + */ enum PresentationRequestMappingError: Error { case presentationDefinitionMissingInRequest } diff --git a/WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift b/WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift index 31e84285..cc3229b5 100644 --- a/WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift +++ b/WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ /** - * This value tells the user if they must get all of the requirements in the list or just one of them. + * This value specifies if all requirements are needed in the list or just one. */ public enum GroupRequirementOperator { case ANY From df7a8e7864721533f4fd465b6d4960f342d6f992 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 10 Feb 2023 08:08:16 -0800 Subject: [PATCH 068/373] Update PresentationDefinition+MappableTests.swift --- .../VCSDK/PresentationDefinition+MappableTests.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDefinition+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDefinition+MappableTests.swift index bab1b4a4..bc439446 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDefinition+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDefinition+MappableTests.swift @@ -53,6 +53,7 @@ class PresentationDefinitionMappingTests: XCTestCase { let mockMapper = MockMapper(callback: callback) + // Act let actualResult = try mockMapper.map(presentationDefinition) // Assert @@ -91,8 +92,10 @@ class PresentationDefinitionMappingTests: XCTestCase { inputDescriptors: inputDescriptors, issuance: nil) + var requirementCount = 0 func callback(type: Any) throws -> Any? { if type is PresentationInputDescriptor { + requirementCount = requirementCount + 1 return mockVerifiedIdRequirement } @@ -101,11 +104,12 @@ class PresentationDefinitionMappingTests: XCTestCase { let mockMapper = MockMapper(callback: callback) + // Act let actualResult = try mockMapper.map(presentationDefinition) // Assert XCTAssert(actualResult is GroupRequirement) - XCTAssertEqual((actualResult as? GroupRequirement)?.requirements.count, 3) + XCTAssertEqual((actualResult as? GroupRequirement)?.requirements.count, requirementCount) XCTAssertIdentical((actualResult as? GroupRequirement)?.requirements.first as AnyObject, mockVerifiedIdRequirement as AnyObject) XCTAssertIdentical((actualResult as? GroupRequirement)?.requirements[1] as AnyObject, mockVerifiedIdRequirement as AnyObject) XCTAssertIdentical((actualResult as? GroupRequirement)?.requirements[2] as AnyObject, mockVerifiedIdRequirement as AnyObject) From 392ee84bf4f5dd4fde319a116695215c04ece951 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 10 Feb 2023 08:52:00 -0800 Subject: [PATCH 069/373] OpenIdRequesterStyle to conform to Equatable --- .../Requests/RequestProtocols/OpenId/OpenIdRequesterStyle.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdRequesterStyle.swift b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdRequesterStyle.swift index dbe03663..85bab362 100644 --- a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdRequesterStyle.swift +++ b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdRequesterStyle.swift @@ -6,6 +6,6 @@ /** * Requester Style that is Open Id specific. */ -struct OpenIdRequesterStyle: RequesterStyle { +struct OpenIdRequesterStyle: RequesterStyle, Equatable { let requester: String } From 6c79ba26580f36cec0a784cdaf151ceb7f3ee01b Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 10 Feb 2023 08:52:05 -0800 Subject: [PATCH 070/373] Update PresentationDefinition+MappableTests.swift --- .../VCSDK/PresentationDefinition+MappableTests.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDefinition+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDefinition+MappableTests.swift index bc439446..6b6f3613 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDefinition+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDefinition+MappableTests.swift @@ -43,15 +43,15 @@ class PresentationDefinitionMappingTests: XCTestCase { inputDescriptors: [inputDescriptor], issuance: nil) - func callback(type: Any) throws -> Any? { - if type is PresentationInputDescriptor { + func mockResults(objectToBeMapped: Any) throws -> Any? { + if objectToBeMapped is PresentationInputDescriptor { return expectedVerifiedIdRequirement } return nil } - let mockMapper = MockMapper(callback: callback) + let mockMapper = MockMapper(mockResults: mockResults) // Act let actualResult = try mockMapper.map(presentationDefinition) @@ -93,8 +93,8 @@ class PresentationDefinitionMappingTests: XCTestCase { issuance: nil) var requirementCount = 0 - func callback(type: Any) throws -> Any? { - if type is PresentationInputDescriptor { + func mockResults(objectToBeMapped: Any) throws -> Any? { + if objectToBeMapped is PresentationInputDescriptor { requirementCount = requirementCount + 1 return mockVerifiedIdRequirement } @@ -102,7 +102,7 @@ class PresentationDefinitionMappingTests: XCTestCase { return nil } - let mockMapper = MockMapper(callback: callback) + let mockMapper = MockMapper(mockResults: mockResults) // Act let actualResult = try mockMapper.map(presentationDefinition) From ea40a7bb2c355496ea5ea36adba961a4f666d209 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 10 Feb 2023 08:52:23 -0800 Subject: [PATCH 071/373] Create unit tests for PresentationRequest mapper. --- .../PresentationRequest+MappableTests.swift | 194 +++++++++++++++--- 1 file changed, 171 insertions(+), 23 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationRequest+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationRequest+MappableTests.swift index cca936fe..2dda412e 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationRequest+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationRequest+MappableTests.swift @@ -5,65 +5,213 @@ import XCTest import VCEntities +import VCToken @testable import WalletLibrary class PresentationRequestMappingTests: XCTestCase { + enum ExpectedError: Error { + case expectedToBeThrown + } + let mapper = Mapper() func testMap_WithNoPresentationDefinitionPresent_ThrowsError() throws { -// // Arrange -// let presentationRequestToken = PresentationRequestToken(headers: Header(), -// content: PresentationRequestClaims() + // Arrange + let mockRequestClaims = RequestedClaims(vpToken: RequestedVPToken(presentationDefinition: nil)) + let token = createPresentationRequestToken(with: mockRequestClaims, and: nil) - // Act -// let actualResult = try mapper.map(input) + let linkedDomainResult = LinkedDomainResult.linkedDomainMissing + let presentationRequest = PresentationRequest(from: token, + linkedDomainResult: linkedDomainResult) - // Assert -// assertEqual(actualResult, expectedResult) + let mockMapper = MockMapper() + + // Act + XCTAssertThrowsError(try mockMapper.map(presentationRequest)) { error in + // Assert + XCTAssert(error is PresentationRequestMappingError) + XCTAssertEqual(error as? PresentationRequestMappingError, .presentationDefinitionMissingInRequest) + } } func testMap_WithInvalidPresentationDefinition_ThrowsError() throws { // Arrange -// let (input, expectedResult) = try setUpInput(encrypted: false, required: false) + let mockPresentationDefinition = PresentationDefinition(id: nil, inputDescriptors: nil, issuance: nil) + let mockRequestClaims = RequestedClaims(vpToken: RequestedVPToken(presentationDefinition: mockPresentationDefinition)) + let token = createPresentationRequestToken(with: mockRequestClaims, and: nil) - // Act -// let actualResult = try mapper.map(input) + let linkedDomainResult = LinkedDomainResult.linkedDomainMissing + let presentationRequest = PresentationRequest(from: token, + linkedDomainResult: linkedDomainResult) + + func mockResults(objectToBeMapped: Any) throws -> Any? { + if objectToBeMapped is PresentationDefinition { + throw ExpectedError.expectedToBeThrown + } + + return nil + } + + let mockMapper = MockMapper(mockResults: mockResults) // Assert -// assertEqual(actualResult, expectedResult) + XCTAssertThrowsError(try mockMapper.map(presentationRequest)) { error in + // Assert + XCTAssert(error is ExpectedError) + XCTAssertEqual(error as? ExpectedError, .expectedToBeThrown) + } } func testMap_WithInvalidRootOfTrust_ThrowsError() throws { // Arrange -// let (input, expectedResult) = try setUpInput(encrypted: false, required: false) + let expectedVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, + required: false, + types: [], + purpose: nil, + issuanceOptions: []) + let mockPresentationDefinition = PresentationDefinition(id: nil, inputDescriptors: nil, issuance: nil) + let mockRequestClaims = RequestedClaims(vpToken: RequestedVPToken(presentationDefinition: mockPresentationDefinition)) + let token = createPresentationRequestToken(with: mockRequestClaims, and: nil) - // Act -// let actualResult = try mapper.map(input) + let linkedDomainResult = LinkedDomainResult.linkedDomainMissing + let presentationRequest = PresentationRequest(from: token, + linkedDomainResult: linkedDomainResult) + + func mockResults(objectToBeMapped: Any) throws -> Any? { + + if objectToBeMapped is PresentationDefinition { + return expectedVerifiedIdRequirement + } + + if objectToBeMapped is LinkedDomainResult { + throw ExpectedError.expectedToBeThrown + } + + return nil + } + + let mockMapper = MockMapper(mockResults: mockResults) // Assert -// assertEqual(actualResult, expectedResult) + XCTAssertThrowsError(try mockMapper.map(presentationRequest)) { error in + // Assert + print(error) + XCTAssert(error is ExpectedError) + XCTAssertEqual(error as? ExpectedError, .expectedToBeThrown) + } } func testMap_WithNoClientNamePresent_ReturnVerifiedIdRequestContent() throws { // Arrange -// let (input, expectedResult) = try setUpInput(encrypted: false, required: false) + let expectedStyle = OpenIdRequesterStyle(requester: "") + let expectedVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, + required: false, + types: [], + purpose: nil, + issuanceOptions: []) + let mockPresentationDefinition = PresentationDefinition(id: nil, inputDescriptors: nil, issuance: nil) + let mockRequestClaims = RequestedClaims(vpToken: RequestedVPToken(presentationDefinition: mockPresentationDefinition)) + let mockRegistration = RegistrationClaims(clientName: nil, + clientPurpose: nil, + logoURI: nil, + subjectIdentifierTypesSupported: nil, + vpFormats: nil) + let token = createPresentationRequestToken(with: mockRequestClaims, and: mockRegistration) + + let expectedRootOfTrust = RootOfTrust(verified: false, source: "") + let linkedDomainResult = LinkedDomainResult.linkedDomainMissing + let presentationRequest = PresentationRequest(from: token, + linkedDomainResult: linkedDomainResult) + + func mockResults(objectToBeMapped: Any) throws -> Any? { + + if objectToBeMapped is PresentationDefinition { + return expectedVerifiedIdRequirement + } + + if objectToBeMapped is LinkedDomainResult { + return expectedRootOfTrust + } + + return nil + } + + let mockMapper = MockMapper(mockResults: mockResults) // Act -// let actualResult = try mapper.map(input) + let actualResult = try mockMapper.map(presentationRequest) - // Assert -// assertEqual(actualResult, expectedResult) + // Act + XCTAssertIdentical(actualResult.requirement as AnyObject, expectedVerifiedIdRequirement as AnyObject) + XCTAssertEqual(actualResult.rootOfTrust, expectedRootOfTrust) + XCTAssertEqual(actualResult.style as? OpenIdRequesterStyle, expectedStyle) } func testMap_WithClientNamePresent_ReturnVerifiedIdRequestContent() throws { // Arrange -// let (input, expectedResult) = try setUpInput(encrypted: false, required: false) + let mockRequesterName = "mockRequesterName235" + let expectedStyle = OpenIdRequesterStyle(requester: mockRequesterName) + let expectedVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, + required: false, + types: [], + purpose: nil, + issuanceOptions: []) + let mockPresentationDefinition = PresentationDefinition(id: nil, inputDescriptors: nil, issuance: nil) + let mockRequestClaims = RequestedClaims(vpToken: RequestedVPToken(presentationDefinition: mockPresentationDefinition)) + let mockRegistration = RegistrationClaims(clientName: mockRequesterName, + clientPurpose: nil, + logoURI: nil, + subjectIdentifierTypesSupported: nil, + vpFormats: nil) + let token = createPresentationRequestToken(with: mockRequestClaims, and: mockRegistration) + + let expectedRootOfTrust = RootOfTrust(verified: false, source: "") + let linkedDomainResult = LinkedDomainResult.linkedDomainMissing + let presentationRequest = PresentationRequest(from: token, + linkedDomainResult: linkedDomainResult) + + func mockResults(objectToBeMapped: Any) throws -> Any? { + + if objectToBeMapped is PresentationDefinition { + return expectedVerifiedIdRequirement + } + + if objectToBeMapped is LinkedDomainResult { + return expectedRootOfTrust + } + + return nil + } + + let mockMapper = MockMapper(mockResults: mockResults) // Act -// let actualResult = try mapper.map(input) + let actualResult = try mockMapper.map(presentationRequest) - // Assert -// assertEqual(actualResult, expectedResult) + // Act + XCTAssertIdentical(actualResult.requirement as AnyObject, expectedVerifiedIdRequirement as AnyObject) + XCTAssertEqual(actualResult.rootOfTrust, expectedRootOfTrust) + XCTAssertEqual(actualResult.style as? OpenIdRequesterStyle, expectedStyle) + } + + private func createPresentationRequestToken(with requestedClaims: RequestedClaims?, + and registration: RegistrationClaims?) -> PresentationRequestToken { + let presentationRequestTokenClaims = PresentationRequestClaims(jti: nil, + clientID: nil, + redirectURI: nil, + responseMode: nil, + responseType: nil, + claims: requestedClaims, + state: nil, + nonce: nil, + scope: nil, + prompt: nil, + registration: registration, + idTokenHint: nil, + iat: nil, + exp: nil) + + return PresentationRequestToken(headers: Header(), content: presentationRequestTokenClaims)! } } From 882e00299eb003e23595904d70904f2c9833236f Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 10 Feb 2023 08:52:34 -0800 Subject: [PATCH 072/373] add comment to mock mapper. --- .../Mocks/Utilities/MockMapper.swift | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Utilities/MockMapper.swift b/WalletLibrary/WalletLibraryTests/Mocks/Utilities/MockMapper.swift index 7630ad1b..c434efd6 100644 --- a/WalletLibrary/WalletLibraryTests/Mocks/Utilities/MockMapper.swift +++ b/WalletLibrary/WalletLibraryTests/Mocks/Utilities/MockMapper.swift @@ -5,19 +5,28 @@ @testable import WalletLibrary +/** + * Mock Mapper can be used to mock results of a mapping using the mockResults callback. + * If mockResults callback is not nil, and the result of the call conforms to the T.T protocol, + * the result of the callback will be returned. Else, object's map function will be used to map like a normal mapper. + * + * Using the mockResults callback is helpful when testing the mapping of an object X with recursive mapping. + * Since unit tests for object X do not need to test the mapping of objects X contains, the mockResults callback + * can be used to return a mock object or mock error instead. + */ struct MockMapper: Mapping { - let callback: ((Any) throws -> (Any?))? + let mockResults: ((Any) throws -> (Any?))? - init(callback: ((Any) throws -> (Any?))? = nil) { - self.callback = callback + init(mockResults: ((Any) throws -> (Any?))? = nil) { + self.mockResults = mockResults } /// Map one object to another. func map(_ object: T) throws -> T.T { - if let callback = callback { - let result = try callback(object) + if let mockResults = mockResults { + let result = try mockResults(object) if let result = result as? T.T { return result } From 62ec2eb53ebad0851a528e972a77d2e2c9b2bd72 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 10 Feb 2023 08:52:39 -0800 Subject: [PATCH 073/373] Update OpenIdRequestHandlerTests.swift --- .../Handlers/OpenIdRequestHandlerTests.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift index b5727be6..2a151bec 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift @@ -23,15 +23,15 @@ class OpenIdRequestHandlerTests: XCTestCase { requirement: expectedRequirement, rootOfTrust: expectedRootOfTrust) - func callback(object: Any) throws -> Any? { - if object is MockOpenIdRawRequest { + func mockResults(objectToBeMapped: Any) throws -> Any? { + if objectToBeMapped is MockOpenIdRawRequest { return expectedContent } return nil } - let mockMapper = MockMapper(callback: callback) + let mockMapper = MockMapper(mockResults: mockResults) let mockRawRequest = MockOpenIdRawRequest(raw: Data()) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration) @@ -48,15 +48,15 @@ class OpenIdRequestHandlerTests: XCTestCase { func testHandleRequest_WithPresentationRequestInvalidMapping_ThrowsError() async throws { // Arrange - func callback(object: Any) throws -> Any? { - if object is MockOpenIdRawRequest { + func mockResults(objectToBeMapped: Any) throws -> Any? { + if objectToBeMapped is MockOpenIdRawRequest { throw ExpectedError.expectedToBeThrown } return nil } - let mockMapper = MockMapper(callback: callback) + let mockMapper = MockMapper(mockResults: mockResults) let mockRawRequest = MockOpenIdRawRequest(raw: Data()) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration) From 666e2132acb69a98a6160bfc3a1ec38724d6c2be Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 10 Feb 2023 08:54:30 -0800 Subject: [PATCH 074/373] Update VerifiableCredential-SDK-iOS --- WalletLibrary/Submodules/VerifiableCredential-SDK-iOS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS b/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS index 3dfed92c..74266408 160000 --- a/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS +++ b/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS @@ -1 +1 @@ -Subproject commit 3dfed92cec48530ef98f939ebf8ea3951924346c +Subproject commit 74266408578c0e0bf0040596268e1425ba98e022 From 9e85884389b4529b23c863bad9e3c822654af0fd Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 10 Feb 2023 09:13:58 -0800 Subject: [PATCH 075/373] Update VC SDK --- WalletLibrary/Submodules/VerifiableCredential-SDK-iOS | 2 +- .../Translations/VCSDK/PresentationRequest+MappableTests.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS b/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS index 74266408..4ea82b7c 160000 --- a/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS +++ b/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS @@ -1 +1 @@ -Subproject commit 74266408578c0e0bf0040596268e1425ba98e022 +Subproject commit 4ea82b7c47a1ee97a41db67ae0aa5456262d65f7 diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationRequest+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationRequest+MappableTests.swift index 2dda412e..71a96413 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationRequest+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationRequest+MappableTests.swift @@ -200,8 +200,8 @@ class PresentationRequestMappingTests: XCTestCase { let presentationRequestTokenClaims = PresentationRequestClaims(jti: nil, clientID: nil, redirectURI: nil, - responseMode: nil, responseType: nil, + responseMode: nil, claims: requestedClaims, state: nil, nonce: nil, From de4a870ca9e62456cd1c21cc6b799fdcb42a173d Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 10 Feb 2023 09:50:39 -0800 Subject: [PATCH 076/373] Add configuration to openidresolver --- .../Requests/Resolvers/OpenIdURLRequestResolver.swift | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Resolvers/OpenIdURLRequestResolver.swift b/WalletLibrary/WalletLibrary/Requests/Resolvers/OpenIdURLRequestResolver.swift index 6b64270b..951e2e13 100644 --- a/WalletLibrary/WalletLibrary/Requests/Resolvers/OpenIdURLRequestResolver.swift +++ b/WalletLibrary/WalletLibrary/Requests/Resolvers/OpenIdURLRequestResolver.swift @@ -17,12 +17,15 @@ enum OpenIdURLRequestResolverError: Error { */ struct OpenIdURLRequestResolver: RequestResolving { + private let openIdScheme = "openid-vc" + private let openIdResolver: OpenIdForVCResolver - private let openIdScheme = "openid-vc" + private let configuration: LibraryConfiguration - init(openIdResolver: OpenIdForVCResolver) { + init(openIdResolver: OpenIdForVCResolver, configuration: LibraryConfiguration) { self.openIdResolver = openIdResolver + self.configuration = configuration } /// Whether or not the request handler given request handler can handle the resolved raw request. From ccd2848acf3e36f50c48b660f62d662b5abf20d2 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 10 Feb 2023 09:51:06 -0800 Subject: [PATCH 077/373] update logconsumer to conform to VCLogConsumer. --- .../WalletLibrary/Utilities/WalletLibraryLogConsumer.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Utilities/WalletLibraryLogConsumer.swift b/WalletLibrary/WalletLibrary/Utilities/WalletLibraryLogConsumer.swift index afcf54de..238e70e8 100644 --- a/WalletLibrary/WalletLibrary/Utilities/WalletLibraryLogConsumer.swift +++ b/WalletLibrary/WalletLibrary/Utilities/WalletLibraryLogConsumer.swift @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import VCEntities + /// The trace level of the data that will be logged. public enum TraceLevel { case VERBOSE @@ -16,7 +18,7 @@ public enum TraceLevel { /** * Protocol for consumers of the library to use to inject logging into the library. */ -public protocol WalletLibraryLogConsumer { +public protocol WalletLibraryLogConsumer: VCLogConsumer { /// Logs a trace with calling function name, line, file. func log(_ traceLevel: TraceLevel, From b552d3951fb3d79173dc11f0fcaa96431ff232fa Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 10 Feb 2023 09:51:29 -0800 Subject: [PATCH 078/373] Add configuration to VerifiedIdClient --- WalletLibrary/WalletLibrary/VerifiedIdClient.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/VerifiedIdClient.swift b/WalletLibrary/WalletLibrary/VerifiedIdClient.swift index 624021b5..a33416ec 100644 --- a/WalletLibrary/WalletLibrary/VerifiedIdClient.swift +++ b/WalletLibrary/WalletLibrary/VerifiedIdClient.swift @@ -13,14 +13,18 @@ enum VerifiedIdClientError: Error { */ public class VerifiedIdClient { + private let configuration: LibraryConfiguration + private let requestResolverFactory: RequestResolverFactory private let requestHandlerFactory: RequestHandlerFactory init(requestResolverFactory: RequestResolverFactory, - requestHandlerFactory: RequestHandlerFactory) { + requestHandlerFactory: RequestHandlerFactory, + configuration: LibraryConfiguration) { self.requestResolverFactory = requestResolverFactory self.requestHandlerFactory = requestHandlerFactory + self.configuration = configuration } /// Creates either an issuance or presentation request from the input. From f208a193d3d1676f847c4a2b3f3057ca683d76b5 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 10 Feb 2023 09:51:50 -0800 Subject: [PATCH 079/373] Add resolver and handler registration to builder. --- .../VerifiedIdClientBuilder.swift | 37 ++++++++++++++++--- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift b/WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift index 05cec31d..3ae5dc70 100644 --- a/WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift +++ b/WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift @@ -3,12 +3,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import VCServices + /** * The VerifiedIdClientBuilder configures VerifiedIdClient with any additional options. */ public class VerifiedIdClientBuilder { - var logger: WalletLibraryLogger? + var logger: WalletLibraryLogger + + var requestResolvers: [any RequestResolving] = [] + + var requestHandlers: [any RequestHandling] = [] public init() { logger = WalletLibraryLogger() @@ -16,16 +22,35 @@ public class VerifiedIdClientBuilder { /// Builds the VerifiedIdClient with the set configuration from the builder. public func build() throws -> VerifiedIdClient { - /// TODO: add supported resolver and handlers. - let requestResolverFactory = RequestResolverFactory(resolvers: []) - let requestHandlerFactory = RequestHandlerFactory(requestHandlers: []) + /// TODO: inject log consumer and access group identifier into vc sdk. + let _ = VCServices.VerifiableCredentialSDK.initialize() + + let configuration = LibraryConfiguration(logger: logger, + mapper: Mapper()) + + registerSupportedResolvers(with: configuration) + registerSupportedRequestHandlers(with: configuration) + + let requestResolverFactory = RequestResolverFactory(resolvers: requestResolvers) + let requestHandlerFactory = RequestHandlerFactory(requestHandlers: requestHandlers) return VerifiedIdClient(requestResolverFactory: requestResolverFactory, - requestHandlerFactory: requestHandlerFactory) + requestHandlerFactory: requestHandlerFactory, + configuration: configuration) } /// Optional method to add a custom log consumer to VerifiedIdClient. public func with(logConsumer: WalletLibraryLogConsumer) -> VerifiedIdClientBuilder { - logger?.add(consumer: logConsumer) + logger.add(consumer: logConsumer) return self } + + private func registerSupportedResolvers(with configuration: LibraryConfiguration) { + let openIdURLResolver = OpenIdURLRequestResolver(openIdResolver: PresentationService(), configuration: configuration) + requestResolvers.append(openIdURLResolver) + } + + private func registerSupportedRequestHandlers(with configuration: LibraryConfiguration) { + let openIdHandler = OpenIdRequestHandler(configuration: configuration) + requestHandlers.append(openIdHandler) + } } From 73240f97548d2c767a62b50075cdcfb894ec86ea Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 10 Feb 2023 10:37:29 -0800 Subject: [PATCH 080/373] Update PresentationDefinition+Mappable.swift --- .../Mappings/VCSDK/PresentationDefinition+Mappable.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift index bd5dd993..6687af91 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift @@ -24,7 +24,7 @@ extension VCEntities.PresentationDefinition: Mappable { throw PresentationDefinitionMappingError.nilInputDescriptors } - if inputDescriptors.capacity == 1, + if inputDescriptors.count == 1, let onlyPresentationInputDescriptor = inputDescriptors.first { return try mapper.map(onlyPresentationInputDescriptor) } From b3990869ac79c05bafd10224b038c57eda60abd9 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 10 Feb 2023 10:37:54 -0800 Subject: [PATCH 081/373] change access level of lists on factories for testing --- .../WalletLibrary/Requests/RequestHandlerFactory.swift | 2 +- .../WalletLibrary/Requests/RequestResolverFactory.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/RequestHandlerFactory.swift b/WalletLibrary/WalletLibrary/Requests/RequestHandlerFactory.swift index 4b3b7fce..d1d7ed77 100644 --- a/WalletLibrary/WalletLibrary/Requests/RequestHandlerFactory.swift +++ b/WalletLibrary/WalletLibrary/Requests/RequestHandlerFactory.swift @@ -16,7 +16,7 @@ enum RequestHandlerFactoryError: Error { */ class RequestHandlerFactory { - private let requestHandlers: [any RequestHandling] + let requestHandlers: [any RequestHandling] init(requestHandlers: [any RequestHandling]) { self.requestHandlers = requestHandlers diff --git a/WalletLibrary/WalletLibrary/Requests/RequestResolverFactory.swift b/WalletLibrary/WalletLibrary/Requests/RequestResolverFactory.swift index 1df94994..281fd7c2 100644 --- a/WalletLibrary/WalletLibrary/Requests/RequestResolverFactory.swift +++ b/WalletLibrary/WalletLibrary/Requests/RequestResolverFactory.swift @@ -16,7 +16,7 @@ enum RequestResolverFactoryError: Error { */ class RequestResolverFactory { - private let resolvers: [any RequestResolving] + let resolvers: [any RequestResolving] init(resolvers: [any RequestResolving]) { self.resolvers = resolvers From da04831be80de1ecb62677bc8204b8e66d05e4b6 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 10 Feb 2023 10:38:12 -0800 Subject: [PATCH 082/373] complete test cases for verified id client builder --- .../WalletLibrary.xcodeproj/project.pbxproj | 8 +++ .../VerifiedIdClientBuilderTests.swift | 67 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 WalletLibrary/WalletLibraryTests/VerifiedIdClientBuilderTests.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 23a1f43a..8a80ecf4 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -47,6 +47,8 @@ 559BD3072995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3062995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift */; }; 559BD3092995ABE800BD61AC /* PresentationRequest+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3082995ABE800BD61AC /* PresentationRequest+MappableTests.swift */; }; 559BD30B2995B1B500BD61AC /* LinkedDomainResult+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD30A2995B1B500BD61AC /* LinkedDomainResult+MappableTests.swift */; }; + 559BD30D2996C12500BD61AC /* VerifiedIdClientBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD30C2996C12500BD61AC /* VerifiedIdClientBuilderTests.swift */; }; + 559BD30F2996C1C500BD61AC /* MockLogConsumer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD30E2996C1C500BD61AC /* MockLogConsumer.swift */; }; 55A81BCA2991AB82002C259A /* RequestResolving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BC92991AB82002C259A /* RequestResolving.swift */; }; 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE02991AB96002C259A /* RequestHandling.swift */; }; 55A81BE52991B2E5002C259A /* RequestResolverFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */; }; @@ -299,6 +301,8 @@ 559BD3062995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationInputDescriptor+MappableTests.swift"; sourceTree = ""; }; 559BD3082995ABE800BD61AC /* PresentationRequest+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationRequest+MappableTests.swift"; sourceTree = ""; }; 559BD30A2995B1B500BD61AC /* LinkedDomainResult+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LinkedDomainResult+MappableTests.swift"; sourceTree = ""; }; + 559BD30C2996C12500BD61AC /* VerifiedIdClientBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdClientBuilderTests.swift; sourceTree = ""; }; + 559BD30E2996C1C500BD61AC /* MockLogConsumer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockLogConsumer.swift; sourceTree = ""; }; 55A81BC92991AB82002C259A /* RequestResolving.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolving.swift; sourceTree = ""; }; 55A81BE02991AB96002C259A /* RequestHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestHandling.swift; sourceTree = ""; }; 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolverFactory.swift; sourceTree = ""; }; @@ -468,6 +472,7 @@ 5534E67C294A392F005D0765 /* Extensions */, 55A81BF02991BB13002C259A /* Requests */, 55E3376929478C3300CD2ED7 /* Utilities */, + 559BD30C2996C12500BD61AC /* VerifiedIdClientBuilderTests.swift */, 552E509F293E6AB200868F47 /* VerifiedIdClientTests.swift */, ); path = WalletLibraryTests; @@ -682,6 +687,7 @@ isa = PBXGroup; children = ( 55E2F08329955E8A0008010D /* MockMapper.swift */, + 559BD30E2996C1C500BD61AC /* MockLogConsumer.swift */, ); path = Utilities; sourceTree = ""; @@ -1239,12 +1245,14 @@ 55A81C072991BB2C002C259A /* RequestResolverFactoryTests.swift in Sources */, 55A81C102991C211002C259A /* MockResolver.swift in Sources */, 55A81C092991BB3A002C259A /* RequestHandlerFactoryTests.swift in Sources */, + 559BD30D2996C12500BD61AC /* VerifiedIdClientBuilderTests.swift in Sources */, 550A91952994025C0014D030 /* OpenIdURLRequestResolverTests.swift in Sources */, 559BD3052995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift in Sources */, 55A81C0D2991BF86002C259A /* MockInput.swift in Sources */, 550A919829940DCE0014D030 /* MockOpenIdforVCResolver.swift in Sources */, 559BD3092995ABE800BD61AC /* PresentationRequest+MappableTests.swift in Sources */, 550A91A0299412790014D030 /* MockOpenIdRawRequest.swift in Sources */, + 559BD30F2996C1C500BD61AC /* MockLogConsumer.swift in Sources */, 55E2F076299555280008010D /* MockRequirement.swift in Sources */, 5534E68C294A8B8F005D0765 /* AccessTokenDescriptor+MappableTests.swift in Sources */, 55E2F077299555280008010D /* MockRequesterStyle.swift in Sources */, diff --git a/WalletLibrary/WalletLibraryTests/VerifiedIdClientBuilderTests.swift b/WalletLibrary/WalletLibraryTests/VerifiedIdClientBuilderTests.swift new file mode 100644 index 00000000..1046aa8d --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/VerifiedIdClientBuilderTests.swift @@ -0,0 +1,67 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import XCTest +@testable import WalletLibrary + +class VerifiedIdClientBuilderTests: XCTestCase { + + func testBuild_WithNoLogConsumers_ReturnsVerifiedIdClient() throws { + // Arrange + let builder = VerifiedIdClientBuilder() + + // Act + let actualResult = try builder.build() + + // Assert + XCTAssertEqual(actualResult.requestHandlerFactory.requestHandlers.count, 1) + XCTAssert(actualResult.requestHandlerFactory.requestHandlers.contains { $0 is OpenIdRequestHandler }) + XCTAssertEqual(actualResult.requestResolverFactory.resolvers.count, 1) + XCTAssert(actualResult.requestResolverFactory.resolvers.contains { $0 is OpenIdURLRequestResolver }) + XCTAssert(actualResult.configuration.logger.consumers.isEmpty) + } + + func testBuild_WithOneLogConsumer_ReturnsVerifiedIdClient() throws { + // Arrange + let mockLogConsumer = MockLogConsumer() + let builder = VerifiedIdClientBuilder() + .with(logConsumer: mockLogConsumer) + + // Act + let actualResult = try builder.build() + + // Assert + XCTAssertEqual(actualResult.requestHandlerFactory.requestHandlers.count, 1) + XCTAssert(actualResult.requestHandlerFactory.requestHandlers.contains { $0 is OpenIdRequestHandler }) + XCTAssertEqual(actualResult.requestResolverFactory.resolvers.count, 1) + XCTAssert(actualResult.requestResolverFactory.resolvers.contains { $0 is OpenIdURLRequestResolver }) + XCTAssertEqual(actualResult.configuration.logger.consumers.count, 1) + XCTAssert(actualResult.configuration.logger.consumers.contains { $0 is MockLogConsumer }) + } + + func testBuild_WithMultipleLogConsumers_ReturnsVerifiedIdClient() throws { + // Arrange + let firstLogConsumer = MockLogConsumer() + let secondLogConsumer = MockLogConsumer() + let thirdLogConsumer = MockLogConsumer() + let builder = VerifiedIdClientBuilder() + .with(logConsumer: firstLogConsumer) + .with(logConsumer: secondLogConsumer) + .with(logConsumer: thirdLogConsumer) + + // Act + let actualResult = try builder.build() + + // Assert + XCTAssertEqual(actualResult.requestHandlerFactory.requestHandlers.count, 1) + XCTAssert(actualResult.requestHandlerFactory.requestHandlers.contains { $0 is OpenIdRequestHandler }) + XCTAssertEqual(actualResult.requestResolverFactory.resolvers.count, 1) + XCTAssert(actualResult.requestResolverFactory.resolvers.contains { $0 is OpenIdURLRequestResolver }) + XCTAssertEqual(actualResult.configuration.logger.consumers.count, 3) + XCTAssert(actualResult.configuration.logger.consumers.contains { $0 as? MockLogConsumer == firstLogConsumer }) + XCTAssert(actualResult.configuration.logger.consumers.contains { $0 as? MockLogConsumer == secondLogConsumer }) + XCTAssert(actualResult.configuration.logger.consumers.contains { $0 as? MockLogConsumer == thirdLogConsumer }) + } +} From 61e2808773cb051ca0f3d52dbfbfc5cde012ac35 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 10 Feb 2023 10:38:23 -0800 Subject: [PATCH 083/373] Update loggers --- .../WalletLibrary/Utilities/WalletLibraryLogConsumer.swift | 2 +- WalletLibrary/WalletLibrary/Utilities/WalletLibraryLogger.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Utilities/WalletLibraryLogConsumer.swift b/WalletLibrary/WalletLibrary/Utilities/WalletLibraryLogConsumer.swift index 238e70e8..7f7512fe 100644 --- a/WalletLibrary/WalletLibrary/Utilities/WalletLibraryLogConsumer.swift +++ b/WalletLibrary/WalletLibrary/Utilities/WalletLibraryLogConsumer.swift @@ -18,7 +18,7 @@ public enum TraceLevel { /** * Protocol for consumers of the library to use to inject logging into the library. */ -public protocol WalletLibraryLogConsumer: VCLogConsumer { +public protocol WalletLibraryLogConsumer { /// Logs a trace with calling function name, line, file. func log(_ traceLevel: TraceLevel, diff --git a/WalletLibrary/WalletLibrary/Utilities/WalletLibraryLogger.swift b/WalletLibrary/WalletLibrary/Utilities/WalletLibraryLogger.swift index 8edc45f1..d5fb865e 100644 --- a/WalletLibrary/WalletLibrary/Utilities/WalletLibraryLogger.swift +++ b/WalletLibrary/WalletLibrary/Utilities/WalletLibraryLogger.swift @@ -8,7 +8,7 @@ */ struct WalletLibraryLogger { - private var consumers: [WalletLibraryLogConsumer] = [] + var consumers: [WalletLibraryLogConsumer] = [] /// Adds a log consumer to logger. mutating func add(consumer: WalletLibraryLogConsumer) { From 92746e96605d29e64f0da04947fd8c49ee23f682 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 10 Feb 2023 10:38:27 -0800 Subject: [PATCH 084/373] Update VerifiedIdClient.swift --- WalletLibrary/WalletLibrary/VerifiedIdClient.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/WalletLibrary/WalletLibrary/VerifiedIdClient.swift b/WalletLibrary/WalletLibrary/VerifiedIdClient.swift index a33416ec..dacf5361 100644 --- a/WalletLibrary/WalletLibrary/VerifiedIdClient.swift +++ b/WalletLibrary/WalletLibrary/VerifiedIdClient.swift @@ -13,11 +13,11 @@ enum VerifiedIdClientError: Error { */ public class VerifiedIdClient { - private let configuration: LibraryConfiguration + let configuration: LibraryConfiguration - private let requestResolverFactory: RequestResolverFactory + let requestResolverFactory: RequestResolverFactory - private let requestHandlerFactory: RequestHandlerFactory + let requestHandlerFactory: RequestHandlerFactory init(requestResolverFactory: RequestResolverFactory, requestHandlerFactory: RequestHandlerFactory, From ad8c64c38f5fac9dd947f5daca740038080df8c3 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 10 Feb 2023 10:38:46 -0800 Subject: [PATCH 085/373] Update VerifiedIdClientBuilder.swift --- WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift b/WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift index 3ae5dc70..e891c0b1 100644 --- a/WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift +++ b/WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift @@ -10,11 +10,11 @@ import VCServices */ public class VerifiedIdClientBuilder { - var logger: WalletLibraryLogger + private var logger: WalletLibraryLogger - var requestResolvers: [any RequestResolving] = [] + private var requestResolvers: [any RequestResolving] = [] - var requestHandlers: [any RequestHandling] = [] + private var requestHandlers: [any RequestHandling] = [] public init() { logger = WalletLibraryLogger() From 0983578afc1e3d02b99fd5e665a99999eef1351e Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 10 Feb 2023 10:38:53 -0800 Subject: [PATCH 086/373] Update OpenIdURLRequestResolverTests.swift --- .../OpenIdURLRequestResolverTests.swift | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift index 2a6afedc..d9c1046a 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift @@ -11,6 +11,7 @@ class OpenIdURLRequestResolverTests: XCTestCase { func testResolve_WithURLInput_ReturnsRawRequest() async throws { // Arrange + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: Mapper()) let mockInput = VerifiedIdRequestURL(url: URL(string: "openid-vc://mock.com")!) let expectedRawData = "test data".data(using: .utf8)! let expectedRawRequest = MockOpenIdRawRequest(raw: expectedRawData) @@ -18,7 +19,7 @@ class OpenIdURLRequestResolverTests: XCTestCase { return expectedRawRequest } let openIdResolver = MockOpenIdForVCResolver(mockGetRequestCallback: mockCallback) - let resolver = OpenIdURLRequestResolver(openIdResolver: openIdResolver) + let resolver = OpenIdURLRequestResolver(openIdResolver: openIdResolver, configuration: configuration) // Act let actualRawRequest = try await resolver.resolve(input: mockInput) @@ -30,9 +31,10 @@ class OpenIdURLRequestResolverTests: XCTestCase { func testResolve_WithInvalidRequestInput_ThrowsError() async throws { // Arrange + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: Mapper()) let mockData = "test data" let mockInput = MockInput(mockData: mockData) - let resolver = OpenIdURLRequestResolver(openIdResolver: MockOpenIdForVCResolver()) + let resolver = OpenIdURLRequestResolver(openIdResolver: MockOpenIdForVCResolver(), configuration: configuration) // Act do { @@ -54,8 +56,9 @@ class OpenIdURLRequestResolverTests: XCTestCase { func testCanResolve_WithInvalidRequestInputType_ReturnsFalse() throws { // Arrange + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: Mapper()) let mockInput = MockInput(mockData: "mock data") - let resolver = OpenIdURLRequestResolver(openIdResolver: MockOpenIdForVCResolver()) + let resolver = OpenIdURLRequestResolver(openIdResolver: MockOpenIdForVCResolver(), configuration: configuration) // Act let actualResult = resolver.canResolve(input: mockInput) @@ -67,8 +70,9 @@ class OpenIdURLRequestResolverTests: XCTestCase { func testCanResolve_WithInvalidRequestInputScheme_ReturnsFalse() throws { // Arrange + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: Mapper()) let mockInput = VerifiedIdRequestURL(url: URL(string: "https://mock.com")!) - let resolver = OpenIdURLRequestResolver(openIdResolver: MockOpenIdForVCResolver()) + let resolver = OpenIdURLRequestResolver(openIdResolver: MockOpenIdForVCResolver(), configuration: configuration) // Act let actualResult = resolver.canResolve(input: mockInput) @@ -80,8 +84,9 @@ class OpenIdURLRequestResolverTests: XCTestCase { func testCanResolve_WithValidRequestInput_ReturnsTrue() throws { // Arrange + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: Mapper()) let mockInput = VerifiedIdRequestURL(url: URL(string: "openid-vc://mock.com")!) - let resolver = OpenIdURLRequestResolver(openIdResolver: MockOpenIdForVCResolver()) + let resolver = OpenIdURLRequestResolver(openIdResolver: MockOpenIdForVCResolver(), configuration: configuration) // Act let actualResult = resolver.canResolve(input: mockInput) @@ -93,8 +98,9 @@ class OpenIdURLRequestResolverTests: XCTestCase { func testCanResolve_WithInvalidRequestHandler_ReturnsFalse() throws { // Arrange + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: Mapper()) let mockHandler = MockHandler() - let resolver = OpenIdURLRequestResolver(openIdResolver: MockOpenIdForVCResolver()) + let resolver = OpenIdURLRequestResolver(openIdResolver: MockOpenIdForVCResolver(), configuration: configuration) // Act let actualResult = resolver.canResolve(using: mockHandler) @@ -106,8 +112,9 @@ class OpenIdURLRequestResolverTests: XCTestCase { func testCanResolve_WithValidRequestHandler_ReturnsTrue() throws { // Arrange - let mockHandler = OpenIdRequestHandler(configuration: LibraryConfiguration(logger: WalletLibraryLogger(), mapper: Mapper())) - let resolver = OpenIdURLRequestResolver(openIdResolver: MockOpenIdForVCResolver()) + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: Mapper()) + let mockHandler = OpenIdRequestHandler(configuration: configuration) + let resolver = OpenIdURLRequestResolver(openIdResolver: MockOpenIdForVCResolver(), configuration: configuration) // Act let actualResult = resolver.canResolve(using: mockHandler) From 63ca6eea33d23d4a88c939aa10213c56702c670d Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 10 Feb 2023 10:39:08 -0800 Subject: [PATCH 087/373] Create MockLogConsumer.swift --- .../Mocks/Utilities/MockLogConsumer.swift | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 WalletLibrary/WalletLibraryTests/Mocks/Utilities/MockLogConsumer.swift diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Utilities/MockLogConsumer.swift b/WalletLibrary/WalletLibraryTests/Mocks/Utilities/MockLogConsumer.swift new file mode 100644 index 00000000..fe076ccc --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Mocks/Utilities/MockLogConsumer.swift @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +@testable import WalletLibrary + +struct MockLogConsumer: WalletLibraryLogConsumer, Equatable { + + func log(_ traceLevel: TraceLevel, + message: String, + functionName: String, + file: String, + line: Int) {} + + func event(name: String, + properties: [String : String]?, + measurements: [String : NSNumber]?) {} +} From 396e2ca83cbf803e78665cfdc1cad3d2eb62d514 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 10 Feb 2023 11:39:26 -0800 Subject: [PATCH 088/373] Add mock verified id request. --- .../WalletLibrary.xcodeproj/project.pbxproj | 4 +++ .../Requests/MockVerifiedIdRequest.swift | 35 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiedIdRequest.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 8a80ecf4..162d7b88 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -49,6 +49,7 @@ 559BD30B2995B1B500BD61AC /* LinkedDomainResult+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD30A2995B1B500BD61AC /* LinkedDomainResult+MappableTests.swift */; }; 559BD30D2996C12500BD61AC /* VerifiedIdClientBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD30C2996C12500BD61AC /* VerifiedIdClientBuilderTests.swift */; }; 559BD30F2996C1C500BD61AC /* MockLogConsumer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD30E2996C1C500BD61AC /* MockLogConsumer.swift */; }; + 559BD3112996D3CE00BD61AC /* MockVerifiedIdRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3102996D3CE00BD61AC /* MockVerifiedIdRequest.swift */; }; 55A81BCA2991AB82002C259A /* RequestResolving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BC92991AB82002C259A /* RequestResolving.swift */; }; 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE02991AB96002C259A /* RequestHandling.swift */; }; 55A81BE52991B2E5002C259A /* RequestResolverFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */; }; @@ -303,6 +304,7 @@ 559BD30A2995B1B500BD61AC /* LinkedDomainResult+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LinkedDomainResult+MappableTests.swift"; sourceTree = ""; }; 559BD30C2996C12500BD61AC /* VerifiedIdClientBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdClientBuilderTests.swift; sourceTree = ""; }; 559BD30E2996C1C500BD61AC /* MockLogConsumer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockLogConsumer.swift; sourceTree = ""; }; + 559BD3102996D3CE00BD61AC /* MockVerifiedIdRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockVerifiedIdRequest.swift; sourceTree = ""; }; 55A81BC92991AB82002C259A /* RequestResolving.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolving.swift; sourceTree = ""; }; 55A81BE02991AB96002C259A /* RequestHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestHandling.swift; sourceTree = ""; }; 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolverFactory.swift; sourceTree = ""; }; @@ -622,6 +624,7 @@ 55E2F074299555280008010D /* Styles */, 55A81C0C2991BF86002C259A /* MockInput.swift */, 550A913C29930D970014D030 /* MockRawRequest.swift */, + 559BD3102996D3CE00BD61AC /* MockVerifiedIdRequest.swift */, ); path = Requests; sourceTree = ""; @@ -1258,6 +1261,7 @@ 55E2F077299555280008010D /* MockRequesterStyle.swift in Sources */, 552E50A0293E6AB200868F47 /* VerifiedIdClientTests.swift in Sources */, 5534E688294A5F16005D0765 /* IdTokenDescriptor+MappableTests.swift in Sources */, + 559BD3112996D3CE00BD61AC /* MockVerifiedIdRequest.swift in Sources */, 550A913D29930D970014D030 /* MockRawRequest.swift in Sources */, 559BD30B2995B1B500BD61AC /* LinkedDomainResult+MappableTests.swift in Sources */, ); diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiedIdRequest.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiedIdRequest.swift new file mode 100644 index 00000000..7ae472ab --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiedIdRequest.swift @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +@testable import WalletLibrary + +class MockVerifiedIdRequest: VerifiedIdRequest { + + var style: RequesterStyle + + var requirement: Requirement + + var rootOfTrust: RootOfTrust + + init(style: RequesterStyle = MockRequesterStyle(requester: ""), + requirement: Requirement = MockRequirement(id: ""), + rootOfTrust: RootOfTrust = RootOfTrust(verified: false, source: "")) { + self.style = style + self.requirement = requirement + self.rootOfTrust = rootOfTrust + } + + func isSatisfied() -> Bool { + return false + } + + func complete() async -> Result { + return Result.success(Void()) + } + + func cancel(message: String?) -> Result { + return Result.success(Void()) + } +} From 7805146a03bf4d6aedb024c7d316cd5f75c2f4d3 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 10 Feb 2023 11:40:03 -0800 Subject: [PATCH 089/373] Make handleRequest function generic instead of using associated type --- .../Protocols/Requests/Handlers/RequestHandling.swift | 3 +-- .../Requests/Handlers/OpenIdRequestHandler.swift | 6 +++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/Handlers/RequestHandling.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/Handlers/RequestHandling.swift index bf1a0ab6..c45260b4 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/Handlers/RequestHandling.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/Handlers/RequestHandling.swift @@ -8,8 +8,7 @@ * then processed, validated and mapped to a verified id request. A conforming object is request protocol specific. */ protocol RequestHandling { - associatedtype RawRequest /// Validate and map an input to a verified id request. - func handleRequest(from: RawRequest) async throws -> any VerifiedIdRequest + func handleRequest(from: RawRequest) async throws -> any VerifiedIdRequest } diff --git a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift index e0a8e2cd..1bb711c5 100644 --- a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift +++ b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift @@ -16,7 +16,11 @@ struct OpenIdRequestHandler: RequestHandling { } /// Create a VeriifiedIdRequest based on the Open Id raw request given. - func handleRequest(from request: any OpenIdRawRequest) async throws -> any VerifiedIdRequest { + func handleRequest(from request: RawRequest) async throws -> any VerifiedIdRequest { + + guard let request = request as? any OpenIdRawRequest else { + throw VerifiedIdClientError.TODO(message: "implement") + } if request.type == .Issuance { return try await handleIssuanceRequest(from: request) From 9d38bf6e2f177859891212f7a4777bb148ac71d1 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 10 Feb 2023 11:40:20 -0800 Subject: [PATCH 090/373] Implement createVerifiedIdRequest --- WalletLibrary/WalletLibrary/VerifiedIdClient.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/VerifiedIdClient.swift b/WalletLibrary/WalletLibrary/VerifiedIdClient.swift index dacf5361..5aea51d6 100644 --- a/WalletLibrary/WalletLibrary/VerifiedIdClient.swift +++ b/WalletLibrary/WalletLibrary/VerifiedIdClient.swift @@ -29,6 +29,9 @@ public class VerifiedIdClient { /// Creates either an issuance or presentation request from the input. public func createVerifiedIdRequest(from input: VerifiedIdRequestInput) async throws -> any VerifiedIdRequest { - throw VerifiedIdClientError.TODO(message: "implement create request") + let resolver = try requestResolverFactory.getResolver(from: input) + let rawRequest = try await resolver.resolve(input: input) + let handler = try requestHandlerFactory.getHandler(from: resolver) + return try await handler.handleRequest(from: rawRequest) } } From 040dfd3a9f9129a24db4304c019f6f7aa6eb62a9 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 10 Feb 2023 11:40:28 -0800 Subject: [PATCH 091/373] Update mocks. --- .../Mocks/Requests/Handlers/MockHandler.swift | 8 ++++---- .../Mocks/Requests/Resolvers/MockResolver.swift | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/Handlers/MockHandler.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/Handlers/MockHandler.swift index 8d5a76d3..eec7bf7b 100644 --- a/WalletLibrary/WalletLibraryTests/Mocks/Requests/Handlers/MockHandler.swift +++ b/WalletLibrary/WalletLibraryTests/Mocks/Requests/Handlers/MockHandler.swift @@ -11,18 +11,18 @@ class MockHandler: RequestHandling { case nilMockHandlerMethod } - let mockHandleRequest: (() -> any VerifiedIdRequest)? + let mockHandleRequest: (() throws -> any VerifiedIdRequest)? - init(mockHandleRequest: (() -> any VerifiedIdRequest)? = nil) { + init(mockHandleRequest: (() throws -> any VerifiedIdRequest)? = nil) { self.mockHandleRequest = mockHandleRequest } - func handleRequest(from: MockRawRequest) async throws -> any VerifiedIdRequest { + func handleRequest(from: RawRequest) async throws -> any VerifiedIdRequest { guard let mockHandleRequest = mockHandleRequest else { throw MockHandlerError.nilMockHandlerMethod } - return mockHandleRequest() + return try mockHandleRequest() } } diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/Resolvers/MockResolver.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/Resolvers/MockResolver.swift index b45ca8fd..f3811596 100644 --- a/WalletLibrary/WalletLibraryTests/Mocks/Requests/Resolvers/MockResolver.swift +++ b/WalletLibrary/WalletLibraryTests/Mocks/Requests/Resolvers/MockResolver.swift @@ -15,11 +15,11 @@ class MockResolver: RequestResolving { let canResolveUsingInput: Bool - let mockResolve: ((VerifiedIdRequestInput) -> RawRequest)? + let mockResolve: ((VerifiedIdRequestInput) throws -> RawRequest)? init(canResolveUsingInput: Bool, canResolveUsingHandler: ((any RequestHandling) -> Bool)? = nil, - mockResolve: ((VerifiedIdRequestInput) -> RawRequest)? = nil) { + mockResolve: ((VerifiedIdRequestInput) throws -> RawRequest)? = nil) { self.canResolveUsingHandler = canResolveUsingHandler self.canResolveUsingInput = canResolveUsingInput self.mockResolve = mockResolve @@ -39,6 +39,6 @@ class MockResolver: RequestResolving { throw MockResolverError.nilMockResolveMethod } - return mockResolve(input) + return try mockResolve(input) } } From d87caadd879c08f490d28fcad8f68186ab2ffe53 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 10 Feb 2023 11:40:42 -0800 Subject: [PATCH 092/373] complete test cases for VerifiedIdClientTests --- .../VerifiedIdClientTests.swift | 146 ++++++++++++++++-- 1 file changed, 135 insertions(+), 11 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/VerifiedIdClientTests.swift b/WalletLibrary/WalletLibraryTests/VerifiedIdClientTests.swift index 4315c35f..a4079014 100644 --- a/WalletLibrary/WalletLibraryTests/VerifiedIdClientTests.swift +++ b/WalletLibrary/WalletLibraryTests/VerifiedIdClientTests.swift @@ -7,19 +7,143 @@ import XCTest @testable import WalletLibrary class VerifiedIdClientTests: XCTestCase { - - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. + + enum ExpectedError: Error { + case expectedToBeThrownInResolver + case expectedToBeThrownInHandler } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. + + func testCreateRequest_WhenResolverFactoryThrows_ThrowError() async throws { + // Arrange + let resolver = MockResolver(canResolveUsingInput: false) + let resolverFactory = RequestResolverFactory(resolvers: [resolver]) + + let mockInput = MockInput(mockData: "") + + let handlerFactory = RequestHandlerFactory(requestHandlers: []) + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: Mapper()) + + let client = VerifiedIdClient(requestResolverFactory: resolverFactory, + requestHandlerFactory: handlerFactory, + configuration: configuration) + + // Act + do { + let _ = try await client.createVerifiedIdRequest(from: mockInput) + XCTFail("Should have thrown.") + } catch { + // Assert + XCTAssert(error is RequestResolverFactoryError) + XCTAssertEqual(error as? RequestResolverFactoryError, .UnsupportedInput) + } } - - func testPerformanceExample() throws { - // This is an example of a performance test case. - self.measure { - // Put the code you want to measure the time of here. + + func testCreateRequest_WhenResolverThrows_ThrowError() async throws { + // Arrange + let resolver = MockResolver(canResolveUsingInput: true, + canResolveUsingHandler: { _ in false }, + mockResolve: { _ in throw ExpectedError.expectedToBeThrownInResolver }) + let resolverFactory = RequestResolverFactory(resolvers: [resolver]) + + let mockInput = MockInput(mockData: "") + + let mockHandler = MockHandler() + let handlerFactory = RequestHandlerFactory(requestHandlers: [mockHandler]) + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: Mapper()) + + let client = VerifiedIdClient(requestResolverFactory: resolverFactory, + requestHandlerFactory: handlerFactory, + configuration: configuration) + + // Act + do { + let _ = try await client.createVerifiedIdRequest(from: mockInput) + XCTFail("Should have thrown.") + } catch { + // Assert + XCTAssert(error is ExpectedError) + XCTAssertEqual(error as? ExpectedError, .expectedToBeThrownInResolver) } } + + func testCreateRequest_WhenHandlerFactoryThrows_ThrowError() async throws { + // Arrange + let resolver = MockResolver(canResolveUsingInput: true, + canResolveUsingHandler: { _ in false }, + mockResolve: {_ in MockRawRequest(raw: "")}) + let resolverFactory = RequestResolverFactory(resolvers: [resolver]) + + let mockInput = MockInput(mockData: "") + + let mockHandler = MockHandler() + let handlerFactory = RequestHandlerFactory(requestHandlers: [mockHandler]) + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: Mapper()) + + let client = VerifiedIdClient(requestResolverFactory: resolverFactory, + requestHandlerFactory: handlerFactory, + configuration: configuration) + + // Act + do { + let _ = try await client.createVerifiedIdRequest(from: mockInput) + XCTFail("Should have thrown.") + } catch { + // Assert + XCTAssert(error is RequestHandlerFactoryError) + XCTAssertEqual(error as? RequestHandlerFactoryError, .UnsupportedResolver) + } + } + + func testCreateRequest_WhenHandlerThrows_ThrowError() async throws { + // Arrange + let resolver = MockResolver(canResolveUsingInput: true, + canResolveUsingHandler: { _ in true }, + mockResolve: {_ in MockRawRequest(raw: "")}) + let resolverFactory = RequestResolverFactory(resolvers: [resolver]) + + let mockInput = MockInput(mockData: "") + + let mockHandler = MockHandler(mockHandleRequest: { throw ExpectedError.expectedToBeThrownInHandler }) + let handlerFactory = RequestHandlerFactory(requestHandlers: [mockHandler]) + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: Mapper()) + + let client = VerifiedIdClient(requestResolverFactory: resolverFactory, + requestHandlerFactory: handlerFactory, + configuration: configuration) + + // Act + do { + let _ = try await client.createVerifiedIdRequest(from: mockInput) + XCTFail("Should have thrown.") + } catch { + // Assert + XCTAssert(error is ExpectedError) + XCTAssertEqual(error as? ExpectedError, .expectedToBeThrownInHandler) + } + } + + func testCreateRequest_WithNoErrorsThrown_ReturnsVerifiedIdRequest() async throws { + // Arrange + let resolver = MockResolver(canResolveUsingInput: true, + canResolveUsingHandler: { _ in true }, + mockResolve: {_ in MockRawRequest(raw: "")}) + let resolverFactory = RequestResolverFactory(resolvers: [resolver]) + + let mockInput = MockInput(mockData: "") + + let expectedRequest = MockVerifiedIdRequest() + + let mockHandler = MockHandler(mockHandleRequest: { return expectedRequest }) + let handlerFactory = RequestHandlerFactory(requestHandlers: [mockHandler]) + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: Mapper()) + + let client = VerifiedIdClient(requestResolverFactory: resolverFactory, + requestHandlerFactory: handlerFactory, + configuration: configuration) + + // Act + let actualResult = try await client.createVerifiedIdRequest(from: mockInput) + + XCTAssertIdentical(actualResult as AnyObject, expectedRequest as AnyObject) + } } From 5b57d3a3e5be584ed40de4d90f405e807646d707 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 10 Feb 2023 12:08:47 -0800 Subject: [PATCH 093/373] Fix nits for PR --- .../Requests/Handlers/OpenIdRequestHandler.swift | 6 +++++- .../WalletLibrary/Utilities/WalletLibraryLogConsumer.swift | 2 -- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift index 1bb711c5..dc67f005 100644 --- a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift +++ b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift @@ -3,6 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +enum OpenIdRequestHandlerError: Error { + case unsupportedRawRequestType +} + /** * Handles a raw Open Id request and configures a VeriifedIdRequest object. * Post Private Preview TODO: add processors to support multiple profiles of open id. @@ -19,7 +23,7 @@ struct OpenIdRequestHandler: RequestHandling { func handleRequest(from request: RawRequest) async throws -> any VerifiedIdRequest { guard let request = request as? any OpenIdRawRequest else { - throw VerifiedIdClientError.TODO(message: "implement") + throw OpenIdRequestHandlerError.unsupportedRawRequestType } if request.type == .Issuance { diff --git a/WalletLibrary/WalletLibrary/Utilities/WalletLibraryLogConsumer.swift b/WalletLibrary/WalletLibrary/Utilities/WalletLibraryLogConsumer.swift index 7f7512fe..afcf54de 100644 --- a/WalletLibrary/WalletLibrary/Utilities/WalletLibraryLogConsumer.swift +++ b/WalletLibrary/WalletLibrary/Utilities/WalletLibraryLogConsumer.swift @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import VCEntities - /// The trace level of the data that will be logged. public enum TraceLevel { case VERBOSE From de7d9a539f76611a20d456eac9521de8f55c81ba Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Feb 2023 13:40:52 -0800 Subject: [PATCH 094/373] Extend VC SDK Issuance service. --- .../WalletLibrary.xcodeproj/project.pbxproj | 44 ++++++++++++++----- .../Services/IssuanceService+Contract.swift | 34 ++++++++++++++ 2 files changed, 66 insertions(+), 12 deletions(-) create mode 100644 WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+Contract.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 162d7b88..707854e7 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -50,6 +50,10 @@ 559BD30D2996C12500BD61AC /* VerifiedIdClientBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD30C2996C12500BD61AC /* VerifiedIdClientBuilderTests.swift */; }; 559BD30F2996C1C500BD61AC /* MockLogConsumer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD30E2996C1C500BD61AC /* MockLogConsumer.swift */; }; 559BD3112996D3CE00BD61AC /* MockVerifiedIdRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3102996D3CE00BD61AC /* MockVerifiedIdRequest.swift */; }; + 559BD3A3299AD95200BD61AC /* IssuanceService+Contract.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3A2299AD95200BD61AC /* IssuanceService+Contract.swift */; }; + 559BD3A6299AD9BD00BD61AC /* ContractResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3A5299AD9BD00BD61AC /* ContractResolver.swift */; }; + 559BD3A8299ADE9C00BD61AC /* RawContract.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3A7299ADE9C00BD61AC /* RawContract.swift */; }; + 559BD3AB299ADEFF00BD61AC /* ContractIssuanceRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3AA299ADEFF00BD61AC /* ContractIssuanceRequest.swift */; }; 55A81BCA2991AB82002C259A /* RequestResolving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BC92991AB82002C259A /* RequestResolving.swift */; }; 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE02991AB96002C259A /* RequestHandling.swift */; }; 55A81BE52991B2E5002C259A /* RequestResolverFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */; }; @@ -79,7 +83,6 @@ 55E33708293FD25900CD2ED7 /* AccessTokenRequirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E33707293FD25900CD2ED7 /* AccessTokenRequirement.swift */; }; 55E3370A293FD3E000CD2ED7 /* IdTokenRequirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E33709293FD3E000CD2ED7 /* IdTokenRequirement.swift */; }; 55E3370C293FD61E00CD2ED7 /* SelfAttestedClaimRequirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E3370B293FD61E00CD2ED7 /* SelfAttestedClaimRequirement.swift */; }; - 55E33714293FDEEF00CD2ED7 /* Contract.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E33713293FDEEF00CD2ED7 /* Contract.swift */; }; 55E337652943749600CD2ED7 /* AsyncWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E337642943749600CD2ED7 /* AsyncWrapper.swift */; }; 55E3376829437E3500CD2ED7 /* OpenIdForVCResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E3376729437E3500CD2ED7 /* OpenIdForVCResolver.swift */; }; 55E3376B29478C5000CD2ED7 /* AsyncWrapperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E3376A29478C5000CD2ED7 /* AsyncWrapperTests.swift */; }; @@ -305,6 +308,10 @@ 559BD30C2996C12500BD61AC /* VerifiedIdClientBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdClientBuilderTests.swift; sourceTree = ""; }; 559BD30E2996C1C500BD61AC /* MockLogConsumer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockLogConsumer.swift; sourceTree = ""; }; 559BD3102996D3CE00BD61AC /* MockVerifiedIdRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockVerifiedIdRequest.swift; sourceTree = ""; }; + 559BD3A2299AD95200BD61AC /* IssuanceService+Contract.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceService+Contract.swift"; sourceTree = ""; }; + 559BD3A5299AD9BD00BD61AC /* ContractResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContractResolver.swift; sourceTree = ""; }; + 559BD3A7299ADE9C00BD61AC /* RawContract.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RawContract.swift; sourceTree = ""; }; + 559BD3AA299ADEFF00BD61AC /* ContractIssuanceRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContractIssuanceRequest.swift; sourceTree = ""; }; 55A81BC92991AB82002C259A /* RequestResolving.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolving.swift; sourceTree = ""; }; 55A81BE02991AB96002C259A /* RequestHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestHandling.swift; sourceTree = ""; }; 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolverFactory.swift; sourceTree = ""; }; @@ -336,7 +343,6 @@ 55E33707293FD25900CD2ED7 /* AccessTokenRequirement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessTokenRequirement.swift; sourceTree = ""; }; 55E33709293FD3E000CD2ED7 /* IdTokenRequirement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdTokenRequirement.swift; sourceTree = ""; }; 55E3370B293FD61E00CD2ED7 /* SelfAttestedClaimRequirement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelfAttestedClaimRequirement.swift; sourceTree = ""; }; - 55E33713293FDEEF00CD2ED7 /* Contract.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Contract.swift; sourceTree = ""; }; 55E337642943749600CD2ED7 /* AsyncWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncWrapper.swift; sourceTree = ""; }; 55E3376729437E3500CD2ED7 /* OpenIdForVCResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenIdForVCResolver.swift; sourceTree = ""; }; 55E3376A29478C5000CD2ED7 /* AsyncWrapperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncWrapperTests.swift; sourceTree = ""; }; @@ -419,6 +425,7 @@ isa = PBXGroup; children = ( 550A919D29940ED70014D030 /* OpenIdRawRequest.swift */, + 559BD3A7299ADE9C00BD61AC /* RawContract.swift */, ); path = RawRequests; sourceTree = ""; @@ -530,6 +537,7 @@ isa = PBXGroup; children = ( 55E337BB2948B5FE00CD2ED7 /* PresentationService+OpenIdForVC.swift */, + 559BD3A2299AD95200BD61AC /* IssuanceService+Contract.swift */, ); path = Services; sourceTree = ""; @@ -594,6 +602,22 @@ path = VCSDK; sourceTree = ""; }; + 559BD3A4299AD9A200BD61AC /* Contract */ = { + isa = PBXGroup; + children = ( + 559BD3A5299AD9BD00BD61AC /* ContractResolver.swift */, + ); + path = Contract; + sourceTree = ""; + }; + 559BD3A9299ADEF300BD61AC /* Contract */ = { + isa = PBXGroup; + children = ( + 559BD3AA299ADEFF00BD61AC /* ContractIssuanceRequest.swift */, + ); + path = Contract; + sourceTree = ""; + }; 55A81BF02991BB13002C259A /* Requests */ = { isa = PBXGroup; children = ( @@ -672,6 +696,7 @@ 55E2F07A2995561E0008010D /* RequestProtocols */ = { isa = PBXGroup; children = ( + 559BD3A9299ADEF300BD61AC /* Contract */, 55E2F07B2995561E0008010D /* OpenId */, ); path = RequestProtocols; @@ -801,7 +826,6 @@ 550A91712993117B0014D030 /* Input */, 550A9156299310D40014D030 /* Handlers */, 550A916D299310D40014D030 /* Resolvers */, - 55E33712293FDEDF00CD2ED7 /* Contract */, 55E336CF293FA6D900CD2ED7 /* Requirements */, 5534E5B42948FEB5005D0765 /* RootOfTrust.swift */, 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */, @@ -811,14 +835,6 @@ path = Requests; sourceTree = ""; }; - 55E33712293FDEDF00CD2ED7 /* Contract */ = { - isa = PBXGroup; - children = ( - 55E33713293FDEEF00CD2ED7 /* Contract.swift */, - ); - path = Contract; - sourceTree = ""; - }; 55E3375B294231F000CD2ED7 /* Protocols */ = { isa = PBXGroup; children = ( @@ -861,6 +877,7 @@ 55E337B62948B4F900CD2ED7 /* Requests */ = { isa = PBXGroup; children = ( + 559BD3A4299AD9A200BD61AC /* Contract */, 550A919C29940ECA0014D030 /* RawRequests */, 55E337B72948B50800CD2ED7 /* Handlers */, 55E3376629437E1000CD2ED7 /* OpenIdForVC */, @@ -1190,8 +1207,10 @@ 550E3212298B1150004E7716 /* VerifiedIdRequestInput.swift in Sources */, 550A919B29940E9E0014D030 /* PresentationRequest+OpenIdRawRequest.swift in Sources */, 5534E667294A0B6C005D0765 /* Mappable.swift in Sources */, + 559BD3A8299ADE9C00BD61AC /* RawContract.swift in Sources */, 55E2F08129955A960008010D /* RequestType.swift in Sources */, 55A81BE72991B2F3002C259A /* RequestHandlerFactory.swift in Sources */, + 559BD3A3299AD95200BD61AC /* IssuanceService+Contract.swift in Sources */, 55E2F07F29955A3B0008010D /* VerifiedIdRequestContent.swift in Sources */, 550A916F299310D40014D030 /* OpenIdRequestHandler.swift in Sources */, 55E3370A293FD3E000CD2ED7 /* IdTokenRequirement.swift in Sources */, @@ -1204,10 +1223,12 @@ 550E3214298B11A5004E7716 /* Requirement.swift in Sources */, 550A9173299311A10014D030 /* VerifiedIdRequestURL.swift in Sources */, 550E3210298B113C004E7716 /* VerifiedIdRequest.swift in Sources */, + 559BD3AB299ADEFF00BD61AC /* ContractIssuanceRequest.swift in Sources */, 5534E5B52948FEB5005D0765 /* RootOfTrust.swift in Sources */, 550E320E298B0FAC004E7716 /* WalletLibraryLogger.swift in Sources */, 559BD3032995993700BD61AC /* PresentationInputDescriptor+Mappable.swift in Sources */, 55E337B52948B4B000CD2ED7 /* PinRequirement.swift in Sources */, + 559BD3A6299AD9BD00BD61AC /* ContractResolver.swift in Sources */, 550A9170299310D40014D030 /* OpenIdURLRequestResolver.swift in Sources */, 5534E669294A0C3F005D0765 /* Mapper.swift in Sources */, 550E320A298B0EA4004E7716 /* VerifiedIdClient.swift in Sources */, @@ -1217,7 +1238,6 @@ 55A81BE52991B2E5002C259A /* RequestResolverFactory.swift in Sources */, 55E2F086299573C30008010D /* OpenIdRequesterStyle.swift in Sources */, 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */, - 55E33714293FDEEF00CD2ED7 /* Contract.swift in Sources */, 55E2F071299554110008010D /* LibraryConfiguration.swift in Sources */, 550E3209298B0EA4004E7716 /* VerifiedIdClientBuilder.swift in Sources */, 550A9192299400820014D030 /* OpenIdForVCResponder.swift in Sources */, diff --git a/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+Contract.swift b/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+Contract.swift new file mode 100644 index 00000000..fa1c9849 --- /dev/null +++ b/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+Contract.swift @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import VCEntities +import VCServices + +/** + * An extension of the VCServices.PresentationService class. + */ +extension IssuanceService: ContractResolver { + + /// Fetches and validates the presentation request. + func getRequest(url: String) async throws -> any RawContract { + return try await AsyncWrapper().wrap { () in + self.getRequest(usingUrl: url) + }() + } + + /// Sends the presentation response container and if successful, returns void, + /// If unsuccessful, throws an error. + func send(response: VCEntities.IssuanceResponseContainer) async throws -> Void { + let _ = try await AsyncWrapper().wrap { () in + self.send(response: response) + }() + } +} + +extension VCEntities.IssuanceRequest: RawContract { + func map(using mapper: Mapping) throws -> VerifiedIdRequestContent { + throw VerifiedIdClientError.TODO(message: "implement") + } +} From ba8b5cacad9a8f720ae347fc925934f683a0a5a7 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Feb 2023 13:41:09 -0800 Subject: [PATCH 095/373] Create protocol for Contract Resolver to use to extend issuance service. --- .../Requests/Contract/ContractResolver.swift | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ContractResolver.swift diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ContractResolver.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ContractResolver.swift new file mode 100644 index 00000000..20a2ae47 --- /dev/null +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ContractResolver.swift @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import VCEntities + +/** + * Protocol is used as a wrapper to wrap the VC SDK get presentation response method. + */ +protocol ContractResolver { + /// Fetches and validates the presentation request. + func getRequest(url: String) async throws -> any RawContract +} From a0f825a0374b61e839d94914606cf3600bc77000 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Feb 2023 13:41:28 -0800 Subject: [PATCH 096/373] Create a raw contract protocol that conforms to mappable. --- .../Protocols/Requests/RawRequests/RawContract.swift | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 WalletLibrary/WalletLibrary/Protocols/Requests/RawRequests/RawContract.swift diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/RawRequests/RawContract.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/RawRequests/RawContract.swift new file mode 100644 index 00000000..041f4473 --- /dev/null +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/RawRequests/RawContract.swift @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +/** + * Representation of a Raw Contract. + * Object that conforms to this protocol must be able to map to VerifiedIdRequestContent. + */ +protocol RawContract: Mappable where T == VerifiedIdRequestContent {} + From 47d8f0c7746e46010fe40d9b2596317562fa1374 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Feb 2023 13:41:32 -0800 Subject: [PATCH 097/373] Update VerifiedIdRequest.swift --- .../WalletLibrary/Protocols/Requests/VerifiedIdRequest.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequest.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequest.swift index 86518cb6..482b5ecf 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequest.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequest.swift @@ -33,7 +33,7 @@ public protocol VerifiedIdRequest { * Internal Protocol that represents an Issuance Request. * TODO: add VerifiedId Style */ -protocol VerifiedIdIssuanceRequest: VerifiedIdRequest where T == any VerifiedIdRequest { } +protocol VerifiedIdIssuanceRequest: VerifiedIdRequest where T == VerifiedId { } /** * Internal Protocol that represents a Presentation Request. From 305bd0f2848b3e492d41a4a651d73e5dd880b5f9 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Feb 2023 13:41:37 -0800 Subject: [PATCH 098/373] Delete Contract.swift --- .../Requests/Contract/Contract.swift | 30 ------------------- 1 file changed, 30 deletions(-) delete mode 100644 WalletLibrary/WalletLibrary/Requests/Contract/Contract.swift diff --git a/WalletLibrary/WalletLibrary/Requests/Contract/Contract.swift b/WalletLibrary/WalletLibrary/Requests/Contract/Contract.swift deleted file mode 100644 index 75e8ce42..00000000 --- a/WalletLibrary/WalletLibrary/Requests/Contract/Contract.swift +++ /dev/null @@ -1,30 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Microsoft Corporation. All rights reserved. -* Licensed under the MIT License. See License.txt in the project root for license information. -*--------------------------------------------------------------------------------------------*/ - -/** - * A contract that contains the information needed to get a Verified Id - * including display information through the trait properties. - * TODO: Add display information properties once we have agreed on design. - */ -public struct Contract { - - /// Root of Trust such as Linked Domain Verified for the request. - public let rootOfTrust: RootOfTrust - - /// information to describe Verified IDs required for issuance. - public let verifiedIdRequirements: [VerifiedIdRequirement] - - /// information to describe id tokens required for issuance. - public let idTokenRequirements: [IdTokenRequirement] - - /// information to describe access tokens required for issuance. - public let accessTokenRequirements: [AccessTokenRequirement] - - /// information to describe self-attested required for issuance. - public let selfAttestedClaimRequirements: [SelfAttestedClaimRequirement] - - /// raw representation of the contract. - let raw: String -} From b04af6a41466cea003420210518279a9e3b07c5d Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Feb 2023 13:42:08 -0800 Subject: [PATCH 099/373] Implement issuance request logic in open id request handler. --- .../Handlers/OpenIdRequestHandler.swift | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift index dc67f005..54561297 100644 --- a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift +++ b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift @@ -5,6 +5,7 @@ enum OpenIdRequestHandlerError: Error { case unsupportedRawRequestType + case noIssuanceOptionsPresentToCreateIssuanceRequest } /** @@ -15,8 +16,11 @@ struct OpenIdRequestHandler: RequestHandling { private let configuration: LibraryConfiguration - init(configuration: LibraryConfiguration) { + private let contractResolver: ContractResolver + + init(configuration: LibraryConfiguration, contractResolver: ContractResolver) { self.configuration = configuration + self.contractResolver = contractResolver } /// Create a VeriifiedIdRequest based on the Open Id raw request given. @@ -26,19 +30,28 @@ struct OpenIdRequestHandler: RequestHandling { throw OpenIdRequestHandlerError.unsupportedRawRequestType } + let requestContent = try configuration.mapper.map(request) + if request.type == .Issuance { - return try await handleIssuanceRequest(from: request) + return try await handleIssuanceRequest(from: requestContent) } - return try handlePresentationRequest(from: request) + return try handlePresentationRequest(from: requestContent) } - private func handleIssuanceRequest(from request: any OpenIdRawRequest) async throws -> any VerifiedIdIssuanceRequest { - throw VerifiedIdClientError.TODO(message: "implement") + private func handleIssuanceRequest(from requestContent: VerifiedIdRequestContent) async throws -> any VerifiedIdIssuanceRequest { + + guard let verifiedIdRequirement = requestContent.requirement as? VerifiedIdRequirement, + let issuanceOption = verifiedIdRequirement.issuanceOptions.first as? VerifiedIdRequestURL else { + throw OpenIdRequestHandlerError.noIssuanceOptionsPresentToCreateIssuanceRequest + } + + let rawContract = try await contractResolver.getRequest(url: issuanceOption.url.absoluteString) + let issuanceRequestContent = try configuration.mapper.map(rawContract) + return ContractIssuanceRequest(content: issuanceRequestContent, configuration: configuration) } - private func handlePresentationRequest(from request: any OpenIdRawRequest) throws -> any VerifiedIdPresentationRequest { - let content = try configuration.mapper.map(request) - return OpenIdPresentationRequest(content: content, configuration: configuration) + private func handlePresentationRequest(from requestContent: VerifiedIdRequestContent) throws -> any VerifiedIdPresentationRequest { + return OpenIdPresentationRequest(content: requestContent, configuration: configuration) } } From c8376fbc0c6bfaa84ffd7b9e3202b61b098c8233 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Feb 2023 13:42:19 -0800 Subject: [PATCH 100/373] Create ContractIssuanceRequest.swift --- .../Contract/ContractIssuanceRequest.swift | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 WalletLibrary/WalletLibrary/Requests/RequestProtocols/Contract/ContractIssuanceRequest.swift diff --git a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Contract/ContractIssuanceRequest.swift b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Contract/ContractIssuanceRequest.swift new file mode 100644 index 00000000..d5259b0a --- /dev/null +++ b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Contract/ContractIssuanceRequest.swift @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +/** + * Issuance Requst that is Contract specific. + * TODO: we will need contract specific data to implement complete and cancel. + * TODO: add VerifiedIdStyle property. + */ +class ContractIssuanceRequest: VerifiedIdIssuanceRequest { + + public let style: RequesterStyle + + public let requirement: Requirement + + public let rootOfTrust: RootOfTrust + + private let configuration: LibraryConfiguration + + init(content: VerifiedIdRequestContent, configuration: LibraryConfiguration) { + self.style = content.style + self.requirement = content.requirement + self.rootOfTrust = content.rootOfTrust + self.configuration = configuration + } + + public func isSatisfied() -> Bool { + /// TODO: implement. + return false + } + + public func complete() async -> Result { + return Result.failure(VerifiedIdClientError.TODO(message: "implement")) + } + + public func cancel(message: String?) -> Result { + return Result.failure(VerifiedIdClientError.TODO(message: "implement")) + } + + +} + From 07609960cb7e3bba87a8cbe46baa93de9749becb Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Feb 2023 13:42:33 -0800 Subject: [PATCH 101/373] add contract resolver param to open id handler --- WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift b/WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift index e891c0b1..d61b9c70 100644 --- a/WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift +++ b/WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift @@ -50,7 +50,7 @@ public class VerifiedIdClientBuilder { } private func registerSupportedRequestHandlers(with configuration: LibraryConfiguration) { - let openIdHandler = OpenIdRequestHandler(configuration: configuration) + let openIdHandler = OpenIdRequestHandler(configuration: configuration, contractResolver: IssuanceService()) requestHandlers.append(openIdHandler) } } From e7f07000236c100550fc35b2f37fbd444b7ab5a1 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Feb 2023 15:19:12 -0800 Subject: [PATCH 102/373] Add Raw Contract as an extension of Issuance Request. --- .../WalletLibrary.xcodeproj/project.pbxproj | 10 +++++++++- .../Entities/IssuanceRequest+RawContract.swift | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceRequest+RawContract.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 707854e7..3fa04c7a 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -54,6 +54,8 @@ 559BD3A6299AD9BD00BD61AC /* ContractResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3A5299AD9BD00BD61AC /* ContractResolver.swift */; }; 559BD3A8299ADE9C00BD61AC /* RawContract.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3A7299ADE9C00BD61AC /* RawContract.swift */; }; 559BD3AB299ADEFF00BD61AC /* ContractIssuanceRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3AA299ADEFF00BD61AC /* ContractIssuanceRequest.swift */; }; + 559BD3AD299AE6C000BD61AC /* IssuanceRequest+RawContract.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3AC299AE6C000BD61AC /* IssuanceRequest+RawContract.swift */; }; + 559BD3AF299AF42C00BD61AC /* MockContractResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3AE299AF42C00BD61AC /* MockContractResolver.swift */; }; 55A81BCA2991AB82002C259A /* RequestResolving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BC92991AB82002C259A /* RequestResolving.swift */; }; 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE02991AB96002C259A /* RequestHandling.swift */; }; 55A81BE52991B2E5002C259A /* RequestResolverFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */; }; @@ -312,6 +314,8 @@ 559BD3A5299AD9BD00BD61AC /* ContractResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContractResolver.swift; sourceTree = ""; }; 559BD3A7299ADE9C00BD61AC /* RawContract.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RawContract.swift; sourceTree = ""; }; 559BD3AA299ADEFF00BD61AC /* ContractIssuanceRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContractIssuanceRequest.swift; sourceTree = ""; }; + 559BD3AC299AE6C000BD61AC /* IssuanceRequest+RawContract.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceRequest+RawContract.swift"; sourceTree = ""; }; + 559BD3AE299AF42C00BD61AC /* MockContractResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockContractResolver.swift; sourceTree = ""; }; 55A81BC92991AB82002C259A /* RequestResolving.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolving.swift; sourceTree = ""; }; 55A81BE02991AB96002C259A /* RequestHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestHandling.swift; sourceTree = ""; }; 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolverFactory.swift; sourceTree = ""; }; @@ -417,6 +421,7 @@ isa = PBXGroup; children = ( 550A919A29940E9E0014D030 /* PresentationRequest+OpenIdRawRequest.swift */, + 559BD3AC299AE6C000BD61AC /* IssuanceRequest+RawContract.swift */, ); path = Entities; sourceTree = ""; @@ -536,8 +541,8 @@ 5534E664294A0B3E005D0765 /* Services */ = { isa = PBXGroup; children = ( - 55E337BB2948B5FE00CD2ED7 /* PresentationService+OpenIdForVC.swift */, 559BD3A2299AD95200BD61AC /* IssuanceService+Contract.swift */, + 55E337BB2948B5FE00CD2ED7 /* PresentationService+OpenIdForVC.swift */, ); path = Services; sourceTree = ""; @@ -649,6 +654,7 @@ 55A81C0C2991BF86002C259A /* MockInput.swift */, 550A913C29930D970014D030 /* MockRawRequest.swift */, 559BD3102996D3CE00BD61AC /* MockVerifiedIdRequest.swift */, + 559BD3AE299AF42C00BD61AC /* MockContractResolver.swift */, ); path = Requests; sourceTree = ""; @@ -1232,6 +1238,7 @@ 550A9170299310D40014D030 /* OpenIdURLRequestResolver.swift in Sources */, 5534E669294A0C3F005D0765 /* Mapper.swift in Sources */, 550E320A298B0EA4004E7716 /* VerifiedIdClient.swift in Sources */, + 559BD3AD299AE6C000BD61AC /* IssuanceRequest+RawContract.swift in Sources */, 5534E64D294A0888005D0765 /* PresentationDescriptor+Mappable.swift in Sources */, 55E3370C293FD61E00CD2ED7 /* SelfAttestedClaimRequirement.swift in Sources */, 55E3376829437E3500CD2ED7 /* OpenIdForVCResolver.swift in Sources */, @@ -1272,6 +1279,7 @@ 550A91952994025C0014D030 /* OpenIdURLRequestResolverTests.swift in Sources */, 559BD3052995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift in Sources */, 55A81C0D2991BF86002C259A /* MockInput.swift in Sources */, + 559BD3AF299AF42C00BD61AC /* MockContractResolver.swift in Sources */, 550A919829940DCE0014D030 /* MockOpenIdforVCResolver.swift in Sources */, 559BD3092995ABE800BD61AC /* PresentationRequest+MappableTests.swift in Sources */, 550A91A0299412790014D030 /* MockOpenIdRawRequest.swift in Sources */, diff --git a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceRequest+RawContract.swift b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceRequest+RawContract.swift new file mode 100644 index 00000000..9eb32ee5 --- /dev/null +++ b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceRequest+RawContract.swift @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import VCEntities + +/** + * An extension of the VCEntities.IssuanceRequest class. + */ +extension VCEntities.IssuanceRequest: RawContract { + func map(using mapper: Mapping) throws -> VerifiedIdRequestContent { + throw VerifiedIdClientError.TODO(message: "implement") + } +} From efd088f95b995d6190bf2f1f6c8c43d4b6c8f208 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Feb 2023 15:19:18 -0800 Subject: [PATCH 103/373] Update IssuanceService+Contract.swift --- .../Services/IssuanceService+Contract.swift | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+Contract.swift b/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+Contract.swift index fa1c9849..db8bb130 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+Contract.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+Contract.swift @@ -7,18 +7,18 @@ import VCEntities import VCServices /** - * An extension of the VCServices.PresentationService class. + * An extension of the VCServices.IssuanceService class. */ extension IssuanceService: ContractResolver { - /// Fetches and validates the presentation request. + /// Fetches and validates the issuance request func getRequest(url: String) async throws -> any RawContract { return try await AsyncWrapper().wrap { () in self.getRequest(usingUrl: url) }() } - /// Sends the presentation response container and if successful, returns void, + /// Sends the issuance response container and if successful, returns void, /// If unsuccessful, throws an error. func send(response: VCEntities.IssuanceResponseContainer) async throws -> Void { let _ = try await AsyncWrapper().wrap { () in @@ -26,9 +26,3 @@ extension IssuanceService: ContractResolver { }() } } - -extension VCEntities.IssuanceRequest: RawContract { - func map(using mapper: Mapping) throws -> VerifiedIdRequestContent { - throw VerifiedIdClientError.TODO(message: "implement") - } -} From ce3a4019b2ae14e19a5d2dcccdc8ccf3e8514ec5 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Feb 2023 15:19:21 -0800 Subject: [PATCH 104/373] Update ContractResolver.swift --- .../Protocols/Requests/Contract/ContractResolver.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ContractResolver.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ContractResolver.swift index 20a2ae47..0b08631e 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ContractResolver.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ContractResolver.swift @@ -6,9 +6,9 @@ import VCEntities /** - * Protocol is used as a wrapper to wrap the VC SDK get presentation response method. + * Protocol is used as a wrapper to wrap the VC SDK get contract method. */ protocol ContractResolver { - /// Fetches and validates the presentation request. + /// Fetches and validates the contract. func getRequest(url: String) async throws -> any RawContract } From f6342a2ec827e80363a173e668bc9ad9baca529a Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Feb 2023 15:19:25 -0800 Subject: [PATCH 105/373] Update ContractIssuanceRequest.swift --- .../RequestProtocols/Contract/ContractIssuanceRequest.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Contract/ContractIssuanceRequest.swift b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Contract/ContractIssuanceRequest.swift index d5259b0a..4b16670b 100644 --- a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Contract/ContractIssuanceRequest.swift +++ b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Contract/ContractIssuanceRequest.swift @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ /** - * Issuance Requst that is Contract specific. + * Issuance Request that is Contract specific. * TODO: we will need contract specific data to implement complete and cancel. * TODO: add VerifiedIdStyle property. */ From eb98a7112708dc049313700ff6fbe348ed79378e Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Feb 2023 15:19:28 -0800 Subject: [PATCH 106/373] Create MockContractResolver.swift --- .../Mocks/Requests/MockContractResolver.swift | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 WalletLibrary/WalletLibraryTests/Mocks/Requests/MockContractResolver.swift diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockContractResolver.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockContractResolver.swift new file mode 100644 index 00000000..fc163196 --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockContractResolver.swift @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +@testable import WalletLibrary + +class MockContractResolver: ContractResolver { + + let mockGetRequestCallback: ((String) throws -> any RawContract)? + + init(mockGetRequestCallback: ((String) throws -> any RawContract)? = nil) { + self.mockGetRequestCallback = mockGetRequestCallback + } + + func getRequest(url: String) async throws -> any RawContract { + return try mockGetRequestCallback?(url) ?? MockRawContract(id: "") + } + +} + +struct MockRawContract: RawContract, Equatable { + + let id: String + + init(id: String) { + self.id = id + } + + func map(using mapper: WalletLibrary.Mapping) throws -> WalletLibrary.VerifiedIdRequestContent { + throw VerifiedIdClientError.TODO(message: "implement") + } +} From 14945c14143719bb219b47a6f2c43caf6fd6d10e Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Feb 2023 15:20:14 -0800 Subject: [PATCH 107/373] Add test cases for issuance flow in open id request handler tests. --- .../Handlers/OpenIdRequestHandlerTests.swift | 272 +++++++++++++++++- 1 file changed, 267 insertions(+), 5 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift index 2a151bec..d614795c 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift @@ -11,9 +11,11 @@ class OpenIdRequestHandlerTests: XCTestCase { enum ExpectedError: Error, Equatable { case expectedToBeThrown + case expectedToBeUnableToResolveContract + case expectedToBeUnableToMapRawContractToVerifiedIdContent } - func testHandleRequest_WithRawPresentationRequest_ReturnsVerifiedIdRequest() async throws { + func testHandlePresentationRequest_WithRawPresentationRequest_ReturnsVerifiedIdRequest() async throws { // Arrange let expectedStyle = MockRequesterStyle(requester: "mock requester") @@ -34,18 +36,20 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockMapper = MockMapper(mockResults: mockResults) let mockRawRequest = MockOpenIdRawRequest(raw: Data()) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) - let handler = OpenIdRequestHandler(configuration: configuration) + let handler = OpenIdRequestHandler(configuration: configuration, contractResolver: MockContractResolver()) // Act let actualRequest = try await handler.handleRequest(from: mockRawRequest) // Assert + XCTAssert(actualRequest is OpenIdPresentationRequest) XCTAssertEqual(actualRequest.style as? MockRequesterStyle, expectedStyle) XCTAssertEqual(actualRequest.requirement as? MockRequirement, expectedRequirement) XCTAssertEqual(actualRequest.rootOfTrust.source, expectedRootOfTrust.source) + XCTAssert(actualRequest.rootOfTrust.verified) } - func testHandleRequest_WithPresentationRequestInvalidMapping_ThrowsError() async throws { + func testHandlePresentationRequest_WithPresentationRequestInvalidMapping_ThrowsError() async throws { // Arrange func mockResults(objectToBeMapped: Any) throws -> Any? { @@ -55,11 +59,11 @@ class OpenIdRequestHandlerTests: XCTestCase { return nil } - + let mockMapper = MockMapper(mockResults: mockResults) let mockRawRequest = MockOpenIdRawRequest(raw: Data()) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) - let handler = OpenIdRequestHandler(configuration: configuration) + let handler = OpenIdRequestHandler(configuration: configuration, contractResolver: MockContractResolver()) // Act do { @@ -71,4 +75,262 @@ class OpenIdRequestHandlerTests: XCTestCase { XCTAssertEqual(error as? ExpectedError, ExpectedError.expectedToBeThrown) } } + + func testHandleIssuanceRequest_WithInvalidRequirementTypeOnPresentationRequest_ThrowsError() async throws { + + // Arrange + let expectedStyle = MockRequesterStyle(requester: "mock requester") + let expectedRequirement = MockRequirement(id: "mockRequirement324") + let expectedRootOfTrust = RootOfTrust(verified: true, source: "mock source") + let expectedContent = VerifiedIdRequestContent(style: expectedStyle, + requirement: expectedRequirement, + rootOfTrust: expectedRootOfTrust) + + func mockResults(objectToBeMapped: Any) throws -> Any? { + if objectToBeMapped is MockOpenIdRawRequest { + return expectedContent + } + + return nil + } + + let mockMapper = MockMapper(mockResults: mockResults) + let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) + let handler = OpenIdRequestHandler(configuration: configuration, contractResolver: MockContractResolver()) + + // Act + do { + let _ = try await handler.handleRequest(from: mockRawRequest) + XCTFail("handler did not throw an error.") + } catch { + // Assert + XCTAssert(error is OpenIdRequestHandlerError) + XCTAssertEqual(error as? OpenIdRequestHandlerError, OpenIdRequestHandlerError.noIssuanceOptionsPresentToCreateIssuanceRequest) + } + } + + func testHandleIssuanceRequest_WithNoIssuanceOptionsOnPresentationRequest_ThrowsError() async throws { + + // Arrange + let expectedStyle = MockRequesterStyle(requester: "mock requester") + let expectedRequirement = VerifiedIdRequirement(encrypted: false, + required: true, + types: [], + purpose: nil, + issuanceOptions: []) + let expectedRootOfTrust = RootOfTrust(verified: true, source: "mock source") + let expectedContent = VerifiedIdRequestContent(style: expectedStyle, + requirement: expectedRequirement, + rootOfTrust: expectedRootOfTrust) + + func mockResults(objectToBeMapped: Any) throws -> Any? { + if objectToBeMapped is MockOpenIdRawRequest { + return expectedContent + } + + return nil + } + + let mockMapper = MockMapper(mockResults: mockResults) + let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) + let handler = OpenIdRequestHandler(configuration: configuration, contractResolver: MockContractResolver()) + + // Act + do { + let _ = try await handler.handleRequest(from: mockRawRequest) + XCTFail("handler did not throw an error.") + } catch { + // Assert + XCTAssert(error is OpenIdRequestHandlerError) + XCTAssertEqual(error as? OpenIdRequestHandlerError, OpenIdRequestHandlerError.noIssuanceOptionsPresentToCreateIssuanceRequest) + } + } + + func testHandleIssuanceRequest_WithIssuanceOptionInvalidType_ThrowsError() async throws { + + // Arrange + let expectedStyle = MockRequesterStyle(requester: "mock requester") + let expectedRequirement = VerifiedIdRequirement(encrypted: false, + required: true, + types: [], + purpose: nil, + issuanceOptions: [MockInput(mockData: "")]) + let expectedRootOfTrust = RootOfTrust(verified: true, source: "mock source") + let expectedContent = VerifiedIdRequestContent(style: expectedStyle, + requirement: expectedRequirement, + rootOfTrust: expectedRootOfTrust) + + func mockResults(objectToBeMapped: Any) throws -> Any? { + if objectToBeMapped is MockOpenIdRawRequest { + return expectedContent + } + + return nil + } + + let mockMapper = MockMapper(mockResults: mockResults) + let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) + let handler = OpenIdRequestHandler(configuration: configuration, contractResolver: MockContractResolver()) + + // Act + do { + let _ = try await handler.handleRequest(from: mockRawRequest) + XCTFail("handler did not throw an error.") + } catch { + // Assert + XCTAssert(error is OpenIdRequestHandlerError) + XCTAssertEqual(error as? OpenIdRequestHandlerError, OpenIdRequestHandlerError.noIssuanceOptionsPresentToCreateIssuanceRequest) + } + } + + func testHandleIssuanceRequest_WhenUnableToResolveContract_ThrowsError() async throws { + + // Arrange + let issuanceOptionURL = URL(string: "https://test.com")! + let expectedStyle = MockRequesterStyle(requester: "mock requester") + let expectedRequirement = VerifiedIdRequirement(encrypted: false, + required: true, + types: [], + purpose: nil, + issuanceOptions: [VerifiedIdRequestURL(url: issuanceOptionURL)]) + let expectedRootOfTrust = RootOfTrust(verified: true, source: "mock source") + let expectedContent = VerifiedIdRequestContent(style: expectedStyle, + requirement: expectedRequirement, + rootOfTrust: expectedRootOfTrust) + + func mockResults(objectToBeMapped: Any) throws -> Any? { + if objectToBeMapped is MockOpenIdRawRequest { + return expectedContent + } + + return nil + } + + func mockResolveContract(url: String) throws -> any RawContract { + throw ExpectedError.expectedToBeUnableToResolveContract + } + + let mockMapper = MockMapper(mockResults: mockResults) + let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) + let handler = OpenIdRequestHandler(configuration: configuration, + contractResolver: MockContractResolver(mockGetRequestCallback: mockResolveContract)) + + // Act + do { + let _ = try await handler.handleRequest(from: mockRawRequest) + XCTFail("handler did not throw an error.") + } catch { + // Assert + XCTAssert(error is ExpectedError) + XCTAssertEqual(error as? ExpectedError, ExpectedError.expectedToBeUnableToResolveContract) + } + } + + func testHandleIssuanceRequest_WhenUnableToMapRawContractToVerifiedIdRequestContent_ThrowsError() async throws { + + // Arrange + let issuanceOptionURL = URL(string: "https://test.com")! + let expectedStyle = MockRequesterStyle(requester: "mock requester") + let expectedRequirement = VerifiedIdRequirement(encrypted: false, + required: true, + types: [], + purpose: nil, + issuanceOptions: [VerifiedIdRequestURL(url: issuanceOptionURL)]) + let expectedRootOfTrust = RootOfTrust(verified: true, source: "mock source") + let expectedContent = VerifiedIdRequestContent(style: expectedStyle, + requirement: expectedRequirement, + rootOfTrust: expectedRootOfTrust) + + func mockResults(objectToBeMapped: Any) throws -> Any? { + if objectToBeMapped is MockOpenIdRawRequest { + return expectedContent + } + + if objectToBeMapped is MockRawContract { + throw ExpectedError.expectedToBeUnableToMapRawContractToVerifiedIdContent + } + + return nil + } + + func mockResolveContract(url: String) throws -> any RawContract { + return MockRawContract(id: "testContract345") + } + + let mockMapper = MockMapper(mockResults: mockResults) + let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) + let handler = OpenIdRequestHandler(configuration: configuration, + contractResolver: MockContractResolver(mockGetRequestCallback: mockResolveContract)) + + // Act + do { + let _ = try await handler.handleRequest(from: mockRawRequest) + XCTFail("handler did not throw an error.") + } catch { + // Assert + XCTAssert(error is ExpectedError) + XCTAssertEqual(error as? ExpectedError, ExpectedError.expectedToBeUnableToMapRawContractToVerifiedIdContent) + } + } + + func testHandleIssuanceRequest_WithValidContract_ReturnsVerifiedIdIssuanceRequest() async throws { + + // Arrange + let issuanceOptionURL = URL(string: "https://test.com")! + let expectedPresentationStyle = MockRequesterStyle(requester: "mock requester") + let expectedPresentationRequirement = VerifiedIdRequirement(encrypted: false, + required: true, + types: [], + purpose: nil, + issuanceOptions: [VerifiedIdRequestURL(url: issuanceOptionURL)]) + let expectedPresentationRootOfTrust = RootOfTrust(verified: true, source: "mock source") + let expectedPresentationContent = VerifiedIdRequestContent(style: expectedPresentationStyle, + requirement: expectedPresentationRequirement, + rootOfTrust: expectedPresentationRootOfTrust) + + let expectedIssuanceStyle = MockRequesterStyle(requester: "mock issuer") + let expectedIssuanceRequirement = MockRequirement(id: "mockRequirement23535") + let expectedIssuanceRootOfTrust = RootOfTrust(verified: true, source: "mock issuer source") + let expectedIssuanceContent = VerifiedIdRequestContent(style: expectedIssuanceStyle, + requirement: expectedIssuanceRequirement, + rootOfTrust: expectedIssuanceRootOfTrust) + + func mockResults(objectToBeMapped: Any) throws -> Any? { + if objectToBeMapped is MockOpenIdRawRequest { + return expectedPresentationContent + } + + if objectToBeMapped is MockRawContract { + return expectedIssuanceContent + } + + return nil + } + + func mockResolveContract(url: String) throws -> any RawContract { + return MockRawContract(id: "testContract345") + } + + let mockMapper = MockMapper(mockResults: mockResults) + let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) + let handler = OpenIdRequestHandler(configuration: configuration, + contractResolver: MockContractResolver(mockGetRequestCallback: mockResolveContract)) + + // Act + let actualRequest = try await handler.handleRequest(from: mockRawRequest) + + // Assert + XCTAssert(actualRequest is ContractIssuanceRequest) + XCTAssertEqual(actualRequest.style as? MockRequesterStyle, expectedIssuanceStyle) + XCTAssertEqual(actualRequest.requirement as? MockRequirement, expectedIssuanceRequirement) + XCTAssertEqual(actualRequest.rootOfTrust.source, expectedIssuanceRootOfTrust.source) + XCTAssert(actualRequest.rootOfTrust.verified) + + } } From ada5a2f7da36906427658274bb1b8d35ef8855fe Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Feb 2023 15:20:19 -0800 Subject: [PATCH 108/373] Update OpenIdURLRequestResolverTests.swift --- .../Requests/Resolvers/OpenIdURLRequestResolverTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift index d9c1046a..42cac768 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift @@ -113,7 +113,7 @@ class OpenIdURLRequestResolverTests: XCTestCase { // Arrange let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: Mapper()) - let mockHandler = OpenIdRequestHandler(configuration: configuration) + let mockHandler = OpenIdRequestHandler(configuration: configuration, contractResolver: MockContractResolver()) let resolver = OpenIdURLRequestResolver(openIdResolver: MockOpenIdForVCResolver(), configuration: configuration) // Act From bc37bfa08f61bc4bd4985772a0c5353ec1317b5a Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Feb 2023 15:23:27 -0800 Subject: [PATCH 109/373] Add period to comment. --- .../Extensions/Services/IssuanceService+Contract.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+Contract.swift b/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+Contract.swift index db8bb130..1e5cc290 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+Contract.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+Contract.swift @@ -11,7 +11,7 @@ import VCServices */ extension IssuanceService: ContractResolver { - /// Fetches and validates the issuance request + /// Fetches and validates the issuance request. func getRequest(url: String) async throws -> any RawContract { return try await AsyncWrapper().wrap { () in self.getRequest(usingUrl: url) From 3ff9f6b366475d079e908ce1566366360c095b54 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 14 Feb 2023 10:47:42 -0800 Subject: [PATCH 110/373] Create AttestationsDescriptor mapper --- .../WalletLibrary.xcodeproj/project.pbxproj | 12 +++++++ .../AttestationsDescriptor+Mappable.swift | 36 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+Mappable.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 3fa04c7a..f8805e37 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -56,6 +56,7 @@ 559BD3AB299ADEFF00BD61AC /* ContractIssuanceRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3AA299ADEFF00BD61AC /* ContractIssuanceRequest.swift */; }; 559BD3AD299AE6C000BD61AC /* IssuanceRequest+RawContract.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3AC299AE6C000BD61AC /* IssuanceRequest+RawContract.swift */; }; 559BD3AF299AF42C00BD61AC /* MockContractResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3AE299AF42C00BD61AC /* MockContractResolver.swift */; }; + 559BD3B2299B0A0400BD61AC /* AttestationsDescriptor+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3B1299B0A0400BD61AC /* AttestationsDescriptor+Mappable.swift */; }; 55A81BCA2991AB82002C259A /* RequestResolving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BC92991AB82002C259A /* RequestResolving.swift */; }; 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE02991AB96002C259A /* RequestHandling.swift */; }; 55A81BE52991B2E5002C259A /* RequestResolverFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */; }; @@ -316,6 +317,7 @@ 559BD3AA299ADEFF00BD61AC /* ContractIssuanceRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContractIssuanceRequest.swift; sourceTree = ""; }; 559BD3AC299AE6C000BD61AC /* IssuanceRequest+RawContract.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceRequest+RawContract.swift"; sourceTree = ""; }; 559BD3AE299AF42C00BD61AC /* MockContractResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockContractResolver.swift; sourceTree = ""; }; + 559BD3B1299B0A0400BD61AC /* AttestationsDescriptor+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttestationsDescriptor+Mappable.swift"; sourceTree = ""; }; 55A81BC92991AB82002C259A /* RequestResolving.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolving.swift; sourceTree = ""; }; 55A81BE02991AB96002C259A /* RequestHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestHandling.swift; sourceTree = ""; }; 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolverFactory.swift; sourceTree = ""; }; @@ -566,6 +568,7 @@ 5534E673294A1A00005D0765 /* VCSDK */ = { isa = PBXGroup; children = ( + 559BD3B0299B09F200BD61AC /* Issuance */, 5534E676294A1FA0005D0765 /* AccessTokenDescriptor+Mappable.swift */, 55E2F0872995743D0008010D /* LinkedDomainResult+Mappable.swift */, 5534E671294A19D5005D0765 /* IdTokenDescriptor+Mappable.swift */, @@ -623,6 +626,14 @@ path = Contract; sourceTree = ""; }; + 559BD3B0299B09F200BD61AC /* Issuance */ = { + isa = PBXGroup; + children = ( + 559BD3B1299B0A0400BD61AC /* AttestationsDescriptor+Mappable.swift */, + ); + path = Issuance; + sourceTree = ""; + }; 55A81BF02991BB13002C259A /* Requests */ = { isa = PBXGroup; children = ( @@ -1239,6 +1250,7 @@ 5534E669294A0C3F005D0765 /* Mapper.swift in Sources */, 550E320A298B0EA4004E7716 /* VerifiedIdClient.swift in Sources */, 559BD3AD299AE6C000BD61AC /* IssuanceRequest+RawContract.swift in Sources */, + 559BD3B2299B0A0400BD61AC /* AttestationsDescriptor+Mappable.swift in Sources */, 5534E64D294A0888005D0765 /* PresentationDescriptor+Mappable.swift in Sources */, 55E3370C293FD61E00CD2ED7 /* SelfAttestedClaimRequirement.swift in Sources */, 55E3376829437E3500CD2ED7 /* OpenIdForVCResolver.swift in Sources */, diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+Mappable.swift new file mode 100644 index 00000000..0e043ca3 --- /dev/null +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+Mappable.swift @@ -0,0 +1,36 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import VCEntities + +/** + * An extension of the VCEntities.AttestationsDescriptor class to be able + * to map AttestationsDescriptor to Requirement. + */ +extension VCEntities.AttestationsDescriptor: Mappable { + + /// Map the access token descriptor to access token requirement. + func map(using mapper: Mapping) throws -> Requirement { + let accessTokenRequirements: [Requirement] = try accessTokens?.compactMap { try mapper.map($0) } ?? [] + let idTokenRequirements: [Requirement] = try idTokens?.compactMap { try mapper.map($0) } ?? [] + let verifiedIdRequirements: [Requirement] = try presentations?.compactMap { try mapper.map($0) } ?? [] + + let selfAttestedClaimRequirements: [Requirement] = selfIssued?.claims?.compactMap { claim in + let isClaimRequired = claim.claimRequired ?? true + return SelfAttestedClaimRequirement(encrypted: false, required: isClaimRequired, claim: claim.claim) + } ?? [] + + let requirements: [Requirement] = accessTokenRequirements + idTokenRequirements + verifiedIdRequirements + selfAttestedClaimRequirements + + if requirements.count == 1, + let onlyRequirement = requirements.first { + return onlyRequirement + } + + return GroupRequirement(required: true, + requirements: requirements, + requirementOperator: .ALL) + } +} From e70c8733c8a264f24d51535f55f439a5cd331edc Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 14 Feb 2023 10:47:51 -0800 Subject: [PATCH 111/373] Update IssuanceRequest+RawContract.swift --- .../Entities/IssuanceRequest+RawContract.swift | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceRequest+RawContract.swift b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceRequest+RawContract.swift index 9eb32ee5..79b13f63 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceRequest+RawContract.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceRequest+RawContract.swift @@ -7,9 +7,22 @@ import VCEntities /** * An extension of the VCEntities.IssuanceRequest class. + * TODO: Update Style to include VerifiedIdStyle and more requester style attributes. */ extension VCEntities.IssuanceRequest: RawContract { func map(using mapper: Mapping) throws -> VerifiedIdRequestContent { - throw VerifiedIdClientError.TODO(message: "implement") + + let attestations = try getRequiredProperty(property: content.input.attestations, propertyName: "Contract Attestations") + let requirement = try mapper.map(attestations) + let rootOfTrust = try mapper.map(linkedDomainResult) + let issuerStyle = IssuerStyle(requester: content.display.card.issuedBy) + + return VerifiedIdRequestContent(style: issuerStyle, + requirement: requirement, + rootOfTrust: rootOfTrust) } } + +struct IssuerStyle: RequesterStyle { + var requester: String +} From 35c234c5e35b7d63641090febaa03c72440c450d Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 14 Feb 2023 10:48:41 -0800 Subject: [PATCH 112/373] GroupRequirement only supports .ALL --- .../Mappings/VCSDK/PresentationDefinition+Mappable.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift index 6687af91..05e189e3 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift @@ -33,9 +33,9 @@ extension VCEntities.PresentationDefinition: Mappable { try mapper.map($0) } - /// VC SDK only supports ANY operator for now. + /// VC SDK only supports ALL operator for now. return GroupRequirement(required: true, requirements: requirements, - requirementOperator: .ANY) + requirementOperator: .ALL) } } From 13f48d81d29855ceb42ba0e0482691f27ad3591b Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 14 Feb 2023 10:48:58 -0800 Subject: [PATCH 113/373] add comment about pin to OpenIdRequestHandler. --- .../WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift index 54561297..510720f0 100644 --- a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift +++ b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift @@ -47,6 +47,7 @@ struct OpenIdRequestHandler: RequestHandling { } let rawContract = try await contractResolver.getRequest(url: issuanceOption.url.absoluteString) + /// TODO: add logic here to add PinRequirement to ContractIssuanceRequest if it exists. let issuanceRequestContent = try configuration.mapper.map(rawContract) return ContractIssuanceRequest(content: issuanceRequestContent, configuration: configuration) } From 4f457679a1d3188fd90a859fbc7358e99997d85a Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 14 Feb 2023 10:49:11 -0800 Subject: [PATCH 114/373] add comment to verified id request content. --- .../WalletLibrary/Requests/VerifiedIdRequestContent.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift b/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift index d4e822ed..547f5696 100644 --- a/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift +++ b/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift @@ -6,6 +6,7 @@ /** * Contents in a Verified Id Request. * This object is used to map protocol specific requests to common request object. + * TODO: make this object extensibility to be able to add VerifiedIdStyle. */ struct VerifiedIdRequestContent { From 82075978dfda50b0fda874c15dbad7ce846d2f62 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 14 Feb 2023 13:53:56 -0800 Subject: [PATCH 115/373] Update PresentationDefinition mapping. --- .../PresentationDefinition+Mappable.swift | 7 ++++--- ...PresentationDefinition+MappableTests.swift | 20 +++++++++++++++++-- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift index 05e189e3..af684888 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift @@ -9,7 +9,7 @@ import VCEntities * Errors thrown in Presentation Definition Mappable extension. */ enum PresentationDefinitionMappingError: Error { - case nilInputDescriptors + case noPresentInputDescriptors } /** @@ -20,8 +20,9 @@ extension VCEntities.PresentationDefinition: Mappable { func map(using mapper: Mapping) throws -> Requirement { - guard let inputDescriptors = self.inputDescriptors else { - throw PresentationDefinitionMappingError.nilInputDescriptors + guard let inputDescriptors = self.inputDescriptors, + !inputDescriptors.isEmpty else { + throw PresentationDefinitionMappingError.noPresentInputDescriptors } if inputDescriptors.count == 1, diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDefinition+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDefinition+MappableTests.swift index 6b6f3613..530e4319 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDefinition+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDefinition+MappableTests.swift @@ -21,7 +21,23 @@ class PresentationDefinitionMappingTests: XCTestCase { XCTAssertThrowsError(try mockMapper.map(presentationDefinition)) { error in // Assert XCTAssert(error is PresentationDefinitionMappingError) - XCTAssertEqual(error as? PresentationDefinitionMappingError, .nilInputDescriptors) + XCTAssertEqual(error as? PresentationDefinitionMappingError, .noPresentInputDescriptors) + } + } + + func testMap_NoInputDescriptors_ThrowsError() throws { + + // Arrange + let mockMapper = MockMapper() + let presentationDefinition = PresentationDefinition(id: nil, + inputDescriptors: [], + issuance: nil) + + // Act + XCTAssertThrowsError(try mockMapper.map(presentationDefinition)) { error in + // Assert + XCTAssert(error is PresentationDefinitionMappingError) + XCTAssertEqual(error as? PresentationDefinitionMappingError, .noPresentInputDescriptors) } } @@ -114,6 +130,6 @@ class PresentationDefinitionMappingTests: XCTestCase { XCTAssertIdentical((actualResult as? GroupRequirement)?.requirements[1] as AnyObject, mockVerifiedIdRequirement as AnyObject) XCTAssertIdentical((actualResult as? GroupRequirement)?.requirements[2] as AnyObject, mockVerifiedIdRequirement as AnyObject) XCTAssert((actualResult as? GroupRequirement)?.required ?? false) - XCTAssertEqual((actualResult as? GroupRequirement)?.requirementOperator, .ANY) + XCTAssertEqual((actualResult as? GroupRequirement)?.requirementOperator, .ALL) } } From f973f15192eb5f32ce98b11c0ff4c51a79dadbe4 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 15 Feb 2023 08:13:13 -0800 Subject: [PATCH 116/373] Create SelfIssuedClaimsDescriptor mapping. --- .../WalletLibrary.xcodeproj/project.pbxproj | 10 +++-- .../AccessTokenDescriptor+Mappable.swift | 0 .../IdTokenDescriptor+Mappable.swift | 0 .../PresentationDescriptor+Mappable.swift | 0 .../Issuance/SelfIssuedClaimsDescriptor.swift | 44 +++++++++++++++++++ 5 files changed, 51 insertions(+), 3 deletions(-) rename WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/{ => Issuance}/AccessTokenDescriptor+Mappable.swift (100%) rename WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/{ => Issuance}/IdTokenDescriptor+Mappable.swift (100%) rename WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/{ => Issuance}/PresentationDescriptor+Mappable.swift (100%) create mode 100644 WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/SelfIssuedClaimsDescriptor.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index f8805e37..248d9c50 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -57,6 +57,7 @@ 559BD3AD299AE6C000BD61AC /* IssuanceRequest+RawContract.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3AC299AE6C000BD61AC /* IssuanceRequest+RawContract.swift */; }; 559BD3AF299AF42C00BD61AC /* MockContractResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3AE299AF42C00BD61AC /* MockContractResolver.swift */; }; 559BD3B2299B0A0400BD61AC /* AttestationsDescriptor+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3B1299B0A0400BD61AC /* AttestationsDescriptor+Mappable.swift */; }; + 559BD414299D37B100BD61AC /* SelfIssuedClaimsDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD413299D37B100BD61AC /* SelfIssuedClaimsDescriptor.swift */; }; 55A81BCA2991AB82002C259A /* RequestResolving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BC92991AB82002C259A /* RequestResolving.swift */; }; 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE02991AB96002C259A /* RequestHandling.swift */; }; 55A81BE52991B2E5002C259A /* RequestResolverFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */; }; @@ -318,6 +319,7 @@ 559BD3AC299AE6C000BD61AC /* IssuanceRequest+RawContract.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceRequest+RawContract.swift"; sourceTree = ""; }; 559BD3AE299AF42C00BD61AC /* MockContractResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockContractResolver.swift; sourceTree = ""; }; 559BD3B1299B0A0400BD61AC /* AttestationsDescriptor+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttestationsDescriptor+Mappable.swift"; sourceTree = ""; }; + 559BD413299D37B100BD61AC /* SelfIssuedClaimsDescriptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelfIssuedClaimsDescriptor.swift; sourceTree = ""; }; 55A81BC92991AB82002C259A /* RequestResolving.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolving.swift; sourceTree = ""; }; 55A81BE02991AB96002C259A /* RequestHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestHandling.swift; sourceTree = ""; }; 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolverFactory.swift; sourceTree = ""; }; @@ -569,11 +571,8 @@ isa = PBXGroup; children = ( 559BD3B0299B09F200BD61AC /* Issuance */, - 5534E676294A1FA0005D0765 /* AccessTokenDescriptor+Mappable.swift */, 55E2F0872995743D0008010D /* LinkedDomainResult+Mappable.swift */, - 5534E671294A19D5005D0765 /* IdTokenDescriptor+Mappable.swift */, 55E2F089299576350008010D /* PresentationDefinition+Mappable.swift */, - 5534E64C294A0888005D0765 /* PresentationDescriptor+Mappable.swift */, 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */, 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */, ); @@ -629,7 +628,11 @@ 559BD3B0299B09F200BD61AC /* Issuance */ = { isa = PBXGroup; children = ( + 5534E676294A1FA0005D0765 /* AccessTokenDescriptor+Mappable.swift */, 559BD3B1299B0A0400BD61AC /* AttestationsDescriptor+Mappable.swift */, + 5534E671294A19D5005D0765 /* IdTokenDescriptor+Mappable.swift */, + 5534E64C294A0888005D0765 /* PresentationDescriptor+Mappable.swift */, + 559BD413299D37B100BD61AC /* SelfIssuedClaimsDescriptor.swift */, ); path = Issuance; sourceTree = ""; @@ -1256,6 +1259,7 @@ 55E3376829437E3500CD2ED7 /* OpenIdForVCResolver.swift in Sources */, 55A81BE52991B2E5002C259A /* RequestResolverFactory.swift in Sources */, 55E2F086299573C30008010D /* OpenIdRequesterStyle.swift in Sources */, + 559BD414299D37B100BD61AC /* SelfIssuedClaimsDescriptor.swift in Sources */, 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */, 55E2F071299554110008010D /* LibraryConfiguration.swift in Sources */, 550E3209298B0EA4004E7716 /* VerifiedIdClientBuilder.swift in Sources */, diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/AccessTokenDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/AccessTokenDescriptor+Mappable.swift similarity index 100% rename from WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/AccessTokenDescriptor+Mappable.swift rename to WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/AccessTokenDescriptor+Mappable.swift diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/IdTokenDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IdTokenDescriptor+Mappable.swift similarity index 100% rename from WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/IdTokenDescriptor+Mappable.swift rename to WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IdTokenDescriptor+Mappable.swift diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+Mappable.swift similarity index 100% rename from WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDescriptor+Mappable.swift rename to WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+Mappable.swift diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/SelfIssuedClaimsDescriptor.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/SelfIssuedClaimsDescriptor.swift new file mode 100644 index 00000000..03a36e3c --- /dev/null +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/SelfIssuedClaimsDescriptor.swift @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import VCEntities + +/** + * An extension of the VCEntities.SelfIssuedClaimsDescriptor class to be able + * to map SelfIssuedClaimsDescriptor to Requirement. + */ +extension VCEntities.SelfIssuedClaimsDescriptor: Mappable { + + func map(using mapper: Mapping) throws -> Requirement? { + + guard let claims = claims, !claims.isEmpty else { + return nil + } + + let areSelfAttestedClaimsRequired = selfIssuedRequired ?? false + + if claims.count == 1, + let onlyClaim = claims.first { + + let isIndividualClaimRequired = onlyClaim.claimRequired ?? false + let required = areSelfAttestedClaimsRequired || isIndividualClaimRequired + return SelfAttestedClaimRequirement(encrypted: false, + required: required, + claim: onlyClaim.claim) + } + + let requirements = claims.compactMap { claim in + + let isIndividualClaimRequired = claim.claimRequired ?? false + return SelfAttestedClaimRequirement(encrypted: false, + required: isIndividualClaimRequired, + claim: claim.claim) + } + + return GroupRequirement(required: areSelfAttestedClaimsRequired, + requirements: requirements, + requirementOperator: .ALL) + } +} From c8af05062f08a9e6d5dc1509d45dd87f66184d47 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 15 Feb 2023 08:13:19 -0800 Subject: [PATCH 117/373] Update AttestationsDescriptor+Mappable.swift --- .../AttestationsDescriptor+Mappable.swift | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+Mappable.swift index 0e043ca3..9a03942a 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+Mappable.swift @@ -13,16 +13,22 @@ extension VCEntities.AttestationsDescriptor: Mappable { /// Map the access token descriptor to access token requirement. func map(using mapper: Mapping) throws -> Requirement { + + var requirements: [Requirement] = [] + let accessTokenRequirements: [Requirement] = try accessTokens?.compactMap { try mapper.map($0) } ?? [] + requirements.append(contentsOf: accessTokenRequirements) + let idTokenRequirements: [Requirement] = try idTokens?.compactMap { try mapper.map($0) } ?? [] - let verifiedIdRequirements: [Requirement] = try presentations?.compactMap { try mapper.map($0) } ?? [] + requirements.append(contentsOf: idTokenRequirements) - let selfAttestedClaimRequirements: [Requirement] = selfIssued?.claims?.compactMap { claim in - let isClaimRequired = claim.claimRequired ?? true - return SelfAttestedClaimRequirement(encrypted: false, required: isClaimRequired, claim: claim.claim) - } ?? [] + let verifiedIdRequirements: [Requirement] = try presentations?.compactMap { try mapper.map($0) } ?? [] + requirements.append(contentsOf: verifiedIdRequirements) - let requirements: [Requirement] = accessTokenRequirements + idTokenRequirements + verifiedIdRequirements + selfAttestedClaimRequirements + if let selfIssued = selfIssued, + let selfAttestedClaimRequirements = try mapper.map(selfIssued) { + requirements.append(selfAttestedClaimRequirements) + } if requirements.count == 1, let onlyRequirement = requirements.first { From c089a37062778350d59b221729d060746f0cd705 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 15 Feb 2023 08:15:57 -0800 Subject: [PATCH 118/373] Update AttestationsDescriptor+Mappable.swift --- .../VCSDK/Issuance/AttestationsDescriptor+Mappable.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+Mappable.swift index 9a03942a..772a7dcc 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+Mappable.swift @@ -11,7 +11,6 @@ import VCEntities */ extension VCEntities.AttestationsDescriptor: Mappable { - /// Map the access token descriptor to access token requirement. func map(using mapper: Mapping) throws -> Requirement { var requirements: [Requirement] = [] From bd9ad6371e9a49c1b9ec2cd86ed76c70efa4e9d3 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 15 Feb 2023 09:10:11 -0800 Subject: [PATCH 119/373] Complete Test Cases for Attestations Descriptor Mapping. --- .../WalletLibrary.xcodeproj/project.pbxproj | 72 +++- ...AttestationsDescriptor+MappableTests.swift | 392 ++++++++++++++++++ 2 files changed, 448 insertions(+), 16 deletions(-) create mode 100644 WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+MappableTests.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 248d9c50..1ba41dd4 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -57,7 +57,11 @@ 559BD3AD299AE6C000BD61AC /* IssuanceRequest+RawContract.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3AC299AE6C000BD61AC /* IssuanceRequest+RawContract.swift */; }; 559BD3AF299AF42C00BD61AC /* MockContractResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3AE299AF42C00BD61AC /* MockContractResolver.swift */; }; 559BD3B2299B0A0400BD61AC /* AttestationsDescriptor+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3B1299B0A0400BD61AC /* AttestationsDescriptor+Mappable.swift */; }; - 559BD414299D37B100BD61AC /* SelfIssuedClaimsDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD413299D37B100BD61AC /* SelfIssuedClaimsDescriptor.swift */; }; + 559BD414299D37B100BD61AC /* SelfIssuedClaimsDescriptor+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD413299D37B100BD61AC /* SelfIssuedClaimsDescriptor+Mappable.swift */; }; + 559BD453299D3DD800BD61AC /* IssuanceRequest+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD452299D3DD800BD61AC /* IssuanceRequest+Mappable.swift */; }; + 559BD455299D3E4800BD61AC /* IssuanceRequest+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD454299D3E4800BD61AC /* IssuanceRequest+MappableTests.swift */; }; + 559BD457299D3E6000BD61AC /* AttestationsDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD456299D3E6000BD61AC /* AttestationsDescriptor+MappableTests.swift */; }; + 559BD459299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD458299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift */; }; 55A81BCA2991AB82002C259A /* RequestResolving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BC92991AB82002C259A /* RequestResolving.swift */; }; 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE02991AB96002C259A /* RequestHandling.swift */; }; 55A81BE52991B2E5002C259A /* RequestResolverFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */; }; @@ -319,7 +323,11 @@ 559BD3AC299AE6C000BD61AC /* IssuanceRequest+RawContract.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceRequest+RawContract.swift"; sourceTree = ""; }; 559BD3AE299AF42C00BD61AC /* MockContractResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockContractResolver.swift; sourceTree = ""; }; 559BD3B1299B0A0400BD61AC /* AttestationsDescriptor+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttestationsDescriptor+Mappable.swift"; sourceTree = ""; }; - 559BD413299D37B100BD61AC /* SelfIssuedClaimsDescriptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelfIssuedClaimsDescriptor.swift; sourceTree = ""; }; + 559BD413299D37B100BD61AC /* SelfIssuedClaimsDescriptor+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SelfIssuedClaimsDescriptor+Mappable.swift"; sourceTree = ""; }; + 559BD452299D3DD800BD61AC /* IssuanceRequest+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceRequest+Mappable.swift"; sourceTree = ""; }; + 559BD454299D3E4800BD61AC /* IssuanceRequest+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceRequest+MappableTests.swift"; sourceTree = ""; }; + 559BD456299D3E6000BD61AC /* AttestationsDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttestationsDescriptor+MappableTests.swift"; sourceTree = ""; }; + 559BD458299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SelfIssuedClaimsDescriptor+MappableTests.swift"; sourceTree = ""; }; 55A81BC92991AB82002C259A /* RequestResolving.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolving.swift; sourceTree = ""; }; 55A81BE02991AB96002C259A /* RequestHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestHandling.swift; sourceTree = ""; }; 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolverFactory.swift; sourceTree = ""; }; @@ -570,11 +578,9 @@ 5534E673294A1A00005D0765 /* VCSDK */ = { isa = PBXGroup; children = ( + 559BD451299D3D6F00BD61AC /* Presentation */, 559BD3B0299B09F200BD61AC /* Issuance */, 55E2F0872995743D0008010D /* LinkedDomainResult+Mappable.swift */, - 55E2F089299576350008010D /* PresentationDefinition+Mappable.swift */, - 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */, - 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */, ); path = VCSDK; sourceTree = ""; @@ -582,28 +588,24 @@ 5534E67C294A392F005D0765 /* Extensions */ = { isa = PBXGroup; children = ( - 5534E67D294A3937005D0765 /* Translations */, + 5534E67D294A3937005D0765 /* Mappings */, ); path = Extensions; sourceTree = ""; }; - 5534E67D294A3937005D0765 /* Translations */ = { + 5534E67D294A3937005D0765 /* Mappings */ = { isa = PBXGroup; children = ( 5534E67E294A3943005D0765 /* VCSDK */, ); - path = Translations; + path = Mappings; sourceTree = ""; }; 5534E67E294A3943005D0765 /* VCSDK */ = { isa = PBXGroup; children = ( - 5534E68B294A8B8F005D0765 /* AccessTokenDescriptor+MappableTests.swift */, - 5534E687294A5F16005D0765 /* IdTokenDescriptor+MappableTests.swift */, - 559BD3042995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift */, - 5534E6AC294AAFA6005D0765 /* PresentationDescriptor+MappableTests.swift */, - 559BD3062995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift */, - 559BD3082995ABE800BD61AC /* PresentationRequest+MappableTests.swift */, + 559BD450299D3D4C00BD61AC /* Presentation */, + 559BD44F299D3D4100BD61AC /* Issuance */, 559BD30A2995B1B500BD61AC /* LinkedDomainResult+MappableTests.swift */, ); path = VCSDK; @@ -631,12 +633,46 @@ 5534E676294A1FA0005D0765 /* AccessTokenDescriptor+Mappable.swift */, 559BD3B1299B0A0400BD61AC /* AttestationsDescriptor+Mappable.swift */, 5534E671294A19D5005D0765 /* IdTokenDescriptor+Mappable.swift */, + 559BD452299D3DD800BD61AC /* IssuanceRequest+Mappable.swift */, 5534E64C294A0888005D0765 /* PresentationDescriptor+Mappable.swift */, - 559BD413299D37B100BD61AC /* SelfIssuedClaimsDescriptor.swift */, + 559BD413299D37B100BD61AC /* SelfIssuedClaimsDescriptor+Mappable.swift */, ); path = Issuance; sourceTree = ""; }; + 559BD44F299D3D4100BD61AC /* Issuance */ = { + isa = PBXGroup; + children = ( + 5534E68B294A8B8F005D0765 /* AccessTokenDescriptor+MappableTests.swift */, + 559BD456299D3E6000BD61AC /* AttestationsDescriptor+MappableTests.swift */, + 5534E687294A5F16005D0765 /* IdTokenDescriptor+MappableTests.swift */, + 559BD454299D3E4800BD61AC /* IssuanceRequest+MappableTests.swift */, + 5534E6AC294AAFA6005D0765 /* PresentationDescriptor+MappableTests.swift */, + 559BD458299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift */, + ); + path = Issuance; + sourceTree = ""; + }; + 559BD450299D3D4C00BD61AC /* Presentation */ = { + isa = PBXGroup; + children = ( + 559BD3042995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift */, + 559BD3062995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift */, + 559BD3082995ABE800BD61AC /* PresentationRequest+MappableTests.swift */, + ); + path = Presentation; + sourceTree = ""; + }; + 559BD451299D3D6F00BD61AC /* Presentation */ = { + isa = PBXGroup; + children = ( + 55E2F089299576350008010D /* PresentationDefinition+Mappable.swift */, + 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */, + 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */, + ); + path = Presentation; + sourceTree = ""; + }; 55A81BF02991BB13002C259A /* Requests */ = { isa = PBXGroup; children = ( @@ -1241,6 +1277,7 @@ 55E336D6293FA78C00CD2ED7 /* VerifiedIdClaim.swift in Sources */, 55E337652943749600CD2ED7 /* AsyncWrapper.swift in Sources */, 550E3214298B11A5004E7716 /* Requirement.swift in Sources */, + 559BD453299D3DD800BD61AC /* IssuanceRequest+Mappable.swift in Sources */, 550A9173299311A10014D030 /* VerifiedIdRequestURL.swift in Sources */, 550E3210298B113C004E7716 /* VerifiedIdRequest.swift in Sources */, 559BD3AB299ADEFF00BD61AC /* ContractIssuanceRequest.swift in Sources */, @@ -1259,7 +1296,7 @@ 55E3376829437E3500CD2ED7 /* OpenIdForVCResolver.swift in Sources */, 55A81BE52991B2E5002C259A /* RequestResolverFactory.swift in Sources */, 55E2F086299573C30008010D /* OpenIdRequesterStyle.swift in Sources */, - 559BD414299D37B100BD61AC /* SelfIssuedClaimsDescriptor.swift in Sources */, + 559BD414299D37B100BD61AC /* SelfIssuedClaimsDescriptor+Mappable.swift in Sources */, 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */, 55E2F071299554110008010D /* LibraryConfiguration.swift in Sources */, 550E3209298B0EA4004E7716 /* VerifiedIdClientBuilder.swift in Sources */, @@ -1286,6 +1323,7 @@ 55E2F06F299553B40008010D /* OpenIdRequestHandlerTests.swift in Sources */, 55E3376B29478C5000CD2ED7 /* AsyncWrapperTests.swift in Sources */, 5534E6AD294AAFA6005D0765 /* PresentationDescriptor+MappableTests.swift in Sources */, + 559BD455299D3E4800BD61AC /* IssuanceRequest+MappableTests.swift in Sources */, 55A81C132991C829002C259A /* MockHandler.swift in Sources */, 559BD3072995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift in Sources */, 55A81C072991BB2C002C259A /* RequestResolverFactoryTests.swift in Sources */, @@ -1297,6 +1335,7 @@ 55A81C0D2991BF86002C259A /* MockInput.swift in Sources */, 559BD3AF299AF42C00BD61AC /* MockContractResolver.swift in Sources */, 550A919829940DCE0014D030 /* MockOpenIdforVCResolver.swift in Sources */, + 559BD459299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift in Sources */, 559BD3092995ABE800BD61AC /* PresentationRequest+MappableTests.swift in Sources */, 550A91A0299412790014D030 /* MockOpenIdRawRequest.swift in Sources */, 559BD30F2996C1C500BD61AC /* MockLogConsumer.swift in Sources */, @@ -1304,6 +1343,7 @@ 5534E68C294A8B8F005D0765 /* AccessTokenDescriptor+MappableTests.swift in Sources */, 55E2F077299555280008010D /* MockRequesterStyle.swift in Sources */, 552E50A0293E6AB200868F47 /* VerifiedIdClientTests.swift in Sources */, + 559BD457299D3E6000BD61AC /* AttestationsDescriptor+MappableTests.swift in Sources */, 5534E688294A5F16005D0765 /* IdTokenDescriptor+MappableTests.swift in Sources */, 559BD3112996D3CE00BD61AC /* MockVerifiedIdRequest.swift in Sources */, 550A913D29930D970014D030 /* MockRawRequest.swift in Sources */, diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+MappableTests.swift new file mode 100644 index 00000000..2b127ef9 --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+MappableTests.swift @@ -0,0 +1,392 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import XCTest +import VCEntities +@testable import WalletLibrary + +class AttestationsDescriptorMappingTests: XCTestCase { + + enum MockError: Error { + case expectedToBeUnableToMapAccessTokenDescriptor + case expectedToBeUnableToMapIdTokenDescriptor + case expectedToBeUnableToMapPresentationDescriptor + case expectedToBeUnableToMapSelfIssuedClaimsDescriptor + } + + func testMap_WithOnlyOneAccessTokenRequirement_ReturnsRequirement() throws { + // Arrange + let expectedAccessTokenRequirement = AccessTokenRequirement(encrypted: false, + required: true, + configuration: "mock configuration", + clientId: "mock client id", + resourceId: "mock resource id", + scope: "mock scope") + let mockAccessTokenDescriptor = AccessTokenDescriptor() + let attestations = AttestationsDescriptor(accessTokens: [mockAccessTokenDescriptor]) + + func mockResults(objectToBeMapped: Any) throws -> Any? { + if objectToBeMapped is AccessTokenDescriptor { + return expectedAccessTokenRequirement + } + + return nil + } + + let mapper = MockMapper(mockResults: mockResults) + + // Act + let actualResult = try mapper.map(attestations) + + // Assert + XCTAssert(actualResult is AccessTokenRequirement) + XCTAssertIdentical(actualResult as AnyObject, expectedAccessTokenRequirement as AnyObject) + } + + func testMap_WhenAccessTokenRequirementMappingThrows_ThrowsError() throws { + // Arrange + let mockAccessTokenDescriptor = AccessTokenDescriptor() + let attestations = AttestationsDescriptor(accessTokens: [mockAccessTokenDescriptor]) + + func mockResults(objectToBeMapped: Any) throws -> Any? { + if objectToBeMapped is AccessTokenDescriptor { + throw MockError.expectedToBeUnableToMapAccessTokenDescriptor + } + + return nil + } + + let mapper = MockMapper(mockResults: mockResults) + + // Act + XCTAssertThrowsError(try mapper.map(attestations)) { error in + // Assert + XCTAssert(error is MockError) + XCTAssertEqual(error as? MockError, .expectedToBeUnableToMapAccessTokenDescriptor) + } + } + + func testMap_WithOnlyOneIdTokenRequirement_ReturnsRequirement() throws { + // Arrange + let expectedIdTokenRequirement = IdTokenRequirement(encrypted: false, + required: true, + configuration: URL(string: "https://test.com")!, + clientId: "mock client id", + redirectUri: "mock redirect uri", + scope: "mock scope") + let mockIdTokenDescriptor = IdTokenDescriptor(claims: [], configuration: "mock config", clientID: "mock client id") + let attestations = AttestationsDescriptor(idTokens: [mockIdTokenDescriptor]) + + func mockResults(objectToBeMapped: Any) throws -> Any? { + if objectToBeMapped is IdTokenDescriptor { + return expectedIdTokenRequirement + } + + return nil + } + + let mapper = MockMapper(mockResults: mockResults) + + // Act + let actualResult = try mapper.map(attestations) + + // Assert + XCTAssert(actualResult is IdTokenRequirement) + XCTAssertIdentical(actualResult as AnyObject, expectedIdTokenRequirement as AnyObject) + } + + func testMap_WhenIdTokenRequirementMappingThrows_ThrowsError() throws { + // Arrange + let mockIdTokenDescriptor = IdTokenDescriptor(claims: [], configuration: "mock config", clientID: "mock client id") + let attestations = AttestationsDescriptor(idTokens: [mockIdTokenDescriptor]) + + func mockResults(objectToBeMapped: Any) throws -> Any? { + if objectToBeMapped is IdTokenDescriptor { + throw MockError.expectedToBeUnableToMapIdTokenDescriptor + } + + return nil + } + + let mapper = MockMapper(mockResults: mockResults) + + // Act + XCTAssertThrowsError(try mapper.map(attestations)) { error in + // Assert + XCTAssert(error is MockError) + XCTAssertEqual(error as? MockError, .expectedToBeUnableToMapIdTokenDescriptor) + } + } + + func testMap_WithOnlyOneVerifiedIdRequirement_ReturnsRequirement() throws { + // Arrange + let expectedVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, + required: true, + types: [], + purpose: nil, + issuanceOptions: []) + let mockPresentationDescriptor = PresentationDescriptor(claims: [], + credentialType: "mock type") + let attestations = AttestationsDescriptor(presentations: [mockPresentationDescriptor]) + + func mockResults(objectToBeMapped: Any) throws -> Any? { + if objectToBeMapped is PresentationDescriptor { + return expectedVerifiedIdRequirement + } + + return nil + } + + let mapper = MockMapper(mockResults: mockResults) + + // Act + let actualResult = try mapper.map(attestations) + + // Assert + XCTAssert(actualResult is VerifiedIdRequirement) + XCTAssertIdentical(actualResult as AnyObject, expectedVerifiedIdRequirement as AnyObject) + } + + func testMap_WhenVerifiedIdRequirementMappingThrows_ThrowsError() throws { + // Arrange + let mockPresentationDescriptor = PresentationDescriptor(claims: [], + credentialType: "mock type") + let attestations = AttestationsDescriptor(presentations: [mockPresentationDescriptor]) + + func mockResults(objectToBeMapped: Any) throws -> Any? { + if objectToBeMapped is PresentationDescriptor { + throw MockError.expectedToBeUnableToMapPresentationDescriptor + } + + return nil + } + + let mapper = MockMapper(mockResults: mockResults) + + // Act + XCTAssertThrowsError(try mapper.map(attestations)) { error in + // Assert + XCTAssert(error is MockError) + XCTAssertEqual(error as? MockError, .expectedToBeUnableToMapPresentationDescriptor) + } + } + + func testMap_WithOnlyOneSelfAttestedRequirement_ReturnsRequirement() throws { + // Arrange + let expectedSelfAttestedClaimRequirement = SelfAttestedClaimRequirement(encrypted: false, + required: true, + claim: "mock claim") + let mockSelfIssuedClaimsDescriptor = SelfIssuedClaimsDescriptor() + let attestations = AttestationsDescriptor(selfIssued: mockSelfIssuedClaimsDescriptor) + + func mockResults(objectToBeMapped: Any) throws -> Any? { + if objectToBeMapped is SelfIssuedClaimsDescriptor { + return expectedSelfAttestedClaimRequirement + } + + return nil + } + + let mapper = MockMapper(mockResults: mockResults) + + // Act + let actualResult = try mapper.map(attestations) + + // Assert + XCTAssert(actualResult is SelfAttestedClaimRequirement) + XCTAssertIdentical(actualResult as AnyObject, expectedSelfAttestedClaimRequirement as AnyObject) + } + + func testMap_WhenSelfAttestedRequirementMappingThrows_ThrowsError() throws { + // Arrange + let mockSelfIssuedClaimsDescriptor = SelfIssuedClaimsDescriptor() + let attestations = AttestationsDescriptor(selfIssued: mockSelfIssuedClaimsDescriptor) + + func mockResults(objectToBeMapped: Any) throws -> Any? { + if objectToBeMapped is SelfIssuedClaimsDescriptor { + throw MockError.expectedToBeUnableToMapSelfIssuedClaimsDescriptor + } + + return nil + } + + let mapper = MockMapper(mockResults: mockResults) + + // Act + XCTAssertThrowsError(try mapper.map(attestations)) { error in + // Assert + XCTAssert(error is MockError) + XCTAssertEqual(error as? MockError, .expectedToBeUnableToMapSelfIssuedClaimsDescriptor) + } + } + + func testMap_WithDifferentMultipleRequirements_ReturnsGroupRequirement() throws { + // Arrange + let expectedSelfAttestedClaimRequirement = SelfAttestedClaimRequirement(encrypted: false, + required: true, + claim: "mock claim") + let expectedVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, + required: true, + types: [], + purpose: nil, + issuanceOptions: []) + let expectedIdTokenRequirement = IdTokenRequirement(encrypted: false, + required: true, + configuration: URL(string: "https://test.com")!, + clientId: "mock client id", + redirectUri: "mock redirect uri", + scope: "mock scope") + let expectedAccessTokenRequirement = AccessTokenRequirement(encrypted: false, + required: true, + configuration: "mock configuration", + clientId: "mock client id", + resourceId: "mock resource id", + scope: "mock scope") + + let mockSelfIssuedClaimsDescriptor = SelfIssuedClaimsDescriptor() + let mockPresentationDescriptor = PresentationDescriptor(claims: [], + credentialType: "mock type") + let mockIdTokenDescriptor = IdTokenDescriptor(claims: [], configuration: "mock config", clientID: "mock client id") + let mockAccessTokenDescriptor = AccessTokenDescriptor() + + let attestations = AttestationsDescriptor(selfIssued: mockSelfIssuedClaimsDescriptor, + presentations: [mockPresentationDescriptor], + idTokens: [mockIdTokenDescriptor], + accessTokens: [mockAccessTokenDescriptor]) + + func mockResults(objectToBeMapped: Any) throws -> Any? { + if objectToBeMapped is SelfIssuedClaimsDescriptor { + return expectedSelfAttestedClaimRequirement + } + + if objectToBeMapped is PresentationDescriptor { + return expectedVerifiedIdRequirement + } + + if objectToBeMapped is IdTokenDescriptor { + return expectedIdTokenRequirement + } + + if objectToBeMapped is AccessTokenDescriptor { + return expectedAccessTokenRequirement + } + + return nil + } + + let mapper = MockMapper(mockResults: mockResults) + + // Act + let actualResult = try mapper.map(attestations) + + // Assert + XCTAssert(actualResult is GroupRequirement) + XCTAssertEqual((actualResult as? GroupRequirement)?.requirements.count, 4) + XCTAssertIdentical((actualResult as? GroupRequirement)?.requirements.first as AnyObject, expectedAccessTokenRequirement) + XCTAssertIdentical((actualResult as? GroupRequirement)?.requirements[1] as AnyObject, expectedIdTokenRequirement) + XCTAssertIdentical((actualResult as? GroupRequirement)?.requirements[2] as AnyObject, expectedVerifiedIdRequirement) + XCTAssertIdentical((actualResult as? GroupRequirement)?.requirements[3] as AnyObject, expectedSelfAttestedClaimRequirement) + } + + func testMap_WithTwoVerifiedIdRequirementAndOneEveryOtherRequirement_ReturnsGroupRequirement() throws { + // Arrange + let expectedSelfAttestedClaimRequirement = SelfAttestedClaimRequirement(encrypted: false, + required: true, + claim: "mock claim") + let expectedVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, + required: true, + types: [], + purpose: nil, + issuanceOptions: []) + let expectedIdTokenRequirement = IdTokenRequirement(encrypted: false, + required: true, + configuration: URL(string: "https://test.com")!, + clientId: "mock client id", + redirectUri: "mock redirect uri", + scope: "mock scope") + let expectedAccessTokenRequirement = AccessTokenRequirement(encrypted: false, + required: true, + configuration: "mock configuration", + clientId: "mock client id", + resourceId: "mock resource id", + scope: "mock scope") + + let mockSelfIssuedClaimsDescriptor = SelfIssuedClaimsDescriptor() + let mockPresentationDescriptor = PresentationDescriptor(claims: [], + credentialType: "mock type") + let mockIdTokenDescriptor = IdTokenDescriptor(claims: [], configuration: "mock config", clientID: "mock client id") + let mockAccessTokenDescriptor = AccessTokenDescriptor() + + let attestations = AttestationsDescriptor(selfIssued: mockSelfIssuedClaimsDescriptor, + presentations: [mockPresentationDescriptor, mockPresentationDescriptor], + idTokens: [mockIdTokenDescriptor], + accessTokens: [mockAccessTokenDescriptor]) + + func mockResults(objectToBeMapped: Any) throws -> Any? { + if objectToBeMapped is SelfIssuedClaimsDescriptor { + return expectedSelfAttestedClaimRequirement + } + + if objectToBeMapped is PresentationDescriptor { + return expectedVerifiedIdRequirement + } + + if objectToBeMapped is IdTokenDescriptor { + return expectedIdTokenRequirement + } + + if objectToBeMapped is AccessTokenDescriptor { + return expectedAccessTokenRequirement + } + + return nil + } + + let mapper = MockMapper(mockResults: mockResults) + + // Act + let actualResult = try mapper.map(attestations) + + // Assert + XCTAssert(actualResult is GroupRequirement) + XCTAssertEqual((actualResult as? GroupRequirement)?.requirements.count, 5) + XCTAssertIdentical((actualResult as? GroupRequirement)?.requirements.first as AnyObject, expectedAccessTokenRequirement) + XCTAssertIdentical((actualResult as? GroupRequirement)?.requirements[1] as AnyObject, expectedIdTokenRequirement) + XCTAssertIdentical((actualResult as? GroupRequirement)?.requirements[2] as AnyObject, expectedVerifiedIdRequirement) + XCTAssertIdentical((actualResult as? GroupRequirement)?.requirements[3] as AnyObject, expectedVerifiedIdRequirement) + XCTAssertIdentical((actualResult as? GroupRequirement)?.requirements[4] as AnyObject, expectedSelfAttestedClaimRequirement) + } + + func testMap_WhenAllNilRequirementInAttestations_ThrowsError() throws { + // Arrange + let attestations = AttestationsDescriptor() + + let mapper = MockMapper() + + // Act + XCTAssertThrowsError(try mapper.map(attestations)) { error in + // Assert + XCTAssert(error is AttestationsDescriptorMappingError) + XCTAssertEqual(error as? AttestationsDescriptorMappingError, .noRequirementsPresent) + } + } + + func testMap_WhenEmptyRequirementInAttestations_ThrowsError() throws { + // Arrange + let attestations = AttestationsDescriptor(selfIssued: nil, + presentations: [], + idTokens: [], + accessTokens: []) + + let mapper = MockMapper() + + // Act + XCTAssertThrowsError(try mapper.map(attestations)) { error in + // Assert + XCTAssert(error is AttestationsDescriptorMappingError) + XCTAssertEqual(error as? AttestationsDescriptorMappingError, .noRequirementsPresent) + } + } +} From a8cf9315973e92e525653eab6ce768337bbba6e7 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 15 Feb 2023 09:10:34 -0800 Subject: [PATCH 120/373] Move presentation and issuance mappings into subfolders. --- .../Issuance/IssuanceRequest+Mappable.swift | 25 +++++++++++++++++++ ...SelfIssuedClaimsDescriptor+Mappable.swift} | 0 .../PresentationDefinition+Mappable.swift | 0 ...PresentationInputDescriptor+Mappable.swift | 0 .../PresentationRequest+Mappable.swift | 0 .../AccessTokenDescriptor+MappableTests.swift | 0 .../IdTokenDescriptor+MappableTests.swift | 0 .../IssuanceRequest+MappableTests.swift | 8 ++++++ ...PresentationDescriptor+MappableTests.swift | 0 ...IssuedClaimsDescriptor+MappableTests.swift | 8 ++++++ .../LinkedDomainResult+MappableTests.swift | 0 ...PresentationDefinition+MappableTests.swift | 0 ...ntationInputDescriptor+MappableTests.swift | 0 .../PresentationRequest+MappableTests.swift | 0 14 files changed, 41 insertions(+) create mode 100644 WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+Mappable.swift rename WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/{SelfIssuedClaimsDescriptor.swift => SelfIssuedClaimsDescriptor+Mappable.swift} (100%) rename WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/{ => Presentation}/PresentationDefinition+Mappable.swift (100%) rename WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/{ => Presentation}/PresentationInputDescriptor+Mappable.swift (100%) rename WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/{ => Presentation}/PresentationRequest+Mappable.swift (100%) rename WalletLibrary/WalletLibraryTests/Extensions/{Translations/VCSDK => Mappings/VCSDK/Issuance}/AccessTokenDescriptor+MappableTests.swift (100%) rename WalletLibrary/WalletLibraryTests/Extensions/{Translations/VCSDK => Mappings/VCSDK/Issuance}/IdTokenDescriptor+MappableTests.swift (100%) create mode 100644 WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+MappableTests.swift rename WalletLibrary/WalletLibraryTests/Extensions/{Translations/VCSDK => Mappings/VCSDK/Issuance}/PresentationDescriptor+MappableTests.swift (100%) create mode 100644 WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/SelfIssuedClaimsDescriptor+MappableTests.swift rename WalletLibrary/WalletLibraryTests/Extensions/{Translations => Mappings}/VCSDK/LinkedDomainResult+MappableTests.swift (100%) rename WalletLibrary/WalletLibraryTests/Extensions/{Translations/VCSDK => Mappings/VCSDK/Presentation}/PresentationDefinition+MappableTests.swift (100%) rename WalletLibrary/WalletLibraryTests/Extensions/{Translations/VCSDK => Mappings/VCSDK/Presentation}/PresentationInputDescriptor+MappableTests.swift (100%) rename WalletLibrary/WalletLibraryTests/Extensions/{Translations/VCSDK => Mappings/VCSDK/Presentation}/PresentationRequest+MappableTests.swift (100%) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+Mappable.swift new file mode 100644 index 00000000..7014ecfd --- /dev/null +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+Mappable.swift @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import VCEntities + +/** + * An extension of the VCEntities.IssuanceRequest class. + * TODO: Update Style to include VerifiedIdStyle and more requester style attributes. + */ +extension VCEntities.IssuanceRequest: Mappable { + func map(using mapper: Mapping) throws -> VerifiedIdRequestContent { + + let attestations = try getRequiredProperty(property: content.input.attestations, + propertyName: "Contract Attestations") + let requirement = try mapper.map(attestations) + let rootOfTrust = try mapper.map(linkedDomainResult) + let issuerStyle = IssuerStyle(requester: content.display.card.issuedBy) + + return VerifiedIdRequestContent(style: issuerStyle, + requirement: requirement, + rootOfTrust: rootOfTrust) + } +} diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/SelfIssuedClaimsDescriptor.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/SelfIssuedClaimsDescriptor+Mappable.swift similarity index 100% rename from WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/SelfIssuedClaimsDescriptor.swift rename to WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/SelfIssuedClaimsDescriptor+Mappable.swift diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+Mappable.swift similarity index 100% rename from WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationDefinition+Mappable.swift rename to WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+Mappable.swift diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationInputDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+Mappable.swift similarity index 100% rename from WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationInputDescriptor+Mappable.swift rename to WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+Mappable.swift diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationRequest+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+Mappable.swift similarity index 100% rename from WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/PresentationRequest+Mappable.swift rename to WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+Mappable.swift diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/AccessTokenDescriptor+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/AccessTokenDescriptor+MappableTests.swift similarity index 100% rename from WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/AccessTokenDescriptor+MappableTests.swift rename to WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/AccessTokenDescriptor+MappableTests.swift diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/IdTokenDescriptor+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/IdTokenDescriptor+MappableTests.swift similarity index 100% rename from WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/IdTokenDescriptor+MappableTests.swift rename to WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/IdTokenDescriptor+MappableTests.swift diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+MappableTests.swift new file mode 100644 index 00000000..b8440271 --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+MappableTests.swift @@ -0,0 +1,8 @@ +// +// IssuanceRequest+MappableTests.swift +// WalletLibraryTests +// +// Created by Sydney Morton on 2/15/23. +// + +import Foundation diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDescriptor+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+MappableTests.swift similarity index 100% rename from WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDescriptor+MappableTests.swift rename to WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+MappableTests.swift diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/SelfIssuedClaimsDescriptor+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/SelfIssuedClaimsDescriptor+MappableTests.swift new file mode 100644 index 00000000..3bc6eec1 --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/SelfIssuedClaimsDescriptor+MappableTests.swift @@ -0,0 +1,8 @@ +// +// SelfIssuedClaimsDescriptor+MappableTests.swift +// WalletLibraryTests +// +// Created by Sydney Morton on 2/15/23. +// + +import Foundation diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/LinkedDomainResult+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/LinkedDomainResult+MappableTests.swift similarity index 100% rename from WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/LinkedDomainResult+MappableTests.swift rename to WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/LinkedDomainResult+MappableTests.swift diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDefinition+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+MappableTests.swift similarity index 100% rename from WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationDefinition+MappableTests.swift rename to WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+MappableTests.swift diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationInputDescriptor+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+MappableTests.swift similarity index 100% rename from WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationInputDescriptor+MappableTests.swift rename to WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+MappableTests.swift diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationRequest+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+MappableTests.swift similarity index 100% rename from WalletLibrary/WalletLibraryTests/Extensions/Translations/VCSDK/PresentationRequest+MappableTests.swift rename to WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+MappableTests.swift From bf02d0d3e200456564bdbf572cc827978e67a24f Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 15 Feb 2023 09:10:50 -0800 Subject: [PATCH 121/373] Add error when no requirements found in attestation. --- .../VCSDK/Issuance/AttestationsDescriptor+Mappable.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+Mappable.swift index 772a7dcc..6e971a4f 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+Mappable.swift @@ -5,6 +5,10 @@ import VCEntities +enum AttestationsDescriptorMappingError: Error { + case noRequirementsPresent +} + /** * An extension of the VCEntities.AttestationsDescriptor class to be able * to map AttestationsDescriptor to Requirement. @@ -29,6 +33,10 @@ extension VCEntities.AttestationsDescriptor: Mappable { requirements.append(selfAttestedClaimRequirements) } + if requirements.isEmpty { + throw AttestationsDescriptorMappingError.noRequirementsPresent + } + if requirements.count == 1, let onlyRequirement = requirements.first { return onlyRequirement From d4042ad53242c19f03e1ddf32a2a5157316503bd Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 15 Feb 2023 09:11:12 -0800 Subject: [PATCH 122/373] Move mapping logic into different file for IssuanceRequest. --- .../Entities/IssuanceRequest+RawContract.swift | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceRequest+RawContract.swift b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceRequest+RawContract.swift index 79b13f63..17180d1c 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceRequest+RawContract.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceRequest+RawContract.swift @@ -7,21 +7,9 @@ import VCEntities /** * An extension of the VCEntities.IssuanceRequest class. - * TODO: Update Style to include VerifiedIdStyle and more requester style attributes. + * TODO: Update RawContract to RawManifest */ -extension VCEntities.IssuanceRequest: RawContract { - func map(using mapper: Mapping) throws -> VerifiedIdRequestContent { - - let attestations = try getRequiredProperty(property: content.input.attestations, propertyName: "Contract Attestations") - let requirement = try mapper.map(attestations) - let rootOfTrust = try mapper.map(linkedDomainResult) - let issuerStyle = IssuerStyle(requester: content.display.card.issuedBy) - - return VerifiedIdRequestContent(style: issuerStyle, - requirement: requirement, - rootOfTrust: rootOfTrust) - } -} +extension VCEntities.IssuanceRequest: RawContract { } struct IssuerStyle: RequesterStyle { var requester: String From 63c91f6769b2e1d5b82ab0d5801151eb8234eebb Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 15 Feb 2023 09:42:54 -0800 Subject: [PATCH 123/373] Complete Test Cases for SelfIssuedClaimsDescriptor Mapping --- .../SelfIssuedClaimsDescriptor+Mappable.swift | 4 +- ...IssuedClaimsDescriptor+MappableTests.swift | 241 +++++++++++++++++- 2 files changed, 236 insertions(+), 9 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/SelfIssuedClaimsDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/SelfIssuedClaimsDescriptor+Mappable.swift index 03a36e3c..14fed2c5 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/SelfIssuedClaimsDescriptor+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/SelfIssuedClaimsDescriptor+Mappable.swift @@ -24,7 +24,7 @@ extension VCEntities.SelfIssuedClaimsDescriptor: Mappable { let isIndividualClaimRequired = onlyClaim.claimRequired ?? false let required = areSelfAttestedClaimsRequired || isIndividualClaimRequired - return SelfAttestedClaimRequirement(encrypted: false, + return SelfAttestedClaimRequirement(encrypted: encrypted ?? false, required: required, claim: onlyClaim.claim) } @@ -32,7 +32,7 @@ extension VCEntities.SelfIssuedClaimsDescriptor: Mappable { let requirements = claims.compactMap { claim in let isIndividualClaimRequired = claim.claimRequired ?? false - return SelfAttestedClaimRequirement(encrypted: false, + return SelfAttestedClaimRequirement(encrypted: encrypted ?? false, required: isIndividualClaimRequired, claim: claim.claim) } diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/SelfIssuedClaimsDescriptor+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/SelfIssuedClaimsDescriptor+MappableTests.swift index 3bc6eec1..aeac97cd 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/SelfIssuedClaimsDescriptor+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/SelfIssuedClaimsDescriptor+MappableTests.swift @@ -1,8 +1,235 @@ -// -// SelfIssuedClaimsDescriptor+MappableTests.swift -// WalletLibraryTests -// -// Created by Sydney Morton on 2/15/23. -// +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ -import Foundation +import XCTest +import VCEntities +@testable import WalletLibrary + +class SelfIssuedClaimsDescriptorMappingTests: XCTestCase { + + func testMap_WithNilClaims_ReturnNil() throws { + // Arrange + let mapper = Mapper() + let selfIssuedClaimsDescriptor = SelfIssuedClaimsDescriptor(encrypted: false, + claims: nil, + selfIssuedRequired: false) + + // Act + let actualResult = try mapper.map(selfIssuedClaimsDescriptor) + + // Assert + XCTAssertNil(actualResult) + } + + func testMap_NoEmptyClaimArray_ReturnNil() throws { + // Arrange + let mapper = Mapper() + let selfIssuedClaimsDescriptor = SelfIssuedClaimsDescriptor(encrypted: false, + claims: [], + selfIssuedRequired: false) + + // Act + let actualResult = try mapper.map(selfIssuedClaimsDescriptor) + + // Assert + XCTAssertNil(actualResult) + } + + func testMap_WithOneSelfIssuedClaimsDescriptor_ReturnsSelfAttestedRequirement() throws { + // Arrange + let mapper = Mapper() + + let mockClaimValue = "mock claim" + let expectedSelfAttestedRequirement = SelfAttestedClaimRequirement(encrypted: false, + required: false, + claim: mockClaimValue) + let mockClaim = ClaimDescriptor(claim: mockClaimValue, claimRequired: false) + let selfIssuedClaimsDescriptor = SelfIssuedClaimsDescriptor(encrypted: false, + claims: [mockClaim], + selfIssuedRequired: false) + + // Act + let actualResult = try mapper.map(selfIssuedClaimsDescriptor) + + // Assert + XCTAssert(actualResult is SelfAttestedClaimRequirement) + XCTAssertEqual((actualResult as? SelfAttestedClaimRequirement)?.required, expectedSelfAttestedRequirement.required) + XCTAssertEqual((actualResult as? SelfAttestedClaimRequirement)?.claim, expectedSelfAttestedRequirement.claim) + XCTAssertEqual((actualResult as? SelfAttestedClaimRequirement)?.encrypted, expectedSelfAttestedRequirement.encrypted) + } + + func testMap_WithOneEncryptedSelfIssuedClaimsDescriptor_ReturnsSelfAttestedRequirement() throws { + // Arrange + let mapper = Mapper() + + let mockClaimValue = "mock claim" + let expectedSelfAttestedRequirement = SelfAttestedClaimRequirement(encrypted: true, + required: false, + claim: mockClaimValue) + let mockClaim = ClaimDescriptor(claim: mockClaimValue, claimRequired: false) + let selfIssuedClaimsDescriptor = SelfIssuedClaimsDescriptor(encrypted: true, + claims: [mockClaim], + selfIssuedRequired: false) + + // Act + let actualResult = try mapper.map(selfIssuedClaimsDescriptor) + + // Assert + XCTAssert(actualResult is SelfAttestedClaimRequirement) + XCTAssertEqual((actualResult as? SelfAttestedClaimRequirement)?.required, expectedSelfAttestedRequirement.required) + XCTAssertEqual((actualResult as? SelfAttestedClaimRequirement)?.claim, expectedSelfAttestedRequirement.claim) + XCTAssertEqual((actualResult as? SelfAttestedClaimRequirement)?.encrypted, expectedSelfAttestedRequirement.encrypted) + } + + func testMap_WithSelfIssuedClaimsDescriptorWithOneRequiredClaim_ReturnsSelfAttestedRequirement() throws { + // Arrange + let mapper = Mapper() + + let mockClaimValue = "mock claim" + let expectedSelfAttestedRequirement = SelfAttestedClaimRequirement(encrypted: false, + required: true, + claim: mockClaimValue) + let mockClaim = ClaimDescriptor(claim: mockClaimValue, claimRequired: true) + let selfIssuedClaimsDescriptor = SelfIssuedClaimsDescriptor(encrypted: false, + claims: [mockClaim], + selfIssuedRequired: false) + + // Act + let actualResult = try mapper.map(selfIssuedClaimsDescriptor) + + // Assert + XCTAssert(actualResult is SelfAttestedClaimRequirement) + XCTAssertEqual((actualResult as? SelfAttestedClaimRequirement)?.required, expectedSelfAttestedRequirement.required) + XCTAssertEqual((actualResult as? SelfAttestedClaimRequirement)?.claim, expectedSelfAttestedRequirement.claim) + XCTAssertEqual((actualResult as? SelfAttestedClaimRequirement)?.encrypted, expectedSelfAttestedRequirement.encrypted) + } + + func testMap_WithOneRequiredSelfIssuedClaimsDescriptor_ReturnsSelfAttestedRequirement() throws { + // Arrange + let mapper = Mapper() + + let mockClaimValue = "mock claim" + let expectedSelfAttestedRequirement = SelfAttestedClaimRequirement(encrypted: false, + required: true, + claim: mockClaimValue) + let mockClaim = ClaimDescriptor(claim: mockClaimValue, claimRequired: false) + let selfIssuedClaimsDescriptor = SelfIssuedClaimsDescriptor(encrypted: false, + claims: [mockClaim], + selfIssuedRequired: true) + + // Act + let actualResult = try mapper.map(selfIssuedClaimsDescriptor) + + // Assert + XCTAssert(actualResult is SelfAttestedClaimRequirement) + XCTAssertEqual((actualResult as? SelfAttestedClaimRequirement)?.required, expectedSelfAttestedRequirement.required) + XCTAssertEqual((actualResult as? SelfAttestedClaimRequirement)?.claim, expectedSelfAttestedRequirement.claim) + XCTAssertEqual((actualResult as? SelfAttestedClaimRequirement)?.encrypted, expectedSelfAttestedRequirement.encrypted) + } + + func testMap_WithMultipleSelfIssuedClaimsAndAllClaimsRequired_ReturnGroupRequirement() throws { + // Arrange + let mapper = Mapper() + + let firstMockClaimValue = "first mock claim" + let secondMockClaimValue = "second mock claim" + let thirdMockClaimValue = "third mock claim" + + + let expectedFirstSelfAttestedRequirement = SelfAttestedClaimRequirement(encrypted: false, + required: false, + claim: firstMockClaimValue) + let expectedSecondSelfAttestedRequirement = SelfAttestedClaimRequirement(encrypted: false, + required: true, + claim: secondMockClaimValue) + let expectedThirdSelfAttestedRequirement = SelfAttestedClaimRequirement(encrypted: false, + required: false, + claim: thirdMockClaimValue) + + let firstMockClaim = ClaimDescriptor(claim: firstMockClaimValue, claimRequired: false) + let secondMockClaim = ClaimDescriptor(claim: secondMockClaimValue, claimRequired: true) + let thirdMockClaim = ClaimDescriptor(claim: thirdMockClaimValue, claimRequired: false) + + let selfIssuedClaimsDescriptor = SelfIssuedClaimsDescriptor(encrypted: false, + claims: [firstMockClaim, secondMockClaim, thirdMockClaim], + selfIssuedRequired: true) + + // Act + let actualResult = try mapper.map(selfIssuedClaimsDescriptor) + + // Assert + XCTAssert(actualResult is GroupRequirement) + + if let groupRequirement = actualResult as? GroupRequirement, + let requirements = groupRequirement.requirements as? [SelfAttestedClaimRequirement] { + XCTAssert(groupRequirement.required) + XCTAssertEqual(requirements.count, 3) + XCTAssertEqual(requirements.first?.required, expectedFirstSelfAttestedRequirement.required) + XCTAssertEqual(requirements.first?.claim, expectedFirstSelfAttestedRequirement.claim) + XCTAssertEqual(requirements.first?.encrypted, expectedFirstSelfAttestedRequirement.encrypted) + XCTAssertEqual(requirements[1].required, expectedSecondSelfAttestedRequirement.required) + XCTAssertEqual(requirements[1].claim, expectedSecondSelfAttestedRequirement.claim) + XCTAssertEqual(requirements[1].encrypted, expectedSecondSelfAttestedRequirement.encrypted) + XCTAssertEqual(requirements[2].required, expectedThirdSelfAttestedRequirement.required) + XCTAssertEqual(requirements[2].claim, expectedThirdSelfAttestedRequirement.claim) + XCTAssertEqual(requirements[2].encrypted, expectedThirdSelfAttestedRequirement.encrypted) + + } else { + XCTFail("Group Requirement not returned.") + } + } + + func testMap_WithMultipleSelfIssuedClaimsAndAllClaimsNotRequired_ReturnsGroupRequirement() throws { + // Arrange + let mapper = Mapper() + + let firstMockClaimValue = "first mock claim" + let secondMockClaimValue = "second mock claim" + let thirdMockClaimValue = "third mock claim" + + + let expectedFirstSelfAttestedRequirement = SelfAttestedClaimRequirement(encrypted: false, + required: true, + claim: firstMockClaimValue) + let expectedSecondSelfAttestedRequirement = SelfAttestedClaimRequirement(encrypted: false, + required: false, + claim: secondMockClaimValue) + let expectedThirdSelfAttestedRequirement = SelfAttestedClaimRequirement(encrypted: false, + required: true, + claim: thirdMockClaimValue) + + let firstMockClaim = ClaimDescriptor(claim: firstMockClaimValue, claimRequired: true) + let secondMockClaim = ClaimDescriptor(claim: secondMockClaimValue, claimRequired: false) + let thirdMockClaim = ClaimDescriptor(claim: thirdMockClaimValue, claimRequired: true) + + let selfIssuedClaimsDescriptor = SelfIssuedClaimsDescriptor(encrypted: false, + claims: [firstMockClaim, secondMockClaim, thirdMockClaim], + selfIssuedRequired: false) + + // Act + let actualResult = try mapper.map(selfIssuedClaimsDescriptor) + + // Assert + XCTAssert(actualResult is GroupRequirement) + + if let groupRequirement = actualResult as? GroupRequirement, + let requirements = groupRequirement.requirements as? [SelfAttestedClaimRequirement] { + XCTAssertFalse(groupRequirement.required) + XCTAssertEqual(requirements.count, 3) + XCTAssertEqual(requirements.first?.required, expectedFirstSelfAttestedRequirement.required) + XCTAssertEqual(requirements.first?.claim, expectedFirstSelfAttestedRequirement.claim) + XCTAssertEqual(requirements.first?.encrypted, expectedFirstSelfAttestedRequirement.encrypted) + XCTAssertEqual(requirements[1].required, expectedSecondSelfAttestedRequirement.required) + XCTAssertEqual(requirements[1].claim, expectedSecondSelfAttestedRequirement.claim) + XCTAssertEqual(requirements[1].encrypted, expectedSecondSelfAttestedRequirement.encrypted) + XCTAssertEqual(requirements[2].required, expectedThirdSelfAttestedRequirement.required) + XCTAssertEqual(requirements[2].claim, expectedThirdSelfAttestedRequirement.claim) + XCTAssertEqual(requirements[2].encrypted, expectedThirdSelfAttestedRequirement.encrypted) + + } else { + XCTFail("Group Requirement not returned.") + } + } +} From 6a86706af8dbf307b26b3b953ab31027ba1afab0 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 15 Feb 2023 10:12:42 -0800 Subject: [PATCH 124/373] Complete test cases for Issuance Request Mapping. --- .../IssuanceRequest+RawContract.swift | 2 +- .../Issuance/IssuanceRequest+Mappable.swift | 2 +- .../IssuanceRequest+MappableTests.swift | 148 +++++++++++++++++- 3 files changed, 142 insertions(+), 10 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceRequest+RawContract.swift b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceRequest+RawContract.swift index 17180d1c..9126b415 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceRequest+RawContract.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceRequest+RawContract.swift @@ -11,6 +11,6 @@ import VCEntities */ extension VCEntities.IssuanceRequest: RawContract { } -struct IssuerStyle: RequesterStyle { +struct IssuerStyle: RequesterStyle, Equatable { var requester: String } diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+Mappable.swift index 7014ecfd..d7859053 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+Mappable.swift @@ -13,7 +13,7 @@ extension VCEntities.IssuanceRequest: Mappable { func map(using mapper: Mapping) throws -> VerifiedIdRequestContent { let attestations = try getRequiredProperty(property: content.input.attestations, - propertyName: "Contract Attestations") + propertyName: "attestations") let requirement = try mapper.map(attestations) let rootOfTrust = try mapper.map(linkedDomainResult) let issuerStyle = IssuerStyle(requester: content.display.card.issuedBy) diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+MappableTests.swift index b8440271..c430cccc 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+MappableTests.swift @@ -1,8 +1,140 @@ -// -// IssuanceRequest+MappableTests.swift -// WalletLibraryTests -// -// Created by Sydney Morton on 2/15/23. -// - -import Foundation +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import XCTest +import VCEntities +import VCToken +@testable import WalletLibrary + +class IssuanceRequestMappingTests: XCTestCase { + + enum MockError: Error { + case expectedToBeUnableToMapAttestationsDescriptor + case expectedToBeUnableToMapLinkedDomainResult + } + + func testMap_WithNilAttestations_ThrowsError() throws { + // Arrange + let mockSignedContract = createMockSignedContract() + let issuanceRequest = IssuanceRequest(from: mockSignedContract, linkedDomainResult: .linkedDomainMissing) + let mapper = MockMapper() + + // Act + XCTAssertThrowsError(try mapper.map(issuanceRequest)) { error in + // Assert + XCTAssert(error is MappingError) + XCTAssertEqual(error as? MappingError, .PropertyNotPresent(property: "attestations", in: String(describing: IssuanceRequest.self))) + } + } + + func testMap_WhenAttestationsMappingThrowsError_ThrowsError() throws { + // Arrange + let attestations = AttestationsDescriptor(accessTokens: []) + let signedContract = createMockSignedContract(attestations: attestations) + let issuanceRequest = IssuanceRequest(from: signedContract, linkedDomainResult: .linkedDomainMissing) + + func mockResults(objectToBeMapped: Any) throws -> Any? { + if objectToBeMapped is AttestationsDescriptor { + throw MockError.expectedToBeUnableToMapAttestationsDescriptor + } + + return nil + } + + let mapper = MockMapper(mockResults: mockResults) + + // Act + XCTAssertThrowsError(try mapper.map(issuanceRequest)) { error in + // Assert + XCTAssert(error is MockError) + XCTAssertEqual(error as? MockError, .expectedToBeUnableToMapAttestationsDescriptor) + } + } + + func testMap_WhenLinkedDomainThrowsError_ThrowsError() throws { + // Arrange + let mockRequirement = MockRequirement(id: "mock requirement") + let attestations = AttestationsDescriptor(accessTokens: []) + let signedContract = createMockSignedContract(attestations: attestations) + let issuanceRequest = IssuanceRequest(from: signedContract, linkedDomainResult: .linkedDomainMissing) + + func mockResults(objectToBeMapped: Any) throws -> Any? { + if objectToBeMapped is AttestationsDescriptor { + return mockRequirement + } + + if objectToBeMapped is LinkedDomainResult { + throw MockError.expectedToBeUnableToMapLinkedDomainResult + } + + return nil + } + + let mapper = MockMapper(mockResults: mockResults) + + // Act + XCTAssertThrowsError(try mapper.map(issuanceRequest)) { error in + // Assert + XCTAssert(error is MockError) + XCTAssertEqual(error as? MockError, .expectedToBeUnableToMapLinkedDomainResult) + } + } + + func testMap_WithValidInput_ReturnsVerifiedIdRequestContent() throws { + // Arrange + let mockRequirement = MockRequirement(id: "mock requirement") + let mockRootOfTrust = RootOfTrust(verified: true, source: "mock source") + let attestations = AttestationsDescriptor(accessTokens: []) + let signedContract = createMockSignedContract(attestations: attestations) + let issuanceRequest = IssuanceRequest(from: signedContract, linkedDomainResult: .linkedDomainMissing) + + func mockResults(objectToBeMapped: Any) throws -> Any? { + if objectToBeMapped is AttestationsDescriptor { + return mockRequirement + } + + if objectToBeMapped is LinkedDomainResult { + return mockRootOfTrust + } + + return nil + } + + let mapper = MockMapper(mockResults: mockResults) + + // Act + let actualResult = try mapper.map(issuanceRequest) + + // Assert + XCTAssertEqual(actualResult.style.requester, "mock issuer") + XCTAssertEqual(actualResult.rootOfTrust, mockRootOfTrust) + XCTAssertEqual(actualResult.requirement as? MockRequirement, mockRequirement) + } + + private func createMockSignedContract(attestations: AttestationsDescriptor? = nil) -> SignedContract { + let mockCardDisplay = CardDisplayDescriptor(title: "mock title", + issuedBy: "mock issuer", + backgroundColor: "mock background color", + textColor: "mock text color", + logo: nil, + cardDescription: "mock description") + let mockConsentDisplay = ConsentDisplayDescriptor(title: nil, + instructions: "mock purpose") + let mockDisplayDescriptor = DisplayDescriptor(id: nil, + locale: nil, + contract: nil, + card: mockCardDisplay, + consent: mockConsentDisplay, + claims: [:]) + let mockContractInputDescriptor = ContractInputDescriptor(credentialIssuer: "mock credential issuer", + issuer: "mock issuer", + attestations: attestations) + let mockContract = Contract(id: "mockContract", + display: mockDisplayDescriptor, + input: mockContractInputDescriptor) + + return SignedContract(headers: Header(), content: mockContract)! + } +} From 8afc4343a379624a1178cf7f4274a56baac7df42 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 15 Feb 2023 10:18:21 -0800 Subject: [PATCH 125/373] Rename Style Classes. --- .../WalletLibrary.xcodeproj/project.pbxproj | 18 +++++++++++------- .../Entities/IssuanceRequest+RawContract.swift | 4 ---- .../Issuance/IssuanceRequest+Mappable.swift | 2 +- .../PresentationRequest+Mappable.swift | 2 +- .../Protocols/Styles/RequesterStyle.swift | 2 +- .../ContractIssuanceRequest.swift | 0 .../Manifest/Manifest2022IssuerStyle.swift | 11 +++++++++++ ...erStyle.swift => OpenIdVerifierStyle.swift} | 4 ++-- .../IssuanceRequest+MappableTests.swift | 2 +- .../PresentationRequest+MappableTests.swift | 8 ++++---- .../Requests/Styles/MockRequesterStyle.swift | 4 ++-- 11 files changed, 34 insertions(+), 23 deletions(-) rename WalletLibrary/WalletLibrary/Requests/RequestProtocols/{Contract => Manifest}/ContractIssuanceRequest.swift (100%) create mode 100644 WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/Manifest2022IssuerStyle.swift rename WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/{OpenIdRequesterStyle.swift => OpenIdVerifierStyle.swift} (82%) diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 1ba41dd4..2a2fcea0 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -62,6 +62,7 @@ 559BD455299D3E4800BD61AC /* IssuanceRequest+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD454299D3E4800BD61AC /* IssuanceRequest+MappableTests.swift */; }; 559BD457299D3E6000BD61AC /* AttestationsDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD456299D3E6000BD61AC /* AttestationsDescriptor+MappableTests.swift */; }; 559BD459299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD458299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift */; }; + 559BD45B299D587200BD61AC /* Manifest2022IssuerStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD45A299D587200BD61AC /* Manifest2022IssuerStyle.swift */; }; 55A81BCA2991AB82002C259A /* RequestResolving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BC92991AB82002C259A /* RequestResolving.swift */; }; 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE02991AB96002C259A /* RequestHandling.swift */; }; 55A81BE52991B2E5002C259A /* RequestResolverFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */; }; @@ -80,7 +81,7 @@ 55E2F07F29955A3B0008010D /* VerifiedIdRequestContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F07E29955A3B0008010D /* VerifiedIdRequestContent.swift */; }; 55E2F08129955A960008010D /* RequestType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F08029955A960008010D /* RequestType.swift */; }; 55E2F08429955E8A0008010D /* MockMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F08329955E8A0008010D /* MockMapper.swift */; }; - 55E2F086299573C30008010D /* OpenIdRequesterStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F085299573C30008010D /* OpenIdRequesterStyle.swift */; }; + 55E2F086299573C30008010D /* OpenIdVerifierStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F085299573C30008010D /* OpenIdVerifierStyle.swift */; }; 55E2F0882995743D0008010D /* LinkedDomainResult+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F0872995743D0008010D /* LinkedDomainResult+Mappable.swift */; }; 55E2F08A299576350008010D /* PresentationDefinition+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F089299576350008010D /* PresentationDefinition+Mappable.swift */; }; 55E2F08C299576730008010D /* GroupRequirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F08B299576730008010D /* GroupRequirement.swift */; }; @@ -328,6 +329,7 @@ 559BD454299D3E4800BD61AC /* IssuanceRequest+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceRequest+MappableTests.swift"; sourceTree = ""; }; 559BD456299D3E6000BD61AC /* AttestationsDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttestationsDescriptor+MappableTests.swift"; sourceTree = ""; }; 559BD458299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SelfIssuedClaimsDescriptor+MappableTests.swift"; sourceTree = ""; }; + 559BD45A299D587200BD61AC /* Manifest2022IssuerStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Manifest2022IssuerStyle.swift; sourceTree = ""; }; 55A81BC92991AB82002C259A /* RequestResolving.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolving.swift; sourceTree = ""; }; 55A81BE02991AB96002C259A /* RequestHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestHandling.swift; sourceTree = ""; }; 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolverFactory.swift; sourceTree = ""; }; @@ -346,7 +348,7 @@ 55E2F07E29955A3B0008010D /* VerifiedIdRequestContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdRequestContent.swift; sourceTree = ""; }; 55E2F08029955A960008010D /* RequestType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestType.swift; sourceTree = ""; }; 55E2F08329955E8A0008010D /* MockMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockMapper.swift; sourceTree = ""; }; - 55E2F085299573C30008010D /* OpenIdRequesterStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenIdRequesterStyle.swift; sourceTree = ""; }; + 55E2F085299573C30008010D /* OpenIdVerifierStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenIdVerifierStyle.swift; sourceTree = ""; }; 55E2F0872995743D0008010D /* LinkedDomainResult+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LinkedDomainResult+Mappable.swift"; sourceTree = ""; }; 55E2F089299576350008010D /* PresentationDefinition+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationDefinition+Mappable.swift"; sourceTree = ""; }; 55E2F08B299576730008010D /* GroupRequirement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupRequirement.swift; sourceTree = ""; }; @@ -619,12 +621,13 @@ path = Contract; sourceTree = ""; }; - 559BD3A9299ADEF300BD61AC /* Contract */ = { + 559BD3A9299ADEF300BD61AC /* Manifest */ = { isa = PBXGroup; children = ( 559BD3AA299ADEFF00BD61AC /* ContractIssuanceRequest.swift */, + 559BD45A299D587200BD61AC /* Manifest2022IssuerStyle.swift */, ); - path = Contract; + path = Manifest; sourceTree = ""; }; 559BD3B0299B09F200BD61AC /* Issuance */ = { @@ -752,7 +755,7 @@ 55E2F07A2995561E0008010D /* RequestProtocols */ = { isa = PBXGroup; children = ( - 559BD3A9299ADEF300BD61AC /* Contract */, + 559BD3A9299ADEF300BD61AC /* Manifest */, 55E2F07B2995561E0008010D /* OpenId */, ); path = RequestProtocols; @@ -762,7 +765,7 @@ isa = PBXGroup; children = ( 55E2F07C2995561E0008010D /* OpenIdPresentationRequest.swift */, - 55E2F085299573C30008010D /* OpenIdRequesterStyle.swift */, + 55E2F085299573C30008010D /* OpenIdVerifierStyle.swift */, ); path = OpenId; sourceTree = ""; @@ -1289,13 +1292,14 @@ 550A9170299310D40014D030 /* OpenIdURLRequestResolver.swift in Sources */, 5534E669294A0C3F005D0765 /* Mapper.swift in Sources */, 550E320A298B0EA4004E7716 /* VerifiedIdClient.swift in Sources */, + 559BD45B299D587200BD61AC /* Manifest2022IssuerStyle.swift in Sources */, 559BD3AD299AE6C000BD61AC /* IssuanceRequest+RawContract.swift in Sources */, 559BD3B2299B0A0400BD61AC /* AttestationsDescriptor+Mappable.swift in Sources */, 5534E64D294A0888005D0765 /* PresentationDescriptor+Mappable.swift in Sources */, 55E3370C293FD61E00CD2ED7 /* SelfAttestedClaimRequirement.swift in Sources */, 55E3376829437E3500CD2ED7 /* OpenIdForVCResolver.swift in Sources */, 55A81BE52991B2E5002C259A /* RequestResolverFactory.swift in Sources */, - 55E2F086299573C30008010D /* OpenIdRequesterStyle.swift in Sources */, + 55E2F086299573C30008010D /* OpenIdVerifierStyle.swift in Sources */, 559BD414299D37B100BD61AC /* SelfIssuedClaimsDescriptor+Mappable.swift in Sources */, 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */, 55E2F071299554110008010D /* LibraryConfiguration.swift in Sources */, diff --git a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceRequest+RawContract.swift b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceRequest+RawContract.swift index 9126b415..e67392a4 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceRequest+RawContract.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceRequest+RawContract.swift @@ -10,7 +10,3 @@ import VCEntities * TODO: Update RawContract to RawManifest */ extension VCEntities.IssuanceRequest: RawContract { } - -struct IssuerStyle: RequesterStyle, Equatable { - var requester: String -} diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+Mappable.swift index d7859053..b4f6438a 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+Mappable.swift @@ -16,7 +16,7 @@ extension VCEntities.IssuanceRequest: Mappable { propertyName: "attestations") let requirement = try mapper.map(attestations) let rootOfTrust = try mapper.map(linkedDomainResult) - let issuerStyle = IssuerStyle(requester: content.display.card.issuedBy) + let issuerStyle = Manifest2020IssuerStyle(name: content.display.card.issuedBy) return VerifiedIdRequestContent(style: issuerStyle, requirement: requirement, diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+Mappable.swift index 16030a09..76b43b46 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+Mappable.swift @@ -28,7 +28,7 @@ extension VCEntities.PresentationRequest: Mappable { let rootOfTrust = try mapper.map(linkedDomainResult) let clientName = content.registration?.clientName ?? "" - let requesterStyle = OpenIdRequesterStyle(requester: clientName) + let requesterStyle = OpenIdVerifierStyle(name: clientName) let content = VerifiedIdRequestContent(style: requesterStyle, requirement: requirement, diff --git a/WalletLibrary/WalletLibrary/Protocols/Styles/RequesterStyle.swift b/WalletLibrary/WalletLibrary/Protocols/Styles/RequesterStyle.swift index 4a5a9ec8..59fbbbdc 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Styles/RequesterStyle.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Styles/RequesterStyle.swift @@ -8,6 +8,6 @@ */ public protocol RequesterStyle { /// The name of the requester. - var requester: String { get } + var name: String { get } } diff --git a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Contract/ContractIssuanceRequest.swift b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift similarity index 100% rename from WalletLibrary/WalletLibrary/Requests/RequestProtocols/Contract/ContractIssuanceRequest.swift rename to WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift diff --git a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/Manifest2022IssuerStyle.swift b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/Manifest2022IssuerStyle.swift new file mode 100644 index 00000000..4ecb83ee --- /dev/null +++ b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/Manifest2022IssuerStyle.swift @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +/** + * Representation of Issuer Style configured by a Manifest implemented in 2020. + */ +struct Manifest2020IssuerStyle: RequesterStyle, Equatable { + var name: String +} diff --git a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdRequesterStyle.swift b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdVerifierStyle.swift similarity index 82% rename from WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdRequesterStyle.swift rename to WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdVerifierStyle.swift index 85bab362..63297833 100644 --- a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdRequesterStyle.swift +++ b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdVerifierStyle.swift @@ -6,6 +6,6 @@ /** * Requester Style that is Open Id specific. */ -struct OpenIdRequesterStyle: RequesterStyle, Equatable { - let requester: String +struct OpenIdVerifierStyle: RequesterStyle, Equatable { + let name: String } diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+MappableTests.swift index c430cccc..c6740617 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+MappableTests.swift @@ -108,7 +108,7 @@ class IssuanceRequestMappingTests: XCTestCase { let actualResult = try mapper.map(issuanceRequest) // Assert - XCTAssertEqual(actualResult.style.requester, "mock issuer") + XCTAssertEqual(actualResult.style.name, "mock issuer") XCTAssertEqual(actualResult.rootOfTrust, mockRootOfTrust) XCTAssertEqual(actualResult.requirement as? MockRequirement, mockRequirement) } diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+MappableTests.swift index 71a96413..bf09a166 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+MappableTests.swift @@ -104,7 +104,7 @@ class PresentationRequestMappingTests: XCTestCase { func testMap_WithNoClientNamePresent_ReturnVerifiedIdRequestContent() throws { // Arrange - let expectedStyle = OpenIdRequesterStyle(requester: "") + let expectedStyle = OpenIdVerifierStyle(name: "") let expectedVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, required: false, types: [], @@ -145,13 +145,13 @@ class PresentationRequestMappingTests: XCTestCase { // Act XCTAssertIdentical(actualResult.requirement as AnyObject, expectedVerifiedIdRequirement as AnyObject) XCTAssertEqual(actualResult.rootOfTrust, expectedRootOfTrust) - XCTAssertEqual(actualResult.style as? OpenIdRequesterStyle, expectedStyle) + XCTAssertEqual(actualResult.style as? OpenIdVerifierStyle, expectedStyle) } func testMap_WithClientNamePresent_ReturnVerifiedIdRequestContent() throws { // Arrange let mockRequesterName = "mockRequesterName235" - let expectedStyle = OpenIdRequesterStyle(requester: mockRequesterName) + let expectedStyle = OpenIdVerifierStyle(name: mockRequesterName) let expectedVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, required: false, types: [], @@ -192,7 +192,7 @@ class PresentationRequestMappingTests: XCTestCase { // Act XCTAssertIdentical(actualResult.requirement as AnyObject, expectedVerifiedIdRequirement as AnyObject) XCTAssertEqual(actualResult.rootOfTrust, expectedRootOfTrust) - XCTAssertEqual(actualResult.style as? OpenIdRequesterStyle, expectedStyle) + XCTAssertEqual(actualResult.style as? OpenIdVerifierStyle, expectedStyle) } private func createPresentationRequestToken(with requestedClaims: RequestedClaims?, diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/Styles/MockRequesterStyle.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/Styles/MockRequesterStyle.swift index 91aa8ad0..3130c1c2 100644 --- a/WalletLibrary/WalletLibraryTests/Mocks/Requests/Styles/MockRequesterStyle.swift +++ b/WalletLibrary/WalletLibraryTests/Mocks/Requests/Styles/MockRequesterStyle.swift @@ -7,9 +7,9 @@ struct MockRequesterStyle: RequesterStyle, Equatable { - let requester: String + let name: String init(requester: String) { - self.requester = requester + self.name = requester } } From f2a0aa07a14302db889fd881758b1f429e2128ec Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 15 Feb 2023 10:22:44 -0800 Subject: [PATCH 126/373] Fix nit comments before PR. --- .../RequestProtocols/Manifest/Manifest2022IssuerStyle.swift | 1 + .../VCSDK/Issuance/IssuanceRequest+MappableTests.swift | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/Manifest2022IssuerStyle.swift b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/Manifest2022IssuerStyle.swift index 4ecb83ee..59a75ef8 100644 --- a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/Manifest2022IssuerStyle.swift +++ b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/Manifest2022IssuerStyle.swift @@ -5,6 +5,7 @@ /** * Representation of Issuer Style configured by a Manifest implemented in 2020. + * TODO: Add more attributes from manifest. */ struct Manifest2020IssuerStyle: RequesterStyle, Equatable { var name: String diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+MappableTests.swift index c6740617..f1cfd086 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+MappableTests.swift @@ -25,7 +25,8 @@ class IssuanceRequestMappingTests: XCTestCase { XCTAssertThrowsError(try mapper.map(issuanceRequest)) { error in // Assert XCTAssert(error is MappingError) - XCTAssertEqual(error as? MappingError, .PropertyNotPresent(property: "attestations", in: String(describing: IssuanceRequest.self))) + XCTAssertEqual(error as? MappingError, + .PropertyNotPresent(property: "attestations", in: String(describing: IssuanceRequest.self))) } } From 5d690f587208fee183c29ef3fbc8b802e1cb0b81 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 15 Feb 2023 12:54:07 -0800 Subject: [PATCH 127/373] add access token validation. --- .../Requirements/AccessTokenRequirement.swift | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Requirements/AccessTokenRequirement.swift b/WalletLibrary/WalletLibrary/Requests/Requirements/AccessTokenRequirement.swift index 6b48a390..a7c8cd0b 100644 --- a/WalletLibrary/WalletLibrary/Requests/Requirements/AccessTokenRequirement.swift +++ b/WalletLibrary/WalletLibrary/Requests/Requirements/AccessTokenRequirement.swift @@ -3,6 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +enum AccessTokenRequirementError: Error { + case accessTokenRequirementHasNotBeenFulfilled +} + /** * Information to describe an access token required for a Verified Id issuance flow. */ @@ -26,6 +30,8 @@ public class AccessTokenRequirement: Requirement { /// The scope value used to get the access token through an authentication library. public let scope: String + var accessToken: String? + init(encrypted: Bool, required: Bool, configuration: String, @@ -40,7 +46,15 @@ public class AccessTokenRequirement: Requirement { self.scope = scope } + /// Throws error if requirement is not valid. public func validate() throws { - throw VerifiedIdClientError.TODO(message: "implement validate") + if accessToken != nil { + throw AccessTokenRequirementError.accessTokenRequirementHasNotBeenFulfilled + } + } + + /// Fulfill requirement with a raw access token. + public func fulfill(with rawToken: String) { + accessToken = rawToken } } From 24446d0abeb75e734f345a927ddb3022d9b5be15 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 15 Feb 2023 12:54:18 -0800 Subject: [PATCH 128/373] Add group requirements validation. --- .../Requirements/GroupRequirement.swift | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift b/WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift index cc3229b5..d38640dd 100644 --- a/WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift +++ b/WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift @@ -3,6 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +enum GroupRequirementError: Error { + case requirementsAreNotValid([Error]) +} + /** * This value specifies if all requirements are needed in the list or just one. */ @@ -32,6 +36,18 @@ public class GroupRequirement: Requirement { } public func validate() throws { - throw VerifiedIdClientError.TODO(message: "implement") + + var errorsThrown: [Error] = [] + for requirement in requirements { + do { + try requirement.validate() + } catch { + errorsThrown.append(error) + } + } + + if !errorsThrown.isEmpty { + throw GroupRequirementError.requirementsAreNotValid(errorsThrown) + } } } From 297ffb20e2dc5c6cbf7d1e306869da10d4f1d2ad Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 15 Feb 2023 13:29:24 -0800 Subject: [PATCH 129/373] Change name of error in PresentationDefinition mapping. --- .../VCSDK/Presentation/PresentationDefinition+Mappable.swift | 4 ++-- .../Presentation/PresentationDefinition+MappableTests.swift | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+Mappable.swift index af684888..840c4c4a 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+Mappable.swift @@ -9,7 +9,7 @@ import VCEntities * Errors thrown in Presentation Definition Mappable extension. */ enum PresentationDefinitionMappingError: Error { - case noPresentInputDescriptors + case missingInputDescriptors } /** @@ -22,7 +22,7 @@ extension VCEntities.PresentationDefinition: Mappable { guard let inputDescriptors = self.inputDescriptors, !inputDescriptors.isEmpty else { - throw PresentationDefinitionMappingError.noPresentInputDescriptors + throw PresentationDefinitionMappingError.missingInputDescriptors } if inputDescriptors.count == 1, diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+MappableTests.swift index 530e4319..48ba189d 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+MappableTests.swift @@ -21,7 +21,7 @@ class PresentationDefinitionMappingTests: XCTestCase { XCTAssertThrowsError(try mockMapper.map(presentationDefinition)) { error in // Assert XCTAssert(error is PresentationDefinitionMappingError) - XCTAssertEqual(error as? PresentationDefinitionMappingError, .noPresentInputDescriptors) + XCTAssertEqual(error as? PresentationDefinitionMappingError, .missingInputDescriptors) } } @@ -37,7 +37,7 @@ class PresentationDefinitionMappingTests: XCTestCase { XCTAssertThrowsError(try mockMapper.map(presentationDefinition)) { error in // Assert XCTAssert(error is PresentationDefinitionMappingError) - XCTAssertEqual(error as? PresentationDefinitionMappingError, .noPresentInputDescriptors) + XCTAssertEqual(error as? PresentationDefinitionMappingError, .missingInputDescriptors) } } From e10621ee2e651efb6eece6e10a836a7aaefc4b2f Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 15 Feb 2023 13:29:34 -0800 Subject: [PATCH 130/373] Fix style naming. --- .../Mappings/VCSDK/Issuance/IssuanceRequest+Mappable.swift | 2 +- .../RequestProtocols/Manifest/Manifest2022IssuerStyle.swift | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+Mappable.swift index b4f6438a..1478f703 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+Mappable.swift @@ -16,7 +16,7 @@ extension VCEntities.IssuanceRequest: Mappable { propertyName: "attestations") let requirement = try mapper.map(attestations) let rootOfTrust = try mapper.map(linkedDomainResult) - let issuerStyle = Manifest2020IssuerStyle(name: content.display.card.issuedBy) + let issuerStyle = Manifest2022IssuerStyle(name: content.display.card.issuedBy) return VerifiedIdRequestContent(style: issuerStyle, requirement: requirement, diff --git a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/Manifest2022IssuerStyle.swift b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/Manifest2022IssuerStyle.swift index 59a75ef8..eb53e770 100644 --- a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/Manifest2022IssuerStyle.swift +++ b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/Manifest2022IssuerStyle.swift @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ /** - * Representation of Issuer Style configured by a Manifest implemented in 2020. + * Representation of Issuer Style configured by a Manifest implemented in 2022. * TODO: Add more attributes from manifest. */ -struct Manifest2020IssuerStyle: RequesterStyle, Equatable { +struct Manifest2022IssuerStyle: RequesterStyle, Equatable { var name: String } From 56bf18b869d6d84a84d112725db71e5d8c5b2395 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 15 Feb 2023 16:24:07 -0800 Subject: [PATCH 131/373] Update WalletLibraryDemo.entitlements --- .../WalletLibraryDemo/WalletLibraryDemo.entitlements | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/WalletLibraryDemo.entitlements b/Demo/WalletLibraryDemo/WalletLibraryDemo/WalletLibraryDemo.entitlements index f2ef3ae0..b66f52f9 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/WalletLibraryDemo.entitlements +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/WalletLibraryDemo.entitlements @@ -2,9 +2,13 @@ - com.apple.security.app-sandbox - - com.apple.security.files.user-selected.read-only - + com.apple.security.app-sandbox + + com.apple.security.files.user-selected.read-only + + keychain-access-groups + + $(AppIdentifierPrefix)com.microsoft.entra.verifiedid.WalletLibraryDemo + From aa3d74025e460c8dfbe3c2e49588cb17bfcee793 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 15 Feb 2023 16:24:28 -0800 Subject: [PATCH 132/373] Create extension of Issuance Response Container. --- .../Entities/IssuanceResponseContainer.swift | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer.swift diff --git a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer.swift b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer.swift new file mode 100644 index 00000000..7e727bee --- /dev/null +++ b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer.swift @@ -0,0 +1,78 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import VCEntities + +/** + * An extension of the VCEntities.IssuanceRequest class. + * TODO: Update RawContract to RawManifest + */ +extension VCEntities.IssuanceResponseContainer { + + init(from rawContract: any RawContract, input: VerifiedIdRequestInput) throws { + + guard let contract = rawContract as? IssuanceRequest else { + throw VerifiedIdClientError.TODO(message: "implement") + } + + guard let input = input as? VerifiedIdRequestURL else { + throw VerifiedIdClientError.TODO(message: "implement") + } + + try self.init(from: contract.content, contractUri: input.url.absoluteString) + } + + mutating func add(requirement: Requirement) throws { + switch (requirement) { + case is GroupRequirement: + try add(groupRequirement: requirement as! GroupRequirement) + case is IdTokenRequirement: + try add(idTokenRequirement: requirement as! IdTokenRequirement) + case is AccessTokenRequirement: + try add(accessTokenRequirement: requirement as! AccessTokenRequirement) + case is VerifiedIdRequirement: + try add(verifiedIdRequirement: requirement as! VerifiedIdRequirement) + case is SelfAttestedClaimRequirement: + try add(selfAttestedRequirement: requirement as! SelfAttestedClaimRequirement) + case is PinRequirement: + try add(pinRequirement: requirement as! PinRequirement) + default: + throw VerifiedIdClientError.TODO(message: "implement") + } + } + + private mutating func add(groupRequirement: GroupRequirement) throws { + try groupRequirement.validate() + for requirement in groupRequirement.requirements { + try add(requirement: requirement) + } + } + + private mutating func add(idTokenRequirement: IdTokenRequirement) throws { + try idTokenRequirement.validate() + self.requestedIdTokenMap[idTokenRequirement.configuration.absoluteString] = idTokenRequirement.idToken + } + + private mutating func add(accessTokenRequirement: AccessTokenRequirement) throws { + try accessTokenRequirement.validate() + self.requestedAccessTokenMap[accessTokenRequirement.configuration] = accessTokenRequirement.accessToken + } + + private mutating func add(verifiedIdRequirement: VerifiedIdRequirement) throws { + throw VerifiedIdClientError.TODO(message: "implement") + } + + private mutating func add(selfAttestedRequirement: SelfAttestedClaimRequirement) throws { + try selfAttestedRequirement.validate() + self.requestedSelfAttestedClaimMap[selfAttestedRequirement.claim] = selfAttestedRequirement.value + } + + private mutating func add(pinRequirement: PinRequirement) throws { + try pinRequirement.validate() + if let pin = pinRequirement.pin { + self.issuancePin = IssuancePin(from: pin, withSalt: pinRequirement.salt) + } + } +} From 228a48cebab738d805e14ac808af9f3bf3da0e5d Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 15 Feb 2023 16:25:06 -0800 Subject: [PATCH 133/373] create ContractResponder --- .../WalletLibrary.xcodeproj/project.pbxproj | 8 ++++++++ .../Requests/Contract/ContractResponder.swift | 14 ++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ContractResponder.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 2a2fcea0..8f62ff4b 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -63,6 +63,8 @@ 559BD457299D3E6000BD61AC /* AttestationsDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD456299D3E6000BD61AC /* AttestationsDescriptor+MappableTests.swift */; }; 559BD459299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD458299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift */; }; 559BD45B299D587200BD61AC /* Manifest2022IssuerStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD45A299D587200BD61AC /* Manifest2022IssuerStyle.swift */; }; + 559BD485299D8DCC00BD61AC /* ContractResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD484299D8DCC00BD61AC /* ContractResponder.swift */; }; + 559BD49C299D913F00BD61AC /* IssuanceResponseContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD49B299D913F00BD61AC /* IssuanceResponseContainer.swift */; }; 55A81BCA2991AB82002C259A /* RequestResolving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BC92991AB82002C259A /* RequestResolving.swift */; }; 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE02991AB96002C259A /* RequestHandling.swift */; }; 55A81BE52991B2E5002C259A /* RequestResolverFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */; }; @@ -330,6 +332,8 @@ 559BD456299D3E6000BD61AC /* AttestationsDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttestationsDescriptor+MappableTests.swift"; sourceTree = ""; }; 559BD458299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SelfIssuedClaimsDescriptor+MappableTests.swift"; sourceTree = ""; }; 559BD45A299D587200BD61AC /* Manifest2022IssuerStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Manifest2022IssuerStyle.swift; sourceTree = ""; }; + 559BD484299D8DCC00BD61AC /* ContractResponder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContractResponder.swift; sourceTree = ""; }; + 559BD49B299D913F00BD61AC /* IssuanceResponseContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssuanceResponseContainer.swift; sourceTree = ""; }; 55A81BC92991AB82002C259A /* RequestResolving.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolving.swift; sourceTree = ""; }; 55A81BE02991AB96002C259A /* RequestHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestHandling.swift; sourceTree = ""; }; 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolverFactory.swift; sourceTree = ""; }; @@ -436,6 +440,7 @@ children = ( 550A919A29940E9E0014D030 /* PresentationRequest+OpenIdRawRequest.swift */, 559BD3AC299AE6C000BD61AC /* IssuanceRequest+RawContract.swift */, + 559BD49B299D913F00BD61AC /* IssuanceResponseContainer.swift */, ); path = Entities; sourceTree = ""; @@ -617,6 +622,7 @@ isa = PBXGroup; children = ( 559BD3A5299AD9BD00BD61AC /* ContractResolver.swift */, + 559BD484299D8DCC00BD61AC /* ContractResponder.swift */, ); path = Contract; sourceTree = ""; @@ -1299,7 +1305,9 @@ 55E3370C293FD61E00CD2ED7 /* SelfAttestedClaimRequirement.swift in Sources */, 55E3376829437E3500CD2ED7 /* OpenIdForVCResolver.swift in Sources */, 55A81BE52991B2E5002C259A /* RequestResolverFactory.swift in Sources */, + 559BD485299D8DCC00BD61AC /* ContractResponder.swift in Sources */, 55E2F086299573C30008010D /* OpenIdVerifierStyle.swift in Sources */, + 559BD49C299D913F00BD61AC /* IssuanceResponseContainer.swift in Sources */, 559BD414299D37B100BD61AC /* SelfIssuedClaimsDescriptor+Mappable.swift in Sources */, 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */, 55E2F071299554110008010D /* LibraryConfiguration.swift in Sources */, diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ContractResponder.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ContractResponder.swift new file mode 100644 index 00000000..914a9a60 --- /dev/null +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ContractResponder.swift @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import VCEntities + +/** + * Protocol is used as a wrapper to wrap the VC SDK send response method. + */ +protocol ContractResponder { + /// Fetches and validates the contract. + func send(response: VCEntities.IssuanceResponseContainer) async throws -> RawVerifiedId +} From 93a8a116e445558f6ae69608549502430f468df6 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 15 Feb 2023 16:25:25 -0800 Subject: [PATCH 134/373] Update AccessTokenRequirement.swift --- .../Requests/Requirements/AccessTokenRequirement.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Requirements/AccessTokenRequirement.swift b/WalletLibrary/WalletLibrary/Requests/Requirements/AccessTokenRequirement.swift index a7c8cd0b..dc5e7f96 100644 --- a/WalletLibrary/WalletLibrary/Requests/Requirements/AccessTokenRequirement.swift +++ b/WalletLibrary/WalletLibrary/Requests/Requirements/AccessTokenRequirement.swift @@ -46,9 +46,9 @@ public class AccessTokenRequirement: Requirement { self.scope = scope } - /// Throws error if requirement is not valid. + /// Throws error if requirement is not complete. public func validate() throws { - if accessToken != nil { + if accessToken == nil { throw AccessTokenRequirementError.accessTokenRequirementHasNotBeenFulfilled } } From 007c80017d2e6a557eaacde90e7d5cedfc744624 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 15 Feb 2023 16:25:38 -0800 Subject: [PATCH 135/373] Add comment to Group Requirement. --- .../WalletLibrary/Requests/Requirements/GroupRequirement.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift b/WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift index d38640dd..4ea3e554 100644 --- a/WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift +++ b/WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift @@ -35,6 +35,7 @@ public class GroupRequirement: Requirement { self.requirementOperator = requirementOperator } + /// Throws error if requirement is not valid or incomplete. public func validate() throws { var errorsThrown: [Error] = [] From 3e31ffb46b2a7b013d130fb18b4081336cd3e797 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 15 Feb 2023 16:25:50 -0800 Subject: [PATCH 136/373] Add validation to IdTokenRequirement. --- .../Requirements/IdTokenRequirement.swift | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Requirements/IdTokenRequirement.swift b/WalletLibrary/WalletLibrary/Requests/Requirements/IdTokenRequirement.swift index 9677558f..4137a59f 100644 --- a/WalletLibrary/WalletLibrary/Requests/Requirements/IdTokenRequirement.swift +++ b/WalletLibrary/WalletLibrary/Requests/Requirements/IdTokenRequirement.swift @@ -3,6 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +enum IdTokenRequirementError: Error { + case idTokenRequirementHasNotBeenFulfilled +} + /** * Information to describe an id token required for a Verified Id issuance flow. */ @@ -31,6 +35,8 @@ public class IdTokenRequirement: Requirement { /// the id token retrieved and can be used for validation during an issuance request to an issuance service. public internal(set) var nonce: String? = nil + var idToken: String? + init(encrypted: Bool, required: Bool, configuration: URL, @@ -45,7 +51,15 @@ public class IdTokenRequirement: Requirement { self.scope = scope } + /// Throws error if requirement is not complete. public func validate() throws { - throw VerifiedIdClientError.TODO(message: "implement validate") + if idToken == nil { + throw IdTokenRequirementError.idTokenRequirementHasNotBeenFulfilled + } + } + + /// Fulfill requirement with a raw id token. + public func fulfill(with rawToken: String) { + idToken = rawToken } } From d10853ab4e329c95199a6a29221371666e870b55 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 15 Feb 2023 16:25:56 -0800 Subject: [PATCH 137/373] Update PinRequirement.swift --- .../Requests/Requirements/PinRequirement.swift | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Requirements/PinRequirement.swift b/WalletLibrary/WalletLibrary/Requests/Requirements/PinRequirement.swift index caec40a7..66b2b6db 100644 --- a/WalletLibrary/WalletLibrary/Requests/Requirements/PinRequirement.swift +++ b/WalletLibrary/WalletLibrary/Requests/Requirements/PinRequirement.swift @@ -17,15 +17,27 @@ public class PinRequirement: Requirement { /// The type of the pin such as alphanumeric or numeric. public let type: String + let salt: String? + + var pin: String? + init(required: Bool, length: Int, - type: String) { + type: String, + salt: String?) { self.required = required self.length = length self.type = type + self.salt = salt } + /// Throws error if requirement is not complete. public func validate() throws { throw VerifiedIdClientError.TODO(message: "implement validate") } + + /// Fulfill requirement with a pin. + public func fulfill(with pin: String) { + self.pin = pin + } } From a7c9638ecf94a91602c126a1cc1c3bbb211ab9c4 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 15 Feb 2023 16:26:01 -0800 Subject: [PATCH 138/373] Update SelfAttestedClaimRequirement.swift --- .../Requirements/SelfAttestedClaimRequirement.swift | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Requirements/SelfAttestedClaimRequirement.swift b/WalletLibrary/WalletLibrary/Requests/Requirements/SelfAttestedClaimRequirement.swift index c4a0a47e..f0e396ec 100644 --- a/WalletLibrary/WalletLibrary/Requests/Requirements/SelfAttestedClaimRequirement.swift +++ b/WalletLibrary/WalletLibrary/Requests/Requirements/SelfAttestedClaimRequirement.swift @@ -17,6 +17,8 @@ public class SelfAttestedClaimRequirement: Requirement { /// The claim requested. public let claim: String + var value: String? + init(encrypted: Bool, required: Bool, claim: String) { self.encrypted = encrypted self.required = required @@ -24,6 +26,13 @@ public class SelfAttestedClaimRequirement: Requirement { } public func validate() throws { - throw VerifiedIdClientError.TODO(message: "implement validate") + if value == nil { + throw VerifiedIdClientError.TODO(message: "implement") + } + } + + /// Fulfill requirement with a self-attested value. + public func fulfill(with value: String) { + self.value = value } } From 1b2b1b7d6b0910b2152fec9a351ad17920a2c1b8 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 15 Feb 2023 16:52:58 -0800 Subject: [PATCH 139/373] Update IssuanceResponseContainer+RequestContent.swift --- ...ner.swift => IssuanceResponseContainer+RequestContent.swift} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename WalletLibrary/WalletLibrary/Extensions/Entities/{IssuanceResponseContainer.swift => IssuanceResponseContainer+RequestContent.swift} (97%) diff --git a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer.swift b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+RequestContent.swift similarity index 97% rename from WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer.swift rename to WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+RequestContent.swift index 7e727bee..9be6981e 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+RequestContent.swift @@ -9,7 +9,7 @@ import VCEntities * An extension of the VCEntities.IssuanceRequest class. * TODO: Update RawContract to RawManifest */ -extension VCEntities.IssuanceResponseContainer { +extension VCEntities.IssuanceResponseContainer: RawRequestContent { init(from rawContract: any RawContract, input: VerifiedIdRequestInput) throws { From 0a92bf9d90ae3097813d0f36ac668b3c282e168d Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 15 Feb 2023 16:53:22 -0800 Subject: [PATCH 140/373] Make VerifiedIdIssuanceRequest public. --- .../WalletLibrary/Protocols/Requests/VerifiedIdRequest.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequest.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequest.swift index 482b5ecf..f8e4d347 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequest.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequest.swift @@ -33,7 +33,7 @@ public protocol VerifiedIdRequest { * Internal Protocol that represents an Issuance Request. * TODO: add VerifiedId Style */ -protocol VerifiedIdIssuanceRequest: VerifiedIdRequest where T == VerifiedId { } +public protocol VerifiedIdIssuanceRequest: VerifiedIdRequest where T == VerifiedId { } /** * Internal Protocol that represents a Presentation Request. From 622fa65479f3acdb74bfd9d7ea4927e7442f2192 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 15 Feb 2023 16:57:34 -0800 Subject: [PATCH 141/373] Update name to manifestService --- .../Requests/Handlers/OpenIdRequestHandler.swift | 14 +++++++++----- .../WalletLibrary/VerifiedIdClientBuilder.swift | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift index 0e4da90e..cda7f480 100644 --- a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift +++ b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift @@ -17,11 +17,11 @@ struct OpenIdRequestHandler: RequestHandling { private let configuration: LibraryConfiguration - private let contractResolver: ContractResolver + private let manifestService: ContractResolver & ContractResponder - init(configuration: LibraryConfiguration, contractResolver: ContractResolver) { + init(configuration: LibraryConfiguration, manifestService: ContractResolver & ContractResponder) { self.configuration = configuration - self.contractResolver = contractResolver + self.manifestService = manifestService } /// Create a VeriifiedIdRequest based on the Open Id raw request given. @@ -50,10 +50,14 @@ struct OpenIdRequestHandler: RequestHandling { throw OpenIdRequestHandlerError.noIssuanceOptionsPresentToCreateIssuanceRequest } - let rawContract = try await contractResolver.getRequest(url: issuanceOption.url.absoluteString) + let rawContract = try await manifestService.getRequest(url: issuanceOption.url.absoluteString) /// TODO: add logic here to add PinRequirement to ContractIssuanceRequest if it exists. let issuanceRequestContent = try configuration.mapper.map(rawContract) - return ContractIssuanceRequest(content: issuanceRequestContent, configuration: configuration) + return ContractIssuanceRequest(content: issuanceRequestContent, + rawContract: rawContract, + input: issuanceOption, + contractResponder: manifestService, + configuration: configuration) } private func handlePresentationRequest(from requestContent: VerifiedIdRequestContent) throws -> any VerifiedIdPresentationRequest { diff --git a/WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift b/WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift index d61b9c70..5a8384d4 100644 --- a/WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift +++ b/WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift @@ -50,7 +50,7 @@ public class VerifiedIdClientBuilder { } private func registerSupportedRequestHandlers(with configuration: LibraryConfiguration) { - let openIdHandler = OpenIdRequestHandler(configuration: configuration, contractResolver: IssuanceService()) + let openIdHandler = OpenIdRequestHandler(configuration: configuration, manifestService: IssuanceService()) requestHandlers.append(openIdHandler) } } From d386804f7900bfbfb2d139e730a12c49d38c8d9f Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 15 Feb 2023 16:58:24 -0800 Subject: [PATCH 142/373] Begin complete implementation on ContractIssuanceRequest --- .../Manifest/ContractIssuanceRequest.swift | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift index 4b16670b..df6e4f38 100644 --- a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift +++ b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import VCEntities + /** * Issuance Request that is Contract specific. * TODO: we will need contract specific data to implement complete and cancel. @@ -16,12 +18,25 @@ class ContractIssuanceRequest: VerifiedIdIssuanceRequest { public let rootOfTrust: RootOfTrust + private let contractResponder: ContractResponder + private let configuration: LibraryConfiguration - init(content: VerifiedIdRequestContent, configuration: LibraryConfiguration) { + private let rawContract: any RawContract + + private let input: VerifiedIdRequestInput + + init(content: VerifiedIdRequestContent, + rawContract: any RawContract, + input: VerifiedIdRequestInput, + contractResponder: ContractResponder, + configuration: LibraryConfiguration) { self.style = content.style self.requirement = content.requirement self.rootOfTrust = content.rootOfTrust + self.rawContract = rawContract + self.input = input + self.contractResponder = contractResponder self.configuration = configuration } @@ -31,7 +46,19 @@ class ContractIssuanceRequest: VerifiedIdIssuanceRequest { } public func complete() async -> Result { - return Result.failure(VerifiedIdClientError.TODO(message: "implement")) + do { + var responseContainer = try IssuanceResponseContainer(from: rawContract, input: input) + try responseContainer.add(requirement: requirement) + let rawVerifiedId = try await contractResponder.send(requestContent: responseContainer) + return Result.success(VerifiedId(id: "test", + type: "type", + claims: [], + expiresOn: Date(), + issuedOn: Date(), + raw: rawVerifiedId.raw)) + } catch { + return Result.failure(error) + } } public func cancel(message: String?) -> Result { From b65cd298b5a1df1b6933603b6aece2ad76d091a9 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 15 Feb 2023 16:58:39 -0800 Subject: [PATCH 143/373] Update ContractResponder.swift --- .../Protocols/Requests/Contract/ContractResponder.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ContractResponder.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ContractResponder.swift index 914a9a60..3b6f8230 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ContractResponder.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ContractResponder.swift @@ -3,12 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import VCEntities - /** * Protocol is used as a wrapper to wrap the VC SDK send response method. */ protocol ContractResponder { /// Fetches and validates the contract. - func send(response: VCEntities.IssuanceResponseContainer) async throws -> RawVerifiedId + func send(requestContent: RawRequestContent) async throws -> RawVerifiedId } From 3b836dd6fd6a315c0f55475fd3700224ed0e73d6 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 15 Feb 2023 16:58:49 -0800 Subject: [PATCH 144/373] Update IssuanceService+Contract.swift --- .../Services/IssuanceService+Contract.swift | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+Contract.swift b/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+Contract.swift index 40155542..7d5cc3ff 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+Contract.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+Contract.swift @@ -9,7 +9,7 @@ import VCServices /** * An extension of the VCServices.IssuanceService class. */ -extension IssuanceService: ContractResolver { +extension IssuanceService: ContractResolver, ContractResponder { /// Fetches and validates the issuance request. func getRequest(url: String) async throws -> any RawContract { @@ -20,9 +20,26 @@ extension IssuanceService: ContractResolver { /// Sends the issuance response container and if successful, returns Verifiable Credential. /// If unsuccessful, throws an error. - func send(response: VCEntities.IssuanceResponseContainer) async throws -> VerifiableCredential { - return try await AsyncWrapper().wrap { () in - self.send(response: response) + func send(requestContent: RawRequestContent) async throws -> RawVerifiedId { + + guard let issuanceResponseContainer = requestContent as? IssuanceResponseContainer else { + throw VerifiedIdClientError.TODO(message: "add error") + } + + let verifiableCredential = try await AsyncWrapper().wrap { () in + self.send(response: issuanceResponseContainer) }() + + let rawValue = try verifiableCredential.serialize() + return RawVerifiedId(raw: rawValue) } } + +struct RawVerifiedId { + let raw: String +} + +protocol RawRequestContent { + + mutating func add(requirement: Requirement) throws +} From 050eca3d3357584efd18f0e4f5cdc7a40f5cc8de Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 15 Feb 2023 16:58:52 -0800 Subject: [PATCH 145/373] Update project.pbxproj --- WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 8f62ff4b..fa6bec3d 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -64,7 +64,7 @@ 559BD459299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD458299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift */; }; 559BD45B299D587200BD61AC /* Manifest2022IssuerStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD45A299D587200BD61AC /* Manifest2022IssuerStyle.swift */; }; 559BD485299D8DCC00BD61AC /* ContractResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD484299D8DCC00BD61AC /* ContractResponder.swift */; }; - 559BD49C299D913F00BD61AC /* IssuanceResponseContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD49B299D913F00BD61AC /* IssuanceResponseContainer.swift */; }; + 559BD49C299D913F00BD61AC /* IssuanceResponseContainer+RequestContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD49B299D913F00BD61AC /* IssuanceResponseContainer+RequestContent.swift */; }; 55A81BCA2991AB82002C259A /* RequestResolving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BC92991AB82002C259A /* RequestResolving.swift */; }; 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE02991AB96002C259A /* RequestHandling.swift */; }; 55A81BE52991B2E5002C259A /* RequestResolverFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */; }; @@ -333,7 +333,7 @@ 559BD458299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SelfIssuedClaimsDescriptor+MappableTests.swift"; sourceTree = ""; }; 559BD45A299D587200BD61AC /* Manifest2022IssuerStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Manifest2022IssuerStyle.swift; sourceTree = ""; }; 559BD484299D8DCC00BD61AC /* ContractResponder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContractResponder.swift; sourceTree = ""; }; - 559BD49B299D913F00BD61AC /* IssuanceResponseContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssuanceResponseContainer.swift; sourceTree = ""; }; + 559BD49B299D913F00BD61AC /* IssuanceResponseContainer+RequestContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceResponseContainer+RequestContent.swift"; sourceTree = ""; }; 55A81BC92991AB82002C259A /* RequestResolving.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolving.swift; sourceTree = ""; }; 55A81BE02991AB96002C259A /* RequestHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestHandling.swift; sourceTree = ""; }; 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolverFactory.swift; sourceTree = ""; }; @@ -440,7 +440,7 @@ children = ( 550A919A29940E9E0014D030 /* PresentationRequest+OpenIdRawRequest.swift */, 559BD3AC299AE6C000BD61AC /* IssuanceRequest+RawContract.swift */, - 559BD49B299D913F00BD61AC /* IssuanceResponseContainer.swift */, + 559BD49B299D913F00BD61AC /* IssuanceResponseContainer+RequestContent.swift */, ); path = Entities; sourceTree = ""; @@ -1307,7 +1307,7 @@ 55A81BE52991B2E5002C259A /* RequestResolverFactory.swift in Sources */, 559BD485299D8DCC00BD61AC /* ContractResponder.swift in Sources */, 55E2F086299573C30008010D /* OpenIdVerifierStyle.swift in Sources */, - 559BD49C299D913F00BD61AC /* IssuanceResponseContainer.swift in Sources */, + 559BD49C299D913F00BD61AC /* IssuanceResponseContainer+RequestContent.swift in Sources */, 559BD414299D37B100BD61AC /* SelfIssuedClaimsDescriptor+Mappable.swift in Sources */, 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */, 55E2F071299554110008010D /* LibraryConfiguration.swift in Sources */, From 549286539c3a33921f85429bd69a2b7378e9149f Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 08:55:35 -0800 Subject: [PATCH 146/373] Rename Classes. --- .../WalletLibrary.xcodeproj/project.pbxproj | 24 +++++++++---------- .../Requests/Contract/ContractResponder.swift | 12 ---------- ...tResolver.swift => ManifestResolver.swift} | 4 ++-- .../Contract/VerifiedIdRequester.swift | 14 +++++++++++ .../{RawContract.swift => RawManifest.swift} | 2 +- 5 files changed, 29 insertions(+), 27 deletions(-) delete mode 100644 WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ContractResponder.swift rename WalletLibrary/WalletLibrary/Protocols/Requests/Contract/{ContractResolver.swift => ManifestResolver.swift} (83%) create mode 100644 WalletLibrary/WalletLibrary/Protocols/Requests/Contract/VerifiedIdRequester.swift rename WalletLibrary/WalletLibrary/Protocols/Requests/RawRequests/{RawContract.swift => RawManifest.swift} (88%) diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index fa6bec3d..e769cdda 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -51,8 +51,8 @@ 559BD30F2996C1C500BD61AC /* MockLogConsumer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD30E2996C1C500BD61AC /* MockLogConsumer.swift */; }; 559BD3112996D3CE00BD61AC /* MockVerifiedIdRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3102996D3CE00BD61AC /* MockVerifiedIdRequest.swift */; }; 559BD3A3299AD95200BD61AC /* IssuanceService+Contract.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3A2299AD95200BD61AC /* IssuanceService+Contract.swift */; }; - 559BD3A6299AD9BD00BD61AC /* ContractResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3A5299AD9BD00BD61AC /* ContractResolver.swift */; }; - 559BD3A8299ADE9C00BD61AC /* RawContract.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3A7299ADE9C00BD61AC /* RawContract.swift */; }; + 559BD3A6299AD9BD00BD61AC /* ManifestResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3A5299AD9BD00BD61AC /* ManifestResolver.swift */; }; + 559BD3A8299ADE9C00BD61AC /* RawManifest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3A7299ADE9C00BD61AC /* RawManifest.swift */; }; 559BD3AB299ADEFF00BD61AC /* ContractIssuanceRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3AA299ADEFF00BD61AC /* ContractIssuanceRequest.swift */; }; 559BD3AD299AE6C000BD61AC /* IssuanceRequest+RawContract.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3AC299AE6C000BD61AC /* IssuanceRequest+RawContract.swift */; }; 559BD3AF299AF42C00BD61AC /* MockContractResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3AE299AF42C00BD61AC /* MockContractResolver.swift */; }; @@ -63,7 +63,7 @@ 559BD457299D3E6000BD61AC /* AttestationsDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD456299D3E6000BD61AC /* AttestationsDescriptor+MappableTests.swift */; }; 559BD459299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD458299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift */; }; 559BD45B299D587200BD61AC /* Manifest2022IssuerStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD45A299D587200BD61AC /* Manifest2022IssuerStyle.swift */; }; - 559BD485299D8DCC00BD61AC /* ContractResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD484299D8DCC00BD61AC /* ContractResponder.swift */; }; + 559BD485299D8DCC00BD61AC /* VerifiedIdRequester.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD484299D8DCC00BD61AC /* VerifiedIdRequester.swift */; }; 559BD49C299D913F00BD61AC /* IssuanceResponseContainer+RequestContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD49B299D913F00BD61AC /* IssuanceResponseContainer+RequestContent.swift */; }; 55A81BCA2991AB82002C259A /* RequestResolving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BC92991AB82002C259A /* RequestResolving.swift */; }; 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE02991AB96002C259A /* RequestHandling.swift */; }; @@ -320,8 +320,8 @@ 559BD30E2996C1C500BD61AC /* MockLogConsumer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockLogConsumer.swift; sourceTree = ""; }; 559BD3102996D3CE00BD61AC /* MockVerifiedIdRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockVerifiedIdRequest.swift; sourceTree = ""; }; 559BD3A2299AD95200BD61AC /* IssuanceService+Contract.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceService+Contract.swift"; sourceTree = ""; }; - 559BD3A5299AD9BD00BD61AC /* ContractResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContractResolver.swift; sourceTree = ""; }; - 559BD3A7299ADE9C00BD61AC /* RawContract.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RawContract.swift; sourceTree = ""; }; + 559BD3A5299AD9BD00BD61AC /* ManifestResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManifestResolver.swift; sourceTree = ""; }; + 559BD3A7299ADE9C00BD61AC /* RawManifest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RawManifest.swift; sourceTree = ""; }; 559BD3AA299ADEFF00BD61AC /* ContractIssuanceRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContractIssuanceRequest.swift; sourceTree = ""; }; 559BD3AC299AE6C000BD61AC /* IssuanceRequest+RawContract.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceRequest+RawContract.swift"; sourceTree = ""; }; 559BD3AE299AF42C00BD61AC /* MockContractResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockContractResolver.swift; sourceTree = ""; }; @@ -332,7 +332,7 @@ 559BD456299D3E6000BD61AC /* AttestationsDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttestationsDescriptor+MappableTests.swift"; sourceTree = ""; }; 559BD458299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SelfIssuedClaimsDescriptor+MappableTests.swift"; sourceTree = ""; }; 559BD45A299D587200BD61AC /* Manifest2022IssuerStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Manifest2022IssuerStyle.swift; sourceTree = ""; }; - 559BD484299D8DCC00BD61AC /* ContractResponder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContractResponder.swift; sourceTree = ""; }; + 559BD484299D8DCC00BD61AC /* VerifiedIdRequester.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdRequester.swift; sourceTree = ""; }; 559BD49B299D913F00BD61AC /* IssuanceResponseContainer+RequestContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceResponseContainer+RequestContent.swift"; sourceTree = ""; }; 55A81BC92991AB82002C259A /* RequestResolving.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolving.swift; sourceTree = ""; }; 55A81BE02991AB96002C259A /* RequestHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestHandling.swift; sourceTree = ""; }; @@ -449,7 +449,7 @@ isa = PBXGroup; children = ( 550A919D29940ED70014D030 /* OpenIdRawRequest.swift */, - 559BD3A7299ADE9C00BD61AC /* RawContract.swift */, + 559BD3A7299ADE9C00BD61AC /* RawManifest.swift */, ); path = RawRequests; sourceTree = ""; @@ -621,8 +621,8 @@ 559BD3A4299AD9A200BD61AC /* Contract */ = { isa = PBXGroup; children = ( - 559BD3A5299AD9BD00BD61AC /* ContractResolver.swift */, - 559BD484299D8DCC00BD61AC /* ContractResponder.swift */, + 559BD3A5299AD9BD00BD61AC /* ManifestResolver.swift */, + 559BD484299D8DCC00BD61AC /* VerifiedIdRequester.swift */, ); path = Contract; sourceTree = ""; @@ -1272,7 +1272,7 @@ 550E3212298B1150004E7716 /* VerifiedIdRequestInput.swift in Sources */, 550A919B29940E9E0014D030 /* PresentationRequest+OpenIdRawRequest.swift in Sources */, 5534E667294A0B6C005D0765 /* Mappable.swift in Sources */, - 559BD3A8299ADE9C00BD61AC /* RawContract.swift in Sources */, + 559BD3A8299ADE9C00BD61AC /* RawManifest.swift in Sources */, 55E2F08129955A960008010D /* RequestType.swift in Sources */, 55A81BE72991B2F3002C259A /* RequestHandlerFactory.swift in Sources */, 559BD3A3299AD95200BD61AC /* IssuanceService+Contract.swift in Sources */, @@ -1294,7 +1294,7 @@ 550E320E298B0FAC004E7716 /* WalletLibraryLogger.swift in Sources */, 559BD3032995993700BD61AC /* PresentationInputDescriptor+Mappable.swift in Sources */, 55E337B52948B4B000CD2ED7 /* PinRequirement.swift in Sources */, - 559BD3A6299AD9BD00BD61AC /* ContractResolver.swift in Sources */, + 559BD3A6299AD9BD00BD61AC /* ManifestResolver.swift in Sources */, 550A9170299310D40014D030 /* OpenIdURLRequestResolver.swift in Sources */, 5534E669294A0C3F005D0765 /* Mapper.swift in Sources */, 550E320A298B0EA4004E7716 /* VerifiedIdClient.swift in Sources */, @@ -1305,7 +1305,7 @@ 55E3370C293FD61E00CD2ED7 /* SelfAttestedClaimRequirement.swift in Sources */, 55E3376829437E3500CD2ED7 /* OpenIdForVCResolver.swift in Sources */, 55A81BE52991B2E5002C259A /* RequestResolverFactory.swift in Sources */, - 559BD485299D8DCC00BD61AC /* ContractResponder.swift in Sources */, + 559BD485299D8DCC00BD61AC /* VerifiedIdRequester.swift in Sources */, 55E2F086299573C30008010D /* OpenIdVerifierStyle.swift in Sources */, 559BD49C299D913F00BD61AC /* IssuanceResponseContainer+RequestContent.swift in Sources */, 559BD414299D37B100BD61AC /* SelfIssuedClaimsDescriptor+Mappable.swift in Sources */, diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ContractResponder.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ContractResponder.swift deleted file mode 100644 index 3b6f8230..00000000 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ContractResponder.swift +++ /dev/null @@ -1,12 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Microsoft Corporation. All rights reserved. -* Licensed under the MIT License. See License.txt in the project root for license information. -*--------------------------------------------------------------------------------------------*/ - -/** - * Protocol is used as a wrapper to wrap the VC SDK send response method. - */ -protocol ContractResponder { - /// Fetches and validates the contract. - func send(requestContent: RawRequestContent) async throws -> RawVerifiedId -} diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ContractResolver.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ManifestResolver.swift similarity index 83% rename from WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ContractResolver.swift rename to WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ManifestResolver.swift index 0b08631e..121d17f8 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ContractResolver.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ManifestResolver.swift @@ -8,7 +8,7 @@ import VCEntities /** * Protocol is used as a wrapper to wrap the VC SDK get contract method. */ -protocol ContractResolver { +protocol ManifestResolver { /// Fetches and validates the contract. - func getRequest(url: String) async throws -> any RawContract + func resolve(with url: String) async throws -> any RawManifest } diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/Contract/VerifiedIdRequester.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/Contract/VerifiedIdRequester.swift new file mode 100644 index 00000000..6226db7a --- /dev/null +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/Contract/VerifiedIdRequester.swift @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +/** + * Protocol defines the before of taking the given generic request and requesting from an issuer, a Verified Id in raw form. + * For example, it is used as a wrapper to wrap the VC SDK send response method. + */ +protocol VerifiedIdRequester { + + /// Giiven generic request, requests a raw Verified Id from an issuer. + func send(request: Request) async throws -> RawVerifiedId +} diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/RawRequests/RawContract.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/RawRequests/RawManifest.swift similarity index 88% rename from WalletLibrary/WalletLibrary/Protocols/Requests/RawRequests/RawContract.swift rename to WalletLibrary/WalletLibrary/Protocols/Requests/RawRequests/RawManifest.swift index 041f4473..405cb341 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/RawRequests/RawContract.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/RawRequests/RawManifest.swift @@ -7,5 +7,5 @@ * Representation of a Raw Contract. * Object that conforms to this protocol must be able to map to VerifiedIdRequestContent. */ -protocol RawContract: Mappable where T == VerifiedIdRequestContent {} +protocol RawManifest: Mappable where T == VerifiedIdRequestContent {} From 106f2727bb150619bbbd147453263db12f7aea91 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 08:56:00 -0800 Subject: [PATCH 147/373] Replace contract noun with manifest. --- .../Extensions/Entities/IssuanceRequest+RawContract.swift | 2 +- .../IssuanceResponseContainer+RequestContent.swift | 6 +++--- .../Extensions/Services/IssuanceService+Contract.swift | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceRequest+RawContract.swift b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceRequest+RawContract.swift index e67392a4..d5421c3c 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceRequest+RawContract.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceRequest+RawContract.swift @@ -9,4 +9,4 @@ import VCEntities * An extension of the VCEntities.IssuanceRequest class. * TODO: Update RawContract to RawManifest */ -extension VCEntities.IssuanceRequest: RawContract { } +extension VCEntities.IssuanceRequest: RawManifest { } diff --git a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+RequestContent.swift b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+RequestContent.swift index 9be6981e..812096be 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+RequestContent.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+RequestContent.swift @@ -11,9 +11,9 @@ import VCEntities */ extension VCEntities.IssuanceResponseContainer: RawRequestContent { - init(from rawContract: any RawContract, input: VerifiedIdRequestInput) throws { + init(from manifest: any RawManifest, input: VerifiedIdRequestInput) throws { - guard let contract = rawContract as? IssuanceRequest else { + guard let manifest = manifest as? IssuanceRequest else { throw VerifiedIdClientError.TODO(message: "implement") } @@ -21,7 +21,7 @@ extension VCEntities.IssuanceResponseContainer: RawRequestContent { throw VerifiedIdClientError.TODO(message: "implement") } - try self.init(from: contract.content, contractUri: input.url.absoluteString) + try self.init(from: manifest.content, contractUri: input.url.absoluteString) } mutating func add(requirement: Requirement) throws { diff --git a/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+Contract.swift b/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+Contract.swift index 7d5cc3ff..5ea09bb9 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+Contract.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+Contract.swift @@ -9,10 +9,10 @@ import VCServices /** * An extension of the VCServices.IssuanceService class. */ -extension IssuanceService: ContractResolver, ContractResponder { +extension IssuanceService: ManifestResolver, VerifiedIdRequester { /// Fetches and validates the issuance request. - func getRequest(url: String) async throws -> any RawContract { + func resolve(with url: String) async throws -> any RawManifest { return try await AsyncWrapper().wrap { () in self.getRequest(usingUrl: url) }() @@ -20,9 +20,9 @@ extension IssuanceService: ContractResolver, ContractResponder { /// Sends the issuance response container and if successful, returns Verifiable Credential. /// If unsuccessful, throws an error. - func send(requestContent: RawRequestContent) async throws -> RawVerifiedId { + func send(request: Request) async throws -> RawVerifiedId { - guard let issuanceResponseContainer = requestContent as? IssuanceResponseContainer else { + guard let issuanceResponseContainer = request as? IssuanceResponseContainer else { throw VerifiedIdClientError.TODO(message: "add error") } From 8a592e703d863acb8207e882064d4009f51a5c4e Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 08:56:36 -0800 Subject: [PATCH 148/373] add manifestService --- .../Requests/Handlers/OpenIdRequestHandler.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift index cda7f480..afa75031 100644 --- a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift +++ b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift @@ -17,9 +17,9 @@ struct OpenIdRequestHandler: RequestHandling { private let configuration: LibraryConfiguration - private let manifestService: ContractResolver & ContractResponder + private let manifestService: ManifestResolver & VerifiedIdRequester - init(configuration: LibraryConfiguration, manifestService: ContractResolver & ContractResponder) { + init(configuration: LibraryConfiguration, manifestService: ManifestResolver & VerifiedIdRequester) { self.configuration = configuration self.manifestService = manifestService } @@ -50,13 +50,13 @@ struct OpenIdRequestHandler: RequestHandling { throw OpenIdRequestHandlerError.noIssuanceOptionsPresentToCreateIssuanceRequest } - let rawContract = try await manifestService.getRequest(url: issuanceOption.url.absoluteString) + let rawContract = try await manifestService.resolve(with: issuanceOption.url.absoluteString) /// TODO: add logic here to add PinRequirement to ContractIssuanceRequest if it exists. let issuanceRequestContent = try configuration.mapper.map(rawContract) return ContractIssuanceRequest(content: issuanceRequestContent, rawContract: rawContract, input: issuanceOption, - contractResponder: manifestService, + verifiedIdRequester: manifestService, configuration: configuration) } From 604746c7ec17ccf8f8645f8226ce7e10e97a664c Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 08:56:55 -0800 Subject: [PATCH 149/373] ContractResponder --> VerifiedIdRequester. --- .../Manifest/ContractIssuanceRequest.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift index df6e4f38..557568ce 100644 --- a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift +++ b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift @@ -18,25 +18,25 @@ class ContractIssuanceRequest: VerifiedIdIssuanceRequest { public let rootOfTrust: RootOfTrust - private let contractResponder: ContractResponder + private let verifiedIdRequester: VerifiedIdRequester private let configuration: LibraryConfiguration - private let rawContract: any RawContract + private let rawContract: any RawManifest private let input: VerifiedIdRequestInput init(content: VerifiedIdRequestContent, - rawContract: any RawContract, + rawContract: any RawManifest, input: VerifiedIdRequestInput, - contractResponder: ContractResponder, + verifiedIdRequester: VerifiedIdRequester, configuration: LibraryConfiguration) { self.style = content.style self.requirement = content.requirement self.rootOfTrust = content.rootOfTrust self.rawContract = rawContract self.input = input - self.contractResponder = contractResponder + self.verifiedIdRequester = verifiedIdRequester self.configuration = configuration } @@ -49,7 +49,7 @@ class ContractIssuanceRequest: VerifiedIdIssuanceRequest { do { var responseContainer = try IssuanceResponseContainer(from: rawContract, input: input) try responseContainer.add(requirement: requirement) - let rawVerifiedId = try await contractResponder.send(requestContent: responseContainer) + let rawVerifiedId = try await verifiedIdRequester.send(request: responseContainer) return Result.success(VerifiedId(id: "test", type: "type", claims: [], From 2a282300c16e9660cc60f54b2af1124ffc606f84 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 08:57:12 -0800 Subject: [PATCH 150/373] Fix naming in tests. --- .../Mocks/Requests/MockContractResolver.swift | 15 ++++++++----- .../Handlers/OpenIdRequestHandlerTests.swift | 22 +++++++++---------- .../OpenIdURLRequestResolverTests.swift | 2 +- 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockContractResolver.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockContractResolver.swift index fc163196..13190e0e 100644 --- a/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockContractResolver.swift +++ b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockContractResolver.swift @@ -4,22 +4,27 @@ *--------------------------------------------------------------------------------------------*/ @testable import WalletLibrary +import VCEntities -class MockContractResolver: ContractResolver { +class MockContractResolver: ManifestResolver, VerifiedIdRequester { - let mockGetRequestCallback: ((String) throws -> any RawContract)? + let mockGetRequestCallback: ((String) throws -> any RawManifest)? - init(mockGetRequestCallback: ((String) throws -> any RawContract)? = nil) { + init(mockGetRequestCallback: ((String) throws -> any RawManifest)? = nil) { self.mockGetRequestCallback = mockGetRequestCallback } - func getRequest(url: String) async throws -> any RawContract { + func resolve(with url: String) async throws -> any RawManifest { return try mockGetRequestCallback?(url) ?? MockRawContract(id: "") } + func send(request: Request) async throws -> RawVerifiedId { + throw VerifiedIdClientError.TODO(message: "implement") + } + } -struct MockRawContract: RawContract, Equatable { +struct MockRawContract: RawManifest, Equatable { let id: String diff --git a/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift index 33fc8b65..0fca5dd8 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift @@ -36,7 +36,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockMapper = MockMapper(mockResults: mockResults) let mockRawRequest = MockOpenIdRawRequest(raw: Data()) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) - let handler = OpenIdRequestHandler(configuration: configuration, contractResolver: MockContractResolver()) + let handler = OpenIdRequestHandler(configuration: configuration, manifestService: MockContractResolver()) // Act let actualRequest = try await handler.handleRequest(from: mockRawRequest) @@ -63,7 +63,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockMapper = MockMapper(mockResults: mockResults) let mockRawRequest = MockOpenIdRawRequest(raw: Data()) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) - let handler = OpenIdRequestHandler(configuration: configuration, contractResolver: MockContractResolver()) + let handler = OpenIdRequestHandler(configuration: configuration, manifestService: MockContractResolver()) // Act do { @@ -97,7 +97,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockMapper = MockMapper(mockResults: mockResults) let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) - let handler = OpenIdRequestHandler(configuration: configuration, contractResolver: MockContractResolver()) + let handler = OpenIdRequestHandler(configuration: configuration, manifestService: MockContractResolver()) // Act do { @@ -135,7 +135,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockMapper = MockMapper(mockResults: mockResults) let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) - let handler = OpenIdRequestHandler(configuration: configuration, contractResolver: MockContractResolver()) + let handler = OpenIdRequestHandler(configuration: configuration, manifestService: MockContractResolver()) // Act do { @@ -173,7 +173,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockMapper = MockMapper(mockResults: mockResults) let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) - let handler = OpenIdRequestHandler(configuration: configuration, contractResolver: MockContractResolver()) + let handler = OpenIdRequestHandler(configuration: configuration, manifestService: MockContractResolver()) // Act do { @@ -209,7 +209,7 @@ class OpenIdRequestHandlerTests: XCTestCase { return nil } - func mockResolveContract(url: String) throws -> any RawContract { + func mockResolveContract(url: String) throws -> any RawManifest { throw ExpectedError.expectedToBeUnableToResolveContract } @@ -217,7 +217,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, - contractResolver: MockContractResolver(mockGetRequestCallback: mockResolveContract)) + manifestService: MockContractResolver(mockGetRequestCallback: mockResolveContract)) // Act do { @@ -257,7 +257,7 @@ class OpenIdRequestHandlerTests: XCTestCase { return nil } - func mockResolveContract(url: String) throws -> any RawContract { + func mockResolveContract(url: String) throws -> any RawManifest { return MockRawContract(id: "testContract345") } @@ -265,7 +265,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, - contractResolver: MockContractResolver(mockGetRequestCallback: mockResolveContract)) + manifestService: MockContractResolver(mockGetRequestCallback: mockResolveContract)) // Act do { @@ -312,7 +312,7 @@ class OpenIdRequestHandlerTests: XCTestCase { return nil } - func mockResolveContract(url: String) throws -> any RawContract { + func mockResolveContract(url: String) throws -> any RawManifest { return MockRawContract(id: "testContract345") } @@ -320,7 +320,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, - contractResolver: MockContractResolver(mockGetRequestCallback: mockResolveContract)) + manifestService: MockContractResolver(mockGetRequestCallback: mockResolveContract)) // Act let actualRequest = try await handler.handleRequest(from: mockRawRequest) diff --git a/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift index 42cac768..fcf2f461 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift @@ -113,7 +113,7 @@ class OpenIdURLRequestResolverTests: XCTestCase { // Arrange let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: Mapper()) - let mockHandler = OpenIdRequestHandler(configuration: configuration, contractResolver: MockContractResolver()) + let mockHandler = OpenIdRequestHandler(configuration: configuration, manifestService: MockContractResolver()) let resolver = OpenIdURLRequestResolver(openIdResolver: MockOpenIdForVCResolver(), configuration: configuration) // Act From e5024027d32b600ae14f8b472a7a0e37bde9d9ba Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 10:07:34 -0800 Subject: [PATCH 151/373] Add errors to IssuanceResponseContainer extension. --- ...onseContainer+RequirementCollection.swift} | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) rename WalletLibrary/WalletLibrary/Extensions/Entities/{IssuanceResponseContainer+RequestContent.swift => IssuanceResponseContainer+RequirementCollection.swift} (62%) diff --git a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+RequestContent.swift b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+RequirementCollection.swift similarity index 62% rename from WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+RequestContent.swift rename to WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+RequirementCollection.swift index 812096be..25f33713 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+RequestContent.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+RequirementCollection.swift @@ -5,20 +5,26 @@ import VCEntities +enum IssuanceResponseContainerError: Error { + case unableToCastVCSDKIssuanceRequestFromRawManifestOfType(String) + case unableToCastVerifiedIdRequestURLFromInputOfType(String) + case unsupportedRequirementOfType(String) +} + /** * An extension of the VCEntities.IssuanceRequest class. * TODO: Update RawContract to RawManifest */ -extension VCEntities.IssuanceResponseContainer: RawRequestContent { +extension VCEntities.IssuanceResponseContainer { init(from manifest: any RawManifest, input: VerifiedIdRequestInput) throws { guard let manifest = manifest as? IssuanceRequest else { - throw VerifiedIdClientError.TODO(message: "implement") + throw IssuanceResponseContainerError.unableToCastVCSDKIssuanceRequestFromRawManifestOfType(String(describing: manifest.self)) } guard let input = input as? VerifiedIdRequestURL else { - throw VerifiedIdClientError.TODO(message: "implement") + throw IssuanceResponseContainerError.unableToCastVerifiedIdRequestURLFromInputOfType(String(describing: input.self)) } try self.init(from: manifest.content, contractUri: input.url.absoluteString) @@ -26,20 +32,20 @@ extension VCEntities.IssuanceResponseContainer: RawRequestContent { mutating func add(requirement: Requirement) throws { switch (requirement) { - case is GroupRequirement: - try add(groupRequirement: requirement as! GroupRequirement) - case is IdTokenRequirement: - try add(idTokenRequirement: requirement as! IdTokenRequirement) - case is AccessTokenRequirement: - try add(accessTokenRequirement: requirement as! AccessTokenRequirement) - case is VerifiedIdRequirement: - try add(verifiedIdRequirement: requirement as! VerifiedIdRequirement) - case is SelfAttestedClaimRequirement: - try add(selfAttestedRequirement: requirement as! SelfAttestedClaimRequirement) - case is PinRequirement: - try add(pinRequirement: requirement as! PinRequirement) + case let groupRequirement as GroupRequirement: + try add(groupRequirement: groupRequirement) + case let idTokenRequirement as IdTokenRequirement: + try add(idTokenRequirement: idTokenRequirement) + case let accessTokenRequirement as AccessTokenRequirement: + try add(accessTokenRequirement: accessTokenRequirement) + case let verifiedIdRequirement as VerifiedIdRequirement: + try add(verifiedIdRequirement: verifiedIdRequirement) + case let selfAttestedClaimRequirement as SelfAttestedClaimRequirement: + try add(selfAttestedRequirement: selfAttestedClaimRequirement) + case let pinRequirement as PinRequirement: + try add(pinRequirement: pinRequirement) default: - throw VerifiedIdClientError.TODO(message: "implement") + throw IssuanceResponseContainerError.unsupportedRequirementOfType(String(describing: requirement.self)) } } From 20953fd24bfa5e729ded3776b34227ee314ed211 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 10:07:53 -0800 Subject: [PATCH 152/373] RawContract --> RawManifest --- ...quest+RawContract.swift => IssuanceRequest+RawManifest.swift} | 1 - 1 file changed, 1 deletion(-) rename WalletLibrary/WalletLibrary/Extensions/Entities/{IssuanceRequest+RawContract.swift => IssuanceRequest+RawManifest.swift} (91%) diff --git a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceRequest+RawContract.swift b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceRequest+RawManifest.swift similarity index 91% rename from WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceRequest+RawContract.swift rename to WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceRequest+RawManifest.swift index d5421c3c..0b9d6303 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceRequest+RawContract.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceRequest+RawManifest.swift @@ -7,6 +7,5 @@ import VCEntities /** * An extension of the VCEntities.IssuanceRequest class. - * TODO: Update RawContract to RawManifest */ extension VCEntities.IssuanceRequest: RawManifest { } From 385f3e5297c0c336296ee24132930d830cb6d893 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 10:08:01 -0800 Subject: [PATCH 153/373] Update IssuanceService+Contract.swift --- .../Extensions/Services/IssuanceService+Contract.swift | 5 ----- 1 file changed, 5 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+Contract.swift b/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+Contract.swift index 5ea09bb9..a392c575 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+Contract.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+Contract.swift @@ -38,8 +38,3 @@ extension IssuanceService: ManifestResolver, VerifiedIdRequester { struct RawVerifiedId { let raw: String } - -protocol RawRequestContent { - - mutating func add(requirement: Requirement) throws -} From 2a8f865884195a8339b4b78a8461a2549c51ee01 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 10:08:06 -0800 Subject: [PATCH 154/373] Update project.pbxproj --- .../WalletLibrary.xcodeproj/project.pbxproj | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index e769cdda..f389e1e2 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -54,7 +54,7 @@ 559BD3A6299AD9BD00BD61AC /* ManifestResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3A5299AD9BD00BD61AC /* ManifestResolver.swift */; }; 559BD3A8299ADE9C00BD61AC /* RawManifest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3A7299ADE9C00BD61AC /* RawManifest.swift */; }; 559BD3AB299ADEFF00BD61AC /* ContractIssuanceRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3AA299ADEFF00BD61AC /* ContractIssuanceRequest.swift */; }; - 559BD3AD299AE6C000BD61AC /* IssuanceRequest+RawContract.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3AC299AE6C000BD61AC /* IssuanceRequest+RawContract.swift */; }; + 559BD3AD299AE6C000BD61AC /* IssuanceRequest+RawManifest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3AC299AE6C000BD61AC /* IssuanceRequest+RawManifest.swift */; }; 559BD3AF299AF42C00BD61AC /* MockContractResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3AE299AF42C00BD61AC /* MockContractResolver.swift */; }; 559BD3B2299B0A0400BD61AC /* AttestationsDescriptor+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3B1299B0A0400BD61AC /* AttestationsDescriptor+Mappable.swift */; }; 559BD414299D37B100BD61AC /* SelfIssuedClaimsDescriptor+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD413299D37B100BD61AC /* SelfIssuedClaimsDescriptor+Mappable.swift */; }; @@ -64,7 +64,7 @@ 559BD459299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD458299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift */; }; 559BD45B299D587200BD61AC /* Manifest2022IssuerStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD45A299D587200BD61AC /* Manifest2022IssuerStyle.swift */; }; 559BD485299D8DCC00BD61AC /* VerifiedIdRequester.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD484299D8DCC00BD61AC /* VerifiedIdRequester.swift */; }; - 559BD49C299D913F00BD61AC /* IssuanceResponseContainer+RequestContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD49B299D913F00BD61AC /* IssuanceResponseContainer+RequestContent.swift */; }; + 559BD49C299D913F00BD61AC /* IssuanceResponseContainer+RequirementCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD49B299D913F00BD61AC /* IssuanceResponseContainer+RequirementCollection.swift */; }; 55A81BCA2991AB82002C259A /* RequestResolving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BC92991AB82002C259A /* RequestResolving.swift */; }; 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE02991AB96002C259A /* RequestHandling.swift */; }; 55A81BE52991B2E5002C259A /* RequestResolverFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */; }; @@ -323,7 +323,7 @@ 559BD3A5299AD9BD00BD61AC /* ManifestResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManifestResolver.swift; sourceTree = ""; }; 559BD3A7299ADE9C00BD61AC /* RawManifest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RawManifest.swift; sourceTree = ""; }; 559BD3AA299ADEFF00BD61AC /* ContractIssuanceRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContractIssuanceRequest.swift; sourceTree = ""; }; - 559BD3AC299AE6C000BD61AC /* IssuanceRequest+RawContract.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceRequest+RawContract.swift"; sourceTree = ""; }; + 559BD3AC299AE6C000BD61AC /* IssuanceRequest+RawManifest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceRequest+RawManifest.swift"; sourceTree = ""; }; 559BD3AE299AF42C00BD61AC /* MockContractResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockContractResolver.swift; sourceTree = ""; }; 559BD3B1299B0A0400BD61AC /* AttestationsDescriptor+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttestationsDescriptor+Mappable.swift"; sourceTree = ""; }; 559BD413299D37B100BD61AC /* SelfIssuedClaimsDescriptor+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SelfIssuedClaimsDescriptor+Mappable.swift"; sourceTree = ""; }; @@ -333,7 +333,7 @@ 559BD458299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SelfIssuedClaimsDescriptor+MappableTests.swift"; sourceTree = ""; }; 559BD45A299D587200BD61AC /* Manifest2022IssuerStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Manifest2022IssuerStyle.swift; sourceTree = ""; }; 559BD484299D8DCC00BD61AC /* VerifiedIdRequester.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdRequester.swift; sourceTree = ""; }; - 559BD49B299D913F00BD61AC /* IssuanceResponseContainer+RequestContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceResponseContainer+RequestContent.swift"; sourceTree = ""; }; + 559BD49B299D913F00BD61AC /* IssuanceResponseContainer+RequirementCollection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceResponseContainer+RequirementCollection.swift"; sourceTree = ""; }; 55A81BC92991AB82002C259A /* RequestResolving.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolving.swift; sourceTree = ""; }; 55A81BE02991AB96002C259A /* RequestHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestHandling.swift; sourceTree = ""; }; 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolverFactory.swift; sourceTree = ""; }; @@ -439,8 +439,8 @@ isa = PBXGroup; children = ( 550A919A29940E9E0014D030 /* PresentationRequest+OpenIdRawRequest.swift */, - 559BD3AC299AE6C000BD61AC /* IssuanceRequest+RawContract.swift */, - 559BD49B299D913F00BD61AC /* IssuanceResponseContainer+RequestContent.swift */, + 559BD3AC299AE6C000BD61AC /* IssuanceRequest+RawManifest.swift */, + 559BD49B299D913F00BD61AC /* IssuanceResponseContainer+RequirementCollection.swift */, ); path = Entities; sourceTree = ""; @@ -1299,7 +1299,7 @@ 5534E669294A0C3F005D0765 /* Mapper.swift in Sources */, 550E320A298B0EA4004E7716 /* VerifiedIdClient.swift in Sources */, 559BD45B299D587200BD61AC /* Manifest2022IssuerStyle.swift in Sources */, - 559BD3AD299AE6C000BD61AC /* IssuanceRequest+RawContract.swift in Sources */, + 559BD3AD299AE6C000BD61AC /* IssuanceRequest+RawManifest.swift in Sources */, 559BD3B2299B0A0400BD61AC /* AttestationsDescriptor+Mappable.swift in Sources */, 5534E64D294A0888005D0765 /* PresentationDescriptor+Mappable.swift in Sources */, 55E3370C293FD61E00CD2ED7 /* SelfAttestedClaimRequirement.swift in Sources */, @@ -1307,7 +1307,7 @@ 55A81BE52991B2E5002C259A /* RequestResolverFactory.swift in Sources */, 559BD485299D8DCC00BD61AC /* VerifiedIdRequester.swift in Sources */, 55E2F086299573C30008010D /* OpenIdVerifierStyle.swift in Sources */, - 559BD49C299D913F00BD61AC /* IssuanceResponseContainer+RequestContent.swift in Sources */, + 559BD49C299D913F00BD61AC /* IssuanceResponseContainer+RequirementCollection.swift in Sources */, 559BD414299D37B100BD61AC /* SelfIssuedClaimsDescriptor+Mappable.swift in Sources */, 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */, 55E2F071299554110008010D /* LibraryConfiguration.swift in Sources */, From cab10d8a2bc23239595dcc1d3654ee00632e090c Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 14:10:26 -0800 Subject: [PATCH 155/373] Split Issuance Service extensions. --- .../WalletLibrary.xcodeproj/project.pbxproj | 32 ++++++++++++++----- .../IssuanceService+ManifestResolver.swift | 20 ++++++++++++ ...rvice+VerifiableCredentialRequester.swift} | 24 ++++---------- 3 files changed, 51 insertions(+), 25 deletions(-) create mode 100644 WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+ManifestResolver.swift rename WalletLibrary/WalletLibrary/Extensions/Services/{IssuanceService+Contract.swift => IssuanceService+VerifiableCredentialRequester.swift} (56%) diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index f389e1e2..ca736180 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -50,7 +50,7 @@ 559BD30D2996C12500BD61AC /* VerifiedIdClientBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD30C2996C12500BD61AC /* VerifiedIdClientBuilderTests.swift */; }; 559BD30F2996C1C500BD61AC /* MockLogConsumer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD30E2996C1C500BD61AC /* MockLogConsumer.swift */; }; 559BD3112996D3CE00BD61AC /* MockVerifiedIdRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3102996D3CE00BD61AC /* MockVerifiedIdRequest.swift */; }; - 559BD3A3299AD95200BD61AC /* IssuanceService+Contract.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3A2299AD95200BD61AC /* IssuanceService+Contract.swift */; }; + 559BD3A3299AD95200BD61AC /* IssuanceService+ManifestResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3A2299AD95200BD61AC /* IssuanceService+ManifestResolver.swift */; }; 559BD3A6299AD9BD00BD61AC /* ManifestResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3A5299AD9BD00BD61AC /* ManifestResolver.swift */; }; 559BD3A8299ADE9C00BD61AC /* RawManifest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3A7299ADE9C00BD61AC /* RawManifest.swift */; }; 559BD3AB299ADEFF00BD61AC /* ContractIssuanceRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3AA299ADEFF00BD61AC /* ContractIssuanceRequest.swift */; }; @@ -63,8 +63,10 @@ 559BD457299D3E6000BD61AC /* AttestationsDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD456299D3E6000BD61AC /* AttestationsDescriptor+MappableTests.swift */; }; 559BD459299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD458299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift */; }; 559BD45B299D587200BD61AC /* Manifest2022IssuerStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD45A299D587200BD61AC /* Manifest2022IssuerStyle.swift */; }; - 559BD485299D8DCC00BD61AC /* VerifiedIdRequester.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD484299D8DCC00BD61AC /* VerifiedIdRequester.swift */; }; + 559BD485299D8DCC00BD61AC /* VerifiableCredentialRequester.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD484299D8DCC00BD61AC /* VerifiableCredentialRequester.swift */; }; 559BD49C299D913F00BD61AC /* IssuanceResponseContainer+RequirementCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD49B299D913F00BD61AC /* IssuanceResponseContainer+RequirementCollection.swift */; }; + 559BD4B3299EDFB400BD61AC /* VerifiableCredential.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4B2299EDFB400BD61AC /* VerifiableCredential.swift */; }; + 559BD4B5299EE10800BD61AC /* IssuanceService+VerifiableCredentialRequester.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4B4299EE10800BD61AC /* IssuanceService+VerifiableCredentialRequester.swift */; }; 55A81BCA2991AB82002C259A /* RequestResolving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BC92991AB82002C259A /* RequestResolving.swift */; }; 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE02991AB96002C259A /* RequestHandling.swift */; }; 55A81BE52991B2E5002C259A /* RequestResolverFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */; }; @@ -319,7 +321,7 @@ 559BD30C2996C12500BD61AC /* VerifiedIdClientBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdClientBuilderTests.swift; sourceTree = ""; }; 559BD30E2996C1C500BD61AC /* MockLogConsumer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockLogConsumer.swift; sourceTree = ""; }; 559BD3102996D3CE00BD61AC /* MockVerifiedIdRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockVerifiedIdRequest.swift; sourceTree = ""; }; - 559BD3A2299AD95200BD61AC /* IssuanceService+Contract.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceService+Contract.swift"; sourceTree = ""; }; + 559BD3A2299AD95200BD61AC /* IssuanceService+ManifestResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceService+ManifestResolver.swift"; sourceTree = ""; }; 559BD3A5299AD9BD00BD61AC /* ManifestResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManifestResolver.swift; sourceTree = ""; }; 559BD3A7299ADE9C00BD61AC /* RawManifest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RawManifest.swift; sourceTree = ""; }; 559BD3AA299ADEFF00BD61AC /* ContractIssuanceRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContractIssuanceRequest.swift; sourceTree = ""; }; @@ -332,8 +334,10 @@ 559BD456299D3E6000BD61AC /* AttestationsDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttestationsDescriptor+MappableTests.swift"; sourceTree = ""; }; 559BD458299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SelfIssuedClaimsDescriptor+MappableTests.swift"; sourceTree = ""; }; 559BD45A299D587200BD61AC /* Manifest2022IssuerStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Manifest2022IssuerStyle.swift; sourceTree = ""; }; - 559BD484299D8DCC00BD61AC /* VerifiedIdRequester.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdRequester.swift; sourceTree = ""; }; + 559BD484299D8DCC00BD61AC /* VerifiableCredentialRequester.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiableCredentialRequester.swift; sourceTree = ""; }; 559BD49B299D913F00BD61AC /* IssuanceResponseContainer+RequirementCollection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceResponseContainer+RequirementCollection.swift"; sourceTree = ""; }; + 559BD4B2299EDFB400BD61AC /* VerifiableCredential.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiableCredential.swift; sourceTree = ""; }; + 559BD4B4299EE10800BD61AC /* IssuanceService+VerifiableCredentialRequester.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceService+VerifiableCredentialRequester.swift"; sourceTree = ""; }; 55A81BC92991AB82002C259A /* RequestResolving.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolving.swift; sourceTree = ""; }; 55A81BE02991AB96002C259A /* RequestHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestHandling.swift; sourceTree = ""; }; 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolverFactory.swift; sourceTree = ""; }; @@ -560,7 +564,8 @@ 5534E664294A0B3E005D0765 /* Services */ = { isa = PBXGroup; children = ( - 559BD3A2299AD95200BD61AC /* IssuanceService+Contract.swift */, + 559BD3A2299AD95200BD61AC /* IssuanceService+ManifestResolver.swift */, + 559BD4B4299EE10800BD61AC /* IssuanceService+VerifiableCredentialRequester.swift */, 55E337BB2948B5FE00CD2ED7 /* PresentationService+OpenIdForVC.swift */, ); path = Services; @@ -622,7 +627,7 @@ isa = PBXGroup; children = ( 559BD3A5299AD9BD00BD61AC /* ManifestResolver.swift */, - 559BD484299D8DCC00BD61AC /* VerifiedIdRequester.swift */, + 559BD484299D8DCC00BD61AC /* VerifiableCredentialRequester.swift */, ); path = Contract; sourceTree = ""; @@ -682,6 +687,14 @@ path = Presentation; sourceTree = ""; }; + 559BD4B1299EDF9E00BD61AC /* VerifiableCredential */ = { + isa = PBXGroup; + children = ( + 559BD4B2299EDFB400BD61AC /* VerifiableCredential.swift */, + ); + path = VerifiableCredential; + sourceTree = ""; + }; 55A81BF02991BB13002C259A /* Requests */ = { isa = PBXGroup; children = ( @@ -831,6 +844,7 @@ 55E336D2293FA74100CD2ED7 /* VerifiedId */ = { isa = PBXGroup; children = ( + 559BD4B1299EDF9E00BD61AC /* VerifiableCredential */, 55E336D3293FA75300CD2ED7 /* VerifiedId.swift */, 55E336D5293FA78C00CD2ED7 /* VerifiedIdClaim.swift */, ); @@ -1275,7 +1289,7 @@ 559BD3A8299ADE9C00BD61AC /* RawManifest.swift in Sources */, 55E2F08129955A960008010D /* RequestType.swift in Sources */, 55A81BE72991B2F3002C259A /* RequestHandlerFactory.swift in Sources */, - 559BD3A3299AD95200BD61AC /* IssuanceService+Contract.swift in Sources */, + 559BD3A3299AD95200BD61AC /* IssuanceService+ManifestResolver.swift in Sources */, 55E2F07F29955A3B0008010D /* VerifiedIdRequestContent.swift in Sources */, 550A916F299310D40014D030 /* OpenIdRequestHandler.swift in Sources */, 55E3370A293FD3E000CD2ED7 /* IdTokenRequirement.swift in Sources */, @@ -1283,6 +1297,7 @@ 550E3217298B1236004E7716 /* RequesterStyle.swift in Sources */, 55E336D1293FA6F400CD2ED7 /* VerifiedIdRequirement.swift in Sources */, 5534E66E294A0F8F005D0765 /* Mapping.swift in Sources */, + 559BD4B3299EDFB400BD61AC /* VerifiableCredential.swift in Sources */, 55E336D6293FA78C00CD2ED7 /* VerifiedIdClaim.swift in Sources */, 55E337652943749600CD2ED7 /* AsyncWrapper.swift in Sources */, 550E3214298B11A5004E7716 /* Requirement.swift in Sources */, @@ -1305,7 +1320,7 @@ 55E3370C293FD61E00CD2ED7 /* SelfAttestedClaimRequirement.swift in Sources */, 55E3376829437E3500CD2ED7 /* OpenIdForVCResolver.swift in Sources */, 55A81BE52991B2E5002C259A /* RequestResolverFactory.swift in Sources */, - 559BD485299D8DCC00BD61AC /* VerifiedIdRequester.swift in Sources */, + 559BD485299D8DCC00BD61AC /* VerifiableCredentialRequester.swift in Sources */, 55E2F086299573C30008010D /* OpenIdVerifierStyle.swift in Sources */, 559BD49C299D913F00BD61AC /* IssuanceResponseContainer+RequirementCollection.swift in Sources */, 559BD414299D37B100BD61AC /* SelfIssuedClaimsDescriptor+Mappable.swift in Sources */, @@ -1320,6 +1335,7 @@ 55E337BC2948B5FE00CD2ED7 /* PresentationService+OpenIdForVC.swift in Sources */, 55E336D4293FA75300CD2ED7 /* VerifiedId.swift in Sources */, 55E2F06C299553300008010D /* PresentationRequest+Mappable.swift in Sources */, + 559BD4B5299EE10800BD61AC /* IssuanceService+VerifiableCredentialRequester.swift in Sources */, 55A81BCA2991AB82002C259A /* RequestResolving.swift in Sources */, 55E2F0882995743D0008010D /* LinkedDomainResult+Mappable.swift in Sources */, 55E33708293FD25900CD2ED7 /* AccessTokenRequirement.swift in Sources */, diff --git a/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+ManifestResolver.swift b/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+ManifestResolver.swift new file mode 100644 index 00000000..5f37400c --- /dev/null +++ b/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+ManifestResolver.swift @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import VCEntities +import VCServices + +/** + * An extension of the VCServices.IssuanceService class. + */ +extension IssuanceService: ManifestResolver { + + /// Fetches and validates the manifest. + func resolve(with url: URL) async throws -> any RawManifest { + return try await AsyncWrapper().wrap { () in + self.getRequest(usingUrl: url.absoluteString) + }() + } +} diff --git a/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+Contract.swift b/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+VerifiableCredentialRequester.swift similarity index 56% rename from WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+Contract.swift rename to WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+VerifiableCredentialRequester.swift index a392c575..77a2c0f3 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+Contract.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+VerifiableCredentialRequester.swift @@ -9,32 +9,22 @@ import VCServices /** * An extension of the VCServices.IssuanceService class. */ -extension IssuanceService: ManifestResolver, VerifiedIdRequester { +extension IssuanceService: VerifiableCredentialRequester { - /// Fetches and validates the issuance request. - func resolve(with url: String) async throws -> any RawManifest { - return try await AsyncWrapper().wrap { () in - self.getRequest(usingUrl: url) - }() - } - - /// Sends the issuance response container and if successful, returns Verifiable Credential. + /// Sends the issuance response container and if successful, returns a Verifiable Credential. /// If unsuccessful, throws an error. - func send(request: Request) async throws -> RawVerifiedId { + func send(request: Request) async throws -> VerifiableCredential { guard let issuanceResponseContainer = request as? IssuanceResponseContainer else { throw VerifiedIdClientError.TODO(message: "add error") } - let verifiableCredential = try await AsyncWrapper().wrap { () in + let rawVerifiableCredential = try await AsyncWrapper().wrap { () in self.send(response: issuanceResponseContainer) }() - let rawValue = try verifiableCredential.serialize() - return RawVerifiedId(raw: rawValue) + let verifiableCredential = VerifiableCredential(raw: rawVerifiableCredential, + from: issuanceResponseContainer.contract) + return verifiableCredential } } - -struct RawVerifiedId { - let raw: String -} From 99061e52e4a231a5864620865dbd049a04c21c03 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 14:10:35 -0800 Subject: [PATCH 156/373] Update ManifestResolver.swift --- .../Protocols/Requests/Contract/ManifestResolver.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ManifestResolver.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ManifestResolver.swift index 121d17f8..c97cdd4e 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ManifestResolver.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ManifestResolver.swift @@ -6,9 +6,10 @@ import VCEntities /** - * Protocol is used as a wrapper to wrap the VC SDK get contract method. + * Protocol defines the behavior of taking a url and resolving a manifest. + * For example, it is used as a wrapper to wrap the VC SDK get contract method. */ protocol ManifestResolver { - /// Fetches and validates the contract. - func resolve(with url: String) async throws -> any RawManifest + /// Fetches and validates the manifest. + func resolve(with url: URL) async throws -> any RawManifest } From 640a41f8883290124c3838522480d3e88fbd7600 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 14:13:30 -0800 Subject: [PATCH 157/373] Update folder structure for manifest. --- WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj | 6 +++--- .../Requests/{Contract => Manifest}/ManifestResolver.swift | 0 .../VerifiableCredentialRequester.swift} | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) rename WalletLibrary/WalletLibrary/Protocols/Requests/{Contract => Manifest}/ManifestResolver.swift (100%) rename WalletLibrary/WalletLibrary/Protocols/Requests/{Contract/VerifiedIdRequester.swift => Manifest/VerifiableCredentialRequester.swift} (67%) diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index ca736180..b257165e 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -623,13 +623,13 @@ path = VCSDK; sourceTree = ""; }; - 559BD3A4299AD9A200BD61AC /* Contract */ = { + 559BD3A4299AD9A200BD61AC /* Manifest */ = { isa = PBXGroup; children = ( 559BD3A5299AD9BD00BD61AC /* ManifestResolver.swift */, 559BD484299D8DCC00BD61AC /* VerifiableCredentialRequester.swift */, ); - path = Contract; + path = Manifest; sourceTree = ""; }; 559BD3A9299ADEF300BD61AC /* Manifest */ = { @@ -956,7 +956,7 @@ 55E337B62948B4F900CD2ED7 /* Requests */ = { isa = PBXGroup; children = ( - 559BD3A4299AD9A200BD61AC /* Contract */, + 559BD3A4299AD9A200BD61AC /* Manifest */, 550A919C29940ECA0014D030 /* RawRequests */, 55E337B72948B50800CD2ED7 /* Handlers */, 55E3376629437E1000CD2ED7 /* OpenIdForVC */, diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ManifestResolver.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/ManifestResolver.swift similarity index 100% rename from WalletLibrary/WalletLibrary/Protocols/Requests/Contract/ManifestResolver.swift rename to WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/ManifestResolver.swift diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/Contract/VerifiedIdRequester.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/VerifiableCredentialRequester.swift similarity index 67% rename from WalletLibrary/WalletLibrary/Protocols/Requests/Contract/VerifiedIdRequester.swift rename to WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/VerifiableCredentialRequester.swift index 6226db7a..aa3b5555 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/Contract/VerifiedIdRequester.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/VerifiableCredentialRequester.swift @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ /** - * Protocol defines the before of taking the given generic request and requesting from an issuer, a Verified Id in raw form. + * Protocol defines the behavior of taking the given generic request and requesting from an issuer, a Verified Id in raw form. * For example, it is used as a wrapper to wrap the VC SDK send response method. */ -protocol VerifiedIdRequester { +protocol VerifiableCredentialRequester { /// Giiven generic request, requests a raw Verified Id from an issuer. - func send(request: Request) async throws -> RawVerifiedId + func send(request: Request) async throws -> VerifiableCredential } From f4ad149261200648b1777a5fefe1ee11d97f6fea Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 14:14:03 -0800 Subject: [PATCH 158/373] Make IssuanceResponseContainer and param in ContractIssuanceRequest init. --- .../Manifest/ContractIssuanceRequest.swift | 27 +++++++------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift index 557568ce..d1bcfda4 100644 --- a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift +++ b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift @@ -18,24 +18,20 @@ class ContractIssuanceRequest: VerifiedIdIssuanceRequest { public let rootOfTrust: RootOfTrust - private let verifiedIdRequester: VerifiedIdRequester + private let verifiedIdRequester: VerifiableCredentialRequester private let configuration: LibraryConfiguration - private let rawContract: any RawManifest - - private let input: VerifiedIdRequestInput + private var responseContainer: IssuanceResponseContainer init(content: VerifiedIdRequestContent, - rawContract: any RawManifest, - input: VerifiedIdRequestInput, - verifiedIdRequester: VerifiedIdRequester, + issuanceResponseContainer: IssuanceResponseContainer, + verifiedIdRequester: VerifiableCredentialRequester, configuration: LibraryConfiguration) { self.style = content.style self.requirement = content.requirement self.rootOfTrust = content.rootOfTrust - self.rawContract = rawContract - self.input = input + self.responseContainer = issuanceResponseContainer self.verifiedIdRequester = verifiedIdRequester self.configuration = configuration } @@ -47,15 +43,10 @@ class ContractIssuanceRequest: VerifiedIdIssuanceRequest { public func complete() async -> Result { do { - var responseContainer = try IssuanceResponseContainer(from: rawContract, input: input) - try responseContainer.add(requirement: requirement) - let rawVerifiedId = try await verifiedIdRequester.send(request: responseContainer) - return Result.success(VerifiedId(id: "test", - type: "type", - claims: [], - expiresOn: Date(), - issuedOn: Date(), - raw: rawVerifiedId.raw)) + try self.responseContainer.add(requirement: requirement) + let verifiableCredential = try await verifiedIdRequester.send(request: responseContainer) + let verifiedId: VerifiedId = try configuration.mapper.map(verifiableCredential) + return Result.success(verifiedId) } catch { return Result.failure(error) } From c927e42e0a16209a3262b83dca65b5983a76cf98 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 14:14:08 -0800 Subject: [PATCH 159/373] Create VerifiableCredential.swift --- .../VerifiableCredential.swift | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift diff --git a/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift b/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift new file mode 100644 index 00000000..ba9da7fe --- /dev/null +++ b/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import VCEntities + +/** + * Verifiable Credential object contains the raw VC, and the contract that created the Verifiable Credential. + * This object conforms to the Mappable protocol and maps VC claims and display contract to a Verified Id. + */ +struct VerifiableCredential: Mappable { + + let raw: VCEntities.VerifiableCredential + + let contract: Contract + + init(raw: VCEntities.VerifiableCredential, from contract: Contract) { + self.raw = raw + self.contract = contract + } + + func map(using mapper: Mapping) throws -> VerifiedId { + throw VerifiedIdClientError.TODO(message: "implement") + } +} From 54959728cf0d138c3f3740fb3aad377c79fbfa9e Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 14:14:18 -0800 Subject: [PATCH 160/373] Update MockContractResolver.swift --- .../Mocks/Requests/MockContractResolver.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockContractResolver.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockContractResolver.swift index 13190e0e..31cdfb09 100644 --- a/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockContractResolver.swift +++ b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockContractResolver.swift @@ -6,7 +6,7 @@ @testable import WalletLibrary import VCEntities -class MockContractResolver: ManifestResolver, VerifiedIdRequester { +class MockContractResolver: ManifestResolver, VerifiableCredentialRequester { let mockGetRequestCallback: ((String) throws -> any RawManifest)? From da6cd08388d079b6f321c9252d6ec935f37f1271 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 14:14:27 -0800 Subject: [PATCH 161/373] Update OpenIdRequestHandler.swift --- .../Requests/Handlers/OpenIdRequestHandler.swift | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift index afa75031..d06078f2 100644 --- a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift +++ b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import VCEntities + enum OpenIdRequestHandlerError: Error { case unsupportedRawRequestType case noIssuanceOptionsPresentToCreateIssuanceRequest @@ -17,9 +19,10 @@ struct OpenIdRequestHandler: RequestHandling { private let configuration: LibraryConfiguration - private let manifestService: ManifestResolver & VerifiedIdRequester + private let manifestService: ManifestResolver & VerifiableCredentialRequester - init(configuration: LibraryConfiguration, manifestService: ManifestResolver & VerifiedIdRequester) { + /// TODO: post private preview, manifest resolving and verified id requester will be handled by processors. + init(configuration: LibraryConfiguration, manifestService: ManifestResolver & VerifiableCredentialRequester) { self.configuration = configuration self.manifestService = manifestService } @@ -50,12 +53,12 @@ struct OpenIdRequestHandler: RequestHandling { throw OpenIdRequestHandlerError.noIssuanceOptionsPresentToCreateIssuanceRequest } - let rawContract = try await manifestService.resolve(with: issuanceOption.url.absoluteString) + let rawContract = try await manifestService.resolve(with: issuanceOption.url) + let issuanceResponseContainer = try IssuanceResponseContainer(from: rawContract, input: issuanceOption) /// TODO: add logic here to add PinRequirement to ContractIssuanceRequest if it exists. let issuanceRequestContent = try configuration.mapper.map(rawContract) return ContractIssuanceRequest(content: issuanceRequestContent, - rawContract: rawContract, - input: issuanceOption, + issuanceResponseContainer: issuanceResponseContainer, verifiedIdRequester: manifestService, configuration: configuration) } From 88b5f2e41ffbfde3f8885702721f2bc1cf975fda Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 14:18:28 -0800 Subject: [PATCH 162/373] Update name of IssuanceResponseContainer extension. --- WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj | 8 ++++---- ...wift => IssuanceResponseContainer+WalletLibrary.swift} | 0 2 files changed, 4 insertions(+), 4 deletions(-) rename WalletLibrary/WalletLibrary/Extensions/Entities/{IssuanceResponseContainer+RequirementCollection.swift => IssuanceResponseContainer+WalletLibrary.swift} (100%) diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index b257165e..9d46a7cf 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -64,7 +64,7 @@ 559BD459299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD458299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift */; }; 559BD45B299D587200BD61AC /* Manifest2022IssuerStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD45A299D587200BD61AC /* Manifest2022IssuerStyle.swift */; }; 559BD485299D8DCC00BD61AC /* VerifiableCredentialRequester.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD484299D8DCC00BD61AC /* VerifiableCredentialRequester.swift */; }; - 559BD49C299D913F00BD61AC /* IssuanceResponseContainer+RequirementCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD49B299D913F00BD61AC /* IssuanceResponseContainer+RequirementCollection.swift */; }; + 559BD49C299D913F00BD61AC /* IssuanceResponseContainer+WalletLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD49B299D913F00BD61AC /* IssuanceResponseContainer+WalletLibrary.swift */; }; 559BD4B3299EDFB400BD61AC /* VerifiableCredential.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4B2299EDFB400BD61AC /* VerifiableCredential.swift */; }; 559BD4B5299EE10800BD61AC /* IssuanceService+VerifiableCredentialRequester.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4B4299EE10800BD61AC /* IssuanceService+VerifiableCredentialRequester.swift */; }; 55A81BCA2991AB82002C259A /* RequestResolving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BC92991AB82002C259A /* RequestResolving.swift */; }; @@ -335,7 +335,7 @@ 559BD458299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SelfIssuedClaimsDescriptor+MappableTests.swift"; sourceTree = ""; }; 559BD45A299D587200BD61AC /* Manifest2022IssuerStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Manifest2022IssuerStyle.swift; sourceTree = ""; }; 559BD484299D8DCC00BD61AC /* VerifiableCredentialRequester.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiableCredentialRequester.swift; sourceTree = ""; }; - 559BD49B299D913F00BD61AC /* IssuanceResponseContainer+RequirementCollection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceResponseContainer+RequirementCollection.swift"; sourceTree = ""; }; + 559BD49B299D913F00BD61AC /* IssuanceResponseContainer+WalletLibrary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceResponseContainer+WalletLibrary.swift"; sourceTree = ""; }; 559BD4B2299EDFB400BD61AC /* VerifiableCredential.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiableCredential.swift; sourceTree = ""; }; 559BD4B4299EE10800BD61AC /* IssuanceService+VerifiableCredentialRequester.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceService+VerifiableCredentialRequester.swift"; sourceTree = ""; }; 55A81BC92991AB82002C259A /* RequestResolving.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolving.swift; sourceTree = ""; }; @@ -444,7 +444,7 @@ children = ( 550A919A29940E9E0014D030 /* PresentationRequest+OpenIdRawRequest.swift */, 559BD3AC299AE6C000BD61AC /* IssuanceRequest+RawManifest.swift */, - 559BD49B299D913F00BD61AC /* IssuanceResponseContainer+RequirementCollection.swift */, + 559BD49B299D913F00BD61AC /* IssuanceResponseContainer+WalletLibrary.swift */, ); path = Entities; sourceTree = ""; @@ -1322,7 +1322,7 @@ 55A81BE52991B2E5002C259A /* RequestResolverFactory.swift in Sources */, 559BD485299D8DCC00BD61AC /* VerifiableCredentialRequester.swift in Sources */, 55E2F086299573C30008010D /* OpenIdVerifierStyle.swift in Sources */, - 559BD49C299D913F00BD61AC /* IssuanceResponseContainer+RequirementCollection.swift in Sources */, + 559BD49C299D913F00BD61AC /* IssuanceResponseContainer+WalletLibrary.swift in Sources */, 559BD414299D37B100BD61AC /* SelfIssuedClaimsDescriptor+Mappable.swift in Sources */, 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */, 55E2F071299554110008010D /* LibraryConfiguration.swift in Sources */, diff --git a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+RequirementCollection.swift b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+WalletLibrary.swift similarity index 100% rename from WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+RequirementCollection.swift rename to WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+WalletLibrary.swift From 216989a13f5528188d2cf76ceecda022ac6d790d Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 15:26:43 -0800 Subject: [PATCH 163/373] Fix error strings in IssuanceResponseContainer. --- .../WalletLibrary.xcodeproj/project.pbxproj | 76 ++++++++++++++++++- ...uanceResponseContainer+WalletLibrary.swift | 10 ++- 2 files changed, 78 insertions(+), 8 deletions(-) diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 9d46a7cf..910339ce 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -55,7 +55,7 @@ 559BD3A8299ADE9C00BD61AC /* RawManifest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3A7299ADE9C00BD61AC /* RawManifest.swift */; }; 559BD3AB299ADEFF00BD61AC /* ContractIssuanceRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3AA299ADEFF00BD61AC /* ContractIssuanceRequest.swift */; }; 559BD3AD299AE6C000BD61AC /* IssuanceRequest+RawManifest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3AC299AE6C000BD61AC /* IssuanceRequest+RawManifest.swift */; }; - 559BD3AF299AF42C00BD61AC /* MockContractResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3AE299AF42C00BD61AC /* MockContractResolver.swift */; }; + 559BD3AF299AF42C00BD61AC /* MockManifestResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3AE299AF42C00BD61AC /* MockManifestResolver.swift */; }; 559BD3B2299B0A0400BD61AC /* AttestationsDescriptor+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3B1299B0A0400BD61AC /* AttestationsDescriptor+Mappable.swift */; }; 559BD414299D37B100BD61AC /* SelfIssuedClaimsDescriptor+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD413299D37B100BD61AC /* SelfIssuedClaimsDescriptor+Mappable.swift */; }; 559BD453299D3DD800BD61AC /* IssuanceRequest+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD452299D3DD800BD61AC /* IssuanceRequest+Mappable.swift */; }; @@ -67,6 +67,15 @@ 559BD49C299D913F00BD61AC /* IssuanceResponseContainer+WalletLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD49B299D913F00BD61AC /* IssuanceResponseContainer+WalletLibrary.swift */; }; 559BD4B3299EDFB400BD61AC /* VerifiableCredential.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4B2299EDFB400BD61AC /* VerifiableCredential.swift */; }; 559BD4B5299EE10800BD61AC /* IssuanceService+VerifiableCredentialRequester.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4B4299EE10800BD61AC /* IssuanceService+VerifiableCredentialRequester.swift */; }; + 559BD4E5299EEA3000BD61AC /* IssuanceResponseContainerExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4E4299EEA3000BD61AC /* IssuanceResponseContainerExtensionTests.swift */; }; + 559BD4E8299EEA8B00BD61AC /* IssuanceService+VerifiableCredentialRequesterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4E7299EEA8B00BD61AC /* IssuanceService+VerifiableCredentialRequesterTests.swift */; }; + 559BD4EB299EEAC400BD61AC /* GroupRequirementTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4EA299EEAC400BD61AC /* GroupRequirementTests.swift */; }; + 559BD4ED299EEAD100BD61AC /* IdTokenRequirementTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4EC299EEAD100BD61AC /* IdTokenRequirementTests.swift */; }; + 559BD4EF299EEADF00BD61AC /* AccessTokenRequirementTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4EE299EEADF00BD61AC /* AccessTokenRequirementTests.swift */; }; + 559BD4F1299EEAEA00BD61AC /* PinRequirementTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4F0299EEAEA00BD61AC /* PinRequirementTests.swift */; }; + 559BD4F3299EEAFD00BD61AC /* SelfAttestedClaimRequirementTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4F2299EEAFD00BD61AC /* SelfAttestedClaimRequirementTests.swift */; }; + 559BD4F5299EEC9500BD61AC /* MockVerifiableCredentialRequester.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4F4299EEC9500BD61AC /* MockVerifiableCredentialRequester.swift */; }; + 559BD4F8299EED5700BD61AC /* MockRawManifest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4F7299EED5700BD61AC /* MockRawManifest.swift */; }; 55A81BCA2991AB82002C259A /* RequestResolving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BC92991AB82002C259A /* RequestResolving.swift */; }; 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE02991AB96002C259A /* RequestHandling.swift */; }; 55A81BE52991B2E5002C259A /* RequestResolverFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */; }; @@ -326,7 +335,7 @@ 559BD3A7299ADE9C00BD61AC /* RawManifest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RawManifest.swift; sourceTree = ""; }; 559BD3AA299ADEFF00BD61AC /* ContractIssuanceRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContractIssuanceRequest.swift; sourceTree = ""; }; 559BD3AC299AE6C000BD61AC /* IssuanceRequest+RawManifest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceRequest+RawManifest.swift"; sourceTree = ""; }; - 559BD3AE299AF42C00BD61AC /* MockContractResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockContractResolver.swift; sourceTree = ""; }; + 559BD3AE299AF42C00BD61AC /* MockManifestResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockManifestResolver.swift; sourceTree = ""; }; 559BD3B1299B0A0400BD61AC /* AttestationsDescriptor+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttestationsDescriptor+Mappable.swift"; sourceTree = ""; }; 559BD413299D37B100BD61AC /* SelfIssuedClaimsDescriptor+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SelfIssuedClaimsDescriptor+Mappable.swift"; sourceTree = ""; }; 559BD452299D3DD800BD61AC /* IssuanceRequest+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceRequest+Mappable.swift"; sourceTree = ""; }; @@ -338,6 +347,15 @@ 559BD49B299D913F00BD61AC /* IssuanceResponseContainer+WalletLibrary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceResponseContainer+WalletLibrary.swift"; sourceTree = ""; }; 559BD4B2299EDFB400BD61AC /* VerifiableCredential.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiableCredential.swift; sourceTree = ""; }; 559BD4B4299EE10800BD61AC /* IssuanceService+VerifiableCredentialRequester.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceService+VerifiableCredentialRequester.swift"; sourceTree = ""; }; + 559BD4E4299EEA3000BD61AC /* IssuanceResponseContainerExtensionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssuanceResponseContainerExtensionTests.swift; sourceTree = ""; }; + 559BD4E7299EEA8B00BD61AC /* IssuanceService+VerifiableCredentialRequesterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceService+VerifiableCredentialRequesterTests.swift"; sourceTree = ""; }; + 559BD4EA299EEAC400BD61AC /* GroupRequirementTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupRequirementTests.swift; sourceTree = ""; }; + 559BD4EC299EEAD100BD61AC /* IdTokenRequirementTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdTokenRequirementTests.swift; sourceTree = ""; }; + 559BD4EE299EEADF00BD61AC /* AccessTokenRequirementTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessTokenRequirementTests.swift; sourceTree = ""; }; + 559BD4F0299EEAEA00BD61AC /* PinRequirementTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinRequirementTests.swift; sourceTree = ""; }; + 559BD4F2299EEAFD00BD61AC /* SelfAttestedClaimRequirementTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelfAttestedClaimRequirementTests.swift; sourceTree = ""; }; + 559BD4F4299EEC9500BD61AC /* MockVerifiableCredentialRequester.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockVerifiableCredentialRequester.swift; sourceTree = ""; }; + 559BD4F7299EED5700BD61AC /* MockRawManifest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRawManifest.swift; sourceTree = ""; }; 55A81BC92991AB82002C259A /* RequestResolving.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolving.swift; sourceTree = ""; }; 55A81BE02991AB96002C259A /* RequestHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestHandling.swift; sourceTree = ""; }; 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolverFactory.swift; sourceTree = ""; }; @@ -600,6 +618,8 @@ 5534E67C294A392F005D0765 /* Extensions */ = { isa = PBXGroup; children = ( + 559BD4E6299EEA6F00BD61AC /* Services */, + 559BD4CE299EEA1500BD61AC /* Entities */, 5534E67D294A3937005D0765 /* Mappings */, ); path = Extensions; @@ -695,11 +715,48 @@ path = VerifiableCredential; sourceTree = ""; }; + 559BD4CE299EEA1500BD61AC /* Entities */ = { + isa = PBXGroup; + children = ( + 559BD4E4299EEA3000BD61AC /* IssuanceResponseContainerExtensionTests.swift */, + ); + path = Entities; + sourceTree = ""; + }; + 559BD4E6299EEA6F00BD61AC /* Services */ = { + isa = PBXGroup; + children = ( + 559BD4E7299EEA8B00BD61AC /* IssuanceService+VerifiableCredentialRequesterTests.swift */, + ); + path = Services; + sourceTree = ""; + }; + 559BD4E9299EEAA200BD61AC /* Requirements */ = { + isa = PBXGroup; + children = ( + 559BD4EE299EEADF00BD61AC /* AccessTokenRequirementTests.swift */, + 559BD4EA299EEAC400BD61AC /* GroupRequirementTests.swift */, + 559BD4EC299EEAD100BD61AC /* IdTokenRequirementTests.swift */, + 559BD4F0299EEAEA00BD61AC /* PinRequirementTests.swift */, + 559BD4F2299EEAFD00BD61AC /* SelfAttestedClaimRequirementTests.swift */, + ); + path = Requirements; + sourceTree = ""; + }; + 559BD4F6299EED4900BD61AC /* Manifest */ = { + isa = PBXGroup; + children = ( + 559BD4F7299EED5700BD61AC /* MockRawManifest.swift */, + ); + path = Manifest; + sourceTree = ""; + }; 55A81BF02991BB13002C259A /* Requests */ = { isa = PBXGroup; children = ( 55E2F06D299553B40008010D /* Handlers */, 550A9193299402450014D030 /* Resolvers */, + 559BD4E9299EEAA200BD61AC /* Requirements */, 55A81C062991BB2C002C259A /* RequestResolverFactoryTests.swift */, 55A81C082991BB3A002C259A /* RequestHandlerFactoryTests.swift */, ); @@ -718,6 +775,7 @@ 55A81C0B2991BF73002C259A /* Requests */ = { isa = PBXGroup; children = ( + 559BD4F6299EED4900BD61AC /* Manifest */, 55A81C112991C81A002C259A /* Handlers */, 550A919629940D810014D030 /* OpenIdForVC */, 55E2F072299555280008010D /* Requirements */, @@ -726,7 +784,8 @@ 55A81C0C2991BF86002C259A /* MockInput.swift */, 550A913C29930D970014D030 /* MockRawRequest.swift */, 559BD3102996D3CE00BD61AC /* MockVerifiedIdRequest.swift */, - 559BD3AE299AF42C00BD61AC /* MockContractResolver.swift */, + 559BD3AE299AF42C00BD61AC /* MockManifestResolver.swift */, + 559BD4F4299EEC9500BD61AC /* MockVerifiableCredentialRequester.swift */, ); path = Requests; sourceTree = ""; @@ -1347,25 +1406,33 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 559BD4F3299EEAFD00BD61AC /* SelfAttestedClaimRequirementTests.swift in Sources */, 55E2F08429955E8A0008010D /* MockMapper.swift in Sources */, 55E2F06F299553B40008010D /* OpenIdRequestHandlerTests.swift in Sources */, 55E3376B29478C5000CD2ED7 /* AsyncWrapperTests.swift in Sources */, 5534E6AD294AAFA6005D0765 /* PresentationDescriptor+MappableTests.swift in Sources */, + 559BD4ED299EEAD100BD61AC /* IdTokenRequirementTests.swift in Sources */, 559BD455299D3E4800BD61AC /* IssuanceRequest+MappableTests.swift in Sources */, 55A81C132991C829002C259A /* MockHandler.swift in Sources */, 559BD3072995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift in Sources */, + 559BD4EB299EEAC400BD61AC /* GroupRequirementTests.swift in Sources */, + 559BD4E8299EEA8B00BD61AC /* IssuanceService+VerifiableCredentialRequesterTests.swift in Sources */, 55A81C072991BB2C002C259A /* RequestResolverFactoryTests.swift in Sources */, + 559BD4EF299EEADF00BD61AC /* AccessTokenRequirementTests.swift in Sources */, 55A81C102991C211002C259A /* MockResolver.swift in Sources */, + 559BD4E5299EEA3000BD61AC /* IssuanceResponseContainerExtensionTests.swift in Sources */, 55A81C092991BB3A002C259A /* RequestHandlerFactoryTests.swift in Sources */, 559BD30D2996C12500BD61AC /* VerifiedIdClientBuilderTests.swift in Sources */, 550A91952994025C0014D030 /* OpenIdURLRequestResolverTests.swift in Sources */, 559BD3052995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift in Sources */, 55A81C0D2991BF86002C259A /* MockInput.swift in Sources */, - 559BD3AF299AF42C00BD61AC /* MockContractResolver.swift in Sources */, + 559BD3AF299AF42C00BD61AC /* MockManifestResolver.swift in Sources */, + 559BD4F8299EED5700BD61AC /* MockRawManifest.swift in Sources */, 550A919829940DCE0014D030 /* MockOpenIdforVCResolver.swift in Sources */, 559BD459299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift in Sources */, 559BD3092995ABE800BD61AC /* PresentationRequest+MappableTests.swift in Sources */, 550A91A0299412790014D030 /* MockOpenIdRawRequest.swift in Sources */, + 559BD4F1299EEAEA00BD61AC /* PinRequirementTests.swift in Sources */, 559BD30F2996C1C500BD61AC /* MockLogConsumer.swift in Sources */, 55E2F076299555280008010D /* MockRequirement.swift in Sources */, 5534E68C294A8B8F005D0765 /* AccessTokenDescriptor+MappableTests.swift in Sources */, @@ -1376,6 +1443,7 @@ 559BD3112996D3CE00BD61AC /* MockVerifiedIdRequest.swift in Sources */, 550A913D29930D970014D030 /* MockRawRequest.swift in Sources */, 559BD30B2995B1B500BD61AC /* LinkedDomainResult+MappableTests.swift in Sources */, + 559BD4F5299EEC9500BD61AC /* MockVerifiableCredentialRequester.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+WalletLibrary.swift b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+WalletLibrary.swift index 25f33713..0ea352ed 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+WalletLibrary.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+WalletLibrary.swift @@ -12,19 +12,21 @@ enum IssuanceResponseContainerError: Error { } /** - * An extension of the VCEntities.IssuanceRequest class. - * TODO: Update RawContract to RawManifest + * An extension of the VCEntities.IssuanceResponseContainer class + * to convert Requirements to mappings in IssuanceResponseContainer. */ extension VCEntities.IssuanceResponseContainer { init(from manifest: any RawManifest, input: VerifiedIdRequestInput) throws { guard let manifest = manifest as? IssuanceRequest else { - throw IssuanceResponseContainerError.unableToCastVCSDKIssuanceRequestFromRawManifestOfType(String(describing: manifest.self)) + let manifestType = String(describing: type(of: manifest)) + throw IssuanceResponseContainerError.unableToCastVCSDKIssuanceRequestFromRawManifestOfType(manifestType) } guard let input = input as? VerifiedIdRequestURL else { - throw IssuanceResponseContainerError.unableToCastVerifiedIdRequestURLFromInputOfType(String(describing: input.self)) + let inputType = String(describing: type(of: input)) + throw IssuanceResponseContainerError.unableToCastVerifiedIdRequestURLFromInputOfType(inputType) } try self.init(from: manifest.content, contractUri: input.url.absoluteString) From b0320eae39c3826d6aa04e2dfbeb22521a038911 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 15:26:53 -0800 Subject: [PATCH 164/373] Update IssuanceService+ManifestResolver.swift --- .../Extensions/Services/IssuanceService+ManifestResolver.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+ManifestResolver.swift b/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+ManifestResolver.swift index 5f37400c..001d1da5 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+ManifestResolver.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+ManifestResolver.swift @@ -7,7 +7,8 @@ import VCEntities import VCServices /** - * An extension of the VCServices.IssuanceService class. + * An extension of the VCServices.IssuanceService class + * that wraps getRequest method with a resolve method that conforms to ManifestResolver protocol. */ extension IssuanceService: ManifestResolver { From 67105af675046ab8cec0fbba5ab1fcaaa0c72a13 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 15:27:07 -0800 Subject: [PATCH 165/373] Add error to Issuance Service extension. --- .../IssuanceService+VerifiableCredentialRequester.swift | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+VerifiableCredentialRequester.swift b/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+VerifiableCredentialRequester.swift index 77a2c0f3..0113af49 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+VerifiableCredentialRequester.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+VerifiableCredentialRequester.swift @@ -6,8 +6,12 @@ import VCEntities import VCServices +enum IssuanceServiceVCRequesterError: Error { + case unableToCastIssuanceResponseContainerFromType(String) +} /** - * An extension of the VCServices.IssuanceService class. + * An extension of the VCServices.IssuanceService class + * that wraps send method with a generic send method that conforms to VerifiableCredentialRequester protocol. */ extension IssuanceService: VerifiableCredentialRequester { @@ -16,7 +20,8 @@ extension IssuanceService: VerifiableCredentialRequester { func send(request: Request) async throws -> VerifiableCredential { guard let issuanceResponseContainer = request as? IssuanceResponseContainer else { - throw VerifiedIdClientError.TODO(message: "add error") + let requestType = String(describing: request.self) + throw IssuanceServiceVCRequesterError.unableToCastIssuanceResponseContainerFromType(requestType) } let rawVerifiableCredential = try await AsyncWrapper().wrap { () in From a188dde8bb85e997a5392e9a0bebe403d528a675 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 15:27:29 -0800 Subject: [PATCH 166/373] Update vc requester comments. --- .../Requests/Manifest/VerifiableCredentialRequester.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/VerifiableCredentialRequester.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/VerifiableCredentialRequester.swift index aa3b5555..e4251406 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/VerifiableCredentialRequester.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/VerifiableCredentialRequester.swift @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ /** - * Protocol defines the behavior of taking the given generic request and requesting from an issuer, a Verified Id in raw form. + * Protocol defines the behavior of given generic request, requests a Verifiable Credential from an issuer. * For example, it is used as a wrapper to wrap the VC SDK send response method. */ protocol VerifiableCredentialRequester { From 4b70143ca902d93b27e18de28b73326cd3325e8a Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 15:27:51 -0800 Subject: [PATCH 167/373] Separate manifest resolver and vc requester in open id handler. --- .../Handlers/OpenIdRequestHandler.swift | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift index d06078f2..fbf46295 100644 --- a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift +++ b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift @@ -19,12 +19,17 @@ struct OpenIdRequestHandler: RequestHandling { private let configuration: LibraryConfiguration - private let manifestService: ManifestResolver & VerifiableCredentialRequester + private let manifestResolver: ManifestResolver + + private let verifiableCredentialRequester: VerifiableCredentialRequester /// TODO: post private preview, manifest resolving and verified id requester will be handled by processors. - init(configuration: LibraryConfiguration, manifestService: ManifestResolver & VerifiableCredentialRequester) { + init(configuration: LibraryConfiguration, + manifestResolver: ManifestResolver, + verifiableCredentialRequester: VerifiableCredentialRequester) { self.configuration = configuration - self.manifestService = manifestService + self.manifestResolver = manifestResolver + self.verifiableCredentialRequester = verifiableCredentialRequester } /// Create a VeriifiedIdRequest based on the Open Id raw request given. @@ -53,13 +58,15 @@ struct OpenIdRequestHandler: RequestHandling { throw OpenIdRequestHandlerError.noIssuanceOptionsPresentToCreateIssuanceRequest } - let rawContract = try await manifestService.resolve(with: issuanceOption.url) + let rawContract = try await manifestResolver.resolve(with: issuanceOption.url) + + let issuanceResponseContainer = try IssuanceResponseContainer(from: rawContract, input: issuanceOption) /// TODO: add logic here to add PinRequirement to ContractIssuanceRequest if it exists. let issuanceRequestContent = try configuration.mapper.map(rawContract) return ContractIssuanceRequest(content: issuanceRequestContent, issuanceResponseContainer: issuanceResponseContainer, - verifiedIdRequester: manifestService, + verifiableCredentialRequester: verifiableCredentialRequester, configuration: configuration) } From f8373c37720ab1c9c0b1e560e608d13f69fa795e Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 15:27:58 -0800 Subject: [PATCH 168/373] Update ContractIssuanceRequest.swift --- .../Manifest/ContractIssuanceRequest.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift index d1bcfda4..a83d111a 100644 --- a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift +++ b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift @@ -18,7 +18,7 @@ class ContractIssuanceRequest: VerifiedIdIssuanceRequest { public let rootOfTrust: RootOfTrust - private let verifiedIdRequester: VerifiableCredentialRequester + private let verifiableCredentialRequester: VerifiableCredentialRequester private let configuration: LibraryConfiguration @@ -26,13 +26,13 @@ class ContractIssuanceRequest: VerifiedIdIssuanceRequest { init(content: VerifiedIdRequestContent, issuanceResponseContainer: IssuanceResponseContainer, - verifiedIdRequester: VerifiableCredentialRequester, + verifiableCredentialRequester: VerifiableCredentialRequester, configuration: LibraryConfiguration) { self.style = content.style self.requirement = content.requirement self.rootOfTrust = content.rootOfTrust self.responseContainer = issuanceResponseContainer - self.verifiedIdRequester = verifiedIdRequester + self.verifiableCredentialRequester = verifiableCredentialRequester self.configuration = configuration } @@ -44,7 +44,7 @@ class ContractIssuanceRequest: VerifiedIdIssuanceRequest { public func complete() async -> Result { do { try self.responseContainer.add(requirement: requirement) - let verifiableCredential = try await verifiedIdRequester.send(request: responseContainer) + let verifiableCredential = try await verifiableCredentialRequester.send(request: responseContainer) let verifiedId: VerifiedId = try configuration.mapper.map(verifiableCredential) return Result.success(verifiedId) } catch { From 8aa0954dfe0eb1d68061fb6901eecda269194793 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 15:28:01 -0800 Subject: [PATCH 169/373] Update VerifiedIdClientBuilder.swift --- WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift b/WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift index 5a8384d4..369d8d2e 100644 --- a/WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift +++ b/WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift @@ -50,7 +50,10 @@ public class VerifiedIdClientBuilder { } private func registerSupportedRequestHandlers(with configuration: LibraryConfiguration) { - let openIdHandler = OpenIdRequestHandler(configuration: configuration, manifestService: IssuanceService()) + let issuanceService = IssuanceService() + let openIdHandler = OpenIdRequestHandler(configuration: configuration, + manifestResolver: issuanceService, + verifiableCredentialRequester: issuanceService) requestHandlers.append(openIdHandler) } } From 5af185c0a69d82f019360e7b2cc5ddf5c900a919 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 15:28:39 -0800 Subject: [PATCH 170/373] Update tests with new data models. --- .../Requests/Manifest/MockRawManifest.swift | 19 ++++++ .../Mocks/Requests/MockContractResolver.swift | 38 ----------- .../Mocks/Requests/MockManifestResolver.swift | 20 ++++++ .../MockVerifiableCredentialRequester.swift | 26 ++++++++ .../Handlers/OpenIdRequestHandlerTests.swift | 65 +++++++++++++++---- .../OpenIdURLRequestResolverTests.swift | 4 +- 6 files changed, 120 insertions(+), 52 deletions(-) create mode 100644 WalletLibrary/WalletLibraryTests/Mocks/Requests/Manifest/MockRawManifest.swift delete mode 100644 WalletLibrary/WalletLibraryTests/Mocks/Requests/MockContractResolver.swift create mode 100644 WalletLibrary/WalletLibraryTests/Mocks/Requests/MockManifestResolver.swift create mode 100644 WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiableCredentialRequester.swift diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/Manifest/MockRawManifest.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/Manifest/MockRawManifest.swift new file mode 100644 index 00000000..6f29330f --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Mocks/Requests/Manifest/MockRawManifest.swift @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +@testable import WalletLibrary + +struct MockRawManifest: RawManifest, Equatable { + + let id: String + + init(id: String) { + self.id = id + } + + func map(using mapper: Mapping) throws -> VerifiedIdRequestContent { + throw VerifiedIdClientError.TODO(message: "implement") + } +} diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockContractResolver.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockContractResolver.swift deleted file mode 100644 index 31cdfb09..00000000 --- a/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockContractResolver.swift +++ /dev/null @@ -1,38 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Microsoft Corporation. All rights reserved. -* Licensed under the MIT License. See License.txt in the project root for license information. -*--------------------------------------------------------------------------------------------*/ - -@testable import WalletLibrary -import VCEntities - -class MockContractResolver: ManifestResolver, VerifiableCredentialRequester { - - let mockGetRequestCallback: ((String) throws -> any RawManifest)? - - init(mockGetRequestCallback: ((String) throws -> any RawManifest)? = nil) { - self.mockGetRequestCallback = mockGetRequestCallback - } - - func resolve(with url: String) async throws -> any RawManifest { - return try mockGetRequestCallback?(url) ?? MockRawContract(id: "") - } - - func send(request: Request) async throws -> RawVerifiedId { - throw VerifiedIdClientError.TODO(message: "implement") - } - -} - -struct MockRawContract: RawManifest, Equatable { - - let id: String - - init(id: String) { - self.id = id - } - - func map(using mapper: WalletLibrary.Mapping) throws -> WalletLibrary.VerifiedIdRequestContent { - throw VerifiedIdClientError.TODO(message: "implement") - } -} diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockManifestResolver.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockManifestResolver.swift new file mode 100644 index 00000000..0d2fc35a --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockManifestResolver.swift @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +@testable import WalletLibrary +import VCEntities + +class MockManifestResolver: ManifestResolver { + + let mockGetRequestCallback: ((String) throws -> any RawManifest)? + + init(mockGetRequestCallback: ((String) throws -> any RawManifest)? = nil) { + self.mockGetRequestCallback = mockGetRequestCallback + } + + func resolve(with url: URL) async throws -> any RawManifest { + return try mockGetRequestCallback?(url.absoluteString) ?? MockRawManifest(id: "") + } +} diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiableCredentialRequester.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiableCredentialRequester.swift new file mode 100644 index 00000000..1461cfd8 --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiableCredentialRequester.swift @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +@testable import WalletLibrary +import VCEntities + +class MockVerifiableCredentialRequester: VerifiableCredentialRequester { + + let sendRequestCallback: ((String) throws -> WalletLibrary.VerifiableCredential)? + + init(sendRequestCallback: ((String) throws -> WalletLibrary.VerifiableCredential)? = nil) { + self.sendRequestCallback = sendRequestCallback + } + + func send(request: Request) async throws -> WalletLibrary.VerifiableCredential { + + if let sendRequestCallback = sendRequestCallback { + return try sendRequestCallback("test") + } + + throw VerifiedIdClientError.TODO(message: "add") + } + +} diff --git a/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift index 0fca5dd8..23ecc18a 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift @@ -5,6 +5,7 @@ import XCTest import VCEntities +import VCToken @testable import WalletLibrary class OpenIdRequestHandlerTests: XCTestCase { @@ -36,7 +37,9 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockMapper = MockMapper(mockResults: mockResults) let mockRawRequest = MockOpenIdRawRequest(raw: Data()) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) - let handler = OpenIdRequestHandler(configuration: configuration, manifestService: MockContractResolver()) + let handler = OpenIdRequestHandler(configuration: configuration, + manifestResolver: MockManifestResolver(), + verifiableCredentialRequester: MockVerifiableCredentialRequester()) // Act let actualRequest = try await handler.handleRequest(from: mockRawRequest) @@ -63,7 +66,9 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockMapper = MockMapper(mockResults: mockResults) let mockRawRequest = MockOpenIdRawRequest(raw: Data()) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) - let handler = OpenIdRequestHandler(configuration: configuration, manifestService: MockContractResolver()) + let handler = OpenIdRequestHandler(configuration: configuration, + manifestResolver: MockManifestResolver(), + verifiableCredentialRequester: MockVerifiableCredentialRequester()) // Act do { @@ -97,7 +102,9 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockMapper = MockMapper(mockResults: mockResults) let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) - let handler = OpenIdRequestHandler(configuration: configuration, manifestService: MockContractResolver()) + let handler = OpenIdRequestHandler(configuration: configuration, + manifestResolver: MockManifestResolver(), + verifiableCredentialRequester: MockVerifiableCredentialRequester()) // Act do { @@ -135,7 +142,9 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockMapper = MockMapper(mockResults: mockResults) let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) - let handler = OpenIdRequestHandler(configuration: configuration, manifestService: MockContractResolver()) + let handler = OpenIdRequestHandler(configuration: configuration, + manifestResolver: MockManifestResolver(), + verifiableCredentialRequester: MockVerifiableCredentialRequester()) // Act do { @@ -173,7 +182,9 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockMapper = MockMapper(mockResults: mockResults) let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) - let handler = OpenIdRequestHandler(configuration: configuration, manifestService: MockContractResolver()) + let handler = OpenIdRequestHandler(configuration: configuration, + manifestResolver: MockManifestResolver(), + verifiableCredentialRequester: MockVerifiableCredentialRequester()) // Act do { @@ -217,7 +228,8 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, - manifestService: MockContractResolver(mockGetRequestCallback: mockResolveContract)) + manifestResolver: MockManifestResolver(mockGetRequestCallback: mockResolveContract), + verifiableCredentialRequester: MockVerifiableCredentialRequester()) // Act do { @@ -250,7 +262,7 @@ class OpenIdRequestHandlerTests: XCTestCase { return expectedContent } - if objectToBeMapped is MockRawContract { + if objectToBeMapped is IssuanceRequest { throw ExpectedError.expectedToBeUnableToMapRawContractToVerifiedIdContent } @@ -258,14 +270,15 @@ class OpenIdRequestHandlerTests: XCTestCase { } func mockResolveContract(url: String) throws -> any RawManifest { - return MockRawContract(id: "testContract345") + return createMockIssuanceRequest() } let mockMapper = MockMapper(mockResults: mockResults) let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, - manifestService: MockContractResolver(mockGetRequestCallback: mockResolveContract)) + manifestResolver: MockManifestResolver(mockGetRequestCallback: mockResolveContract), + verifiableCredentialRequester: MockVerifiableCredentialRequester()) // Act do { @@ -305,7 +318,7 @@ class OpenIdRequestHandlerTests: XCTestCase { return expectedPresentationContent } - if objectToBeMapped is MockRawContract { + if objectToBeMapped is IssuanceRequest { return expectedIssuanceContent } @@ -313,14 +326,15 @@ class OpenIdRequestHandlerTests: XCTestCase { } func mockResolveContract(url: String) throws -> any RawManifest { - return MockRawContract(id: "testContract345") + return createMockIssuanceRequest() } let mockMapper = MockMapper(mockResults: mockResults) let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, - manifestService: MockContractResolver(mockGetRequestCallback: mockResolveContract)) + manifestResolver: MockManifestResolver(mockGetRequestCallback: mockResolveContract), + verifiableCredentialRequester: MockVerifiableCredentialRequester()) // Act let actualRequest = try await handler.handleRequest(from: mockRawRequest) @@ -331,6 +345,31 @@ class OpenIdRequestHandlerTests: XCTestCase { XCTAssertEqual(actualRequest.requirement as? MockRequirement, expectedIssuanceRequirement) XCTAssertEqual(actualRequest.rootOfTrust.source, expectedIssuanceRootOfTrust.source) XCTAssert(actualRequest.rootOfTrust.verified) - + } + + private func createMockIssuanceRequest() -> IssuanceRequest { + let mockCardDisplay = CardDisplayDescriptor(title: "mock title", + issuedBy: "mock issuer", + backgroundColor: "mock background color", + textColor: "mock text color", + logo: nil, + cardDescription: "mock description") + let mockConsentDisplay = ConsentDisplayDescriptor(title: nil, + instructions: "mock purpose") + let mockDisplayDescriptor = DisplayDescriptor(id: nil, + locale: nil, + contract: nil, + card: mockCardDisplay, + consent: mockConsentDisplay, + claims: [:]) + let mockContractInputDescriptor = ContractInputDescriptor(credentialIssuer: "mock credential issuer", + issuer: "mock issuer", + attestations: nil) + let mockContract = Contract(id: "mockContract", + display: mockDisplayDescriptor, + input: mockContractInputDescriptor) + + let mockSignedContract = SignedContract(headers: Header(), content: mockContract)! + return IssuanceRequest(from: mockSignedContract, linkedDomainResult: .linkedDomainMissing) } } diff --git a/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift index fcf2f461..70430fa5 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift @@ -113,7 +113,9 @@ class OpenIdURLRequestResolverTests: XCTestCase { // Arrange let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: Mapper()) - let mockHandler = OpenIdRequestHandler(configuration: configuration, manifestService: MockContractResolver()) + let mockHandler = OpenIdRequestHandler(configuration: configuration, + manifestResolver: MockManifestResolver(), + verifiableCredentialRequester: MockVerifiableCredentialRequester()) let resolver = OpenIdURLRequestResolver(openIdResolver: MockOpenIdForVCResolver(), configuration: configuration) // Act From 04e927501d894693298aa4aed2270a00059af3f8 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 15:42:11 -0800 Subject: [PATCH 171/373] Complete test cases for pin requirement. --- .../Requirements/PinRequirement.swift | 8 ++- .../Requirements/PinRequirementTests.swift | 55 +++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 WalletLibrary/WalletLibraryTests/Requests/Requirements/PinRequirementTests.swift diff --git a/WalletLibrary/WalletLibrary/Requests/Requirements/PinRequirement.swift b/WalletLibrary/WalletLibrary/Requests/Requirements/PinRequirement.swift index 66b2b6db..561f85b3 100644 --- a/WalletLibrary/WalletLibrary/Requests/Requirements/PinRequirement.swift +++ b/WalletLibrary/WalletLibrary/Requests/Requirements/PinRequirement.swift @@ -3,6 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +enum PinRequirementError: Error { + case pinRequirementHasNotBeenFulfilled +} + /** * Information to describe a pin that is required. */ @@ -33,7 +37,9 @@ public class PinRequirement: Requirement { /// Throws error if requirement is not complete. public func validate() throws { - throw VerifiedIdClientError.TODO(message: "implement validate") + if pin == nil { + throw PinRequirementError.pinRequirementHasNotBeenFulfilled + } } /// Fulfill requirement with a pin. diff --git a/WalletLibrary/WalletLibraryTests/Requests/Requirements/PinRequirementTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Requirements/PinRequirementTests.swift new file mode 100644 index 00000000..a00cd3bf --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Requests/Requirements/PinRequirementTests.swift @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import XCTest +@testable import WalletLibrary + +class PinRequirementTests: XCTestCase { + + func testValidate_WithoutPin_ThrowsError() async throws { + + // Arrange + let pinRequirement = PinRequirement(required: false, + length: 0, + type: "", + salt: nil) + + // Act + XCTAssertThrowsError(try pinRequirement.validate()) { error in + // Assert + XCTAssert(error is PinRequirementError) + XCTAssertEqual(error as? PinRequirementError, .pinRequirementHasNotBeenFulfilled) + } + } + + func testValidate_WithPin_DoesNotThrow() async throws { + + // Arrange + let pinRequirement = PinRequirement(required: false, + length: 0, + type: "", + salt: nil) + pinRequirement.fulfill(with: "mock pin") + + // Act / Assert + XCTAssertNoThrow(try pinRequirement.validate()) + } + + func testFulfill_WithPin_SetsAccessToken() async throws { + + // Arrange + let pinRequirement = PinRequirement(required: false, + length: 0, + type: "", + salt: nil) + let mockPin = "mock pin" + + // Act + pinRequirement.fulfill(with: mockPin) + + // Assert + XCTAssertEqual(pinRequirement.pin, mockPin) + } +} From ac37a8b460f7112253031725a938dbc27b1c1a2b Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 15:42:25 -0800 Subject: [PATCH 172/373] Complete test cases for id token requirement. --- .../IdTokenRequirementTests.swift | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 WalletLibrary/WalletLibraryTests/Requests/Requirements/IdTokenRequirementTests.swift diff --git a/WalletLibrary/WalletLibraryTests/Requests/Requirements/IdTokenRequirementTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Requirements/IdTokenRequirementTests.swift new file mode 100644 index 00000000..7df6c63f --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Requests/Requirements/IdTokenRequirementTests.swift @@ -0,0 +1,61 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import XCTest +@testable import WalletLibrary + +class IdTokenRequirementTests: XCTestCase { + + func testValidate_WithoutIdToken_ThrowsError() async throws { + + // Arrange + let idTokenRequirement = IdTokenRequirement(encrypted: false, + required: true, + configuration: URL(string: "https://test.com")!, + clientId: "", + redirectUri: "", + scope: "") + + // Act + XCTAssertThrowsError(try idTokenRequirement.validate()) { error in + // Assert + XCTAssert(error is IdTokenRequirementError) + XCTAssertEqual(error as? IdTokenRequirementError, .idTokenRequirementHasNotBeenFulfilled) + } + } + + func testValidate_WithIdToken_DoesNotThrow() async throws { + + // Arrange + let idTokenRequirement = IdTokenRequirement(encrypted: false, + required: true, + configuration: URL(string: "https://test.com")!, + clientId: "", + redirectUri: "", + scope: "") + idTokenRequirement.fulfill(with: "mock token") + + // Act / Assert + XCTAssertNoThrow(try idTokenRequirement.validate()) + } + + func testFulfill_WithIdToken_SetsAccessToken() async throws { + + // Arrange + let idTokenRequirement = IdTokenRequirement(encrypted: false, + required: true, + configuration: URL(string: "https://test.com")!, + clientId: "", + redirectUri: "", + scope: "") + let mockToken = "mock token" + + // Act + idTokenRequirement.fulfill(with: mockToken) + + // Assert + XCTAssertEqual(idTokenRequirement.idToken, mockToken) + } +} From d063a7a4aca1654211c98c6d0b33b04ce8561ca8 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 15:42:37 -0800 Subject: [PATCH 173/373] Complete test cases for access token requirement. --- .../AccessTokenRequirementTests.swift | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 WalletLibrary/WalletLibraryTests/Requests/Requirements/AccessTokenRequirementTests.swift diff --git a/WalletLibrary/WalletLibraryTests/Requests/Requirements/AccessTokenRequirementTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Requirements/AccessTokenRequirementTests.swift new file mode 100644 index 00000000..8203ecff --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Requests/Requirements/AccessTokenRequirementTests.swift @@ -0,0 +1,61 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import XCTest +@testable import WalletLibrary + +class AccessTokenRequirementTests: XCTestCase { + + func testValidate_WithoutAccessToken_ThrowsError() async throws { + + // Arrange + let accessTokenRequirement = AccessTokenRequirement(encrypted: false, + required: false, + configuration: "", + clientId: nil, + resourceId: "", + scope: "") + + // Act + XCTAssertThrowsError(try accessTokenRequirement.validate()) { error in + // Assert + XCTAssert(error is AccessTokenRequirementError) + XCTAssertEqual(error as? AccessTokenRequirementError, .accessTokenRequirementHasNotBeenFulfilled) + } + } + + func testValidate_WithAccessToken_DoesNotThrow() async throws { + + // Arrange + let accessTokenRequirement = AccessTokenRequirement(encrypted: false, + required: false, + configuration: "", + clientId: nil, + resourceId: "", + scope: "") + accessTokenRequirement.fulfill(with: "mock token") + + // Act / Assert + XCTAssertNoThrow(try accessTokenRequirement.validate()) + } + + func testFulfill_WithAccessToken_SetsAccessToken() async throws { + + // Arrange + let accessTokenRequirement = AccessTokenRequirement(encrypted: false, + required: false, + configuration: "", + clientId: nil, + resourceId: "", + scope: "") + let mockToken = "mock token" + + // Act + accessTokenRequirement.fulfill(with: mockToken) + + // Assert + XCTAssertEqual(accessTokenRequirement.accessToken, mockToken) + } +} From 738021fc5911f1b19385d7eea4ed6e35c6ef77fb Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 15:46:24 -0800 Subject: [PATCH 174/373] Complete test cases for SelfAttestedClaimRequirement. --- .../SelfAttestedClaimRequirement.swift | 6 ++- .../SelfAttestedClaimRequirementTests.swift | 52 +++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 WalletLibrary/WalletLibraryTests/Requests/Requirements/SelfAttestedClaimRequirementTests.swift diff --git a/WalletLibrary/WalletLibrary/Requests/Requirements/SelfAttestedClaimRequirement.swift b/WalletLibrary/WalletLibrary/Requests/Requirements/SelfAttestedClaimRequirement.swift index f0e396ec..cb4b16aa 100644 --- a/WalletLibrary/WalletLibrary/Requests/Requirements/SelfAttestedClaimRequirement.swift +++ b/WalletLibrary/WalletLibrary/Requests/Requirements/SelfAttestedClaimRequirement.swift @@ -3,6 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +enum SelfAttestedClaimRequirementError: Error { + case selfAttestedClaimRequirementHasNotBeenFulfilled +} + /** * Information to describe a self attested claim required for a Verified Id issuance flow. */ @@ -27,7 +31,7 @@ public class SelfAttestedClaimRequirement: Requirement { public func validate() throws { if value == nil { - throw VerifiedIdClientError.TODO(message: "implement") + throw SelfAttestedClaimRequirementError.selfAttestedClaimRequirementHasNotBeenFulfilled } } diff --git a/WalletLibrary/WalletLibraryTests/Requests/Requirements/SelfAttestedClaimRequirementTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Requirements/SelfAttestedClaimRequirementTests.swift new file mode 100644 index 00000000..9328562e --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Requests/Requirements/SelfAttestedClaimRequirementTests.swift @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import XCTest +@testable import WalletLibrary + +class SelfAttestedClaimRequirementTests: XCTestCase { + + func testValidate_WithoutValue_ThrowsError() async throws { + + // Arrange + let requirement = SelfAttestedClaimRequirement(encrypted: false, + required: true, + claim: "") + + // Act + XCTAssertThrowsError(try requirement.validate()) { error in + // Assert + XCTAssert(error is SelfAttestedClaimRequirementError) + XCTAssertEqual(error as? SelfAttestedClaimRequirementError, .selfAttestedClaimRequirementHasNotBeenFulfilled) + } + } + + func testValidate_WithPin_DoesNotThrow() async throws { + + // Arrange + let requirement = SelfAttestedClaimRequirement(encrypted: false, + required: true, + claim: "") + requirement.fulfill(with: "mock value") + + // Act / Assert + XCTAssertNoThrow(try requirement.validate()) + } + + func testFulfill_WithPin_SetsAccessToken() async throws { + + // Arrange + let requirement = SelfAttestedClaimRequirement(encrypted: false, + required: true, + claim: "") + let mockValue = "mock value" + + // Act + requirement.fulfill(with: mockValue) + + // Assert + XCTAssertEqual(requirement.value, mockValue) + } +} From 64560ef585b453d64a84847f5904124566fcc46e Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 15:59:31 -0800 Subject: [PATCH 175/373] Complete test cases for Group Requirement. --- .../Requirements/GroupRequirementTests.swift | 85 +++++++++++++++++++ .../SelfAttestedClaimRequirementTests.swift | 4 +- 2 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 WalletLibrary/WalletLibraryTests/Requests/Requirements/GroupRequirementTests.swift diff --git a/WalletLibrary/WalletLibraryTests/Requests/Requirements/GroupRequirementTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Requirements/GroupRequirementTests.swift new file mode 100644 index 00000000..5158c313 --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Requests/Requirements/GroupRequirementTests.swift @@ -0,0 +1,85 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import XCTest +@testable import WalletLibrary + +class GroupRequirementTests: XCTestCase { + + enum MockError: Error { + case expectedInvalidRequirement + case secondExpectedInvalidRequirement + case thirdExpectedInvalidRequirement + } + + func testValidate_WithOneInvalidRequirementInGroup_ThrowsError() async throws { + + // Arrange + let requirement = MockRequirement(id: "", mockValidateCallback: { throw MockError.expectedInvalidRequirement }) + let groupRequirement = GroupRequirement(required: true, + requirements: [requirement], + requirementOperator: .ALL) + + // Act + XCTAssertThrowsError(try groupRequirement.validate()) { error in + // Assert + XCTAssert(error is GroupRequirementError) + + switch (error as? GroupRequirementError) { + case .requirementsAreNotValid(let errors): + XCTAssertEqual(errors.count, 1) + XCTAssertEqual(errors.first as? MockError, MockError.expectedInvalidRequirement) + default: + XCTFail("Error thrown was not group requirement error.") + } + } + } + + func testValidate_WithMultipleInvalidRequirementInGroup_ThrowsError() async throws { + + // Arrange + let firstRequirement = MockRequirement(id: "", mockValidateCallback: { throw MockError.expectedInvalidRequirement }) + let secondRequirement = MockRequirement(id: "", mockValidateCallback: { throw MockError.secondExpectedInvalidRequirement }) + let thirdRequirement = MockRequirement(id: "", mockValidateCallback: { throw MockError.thirdExpectedInvalidRequirement }) + let fourthRequirement = MockRequirement(id: "", mockValidateCallback: { throw MockError.expectedInvalidRequirement }) + + let groupRequirement = GroupRequirement(required: true, + requirements: [firstRequirement, secondRequirement, thirdRequirement, fourthRequirement], + requirementOperator: .ALL) + + // Act + XCTAssertThrowsError(try groupRequirement.validate()) { error in + // Assert + XCTAssert(error is GroupRequirementError) + + switch (error as? GroupRequirementError) { + case .requirementsAreNotValid(let errors): + XCTAssertEqual(errors.count, 4) + XCTAssertEqual(errors.first as? MockError, MockError.expectedInvalidRequirement) + XCTAssertEqual(errors[1] as? MockError, MockError.secondExpectedInvalidRequirement) + XCTAssertEqual(errors[2] as? MockError, MockError.thirdExpectedInvalidRequirement) + XCTAssertEqual(errors[3] as? MockError, MockError.expectedInvalidRequirement) + + default: + XCTFail("Error thrown was not group requirement error.") + } + } + } + + func testValidate_WithAllValidRequirementsInGroup_DoesNotThrow() async throws { + // Arrange + let firstRequirement = MockRequirement(id: "") + let secondRequirement = MockRequirement(id: "") + let thirdRequirement = MockRequirement(id: "") + let fourthRequirement = MockRequirement(id: "") + + let groupRequirement = GroupRequirement(required: true, + requirements: [firstRequirement, secondRequirement, thirdRequirement, fourthRequirement], + requirementOperator: .ALL) + + // Act + XCTAssertNoThrow(try groupRequirement.validate()) + } +} diff --git a/WalletLibrary/WalletLibraryTests/Requests/Requirements/SelfAttestedClaimRequirementTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Requirements/SelfAttestedClaimRequirementTests.swift index 9328562e..109af918 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/Requirements/SelfAttestedClaimRequirementTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/Requirements/SelfAttestedClaimRequirementTests.swift @@ -23,7 +23,7 @@ class SelfAttestedClaimRequirementTests: XCTestCase { } } - func testValidate_WithPin_DoesNotThrow() async throws { + func testValidate_WithValue_DoesNotThrow() async throws { // Arrange let requirement = SelfAttestedClaimRequirement(encrypted: false, @@ -35,7 +35,7 @@ class SelfAttestedClaimRequirementTests: XCTestCase { XCTAssertNoThrow(try requirement.validate()) } - func testFulfill_WithPin_SetsAccessToken() async throws { + func testFulfill_WithValue_SetsAccessToken() async throws { // Arrange let requirement = SelfAttestedClaimRequirement(encrypted: false, From 95e5752be3a19fe4b357e156713662fae4ea7cf7 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 16:36:48 -0800 Subject: [PATCH 176/373] Complete test cases for Issuance Response Container Extension. --- .../WalletLibrary.xcodeproj/project.pbxproj | 12 - ...uanceResponseContainer+WalletLibrary.swift | 5 +- ...uanceResponseContainerExtensionTests.swift | 273 ++++++++++++++++++ 3 files changed, 276 insertions(+), 14 deletions(-) create mode 100644 WalletLibrary/WalletLibraryTests/Extensions/Entities/IssuanceResponseContainerExtensionTests.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 910339ce..519ed79f 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -68,7 +68,6 @@ 559BD4B3299EDFB400BD61AC /* VerifiableCredential.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4B2299EDFB400BD61AC /* VerifiableCredential.swift */; }; 559BD4B5299EE10800BD61AC /* IssuanceService+VerifiableCredentialRequester.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4B4299EE10800BD61AC /* IssuanceService+VerifiableCredentialRequester.swift */; }; 559BD4E5299EEA3000BD61AC /* IssuanceResponseContainerExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4E4299EEA3000BD61AC /* IssuanceResponseContainerExtensionTests.swift */; }; - 559BD4E8299EEA8B00BD61AC /* IssuanceService+VerifiableCredentialRequesterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4E7299EEA8B00BD61AC /* IssuanceService+VerifiableCredentialRequesterTests.swift */; }; 559BD4EB299EEAC400BD61AC /* GroupRequirementTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4EA299EEAC400BD61AC /* GroupRequirementTests.swift */; }; 559BD4ED299EEAD100BD61AC /* IdTokenRequirementTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4EC299EEAD100BD61AC /* IdTokenRequirementTests.swift */; }; 559BD4EF299EEADF00BD61AC /* AccessTokenRequirementTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4EE299EEADF00BD61AC /* AccessTokenRequirementTests.swift */; }; @@ -348,7 +347,6 @@ 559BD4B2299EDFB400BD61AC /* VerifiableCredential.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiableCredential.swift; sourceTree = ""; }; 559BD4B4299EE10800BD61AC /* IssuanceService+VerifiableCredentialRequester.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceService+VerifiableCredentialRequester.swift"; sourceTree = ""; }; 559BD4E4299EEA3000BD61AC /* IssuanceResponseContainerExtensionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssuanceResponseContainerExtensionTests.swift; sourceTree = ""; }; - 559BD4E7299EEA8B00BD61AC /* IssuanceService+VerifiableCredentialRequesterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceService+VerifiableCredentialRequesterTests.swift"; sourceTree = ""; }; 559BD4EA299EEAC400BD61AC /* GroupRequirementTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupRequirementTests.swift; sourceTree = ""; }; 559BD4EC299EEAD100BD61AC /* IdTokenRequirementTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdTokenRequirementTests.swift; sourceTree = ""; }; 559BD4EE299EEADF00BD61AC /* AccessTokenRequirementTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessTokenRequirementTests.swift; sourceTree = ""; }; @@ -618,7 +616,6 @@ 5534E67C294A392F005D0765 /* Extensions */ = { isa = PBXGroup; children = ( - 559BD4E6299EEA6F00BD61AC /* Services */, 559BD4CE299EEA1500BD61AC /* Entities */, 5534E67D294A3937005D0765 /* Mappings */, ); @@ -723,14 +720,6 @@ path = Entities; sourceTree = ""; }; - 559BD4E6299EEA6F00BD61AC /* Services */ = { - isa = PBXGroup; - children = ( - 559BD4E7299EEA8B00BD61AC /* IssuanceService+VerifiableCredentialRequesterTests.swift */, - ); - path = Services; - sourceTree = ""; - }; 559BD4E9299EEAA200BD61AC /* Requirements */ = { isa = PBXGroup; children = ( @@ -1416,7 +1405,6 @@ 55A81C132991C829002C259A /* MockHandler.swift in Sources */, 559BD3072995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift in Sources */, 559BD4EB299EEAC400BD61AC /* GroupRequirementTests.swift in Sources */, - 559BD4E8299EEA8B00BD61AC /* IssuanceService+VerifiableCredentialRequesterTests.swift in Sources */, 55A81C072991BB2C002C259A /* RequestResolverFactoryTests.swift in Sources */, 559BD4EF299EEADF00BD61AC /* AccessTokenRequirementTests.swift in Sources */, 55A81C102991C211002C259A /* MockResolver.swift in Sources */, diff --git a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+WalletLibrary.swift b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+WalletLibrary.swift index 0ea352ed..2b50e548 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+WalletLibrary.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+WalletLibrary.swift @@ -5,7 +5,7 @@ import VCEntities -enum IssuanceResponseContainerError: Error { +enum IssuanceResponseContainerError: Error, Equatable { case unableToCastVCSDKIssuanceRequestFromRawManifestOfType(String) case unableToCastVerifiedIdRequestURLFromInputOfType(String) case unsupportedRequirementOfType(String) @@ -47,7 +47,8 @@ extension VCEntities.IssuanceResponseContainer { case let pinRequirement as PinRequirement: try add(pinRequirement: pinRequirement) default: - throw IssuanceResponseContainerError.unsupportedRequirementOfType(String(describing: requirement.self)) + let requirementType = String(describing: type(of: requirement)) + throw IssuanceResponseContainerError.unsupportedRequirementOfType(requirementType) } } diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Entities/IssuanceResponseContainerExtensionTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Entities/IssuanceResponseContainerExtensionTests.swift new file mode 100644 index 00000000..dd2e4853 --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Extensions/Entities/IssuanceResponseContainerExtensionTests.swift @@ -0,0 +1,273 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import XCTest +import VCEntities +import VCToken +@testable import WalletLibrary + +class IssuanceResponseContainerExtensionTests: XCTestCase { + + enum MockError: Error { + case expectedInvalidRequirement + case secondExpectedInvalidRequirement + case thirdExpectedInvalidRequirement + } + + func testInit_WithInvalidRawManifest_ThrowsError() throws { + + // Arrange + let mockRawManifest = MockRawManifest(id: "") + let mockInput = MockInput(mockData: "") + + // Act + XCTAssertThrowsError(try IssuanceResponseContainer(from: mockRawManifest, input: mockInput)) { error in + // Assert + XCTAssert(error is IssuanceResponseContainerError) + XCTAssertEqual(error as? IssuanceResponseContainerError, .unableToCastVCSDKIssuanceRequestFromRawManifestOfType("MockRawManifest")) + } + } + + func testInit_WithInvalidInput_ThrowsError() throws { + + // Arrange + let issuanceRequest = createMockIssuanceRequest() + let mockInput = MockInput(mockData: "") + + // Act + XCTAssertThrowsError(try IssuanceResponseContainer(from: issuanceRequest, input: mockInput)) { error in + // Assert + XCTAssert(error is IssuanceResponseContainerError) + XCTAssertEqual(error as? IssuanceResponseContainerError, .unableToCastVerifiedIdRequestURLFromInputOfType("MockInput")) + + } + } + + func testInit_WithValidInput_ReturnIssuanceResponseContainer() throws { + // Arrange + let issuanceRequest = createMockIssuanceRequest() + let mockInput = VerifiedIdRequestURL(url: URL(string: "https://test.com")!) + + // Act + let container = try IssuanceResponseContainer(from: issuanceRequest, input: mockInput) + + // Assert + XCTAssertEqual(container.contract, issuanceRequest.content) + XCTAssertEqual(container.contractUri, mockInput.url.absoluteString) + } + + func testAddRequirement_WithIdTokenRequirement_UpdatesMap() async throws { + // Arrange + let issuanceRequest = createMockIssuanceRequest() + let mockInput = VerifiedIdRequestURL(url: URL(string: "https://test.com")!) + var container = try IssuanceResponseContainer(from: issuanceRequest, input: mockInput) + + let requirement = IdTokenRequirement(encrypted: false, + required: true, + configuration: URL(string: "https://configuration.com")!, + clientId: "", + redirectUri: "", + scope: "") + requirement.fulfill(with: "mock token") + + // Act + try container.add(requirement: requirement) + + // Assert + XCTAssertEqual(container.requestedIdTokenMap, [requirement.configuration.absoluteString: requirement.idToken]) + XCTAssert(container.requestedAccessTokenMap.isEmpty) + XCTAssert(container.requestedSelfAttestedClaimMap.isEmpty) + XCTAssert(container.requestVCMap.isEmpty) + XCTAssertNil(container.issuancePin) + } + + func testAddRequirement_WithAccessTokenRequirement_UpdatesMap() async throws { + // Arrange + let issuanceRequest = createMockIssuanceRequest() + let mockInput = VerifiedIdRequestURL(url: URL(string: "https://test.com")!) + var container = try IssuanceResponseContainer(from: issuanceRequest, input: mockInput) + + let requirement = AccessTokenRequirement(encrypted: false, + required: true, + configuration: "mock configuration", + clientId: "", + resourceId: "", + scope: "") + requirement.fulfill(with: "mock token") + + // Act + try container.add(requirement: requirement) + + // Assert + XCTAssertEqual(container.requestedAccessTokenMap, [requirement.configuration: requirement.accessToken]) + XCTAssert(container.requestedIdTokenMap.isEmpty) + XCTAssert(container.requestedSelfAttestedClaimMap.isEmpty) + XCTAssert(container.requestVCMap.isEmpty) + XCTAssertNil(container.issuancePin) + } + + func testAddRequirement_WithSelfAttestedRequirement_UpdatesMap() async throws { + // Arrange + let issuanceRequest = createMockIssuanceRequest() + let mockInput = VerifiedIdRequestURL(url: URL(string: "https://test.com")!) + var container = try IssuanceResponseContainer(from: issuanceRequest, input: mockInput) + + let requirement = SelfAttestedClaimRequirement(encrypted: false, + required: true, + claim: "mock claim") + requirement.fulfill(with: "mock value") + + // Act + try container.add(requirement: requirement) + + // Assert + XCTAssertEqual(container.requestedSelfAttestedClaimMap, [requirement.claim: requirement.value]) + XCTAssert(container.requestedIdTokenMap.isEmpty) + XCTAssert(container.requestedAccessTokenMap.isEmpty) + XCTAssert(container.requestVCMap.isEmpty) + XCTAssertNil(container.issuancePin) + } + + func testAddRequirement_WithPinRequirement_UpdatesPin() async throws { + // Arrange + let issuanceRequest = createMockIssuanceRequest() + let mockInput = VerifiedIdRequestURL(url: URL(string: "https://test.com")!) + var container = try IssuanceResponseContainer(from: issuanceRequest, input: mockInput) + + let requirement = PinRequirement(required: false, + length: 5, + type: "", + salt: "mock salt") + requirement.fulfill(with: "mock value") + + // Act + try container.add(requirement: requirement) + + // Assert + XCTAssert(container.requestedSelfAttestedClaimMap.isEmpty) + XCTAssert(container.requestedIdTokenMap.isEmpty) + XCTAssert(container.requestedAccessTokenMap.isEmpty) + XCTAssert(container.requestVCMap.isEmpty) + XCTAssertEqual(container.issuancePin, IssuancePin(from: requirement.pin!, withSalt: requirement.salt)) + } + + func testAddRequirement_WithVerifiedIdRequirement_UpdatesMap() async throws { + // TODO + } + + func testAddRequirement_WithGroupRequirementWithTwoSelfAttestedRequirements_UpdatesMap() async throws { + // Arrange + let issuanceRequest = createMockIssuanceRequest() + let mockInput = VerifiedIdRequestURL(url: URL(string: "https://test.com")!) + var container = try IssuanceResponseContainer(from: issuanceRequest, input: mockInput) + + let requirement = SelfAttestedClaimRequirement(encrypted: false, + required: true, + claim: "mock claim") + let secondRequirement = SelfAttestedClaimRequirement(encrypted: false, + required: true, + claim: "mock claim 2") + requirement.fulfill(with: "mock value") + secondRequirement.fulfill(with: "mock second value") + + let groupRequirement = GroupRequirement(required: false, + requirements: [requirement, secondRequirement], + requirementOperator: .ALL) + + // Act + try container.add(requirement: groupRequirement) + + // Assert + let expectedMap = [requirement.claim: requirement.value, secondRequirement.claim: secondRequirement.value] + XCTAssertEqual(container.requestedSelfAttestedClaimMap, expectedMap) + XCTAssert(container.requestedIdTokenMap.isEmpty) + XCTAssert(container.requestedAccessTokenMap.isEmpty) + XCTAssert(container.requestVCMap.isEmpty) + XCTAssertNil(container.issuancePin) + } + + func testAddRequirement_WithGroupRequirementWithMultipleRequirements_UpdatesMap() async throws { + // Arrange + let issuanceRequest = createMockIssuanceRequest() + let mockInput = VerifiedIdRequestURL(url: URL(string: "https://test.com")!) + var container = try IssuanceResponseContainer(from: issuanceRequest, input: mockInput) + + let requirement = SelfAttestedClaimRequirement(encrypted: false, + required: true, + claim: "mock claim") + let accessTokenRequirement = AccessTokenRequirement(encrypted: false, + required: true, + configuration: "mock configuration", + clientId: "", + resourceId: "", + scope: "") + let idTokenRequirement = IdTokenRequirement(encrypted: false, + required: true, + configuration: URL(string: "https://configuration.com")!, + clientId: "", + redirectUri: "", + scope: "") + + requirement.fulfill(with: "mock value") + accessTokenRequirement.fulfill(with: "mock access token") + idTokenRequirement.fulfill(with: "mock id token") + + let groupRequirement = GroupRequirement(required: false, + requirements: [requirement, accessTokenRequirement, idTokenRequirement], + requirementOperator: .ALL) + + // Act + try container.add(requirement: groupRequirement) + + // Assert + XCTAssertEqual(container.requestedSelfAttestedClaimMap, [requirement.claim: requirement.value]) + XCTAssertEqual(container.requestedAccessTokenMap, [accessTokenRequirement.configuration: accessTokenRequirement.accessToken]) + XCTAssertEqual(container.requestedIdTokenMap, [idTokenRequirement.configuration.absoluteString: idTokenRequirement.idToken]) + XCTAssert(container.requestVCMap.isEmpty) + XCTAssertNil(container.issuancePin) + } + + func testAddRequirement_WithUnsupportedRequirementType_ThrowsError() async throws { + // Arrange + let issuanceRequest = createMockIssuanceRequest() + let mockInput = VerifiedIdRequestURL(url: URL(string: "https://test.com")!) + var container = try IssuanceResponseContainer(from: issuanceRequest, input: mockInput) + + let mockRequirement = MockRequirement(id: "") + + // Act + XCTAssertThrowsError(try container.add(requirement: mockRequirement)) { error in + // Assert + XCTAssert(error is IssuanceResponseContainerError) + XCTAssertEqual(error as? IssuanceResponseContainerError, .unsupportedRequirementOfType("MockRequirement")) + } + } + + private func createMockIssuanceRequest() -> IssuanceRequest { + let mockCardDisplay = CardDisplayDescriptor(title: "mock title", + issuedBy: "mock issuer", + backgroundColor: "mock background color", + textColor: "mock text color", + logo: nil, + cardDescription: "mock description") + let mockConsentDisplay = ConsentDisplayDescriptor(title: nil, + instructions: "mock purpose") + let mockDisplayDescriptor = DisplayDescriptor(id: nil, + locale: nil, + contract: nil, + card: mockCardDisplay, + consent: mockConsentDisplay, + claims: [:]) + let mockContractInputDescriptor = ContractInputDescriptor(credentialIssuer: "mock credential issuer", + issuer: "mock issuer", + attestations: nil) + let mockContract = Contract(id: "mockContract", + display: mockDisplayDescriptor, + input: mockContractInputDescriptor) + + let mockSignedContract = SignedContract(headers: Header(), content: mockContract)! + return IssuanceRequest(from: mockSignedContract, linkedDomainResult: .linkedDomainMissing) + } +} From 429526155afb1d24e303a5098e56cafc4f449f3f Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 17:04:47 -0800 Subject: [PATCH 177/373] Fix nits before PR. --- .../Services/IssuanceService+ManifestResolver.swift | 1 - .../Requests/Manifest/VerifiableCredentialRequester.swift | 2 +- .../Requests/Handlers/OpenIdRequestHandler.swift | 1 - .../RequestProtocols/Manifest/ContractIssuanceRequest.swift | 1 - .../Entities/IssuanceResponseContainerExtensionTests.swift | 6 ++++-- .../Requests/Requirements/GroupRequirementTests.swift | 2 +- 6 files changed, 6 insertions(+), 7 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+ManifestResolver.swift b/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+ManifestResolver.swift index 001d1da5..7fdb1dd6 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+ManifestResolver.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+ManifestResolver.swift @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import VCEntities import VCServices /** diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/VerifiableCredentialRequester.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/VerifiableCredentialRequester.swift index e4251406..4d38db10 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/VerifiableCredentialRequester.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/VerifiableCredentialRequester.swift @@ -9,6 +9,6 @@ */ protocol VerifiableCredentialRequester { - /// Giiven generic request, requests a raw Verified Id from an issuer. + /// Given generic request, requests a raw Verified Id from an issuer. func send(request: Request) async throws -> VerifiableCredential } diff --git a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift index fbf46295..54d221c1 100644 --- a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift +++ b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift @@ -60,7 +60,6 @@ struct OpenIdRequestHandler: RequestHandling { let rawContract = try await manifestResolver.resolve(with: issuanceOption.url) - let issuanceResponseContainer = try IssuanceResponseContainer(from: rawContract, input: issuanceOption) /// TODO: add logic here to add PinRequirement to ContractIssuanceRequest if it exists. let issuanceRequestContent = try configuration.mapper.map(rawContract) diff --git a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift index a83d111a..7d1a7ae9 100644 --- a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift +++ b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift @@ -7,7 +7,6 @@ import VCEntities /** * Issuance Request that is Contract specific. - * TODO: we will need contract specific data to implement complete and cancel. * TODO: add VerifiedIdStyle property. */ class ContractIssuanceRequest: VerifiedIdIssuanceRequest { diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Entities/IssuanceResponseContainerExtensionTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Entities/IssuanceResponseContainerExtensionTests.swift index dd2e4853..cad9f5f5 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Entities/IssuanceResponseContainerExtensionTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Entities/IssuanceResponseContainerExtensionTests.swift @@ -26,7 +26,8 @@ class IssuanceResponseContainerExtensionTests: XCTestCase { XCTAssertThrowsError(try IssuanceResponseContainer(from: mockRawManifest, input: mockInput)) { error in // Assert XCTAssert(error is IssuanceResponseContainerError) - XCTAssertEqual(error as? IssuanceResponseContainerError, .unableToCastVCSDKIssuanceRequestFromRawManifestOfType("MockRawManifest")) + XCTAssertEqual(error as? IssuanceResponseContainerError, + .unableToCastVCSDKIssuanceRequestFromRawManifestOfType("MockRawManifest")) } } @@ -40,7 +41,8 @@ class IssuanceResponseContainerExtensionTests: XCTestCase { XCTAssertThrowsError(try IssuanceResponseContainer(from: issuanceRequest, input: mockInput)) { error in // Assert XCTAssert(error is IssuanceResponseContainerError) - XCTAssertEqual(error as? IssuanceResponseContainerError, .unableToCastVerifiedIdRequestURLFromInputOfType("MockInput")) + XCTAssertEqual(error as? IssuanceResponseContainerError, + .unableToCastVerifiedIdRequestURLFromInputOfType("MockInput")) } } diff --git a/WalletLibrary/WalletLibraryTests/Requests/Requirements/GroupRequirementTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Requirements/GroupRequirementTests.swift index 5158c313..e2f7956a 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/Requirements/GroupRequirementTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/Requirements/GroupRequirementTests.swift @@ -79,7 +79,7 @@ class GroupRequirementTests: XCTestCase { requirements: [firstRequirement, secondRequirement, thirdRequirement, fourthRequirement], requirementOperator: .ALL) - // Act + // Act / Assert XCTAssertNoThrow(try groupRequirement.validate()) } } From 99e299a1330a8dc30dfeaf64a6b152b572c2f41c Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 17:05:08 -0800 Subject: [PATCH 178/373] Add comments to requirements. --- .../Requests/Requirements/AccessTokenRequirement.swift | 1 + .../Requests/Requirements/IdTokenRequirement.swift | 1 + .../WalletLibrary/Requests/Requirements/PinRequirement.swift | 2 ++ .../Requests/Requirements/SelfAttestedClaimRequirement.swift | 1 + 4 files changed, 5 insertions(+) diff --git a/WalletLibrary/WalletLibrary/Requests/Requirements/AccessTokenRequirement.swift b/WalletLibrary/WalletLibrary/Requests/Requirements/AccessTokenRequirement.swift index dc5e7f96..7881f654 100644 --- a/WalletLibrary/WalletLibrary/Requests/Requirements/AccessTokenRequirement.swift +++ b/WalletLibrary/WalletLibrary/Requests/Requirements/AccessTokenRequirement.swift @@ -30,6 +30,7 @@ public class AccessTokenRequirement: Requirement { /// The scope value used to get the access token through an authentication library. public let scope: String + /// The access token that fulfills the requirement. var accessToken: String? init(encrypted: Bool, diff --git a/WalletLibrary/WalletLibrary/Requests/Requirements/IdTokenRequirement.swift b/WalletLibrary/WalletLibrary/Requests/Requirements/IdTokenRequirement.swift index 4137a59f..651833e1 100644 --- a/WalletLibrary/WalletLibrary/Requests/Requirements/IdTokenRequirement.swift +++ b/WalletLibrary/WalletLibrary/Requests/Requirements/IdTokenRequirement.swift @@ -35,6 +35,7 @@ public class IdTokenRequirement: Requirement { /// the id token retrieved and can be used for validation during an issuance request to an issuance service. public internal(set) var nonce: String? = nil + /// The id token that fulfills the requirement. var idToken: String? init(encrypted: Bool, diff --git a/WalletLibrary/WalletLibrary/Requests/Requirements/PinRequirement.swift b/WalletLibrary/WalletLibrary/Requests/Requirements/PinRequirement.swift index 561f85b3..ecc9c215 100644 --- a/WalletLibrary/WalletLibrary/Requests/Requirements/PinRequirement.swift +++ b/WalletLibrary/WalletLibrary/Requests/Requirements/PinRequirement.swift @@ -21,8 +21,10 @@ public class PinRequirement: Requirement { /// The type of the pin such as alphanumeric or numeric. public let type: String + /// The optional salt. let salt: String? + /// The pin that fulfills the requirement. var pin: String? init(required: Bool, diff --git a/WalletLibrary/WalletLibrary/Requests/Requirements/SelfAttestedClaimRequirement.swift b/WalletLibrary/WalletLibrary/Requests/Requirements/SelfAttestedClaimRequirement.swift index cb4b16aa..d45382fb 100644 --- a/WalletLibrary/WalletLibrary/Requests/Requirements/SelfAttestedClaimRequirement.swift +++ b/WalletLibrary/WalletLibrary/Requests/Requirements/SelfAttestedClaimRequirement.swift @@ -21,6 +21,7 @@ public class SelfAttestedClaimRequirement: Requirement { /// The claim requested. public let claim: String + /// The claim value that fulfills the requirement. var value: String? init(encrypted: Bool, required: Bool, claim: String) { From 4409d666fe51223b56843596f9ced23d7c85d763 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 17:05:12 -0800 Subject: [PATCH 179/373] Update MockVerifiableCredentialRequester.swift --- .../Requests/MockVerifiableCredentialRequester.swift | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiableCredentialRequester.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiableCredentialRequester.swift index 1461cfd8..6a39796d 100644 --- a/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiableCredentialRequester.swift +++ b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiableCredentialRequester.swift @@ -4,10 +4,13 @@ *--------------------------------------------------------------------------------------------*/ @testable import WalletLibrary -import VCEntities class MockVerifiableCredentialRequester: VerifiableCredentialRequester { + enum MockVerifiableCredentialRequestError: Error { + case missingCallback + } + let sendRequestCallback: ((String) throws -> WalletLibrary.VerifiableCredential)? init(sendRequestCallback: ((String) throws -> WalletLibrary.VerifiableCredential)? = nil) { @@ -16,11 +19,12 @@ class MockVerifiableCredentialRequester: VerifiableCredentialRequester { func send(request: Request) async throws -> WalletLibrary.VerifiableCredential { - if let sendRequestCallback = sendRequestCallback { - return try sendRequestCallback("test") + if let sendRequestCallback = sendRequestCallback, + let request = request as? String { + return try sendRequestCallback(request) } - throw VerifiedIdClientError.TODO(message: "add") + throw MockVerifiableCredentialRequestError.missingCallback } } From ed641634fedd5ed6f8bb29ecde558f8e945b5fd1 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 16 Feb 2023 17:10:00 -0800 Subject: [PATCH 180/373] Update VerifiableCredential-SDK-iOS --- WalletLibrary/Submodules/VerifiableCredential-SDK-iOS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS b/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS index 4ea82b7c..a1110b20 160000 --- a/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS +++ b/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS @@ -1 +1 @@ -Subproject commit 4ea82b7c47a1ee97a41db67ae0aa5456262d65f7 +Subproject commit a1110b20a39414144ac807c3a7b1a5fb64dce9a0 From c938793953c67e72d21ae40203363c906b52b497 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 17 Feb 2023 11:48:54 -0800 Subject: [PATCH 181/373] Update PresentationRequest+MappableTests.swift --- .../Presentation/PresentationRequest+MappableTests.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+MappableTests.swift index bf09a166..231ad19a 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+MappableTests.swift @@ -200,8 +200,8 @@ class PresentationRequestMappingTests: XCTestCase { let presentationRequestTokenClaims = PresentationRequestClaims(jti: nil, clientID: nil, redirectURI: nil, - responseType: nil, responseMode: nil, + responseType: nil, claims: requestedClaims, state: nil, nonce: nil, @@ -210,7 +210,8 @@ class PresentationRequestMappingTests: XCTestCase { registration: registration, idTokenHint: nil, iat: nil, - exp: nil) + exp: nil, + pin: nil) return PresentationRequestToken(headers: Header(), content: presentationRequestTokenClaims)! } From 51486680e9f9c3392d066e56f9ff05ebda4dc44b Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 22 Feb 2023 13:35:17 -0800 Subject: [PATCH 182/373] Add app icon to test app. --- .../AppIcon.appiconset/Contents.json | 1 + .../AppIcon.appiconset/appIcon.jpg | Bin 0 -> 217990 bytes 2 files changed, 1 insertion(+) create mode 100644 Demo/WalletLibraryDemo/WalletLibraryDemo/Assets.xcassets/AppIcon.appiconset/appIcon.jpg diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/Assets.xcassets/AppIcon.appiconset/Contents.json b/Demo/WalletLibraryDemo/WalletLibraryDemo/Assets.xcassets/AppIcon.appiconset/Contents.json index 532cd729..86520393 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,6 +1,7 @@ { "images" : [ { + "filename" : "appIcon.jpg", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/Assets.xcassets/AppIcon.appiconset/appIcon.jpg b/Demo/WalletLibraryDemo/WalletLibraryDemo/Assets.xcassets/AppIcon.appiconset/appIcon.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8bcc1e215fe0bbdb05bbbf509bdc8b833b76abb1 GIT binary patch literal 217990 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%0DCElq#;>u5@5n7R@CvYKW_K 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 }!TtJdn1sXO=pLfFc%!=jTt3MyTlH=<$o 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

yg0;oVNsBW>=D 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%0A1+{)iWOpGmCK4oJos7Yl)2>I--JrJyJV$>n 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 UmF;|Xexi=@A=KaYLHJVVKXV261poj5 literal 0 HcmV?d00001 From 5e988602e48f4041071209a21dc085c2da73b56d Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 22 Feb 2023 13:35:34 -0800 Subject: [PATCH 183/373] Add functionality to test app. --- .../project.pbxproj | 8 + .../WalletLibraryDemo/ContentView.swift | 128 +++++++++++++- .../WalletLibraryDemo/RequirementState.swift | 78 ++++++++ .../WalletLibraryDemo/SampleViewModel.swift | 167 ++++++++++++++++++ 4 files changed, 380 insertions(+), 1 deletion(-) create mode 100644 Demo/WalletLibraryDemo/WalletLibraryDemo/RequirementState.swift create mode 100644 Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo.xcodeproj/project.pbxproj b/Demo/WalletLibraryDemo/WalletLibraryDemo.xcodeproj/project.pbxproj index 981ecf4f..5cd59ea6 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo.xcodeproj/project.pbxproj +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 5585BDE129A047370059710B /* SampleViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585BDE029A047370059710B /* SampleViewModel.swift */; }; + 5585BDE529A692E10059710B /* RequirementState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585BDE429A692E10059710B /* RequirementState.swift */; }; 55E33698293E8F7500CD2ED7 /* WalletLibraryDemoApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E33697293E8F7500CD2ED7 /* WalletLibraryDemoApp.swift */; }; 55E3369A293E8F7500CD2ED7 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E33699293E8F7500CD2ED7 /* Persistence.swift */; }; 55E3369D293E8F7500CD2ED7 /* WalletLibraryDemo.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 55E3369B293E8F7500CD2ED7 /* WalletLibraryDemo.xcdatamodeld */; }; @@ -32,6 +34,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 5585BDE029A047370059710B /* SampleViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleViewModel.swift; sourceTree = ""; }; + 5585BDE429A692E10059710B /* RequirementState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequirementState.swift; sourceTree = ""; }; 55E33694293E8F7500CD2ED7 /* WalletLibraryDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WalletLibraryDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 55E33697293E8F7500CD2ED7 /* WalletLibraryDemoApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletLibraryDemoApp.swift; sourceTree = ""; }; 55E33699293E8F7500CD2ED7 /* Persistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = ""; }; @@ -77,6 +81,8 @@ children = ( 55E33697293E8F7500CD2ED7 /* WalletLibraryDemoApp.swift */, 55E33699293E8F7500CD2ED7 /* Persistence.swift */, + 5585BDE429A692E10059710B /* RequirementState.swift */, + 5585BDE029A047370059710B /* SampleViewModel.swift */, 55E3369E293E8F7500CD2ED7 /* ContentView.swift */, 55E336A0293E8F7700CD2ED7 /* Assets.xcassets */, 55E336A2293E8F7700CD2ED7 /* WalletLibraryDemo.entitlements */, @@ -176,7 +182,9 @@ 55E3369D293E8F7500CD2ED7 /* WalletLibraryDemo.xcdatamodeld in Sources */, 55E33698293E8F7500CD2ED7 /* WalletLibraryDemoApp.swift in Sources */, 55E3369F293E8F7500CD2ED7 /* ContentView.swift in Sources */, + 5585BDE529A692E10059710B /* RequirementState.swift in Sources */, 55E3369A293E8F7500CD2ED7 /* Persistence.swift in Sources */, + 5585BDE129A047370059710B /* SampleViewModel.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/ContentView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/ContentView.swift index d64380b5..33fb8a97 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/ContentView.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/ContentView.swift @@ -10,9 +10,135 @@ import WalletLibrary struct ContentView: View { @Environment(\.managedObjectContext) private var viewContext + @StateObject private var viewModel = ViewModel() + var body: some View { NavigationView { - Text("Hello World") + VStack { + Text("Sample Request URL:") + TextField( + "OpenId Request URL", + text: $viewModel.input + ) + .padding(EdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5)) + .textInputAutocapitalization(.never) + .disableAutocorrection(true) + .textFieldStyle(.roundedBorder) + .frame(width: UIScreen.main.bounds.width - 20) + NavigationLink(destination: RequestView()) { + Text("Create Request") + }.navigationTitle("Verified Id Sample App") + }.onDisappear { + viewModel.createRequest() + } + } + .environmentObject(viewModel) + } +} + +struct RequestView: View { + + @EnvironmentObject var viewModel: ViewModel + + @Environment(\.dismiss) var dismiss + + var body: some View { + VStack { + if viewModel.isProgressViewShowing { + ProgressView() + } + else if let errorMessage = viewModel.errorMessage { + Text(errorMessage) + .foregroundColor(.red) + Button { + viewModel.reset() + dismiss() + } label: { + Text("Reset") + } + } + else if let successMessage = viewModel.successMessage { + Text(successMessage) + Button { + viewModel.reset() + dismiss() + } label: { + Text("Reset") + } + } + else { + List(viewModel.requirements) { requirement in + NavigationLink(destination: RequirementView(requirement: requirement)) { + Text(requirement.label) + } + } + Button { + viewModel.complete() + } label: { + Text("Complete") + }.disabled(!viewModel.isCompleteButtonEnabled) + } } } } + +struct RequirementView: View { + + @EnvironmentObject var viewModel: ViewModel + + @State private var userInput: String = "" + + @Environment(\.dismiss) var dismiss + + var requirement: RequirementState + + var body: some View { + VStack { + Text(requirement.label) + TextField( + "", + text: $userInput + ) + .padding(EdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5)) + .textInputAutocapitalization(.never) + .disableAutocorrection(true) + .textFieldStyle(.roundedBorder) + .frame(width: UIScreen.main.bounds.width - 20) + Button { + fulfill(with: userInput) + } label: { + Text("Add") + } + } + } + + private func fulfill(with value: String) { + do { + try viewModel.fulfill(requirementState: requirement, with: value) + dismiss() + } catch { + print("invalid") + } + } +} + +//struct PinRequirementView: View { +// +// @EnvironmentObject var viewModel: ViewModel +// +// var requirement: RequirementMapping +// +// var body: some View { +// VStack { +// if let pinRequirement = requirement.requirement as? PinRequirement { +// Text(pinRequirement.type) +// Button { +// pinRequirement.fulfill(with: "test test test") +// viewModel.fulfill(requirement: pinRequirement, with: "test test test", id: requirement.id) +// } label: { +// Text("add") +// } +// } +// } +// } +//} diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/RequirementState.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/RequirementState.swift new file mode 100644 index 00000000..f8cd0738 --- /dev/null +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/RequirementState.swift @@ -0,0 +1,78 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import WalletLibrary + +enum RequirementStateError: String, Error { + case unsupportedRequirementType = "Unsupported Requirement Type" + case invalidInputToFulfillRequirement = "Invalid Input to Fulfill Requirement" +} + +/// Requirement Status based on whether requirement is valid or missing. +enum RequirementStatus { + case missing + case valid + case invalid +} + +class RequirementState: Identifiable { + + var label: String + + var status: RequirementStatus + + let requirement: Requirement + + let id = UUID() + + init(label: String, + status: RequirementStatus, + requirement: Requirement) { + self.label = label + self.status = status + self.requirement = requirement + } + + init(requirement: Requirement) throws { + self.requirement = requirement + self.status = .missing + switch (requirement) { + case let selfAttestedClaimRequirement as SelfAttestedClaimRequirement: + self.label = "Add User Input for \(selfAttestedClaimRequirement.claim)" + case is PinRequirement: + self.label = "Add Pin" + case let idTokenRequirement as IdTokenRequirement: + self.label = "Id Token for: \(idTokenRequirement.configuration)" + try? idTokenRequirement.validate() + self.status = .valid + default: + throw RequirementStateError.unsupportedRequirementType + } + } + + func fulfill(with value: String) throws { + switch (requirement) { + case let selfAttestedClaimRequirement as SelfAttestedClaimRequirement: + selfAttestedClaimRequirement.fulfill(with: value) + try addNewLabelIfValid(newLabel: "User Input: \(value)") + case let pinRequirement as PinRequirement: + pinRequirement.fulfill(with: value) + try addNewLabelIfValid(newLabel: "Pin Input: \(value)") + default: + throw RequirementStateError.unsupportedRequirementType + } + } + + private func addNewLabelIfValid(newLabel: String) throws { + do { + try requirement.validate() + label = newLabel + status = .valid + } catch { + status = .invalid + throw RequirementStateError.invalidInputToFulfillRequirement + } + } +} diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift new file mode 100644 index 00000000..596b63e2 --- /dev/null +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift @@ -0,0 +1,167 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import SwiftUI +import WalletLibrary + +enum SampleViewModelError: String, Error { + case unableToCreateInput = "Invalid Input." + case unableToCreateRequest = "Unable to create request." + case requestIsUndefined = "Verified Id Request is Undefined." + case unsupportedRequirementType = "One of the requirement types is not supported." + case TODO = "TODO" +} + + +@MainActor class ViewModel: ObservableObject { + + /// The requirements to be gathered by the user. + @Published var requirements: [RequirementState] = [] + + /// The input string to kick off the flow (e.g. openid request url). + @Published var input: String = "" + + /// If all requirements are satisfied, complete button is enabled. + @Published var isCompleteButtonEnabled: Bool = false + + /// Show a progress view when while doing internal logic. + @Published var isProgressViewShowing: Bool = true + + /// if not nil, error message to be displayed. + @Published var errorMessage: String? = nil + + /// If not nil, success message to be displayed. + @Published var successMessage: String? = nil + + /// The Verified Id Client is used to create requests with a configuration set by the Builder. + private let verifiedIdClient: VerifiedIdClient? + + /// The current issuance or presentation request that is being processed. + private var request: (any VerifiedIdRequest)? = nil + + init() { + do { + let builder = VerifiedIdClientBuilder() + verifiedIdClient = try builder.build() + } catch { + verifiedIdClient = nil + showErrorMessage(from: error) + } + } + + func createRequest() { + Task { + isProgressViewShowing = true + do { + let input = try createInput() + self.request = try await verifiedIdClient?.createVerifiedIdRequest(from: input) + + if let request = request { + try configureRequirements(requirement: request.requirement) + } else { + showErrorMessage(from: SampleViewModelError.unableToCreateRequest) + } + + } catch { + showErrorMessage(from: error, additionalInfo: "Unable to create request.") + } + isProgressViewShowing = false + } + } + + private func configureRequirements(requirement: Requirement) throws { + + if let groupRequirement = requirement as? GroupRequirement { + for req in groupRequirement.requirements { + try configureRequirements(requirement: req) + } + return + } + + do { + let requirementState = try RequirementState(requirement: requirement) + requirements.append(requirementState) + } catch { + throw SampleViewModelError.unsupportedRequirementType + } + } + + private func createInput() throws -> VerifiedIdRequestInput { + + guard let openidUrl = URL(string: input) else { + throw SampleViewModelError.unableToCreateInput + } + + return VerifiedIdRequestURL(url: openidUrl) + } + + func complete() { + switch (request) { + case let issuanceRequest as any VerifiedIdIssuanceRequest: + complete(issuanceRequest: issuanceRequest) + case let presentationRequest as any VerifiedIdPresentationRequest: + complete(presentationRequest: presentationRequest) + default: + showErrorMessage(from: SampleViewModelError.requestIsUndefined) + } + } + + private func complete(issuanceRequest: any VerifiedIdIssuanceRequest) { + Task { + let result = await issuanceRequest.complete() + switch (result) { + case .success(let verifiedId): + showSuccessMessage(message: String(describing: verifiedId)) + case .failure(let error): + showErrorMessage(from: error) + } + } + } + + private func complete(presentationRequest: any VerifiedIdPresentationRequest) { + Task { + let result = await presentationRequest.complete() + switch (result) { + case .success(_): + showSuccessMessage(message: "Presented Verified IDs successfully.") + case .failure(let error): + showErrorMessage(from: error) + } + } + } + + func fulfill(requirementState: RequirementState, with value: String) throws { + do { + try requirementState.fulfill(with: value) + } catch RequirementStateError.invalidInputToFulfillRequirement { + showErrorMessage(from: RequirementStateError.invalidInputToFulfillRequirement, additionalInfo: "Value == \(value)") + } + self.isCompleteButtonEnabled = request?.isSatisfied() ?? false + } + + private func showErrorMessage(from error: Error, additionalInfo: String? = nil) { + print(error) + if let error = error as? SampleViewModelError { + errorMessage = error.rawValue + } else if let error = error as? RequirementStateError { + errorMessage = error.rawValue + } else { + errorMessage = error.localizedDescription + } + + if let additionalInfo = additionalInfo { + errorMessage?.append("\n\(additionalInfo)") + } + } + + private func showSuccessMessage(message: String) { + successMessage = message + } + + func reset() { + errorMessage = nil + successMessage = nil + } +} From ebcf0675f9e7c38198190c875d6e1817c4df344a Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 22 Feb 2023 13:36:08 -0800 Subject: [PATCH 184/373] Support id token hint flow in issuance response container extension. --- .../Entities/IssuanceResponseContainer+WalletLibrary.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+WalletLibrary.swift b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+WalletLibrary.swift index 2b50e548..6a143fde 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+WalletLibrary.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+WalletLibrary.swift @@ -61,7 +61,12 @@ extension VCEntities.IssuanceResponseContainer { private mutating func add(idTokenRequirement: IdTokenRequirement) throws { try idTokenRequirement.validate() - self.requestedIdTokenMap[idTokenRequirement.configuration.absoluteString] = idTokenRequirement.idToken + + if idTokenRequirement.configuration.absoluteString == "https://self-issued.me" { + self.issuanceIdToken = idTokenRequirement.idToken + } else { + self.requestedIdTokenMap[idTokenRequirement.configuration.absoluteString] = idTokenRequirement.idToken + } } private mutating func add(accessTokenRequirement: AccessTokenRequirement) throws { From c627c0048a25015bf381f2a385ec6ddf14e56d55 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 22 Feb 2023 13:36:29 -0800 Subject: [PATCH 185/373] Remove scope as required value in id token descriptor mapping. --- .../Mappings/VCSDK/Issuance/IdTokenDescriptor+Mappable.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IdTokenDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IdTokenDescriptor+Mappable.swift index 77693215..bb53bdea 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IdTokenDescriptor+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IdTokenDescriptor+Mappable.swift @@ -20,7 +20,6 @@ extension VCEntities.IdTokenDescriptor: Mappable { } let redirectUri = try getRequiredProperty(property: redirectURI, propertyName: "redirectURI") - let scope = try getRequiredProperty(property: scope, propertyName: "scope") return IdTokenRequirement(encrypted: encrypted ?? false, required: idTokenRequired ?? false, From 44756e9884b78ac1f928ffe8c6224af4cd8edb51 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 22 Feb 2023 13:36:48 -0800 Subject: [PATCH 186/373] Support id token hint flow in presentation request mapping. --- .../PresentationRequest+Mappable.swift | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+Mappable.swift index 76b43b46..e8aeb394 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+Mappable.swift @@ -26,14 +26,44 @@ extension VCEntities.PresentationRequest: Mappable { let requirement = try mapper.map(presentationDefinition) let rootOfTrust = try mapper.map(linkedDomainResult) + let injectedIdToken = createInjectedIdTokenIfExists() let clientName = content.registration?.clientName ?? "" let requesterStyle = OpenIdVerifierStyle(name: clientName) let content = VerifiedIdRequestContent(style: requesterStyle, requirement: requirement, - rootOfTrust: rootOfTrust) + rootOfTrust: rootOfTrust, + injectedIdToken: injectedIdToken) return content } + + private func createInjectedIdTokenIfExists() -> InjectedIdToken? { + if let idTokenHint = content.idTokenHint { + return InjectedIdToken(rawToken: idTokenHint, + pin: createPinRequirementIfExists()) + } + + return nil + } + + private func createPinRequirementIfExists() -> PinRequirement? { + if let pin = content.pin, + let type = pin.type { + let pinRequirement = PinRequirement(required: true, + length: pin.length, + type: type, + salt: pin.salt) + return pinRequirement + } + + return nil + } +} + +struct InjectedIdToken { + let rawToken: String + + let pin: PinRequirement? } From 5ebc10520e280c94ce3929575e9959183f6d29b2 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 22 Feb 2023 13:36:53 -0800 Subject: [PATCH 187/373] Update VerifiedIdRequest.swift --- .../WalletLibrary/Protocols/Requests/VerifiedIdRequest.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequest.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequest.swift index f8e4d347..023e3aa7 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequest.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequest.swift @@ -38,4 +38,4 @@ public protocol VerifiedIdIssuanceRequest: VerifiedIdRequest where T == Verified /** * Internal Protocol that represents a Presentation Request. */ -protocol VerifiedIdPresentationRequest: VerifiedIdRequest where T == Void { } +public protocol VerifiedIdPresentationRequest: VerifiedIdRequest where T == Void { } From 1e0dcf1acbec3351cb95357a2f0b1b3dcb6efed9 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 22 Feb 2023 13:37:05 -0800 Subject: [PATCH 188/373] Support id token hint flow in OpenIdRequestHandler. --- .../Handlers/OpenIdRequestHandler.swift | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift index 54d221c1..d3391519 100644 --- a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift +++ b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift @@ -61,14 +61,30 @@ struct OpenIdRequestHandler: RequestHandling { let rawContract = try await manifestResolver.resolve(with: issuanceOption.url) let issuanceResponseContainer = try IssuanceResponseContainer(from: rawContract, input: issuanceOption) - /// TODO: add logic here to add PinRequirement to ContractIssuanceRequest if it exists. - let issuanceRequestContent = try configuration.mapper.map(rawContract) + var issuanceRequestContent = try configuration.mapper.map(rawContract) + repopulateIssuanceRequirementsIfInjectedIdTokenExists(presentationRequestContent: requestContent, + issuanceRequestContent: &issuanceRequestContent) + return ContractIssuanceRequest(content: issuanceRequestContent, issuanceResponseContainer: issuanceResponseContainer, verifiableCredentialRequester: verifiableCredentialRequester, configuration: configuration) } + private func repopulateIssuanceRequirementsIfInjectedIdTokenExists(presentationRequestContent: VerifiedIdRequestContent, + issuanceRequestContent: inout VerifiedIdRequestContent) { + if let injectedIdToken = presentationRequestContent.injectedIdToken, + let idTokenRequirement = issuanceRequestContent.requirement as? IdTokenRequirement { + idTokenRequirement.fulfill(with: injectedIdToken.rawToken) + if let pinRequirement = injectedIdToken.pin { + let groupRequirement = GroupRequirement(required: true, + requirements: [idTokenRequirement, pinRequirement], + requirementOperator: .ALL) + issuanceRequestContent.requirement = groupRequirement + } + } + } + private func handlePresentationRequest(from requestContent: VerifiedIdRequestContent) throws -> any VerifiedIdPresentationRequest { return OpenIdPresentationRequest(content: requestContent, configuration: configuration) } From 9dcf2b1ceb1edb641676e5f982930b880d12661e Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 22 Feb 2023 13:37:25 -0800 Subject: [PATCH 189/373] Implement isSatisfied in ContractIssuanceRequest. --- .../Manifest/ContractIssuanceRequest.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift index 7d1a7ae9..952e7cfe 100644 --- a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift +++ b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift @@ -36,8 +36,12 @@ class ContractIssuanceRequest: VerifiedIdIssuanceRequest { } public func isSatisfied() -> Bool { - /// TODO: implement. - return false + do { + try requirement.validate() + return true + } catch { + return false + } } public func complete() async -> Result { From 350d5da7e878f6bd07a397883f4d76ac856a23a8 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 22 Feb 2023 13:37:32 -0800 Subject: [PATCH 190/373] Update IdTokenRequirement.swift --- .../Requests/Requirements/IdTokenRequirement.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Requirements/IdTokenRequirement.swift b/WalletLibrary/WalletLibrary/Requests/Requirements/IdTokenRequirement.swift index 651833e1..729cd1fb 100644 --- a/WalletLibrary/WalletLibrary/Requests/Requirements/IdTokenRequirement.swift +++ b/WalletLibrary/WalletLibrary/Requests/Requirements/IdTokenRequirement.swift @@ -28,7 +28,7 @@ public class IdTokenRequirement: Requirement { public let redirectUri: String /// The scope used to get the id token through an authentication library. - public let scope: String + public let scope: String? /// The nonce acts as an extra level of security and is used as an additional property /// within the id token request through an authentication library. The nonce will be placed within @@ -43,7 +43,7 @@ public class IdTokenRequirement: Requirement { configuration: URL, clientId: String, redirectUri: String, - scope: String) { + scope: String?) { self.encrypted = encrypted self.required = required self.configuration = configuration From e78765cbc04c0d3fa87dd835698438439f6dcaa5 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 22 Feb 2023 13:37:45 -0800 Subject: [PATCH 191/373] Support id token hint flow in VerifiedIdRequestContent. --- .../Requests/VerifiedIdRequestContent.swift | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift b/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift index 547f5696..bf6ab220 100644 --- a/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift +++ b/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift @@ -12,7 +12,19 @@ struct VerifiedIdRequestContent { let style: RequesterStyle - let requirement: Requirement + var requirement: Requirement let rootOfTrust: RootOfTrust + + let injectedIdToken: InjectedIdToken? + + init(style: RequesterStyle, + requirement: Requirement, + rootOfTrust: RootOfTrust, + injectedIdToken: InjectedIdToken? = nil) { + self.style = style + self.requirement = requirement + self.rootOfTrust = rootOfTrust + self.injectedIdToken = injectedIdToken + } } From 55bf5b4499932d1fdc3c7667b78400b523ff396a Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 22 Feb 2023 13:38:17 -0800 Subject: [PATCH 192/373] Add VC mapping. --- .../VerifiableCredential/VerifiableCredential.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift b/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift index ba9da7fe..0a15aaf0 100644 --- a/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift +++ b/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift @@ -21,6 +21,11 @@ struct VerifiableCredential: Mappable { } func map(using mapper: Mapping) throws -> VerifiedId { - throw VerifiedIdClientError.TODO(message: "implement") + return VerifiedId(id: "id", + type: raw.content.vc?.type?.first ?? "", + claims: [], + expiresOn: Date(timeIntervalSince1970: raw.content.iat ?? 0), + issuedOn: Date(timeIntervalSince1970: raw.content.exp ?? 0), + raw: raw.rawValue ?? "") } } From adac1e1d76e6b544950823816ef3c289c78a8eb4 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 22 Feb 2023 13:43:18 -0800 Subject: [PATCH 193/373] Create InjectedIdToken. --- .../WalletLibrary.xcodeproj/project.pbxproj | 4 ++++ .../Presentation/PresentationRequest+Mappable.swift | 6 ------ .../RequestProtocols/OpenId/InjectedIdToken.swift | 13 +++++++++++++ 3 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/InjectedIdToken.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 519ed79f..1084acf2 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -42,6 +42,7 @@ 5534E688294A5F16005D0765 /* IdTokenDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5534E687294A5F16005D0765 /* IdTokenDescriptor+MappableTests.swift */; }; 5534E68C294A8B8F005D0765 /* AccessTokenDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5534E68B294A8B8F005D0765 /* AccessTokenDescriptor+MappableTests.swift */; }; 5534E6AD294AAFA6005D0765 /* PresentationDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5534E6AC294AAFA6005D0765 /* PresentationDescriptor+MappableTests.swift */; }; + 5585BDE729A6C38C0059710B /* InjectedIdToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585BDE629A6C38C0059710B /* InjectedIdToken.swift */; }; 559BD3032995993700BD61AC /* PresentationInputDescriptor+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */; }; 559BD3052995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3042995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift */; }; 559BD3072995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3062995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift */; }; @@ -321,6 +322,7 @@ 5534E687294A5F16005D0765 /* IdTokenDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IdTokenDescriptor+MappableTests.swift"; sourceTree = ""; }; 5534E68B294A8B8F005D0765 /* AccessTokenDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AccessTokenDescriptor+MappableTests.swift"; sourceTree = ""; }; 5534E6AC294AAFA6005D0765 /* PresentationDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationDescriptor+MappableTests.swift"; sourceTree = ""; }; + 5585BDE629A6C38C0059710B /* InjectedIdToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InjectedIdToken.swift; sourceTree = ""; }; 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationInputDescriptor+Mappable.swift"; sourceTree = ""; }; 559BD3042995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationDefinition+MappableTests.swift"; sourceTree = ""; }; 559BD3062995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationInputDescriptor+MappableTests.swift"; sourceTree = ""; }; @@ -833,6 +835,7 @@ children = ( 55E2F07C2995561E0008010D /* OpenIdPresentationRequest.swift */, 55E2F085299573C30008010D /* OpenIdVerifierStyle.swift */, + 5585BDE629A6C38C0059710B /* InjectedIdToken.swift */, ); path = OpenId; sourceTree = ""; @@ -1341,6 +1344,7 @@ 55E2F07F29955A3B0008010D /* VerifiedIdRequestContent.swift in Sources */, 550A916F299310D40014D030 /* OpenIdRequestHandler.swift in Sources */, 55E3370A293FD3E000CD2ED7 /* IdTokenRequirement.swift in Sources */, + 5585BDE729A6C38C0059710B /* InjectedIdToken.swift in Sources */, 550E320C298B0F30004E7716 /* WalletLibraryLogConsumer.swift in Sources */, 550E3217298B1236004E7716 /* RequesterStyle.swift in Sources */, 55E336D1293FA6F400CD2ED7 /* VerifiedIdRequirement.swift in Sources */, diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+Mappable.swift index e8aeb394..7915dd42 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+Mappable.swift @@ -61,9 +61,3 @@ extension VCEntities.PresentationRequest: Mappable { return nil } } - -struct InjectedIdToken { - let rawToken: String - - let pin: PinRequirement? -} diff --git a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/InjectedIdToken.swift b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/InjectedIdToken.swift new file mode 100644 index 00000000..436c581a --- /dev/null +++ b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/InjectedIdToken.swift @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +/** + * An object that represents an id token that is injected from a presentation request into an issuance request. + */ +struct InjectedIdToken { + let rawToken: String + + let pin: PinRequirement? +} From 744773cba86be9351de13ee74095245ab74d7a59 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 22 Feb 2023 14:02:58 -0800 Subject: [PATCH 194/373] Create file for RequestView. --- .../project.pbxproj | 8 +++ .../WalletLibraryDemo/RequestView.swift | 59 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 Demo/WalletLibraryDemo/WalletLibraryDemo/RequestView.swift diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo.xcodeproj/project.pbxproj b/Demo/WalletLibraryDemo/WalletLibraryDemo.xcodeproj/project.pbxproj index 5cd59ea6..9dd19dc7 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo.xcodeproj/project.pbxproj +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo.xcodeproj/project.pbxproj @@ -9,6 +9,8 @@ /* Begin PBXBuildFile section */ 5585BDE129A047370059710B /* SampleViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585BDE029A047370059710B /* SampleViewModel.swift */; }; 5585BDE529A692E10059710B /* RequirementState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585BDE429A692E10059710B /* RequirementState.swift */; }; + 5585BDFE29A6C4CA0059710B /* RequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585BDFD29A6C4CA0059710B /* RequestView.swift */; }; + 5585BE0029A6C5070059710B /* RequirementView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585BDFF29A6C5070059710B /* RequirementView.swift */; }; 55E33698293E8F7500CD2ED7 /* WalletLibraryDemoApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E33697293E8F7500CD2ED7 /* WalletLibraryDemoApp.swift */; }; 55E3369A293E8F7500CD2ED7 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E33699293E8F7500CD2ED7 /* Persistence.swift */; }; 55E3369D293E8F7500CD2ED7 /* WalletLibraryDemo.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 55E3369B293E8F7500CD2ED7 /* WalletLibraryDemo.xcdatamodeld */; }; @@ -36,6 +38,8 @@ /* Begin PBXFileReference section */ 5585BDE029A047370059710B /* SampleViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleViewModel.swift; sourceTree = ""; }; 5585BDE429A692E10059710B /* RequirementState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequirementState.swift; sourceTree = ""; }; + 5585BDFD29A6C4CA0059710B /* RequestView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestView.swift; sourceTree = ""; }; + 5585BDFF29A6C5070059710B /* RequirementView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequirementView.swift; sourceTree = ""; }; 55E33694293E8F7500CD2ED7 /* WalletLibraryDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WalletLibraryDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 55E33697293E8F7500CD2ED7 /* WalletLibraryDemoApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletLibraryDemoApp.swift; sourceTree = ""; }; 55E33699293E8F7500CD2ED7 /* Persistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = ""; }; @@ -81,6 +85,8 @@ children = ( 55E33697293E8F7500CD2ED7 /* WalletLibraryDemoApp.swift */, 55E33699293E8F7500CD2ED7 /* Persistence.swift */, + 5585BDFD29A6C4CA0059710B /* RequestView.swift */, + 5585BDFF29A6C5070059710B /* RequirementView.swift */, 5585BDE429A692E10059710B /* RequirementState.swift */, 5585BDE029A047370059710B /* SampleViewModel.swift */, 55E3369E293E8F7500CD2ED7 /* ContentView.swift */, @@ -179,9 +185,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 5585BDFE29A6C4CA0059710B /* RequestView.swift in Sources */, 55E3369D293E8F7500CD2ED7 /* WalletLibraryDemo.xcdatamodeld in Sources */, 55E33698293E8F7500CD2ED7 /* WalletLibraryDemoApp.swift in Sources */, 55E3369F293E8F7500CD2ED7 /* ContentView.swift in Sources */, + 5585BE0029A6C5070059710B /* RequirementView.swift in Sources */, 5585BDE529A692E10059710B /* RequirementState.swift in Sources */, 55E3369A293E8F7500CD2ED7 /* Persistence.swift in Sources */, 5585BDE129A047370059710B /* SampleViewModel.swift in Sources */, diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/RequestView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/RequestView.swift new file mode 100644 index 00000000..ae39310e --- /dev/null +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/RequestView.swift @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import SwiftUI + +struct RequestView: View { + + @EnvironmentObject var viewModel: ViewModel + + @Environment(\.dismiss) var dismiss + + var body: some View { + VStack { + if viewModel.isProgressViewShowing { + ProgressView() + } + else if let errorMessage = viewModel.errorMessage { + Text(errorMessage) + .foregroundColor(.red) + Button { + viewModel.reset() + dismiss() + } label: { + Text("Reset") + } + } + else if let successMessage = viewModel.successMessage { + Text(successMessage) + Button { + viewModel.reset() + dismiss() + } label: { + Text("Reset") + } + } + else { + List(viewModel.requirements) { requirement in + NavigationLink(destination: RequirementView(requirement: requirement)) { + HStack { + if requirement.status == .valid { + Image(systemName: "checkmark.circle.fill") + .foregroundColor(.green) + } + Text(requirement.label) + } + } + } + Button { + viewModel.complete() + } label: { + Text("Complete") + }.disabled(!viewModel.isCompleteButtonEnabled) + } + } + } +} + From 2ee900c54f8090a9d87d1458361e2f50c27a63c1 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 22 Feb 2023 14:03:11 -0800 Subject: [PATCH 195/373] Create RequirementView.swift --- .../WalletLibraryDemo/RequirementView.swift | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 Demo/WalletLibraryDemo/WalletLibraryDemo/RequirementView.swift diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/RequirementView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/RequirementView.swift new file mode 100644 index 00000000..4072cd8a --- /dev/null +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/RequirementView.swift @@ -0,0 +1,54 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import SwiftUI + +struct RequirementView: View { + + @EnvironmentObject var viewModel: ViewModel + + @State private var userInput: String = "" + + @State private var isInvalidInput: Bool = false + + @Environment(\.dismiss) var dismiss + + var requirement: RequirementState + + var body: some View { + VStack { + if isInvalidInput { + Text("Invalid Input") + .foregroundColor(.red) + } + Text(requirement.label) + TextField( + "", + text: $userInput + ) + .padding(EdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5)) + .textInputAutocapitalization(.never) + .disableAutocorrection(true) + .textFieldStyle(.roundedBorder) + .frame(width: UIScreen.main.bounds.width - 20) + Button { + fulfill(with: userInput) + } label: { + Text("Add") + } + }.onDisappear { + isInvalidInput = false + } + } + + private func fulfill(with value: String) { + do { + try viewModel.fulfill(requirementState: requirement, with: value) + dismiss() + } catch { + isInvalidInput = true + } + } +} From a039d370278219b2bef6b540ad2d9b54e98db344 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 22 Feb 2023 14:03:13 -0800 Subject: [PATCH 196/373] Update ContentView.swift --- .../WalletLibraryDemo/ContentView.swift | 107 ------------------ 1 file changed, 107 deletions(-) diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/ContentView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/ContentView.swift index 33fb8a97..6006557c 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/ContentView.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/ContentView.swift @@ -35,110 +35,3 @@ struct ContentView: View { .environmentObject(viewModel) } } - -struct RequestView: View { - - @EnvironmentObject var viewModel: ViewModel - - @Environment(\.dismiss) var dismiss - - var body: some View { - VStack { - if viewModel.isProgressViewShowing { - ProgressView() - } - else if let errorMessage = viewModel.errorMessage { - Text(errorMessage) - .foregroundColor(.red) - Button { - viewModel.reset() - dismiss() - } label: { - Text("Reset") - } - } - else if let successMessage = viewModel.successMessage { - Text(successMessage) - Button { - viewModel.reset() - dismiss() - } label: { - Text("Reset") - } - } - else { - List(viewModel.requirements) { requirement in - NavigationLink(destination: RequirementView(requirement: requirement)) { - Text(requirement.label) - } - } - Button { - viewModel.complete() - } label: { - Text("Complete") - }.disabled(!viewModel.isCompleteButtonEnabled) - } - } - } -} - -struct RequirementView: View { - - @EnvironmentObject var viewModel: ViewModel - - @State private var userInput: String = "" - - @Environment(\.dismiss) var dismiss - - var requirement: RequirementState - - var body: some View { - VStack { - Text(requirement.label) - TextField( - "", - text: $userInput - ) - .padding(EdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5)) - .textInputAutocapitalization(.never) - .disableAutocorrection(true) - .textFieldStyle(.roundedBorder) - .frame(width: UIScreen.main.bounds.width - 20) - Button { - fulfill(with: userInput) - } label: { - Text("Add") - } - } - } - - private func fulfill(with value: String) { - do { - try viewModel.fulfill(requirementState: requirement, with: value) - dismiss() - } catch { - print("invalid") - } - } -} - -//struct PinRequirementView: View { -// -// @EnvironmentObject var viewModel: ViewModel -// -// var requirement: RequirementMapping -// -// var body: some View { -// VStack { -// if let pinRequirement = requirement.requirement as? PinRequirement { -// Text(pinRequirement.type) -// Button { -// pinRequirement.fulfill(with: "test test test") -// viewModel.fulfill(requirement: pinRequirement, with: "test test test", id: requirement.id) -// } label: { -// Text("add") -// } -// } -// } -// } -//} From 9cedd3fb1c6ea7534652ac28d92cc7fd4a2da523 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 22 Feb 2023 14:03:18 -0800 Subject: [PATCH 197/373] Update RequirementState.swift --- .../WalletLibraryDemo/WalletLibraryDemo/RequirementState.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/RequirementState.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/RequirementState.swift index f8cd0738..4eb8f5d6 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/RequirementState.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/RequirementState.swift @@ -45,8 +45,7 @@ class RequirementState: Identifiable { self.label = "Add Pin" case let idTokenRequirement as IdTokenRequirement: self.label = "Id Token for: \(idTokenRequirement.configuration)" - try? idTokenRequirement.validate() - self.status = .valid + try? addNewLabelIfValid(newLabel: "Valid Id Token Present.") default: throw RequirementStateError.unsupportedRequirementType } From 2d86172e6244866d495431868e3fabdb7461d342 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 22 Feb 2023 14:03:34 -0800 Subject: [PATCH 198/373] Update comments in SampleViewModel. --- .../WalletLibraryDemo/SampleViewModel.swift | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift index 596b63e2..ebb4f783 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift @@ -11,7 +11,6 @@ enum SampleViewModelError: String, Error { case unableToCreateRequest = "Unable to create request." case requestIsUndefined = "Verified Id Request is Undefined." case unsupportedRequirementType = "One of the requirement types is not supported." - case TODO = "TODO" } @@ -41,6 +40,7 @@ enum SampleViewModelError: String, Error { /// The current issuance or presentation request that is being processed. private var request: (any VerifiedIdRequest)? = nil + /// TODO: enable deeplinking and the rest of the requirements. init() { do { let builder = VerifiedIdClientBuilder() @@ -59,7 +59,8 @@ enum SampleViewModelError: String, Error { self.request = try await verifiedIdClient?.createVerifiedIdRequest(from: input) if let request = request { - try configureRequirements(requirement: request.requirement) + try configureRequirements(requirement: request.requirement) + isCompleteButtonEnabled = request.isSatisfied() } else { showErrorMessage(from: SampleViewModelError.unableToCreateRequest) } @@ -163,5 +164,7 @@ enum SampleViewModelError: String, Error { func reset() { errorMessage = nil successMessage = nil + requirements = [] + request = nil } } From dc414577c0ebe8d0a7b47974d2f7c4ea20b183fb Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 22 Feb 2023 16:17:25 -0800 Subject: [PATCH 199/373] Add Verified Id Type. --- WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj | 4 ++++ .../WalletLibrary/VerifiedId/VerifiedIdType.swift | 8 ++++++++ 2 files changed, 12 insertions(+) create mode 100644 WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdType.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 1084acf2..f217d635 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -43,6 +43,7 @@ 5534E68C294A8B8F005D0765 /* AccessTokenDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5534E68B294A8B8F005D0765 /* AccessTokenDescriptor+MappableTests.swift */; }; 5534E6AD294AAFA6005D0765 /* PresentationDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5534E6AC294AAFA6005D0765 /* PresentationDescriptor+MappableTests.swift */; }; 5585BDE729A6C38C0059710B /* InjectedIdToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585BDE629A6C38C0059710B /* InjectedIdToken.swift */; }; + 5585BE0229A6D42B0059710B /* VerifiedIdType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585BE0129A6D42B0059710B /* VerifiedIdType.swift */; }; 559BD3032995993700BD61AC /* PresentationInputDescriptor+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */; }; 559BD3052995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3042995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift */; }; 559BD3072995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3062995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift */; }; @@ -323,6 +324,7 @@ 5534E68B294A8B8F005D0765 /* AccessTokenDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AccessTokenDescriptor+MappableTests.swift"; sourceTree = ""; }; 5534E6AC294AAFA6005D0765 /* PresentationDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationDescriptor+MappableTests.swift"; sourceTree = ""; }; 5585BDE629A6C38C0059710B /* InjectedIdToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InjectedIdToken.swift; sourceTree = ""; }; + 5585BE0129A6D42B0059710B /* VerifiedIdType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdType.swift; sourceTree = ""; }; 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationInputDescriptor+Mappable.swift"; sourceTree = ""; }; 559BD3042995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationDefinition+MappableTests.swift"; sourceTree = ""; }; 559BD3062995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationInputDescriptor+MappableTests.swift"; sourceTree = ""; }; @@ -898,6 +900,7 @@ 559BD4B1299EDF9E00BD61AC /* VerifiableCredential */, 55E336D3293FA75300CD2ED7 /* VerifiedId.swift */, 55E336D5293FA78C00CD2ED7 /* VerifiedIdClaim.swift */, + 5585BE0129A6D42B0059710B /* VerifiedIdType.swift */, ); path = VerifiedId; sourceTree = ""; @@ -1361,6 +1364,7 @@ 550E320E298B0FAC004E7716 /* WalletLibraryLogger.swift in Sources */, 559BD3032995993700BD61AC /* PresentationInputDescriptor+Mappable.swift in Sources */, 55E337B52948B4B000CD2ED7 /* PinRequirement.swift in Sources */, + 5585BE0229A6D42B0059710B /* VerifiedIdType.swift in Sources */, 559BD3A6299AD9BD00BD61AC /* ManifestResolver.swift in Sources */, 550A9170299310D40014D030 /* OpenIdURLRequestResolver.swift in Sources */, 5534E669294A0C3F005D0765 /* Mapper.swift in Sources */, diff --git a/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdType.swift b/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdType.swift new file mode 100644 index 00000000..ed6506f1 --- /dev/null +++ b/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdType.swift @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +public enum VerifiedIdType { + case verifiableCredential +} From 30cf90866827a7158c1c2580872ef2db05a8476e Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 22 Feb 2023 16:17:30 -0800 Subject: [PATCH 200/373] Update VerifiedId.swift --- WalletLibrary/WalletLibrary/VerifiedId/VerifiedId.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/VerifiedId/VerifiedId.swift b/WalletLibrary/WalletLibrary/VerifiedId/VerifiedId.swift index 9029c9a0..ebdd5f1c 100644 --- a/WalletLibrary/WalletLibrary/VerifiedId/VerifiedId.swift +++ b/WalletLibrary/WalletLibrary/VerifiedId/VerifiedId.swift @@ -14,7 +14,7 @@ public struct VerifiedId { public let id: String /// the type of the verified id. For example, Verifiable Credential would be the type of a VC. - public let type: String + public let type: VerifiedIdType /// a list of claims within the verified id. public let claims: [VerifiedIdClaim] From c49fb18d2d4bca05df1ba3f144924601037fd448 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 22 Feb 2023 16:17:46 -0800 Subject: [PATCH 201/373] Implement mapping for VerifiableCredential. --- .../VerifiableCredential.swift | 41 ++++++++++++++++--- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift b/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift index 0a15aaf0..fe930b9c 100644 --- a/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift +++ b/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift @@ -5,6 +5,10 @@ import VCEntities +enum VerifiableCredentialMappingError: Error { + case missingJtiInVerifiableCredential +} + /** * Verifiable Credential object contains the raw VC, and the contract that created the Verifiable Credential. * This object conforms to the Mappable protocol and maps VC claims and display contract to a Verified Id. @@ -21,11 +25,36 @@ struct VerifiableCredential: Mappable { } func map(using mapper: Mapping) throws -> VerifiedId { - return VerifiedId(id: "id", - type: raw.content.vc?.type?.first ?? "", - claims: [], - expiresOn: Date(timeIntervalSince1970: raw.content.iat ?? 0), - issuedOn: Date(timeIntervalSince1970: raw.content.exp ?? 0), - raw: raw.rawValue ?? "") + + let rawToken = try raw.serialize() + let id = try getRequiredProperty(property: raw.content.jti, propertyName: "jti") + let issuedOnDate = try getRequiredProperty(property: raw.content.iat, propertyName: "issued on date") + let expiresOnDate = try getRequiredProperty(property: raw.content.exp, propertyName: "expires on date") + + return VerifiedId(id: id, + type: VerifiedIdType.verifiableCredential, + claims: try createClaims(), + expiresOn: Date(timeIntervalSince1970: expiresOnDate), + issuedOn: Date(timeIntervalSince1970: issuedOnDate), + raw: rawToken) + } + + private func createClaims() throws -> [VerifiedIdClaim] { + let vcClaims = try getRequiredProperty(property: raw.content.vc?.credentialSubject, propertyName: "claims") + let claimLabels = contract.display.claims + + var verifiedIdClaims: [VerifiedIdClaim] = [] + /// TODO: add casting to correct type from contract. + for (claim, value) in vcClaims { + if let claimStyle = claimLabels["vc.credentialSubject.\(claim)"] { + verifiedIdClaims.append(VerifiedIdClaim(id: claimStyle.label, + value: value)) + } else { + verifiedIdClaims.append(VerifiedIdClaim(id: claim, + value: value)) + } + } + + return verifiedIdClaims } } From 3561f0b19cc61a6d19ac9b20d04f8141ea3b8065 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 22 Feb 2023 16:17:50 -0800 Subject: [PATCH 202/373] Update VerifiedIdClaim.swift --- WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdClaim.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdClaim.swift b/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdClaim.swift index 2e589108..5a3a7ae4 100644 --- a/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdClaim.swift +++ b/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdClaim.swift @@ -8,9 +8,9 @@ */ public struct VerifiedIdClaim { /// id of the claim. For example, within a VC, it is the key value of credentialSubject. - let id: String + public let id: String /// the value of the claim. - let value: Any + public let value: Any } From 41caadc8dff1c9d4a122320bb141c66b33ac36f9 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 22 Feb 2023 16:18:11 -0800 Subject: [PATCH 203/373] Add showing Verified Id on Request View. --- .../WalletLibraryDemo/RequestView.swift | 63 ++++++++++++++----- 1 file changed, 49 insertions(+), 14 deletions(-) diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/RequestView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/RequestView.swift index ae39310e..0cb4759e 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/RequestView.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/RequestView.swift @@ -35,23 +35,58 @@ struct RequestView: View { Text("Reset") } } - else { - List(viewModel.requirements) { requirement in - NavigationLink(destination: RequirementView(requirement: requirement)) { + else if let issuedVerifiedId = viewModel.issuedVerifiedId { + VStack { + Text("\(issuedVerifiedId.id)") + .multilineTextAlignment(.center) + Spacer() + Text("Issued on: \(issuedVerifiedId.issuedOn.formatted())") + .multilineTextAlignment(.center) + Spacer() + Text("Expires on: \(issuedVerifiedId.expiresOn.formatted())") + .multilineTextAlignment(.center) + Spacer() + Text("Claims:") + .multilineTextAlignment(.center) + List(issuedVerifiedId.claims.indices, id: \.self) { index in HStack { - if requirement.status == .valid { - Image(systemName: "checkmark.circle.fill") - .foregroundColor(.green) + Text("\(issuedVerifiedId.claims[index].id):") + Text(String(describing: issuedVerifiedId.claims[index].value)) + } + }.listStyle(.inset) + }.navigationTitle("Verified Id Successfully Issued!") + .navigationBarTitleDisplayMode(.inline) + } + else { + VStack { + List(viewModel.requirements) { requirement in + NavigationLink(destination: RequirementView(requirement: requirement)) { + HStack { + switch (requirement.status) { + case .valid: + Image(systemName: "checkmark.circle.fill") + .foregroundColor(.green) + case .invalid: + Image(systemName: "x.circle.fill") + .foregroundColor(.red) + case .missing: + Image(systemName: "circle.fill") + .foregroundColor(.accentColor) + } + Text(requirement.label) } - Text(requirement.label) } - } - } - Button { - viewModel.complete() - } label: { - Text("Complete") - }.disabled(!viewModel.isCompleteButtonEnabled) + }.listStyle(.inset) + Spacer() + Divider() + Button { + viewModel.complete() + } label: { + Text("Complete") + }.disabled(!viewModel.isCompleteButtonEnabled) + Spacer() + }.navigationTitle("Fulfill Requirements") + .navigationBarTitleDisplayMode(.inline) } } } From ca5aa4edf1b7b57bda9a38e9537966da8a6e6105 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 22 Feb 2023 16:18:18 -0800 Subject: [PATCH 204/373] Update RequirementState.swift --- Demo/WalletLibraryDemo/WalletLibraryDemo/RequirementState.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/RequirementState.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/RequirementState.swift index 4eb8f5d6..01f0edbd 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/RequirementState.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/RequirementState.swift @@ -45,7 +45,7 @@ class RequirementState: Identifiable { self.label = "Add Pin" case let idTokenRequirement as IdTokenRequirement: self.label = "Id Token for: \(idTokenRequirement.configuration)" - try? addNewLabelIfValid(newLabel: "Valid Id Token Present.") + try? addNewLabelIfValid(newLabel: "Valid Id Token Present") default: throw RequirementStateError.unsupportedRequirementType } From 7a3ca6914016729a71074b9aa5dfc84bdeea4a0b Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 22 Feb 2023 16:18:22 -0800 Subject: [PATCH 205/373] Update ContentView.swift --- Demo/WalletLibraryDemo/WalletLibraryDemo/ContentView.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/ContentView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/ContentView.swift index 6006557c..d6c05c3f 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/ContentView.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/ContentView.swift @@ -18,7 +18,8 @@ struct ContentView: View { Text("Sample Request URL:") TextField( "OpenId Request URL", - text: $viewModel.input + text: $viewModel.input, + axis: .vertical ) .padding(EdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5)) .textInputAutocapitalization(.never) From a4ca47b9ee16331a9d4269f4368a2f7cdadfda19 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 22 Feb 2023 16:18:27 -0800 Subject: [PATCH 206/373] Update SampleViewModel.swift --- .../WalletLibraryDemo/SampleViewModel.swift | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift index ebb4f783..e9db42d8 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift @@ -34,6 +34,9 @@ enum SampleViewModelError: String, Error { /// If not nil, success message to be displayed. @Published var successMessage: String? = nil + /// If not nil, show issued verified id. + @Published var issuedVerifiedId: VerifiedId? = nil + /// The Verified Id Client is used to create requests with a configuration set by the Builder. private let verifiedIdClient: VerifiedIdClient? @@ -53,6 +56,7 @@ enum SampleViewModelError: String, Error { func createRequest() { Task { + reset() isProgressViewShowing = true do { let input = try createInput() @@ -114,7 +118,7 @@ enum SampleViewModelError: String, Error { let result = await issuanceRequest.complete() switch (result) { case .success(let verifiedId): - showSuccessMessage(message: String(describing: verifiedId)) + showSuccessfulFlow(with: verifiedId) case .failure(let error): showErrorMessage(from: error) } @@ -126,7 +130,7 @@ enum SampleViewModelError: String, Error { let result = await presentationRequest.complete() switch (result) { case .success(_): - showSuccessMessage(message: "Presented Verified IDs successfully.") + showSuccessfulFlow() case .failure(let error): showErrorMessage(from: error) } @@ -157,13 +161,19 @@ enum SampleViewModelError: String, Error { } } - private func showSuccessMessage(message: String) { - successMessage = message + private func showSuccessfulFlow(with verifiedId: VerifiedId? = nil) { + + if let verifiedId = verifiedId { + issuedVerifiedId = verifiedId + } else { + successMessage = "Presentation Successful!" + } } func reset() { errorMessage = nil successMessage = nil + issuedVerifiedId = nil requirements = [] request = nil } From 83b29dee8a9c2270a24295f36dd4e5df2d0529f3 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 09:58:58 -0800 Subject: [PATCH 207/373] Update how we import VC SDK. --- .../WalletLibrary.xcodeproj/project.pbxproj | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index f217d635..43d57a6b 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -42,6 +42,14 @@ 5534E688294A5F16005D0765 /* IdTokenDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5534E687294A5F16005D0765 /* IdTokenDescriptor+MappableTests.swift */; }; 5534E68C294A8B8F005D0765 /* AccessTokenDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5534E68B294A8B8F005D0765 /* AccessTokenDescriptor+MappableTests.swift */; }; 5534E6AD294AAFA6005D0765 /* PresentationDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5534E6AC294AAFA6005D0765 /* PresentationDescriptor+MappableTests.swift */; }; + 553CC06A29A818F7005A5FD6 /* PromiseKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55E33679293E7B5F00CD2ED7 /* PromiseKit.framework */; }; + 553CC06B29A818F7005A5FD6 /* PromiseKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 55E33679293E7B5F00CD2ED7 /* PromiseKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 553CC08129A81931005A5FD6 /* VCToken.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55E336F1293FCE5E00CD2ED7 /* VCToken.framework */; platformFilter = ios; }; + 553CC08229A81931005A5FD6 /* VCToken.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 55E336F1293FCE5E00CD2ED7 /* VCToken.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 553CC08329A8193E005A5FD6 /* VCNetworking.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55E33704293FCE9C00CD2ED7 /* VCNetworking.framework */; platformFilter = ios; }; + 553CC08429A8193E005A5FD6 /* VCNetworking.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 55E33704293FCE9C00CD2ED7 /* VCNetworking.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 553CC08529A81949005A5FD6 /* VCCrypto.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55E336FA293FCE8200CD2ED7 /* VCCrypto.framework */; platformFilter = ios; }; + 553CC08629A81949005A5FD6 /* VCCrypto.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 55E336FA293FCE8200CD2ED7 /* VCCrypto.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 5585BDE729A6C38C0059710B /* InjectedIdToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585BDE629A6C38C0059710B /* InjectedIdToken.swift */; }; 5585BE0229A6D42B0059710B /* VerifiedIdType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585BE0129A6D42B0059710B /* VerifiedIdType.swift */; }; 559BD3032995993700BD61AC /* PresentationInputDescriptor+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */; }; @@ -99,7 +107,6 @@ 55E2F0882995743D0008010D /* LinkedDomainResult+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F0872995743D0008010D /* LinkedDomainResult+Mappable.swift */; }; 55E2F08A299576350008010D /* PresentationDefinition+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F089299576350008010D /* PresentationDefinition+Mappable.swift */; }; 55E2F08C299576730008010D /* GroupRequirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F08B299576730008010D /* GroupRequirement.swift */; }; - 55E33684293E7B6600CD2ED7 /* PromiseKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55E33679293E7B5F00CD2ED7 /* PromiseKit.framework */; }; 55E336D1293FA6F400CD2ED7 /* VerifiedIdRequirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E336D0293FA6F400CD2ED7 /* VerifiedIdRequirement.swift */; }; 55E336D4293FA75300CD2ED7 /* VerifiedId.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E336D3293FA75300CD2ED7 /* VerifiedId.swift */; }; 55E336D6293FA78C00CD2ED7 /* VerifiedIdClaim.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E336D5293FA78C00CD2ED7 /* VerifiedIdClaim.swift */; }; @@ -277,8 +284,12 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( + 553CC06B29A818F7005A5FD6 /* PromiseKit.framework in Embed Frameworks */, 5534E5AD2948B8C8005D0765 /* VCEntities.framework in Embed Frameworks */, + 553CC08629A81949005A5FD6 /* VCCrypto.framework in Embed Frameworks */, 5534E5982948B8C2005D0765 /* VCServices.framework in Embed Frameworks */, + 553CC08429A8193E005A5FD6 /* VCNetworking.framework in Embed Frameworks */, + 553CC08229A81931005A5FD6 /* VCToken.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -401,9 +412,12 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 553CC06A29A818F7005A5FD6 /* PromiseKit.framework in Frameworks */, 5534E5AC2948B8C8005D0765 /* VCEntities.framework in Frameworks */, - 55E33684293E7B6600CD2ED7 /* PromiseKit.framework in Frameworks */, + 553CC08529A81949005A5FD6 /* VCCrypto.framework in Frameworks */, 5534E5972948B8C2005D0765 /* VCServices.framework in Frameworks */, + 553CC08329A8193E005A5FD6 /* VCNetworking.framework in Frameworks */, + 553CC08129A81931005A5FD6 /* VCToken.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; From 5b2af96124c0b490a4f418b63b5cffeb1bd16634 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 11:04:56 -0800 Subject: [PATCH 208/373] Make VerifiedId a protocol. --- .../WalletLibrary/VerifiedId/VerifiedId.swift | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/WalletLibrary/WalletLibrary/VerifiedId/VerifiedId.swift b/WalletLibrary/WalletLibrary/VerifiedId/VerifiedId.swift index ebdd5f1c..c2269c10 100644 --- a/WalletLibrary/WalletLibrary/VerifiedId/VerifiedId.swift +++ b/WalletLibrary/WalletLibrary/VerifiedId/VerifiedId.swift @@ -8,24 +8,18 @@ import Foundation /** * The Verified Id Data Model. */ -public struct VerifiedId { +public protocol VerifiedId: Codable { /// the id of the verified id. For example, this value would equal the jti of a Verifiable Credential. - public let id: String - - /// the type of the verified id. For example, Verifiable Credential would be the type of a VC. - public let type: VerifiedIdType + var id: String { get } /// a list of claims within the verified id. - public let claims: [VerifiedIdClaim] + var claims: [VerifiedIdClaim] { mutating get } /// date the verified id expires. - public let expiresOn: Date + var expiresOn: Date? { get } /// date the verified id was issued. - public let issuedOn: Date - - /// the raw representation of the verified id. - let raw: String + var issuedOn: Date { get } } From 275aec879f7ed1c41ea9cf89b1808a8de220d7f5 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 11:05:12 -0800 Subject: [PATCH 209/373] VerifiableCredential conforms to Verified Id --- .../VerifiableCredential.swift | 73 ++++++++++++++----- 1 file changed, 56 insertions(+), 17 deletions(-) diff --git a/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift b/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift index fe930b9c..e12d521e 100644 --- a/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift +++ b/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift @@ -7,40 +7,79 @@ import VCEntities enum VerifiableCredentialMappingError: Error { case missingJtiInVerifiableCredential + case unableToDecodeRawVerifiableCredentialToken + case missingIssuedOnValueInVerifiableCredential } /** * Verifiable Credential object contains the raw VC, and the contract that created the Verifiable Credential. * This object conforms to the Mappable protocol and maps VC claims and display contract to a Verified Id. */ -struct VerifiableCredential: Mappable { +struct VerifiableCredential: VerifiedId { + + public let id: String + + public private(set) lazy var claims: [VerifiedIdClaim] = { + return createClaims() + }() + + public let expiresOn: Date? + + public let issuedOn: Date let raw: VCEntities.VerifiableCredential let contract: Contract - init(raw: VCEntities.VerifiableCredential, from contract: Contract) { + init(raw: VCEntities.VerifiableCredential, from contract: Contract) throws { + + guard let issuedOn = raw.content.iat else { + throw VerifiableCredentialMappingError.missingIssuedOnValueInVerifiableCredential + } + + guard let id = raw.content.jti else { + throw VerifiableCredentialMappingError.missingJtiInVerifiableCredential + } + self.raw = raw self.contract = contract + self.issuedOn = Date(timeIntervalSince1970: issuedOn) + self.id = id + + if let expiresOn = raw.content.exp { + self.expiresOn = Date(timeIntervalSince1970: expiresOn) + } else { + self.expiresOn = nil + } } - func map(using mapper: Mapping) throws -> VerifiedId { - - let rawToken = try raw.serialize() - let id = try getRequiredProperty(property: raw.content.jti, propertyName: "jti") - let issuedOnDate = try getRequiredProperty(property: raw.content.iat, propertyName: "issued on date") - let expiresOnDate = try getRequiredProperty(property: raw.content.exp, propertyName: "expires on date") - - return VerifiedId(id: id, - type: VerifiedIdType.verifiableCredential, - claims: try createClaims(), - expiresOn: Date(timeIntervalSince1970: expiresOnDate), - issuedOn: Date(timeIntervalSince1970: issuedOnDate), - raw: rawToken) + enum CodingKeys: String, CodingKey { + case raw, contract + } + + init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + let rawToken = try values.decode(String.self, forKey: .raw) + guard let raw = VCEntities.VerifiableCredential(from: rawToken) else { + throw VerifiableCredentialMappingError.unableToDecodeRawVerifiableCredentialToken + } + let contract = try values.decode(Contract.self, forKey: .contract) + try self.init(raw: raw, from: contract) } - private func createClaims() throws -> [VerifiedIdClaim] { - let vcClaims = try getRequiredProperty(property: raw.content.vc?.credentialSubject, propertyName: "claims") + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + let serializedToken = try raw.serialize() + try container.encode(serializedToken, forKey: .raw) + try container.encode(contract, forKey: .contract) + } + + private func createClaims() -> [VerifiedIdClaim] { + + guard let vcClaims = raw.content.vc?.credentialSubject else { + return [] + } + let claimLabels = contract.display.claims var verifiedIdClaims: [VerifiedIdClaim] = [] From f1cb00c512a290066bd6db53deee6b31f8d7e7f0 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 11:05:16 -0800 Subject: [PATCH 210/373] Update IdTokenDescriptor+MappableTests.swift --- .../IdTokenDescriptor+MappableTests.swift | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/IdTokenDescriptor+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/IdTokenDescriptor+MappableTests.swift index af080b17..63913ae7 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/IdTokenDescriptor+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/IdTokenDescriptor+MappableTests.swift @@ -82,22 +82,6 @@ class IdTokenDescriptorMappingTests: XCTestCase { } } - func testMappingWithNoScopePresentError() throws { - let input = IdTokenDescriptor(encrypted: false, - claims: [], - idTokenRequired: false, - configuration: expectedConfiguration, - clientID: expectedClientId, - redirectURI: expectedRedirectUri, - scope: nil) - - XCTAssertThrowsError(try mapper.map(input)) { error in - XCTAssert(error is MappingError) - XCTAssertEqual(error as? MappingError, - .PropertyNotPresent(property: "scope", in: String(describing: IdTokenDescriptor.self))) - } - } - private func assertEqual(_ actual: IdTokenRequirement, _ expected: IdTokenRequirement) { XCTAssertEqual(actual.encrypted, expected.encrypted) XCTAssertEqual(actual.required, expected.required) From 8c2d0c2279291353b54a1b4674c6f7cd5dd427fe Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 11:05:31 -0800 Subject: [PATCH 211/373] Update classes who depend on VC. --- .../IssuanceService+VerifiableCredentialRequester.swift | 4 ++-- .../RequestProtocols/Manifest/ContractIssuanceRequest.swift | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+VerifiableCredentialRequester.swift b/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+VerifiableCredentialRequester.swift index 0113af49..447801bc 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+VerifiableCredentialRequester.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+VerifiableCredentialRequester.swift @@ -28,8 +28,8 @@ extension IssuanceService: VerifiableCredentialRequester { self.send(response: issuanceResponseContainer) }() - let verifiableCredential = VerifiableCredential(raw: rawVerifiableCredential, - from: issuanceResponseContainer.contract) + let verifiableCredential = try VerifiableCredential(raw: rawVerifiableCredential, + from: issuanceResponseContainer.contract) return verifiableCredential } } diff --git a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift index 952e7cfe..3201e142 100644 --- a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift +++ b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift @@ -48,8 +48,7 @@ class ContractIssuanceRequest: VerifiedIdIssuanceRequest { do { try self.responseContainer.add(requirement: requirement) let verifiableCredential = try await verifiableCredentialRequester.send(request: responseContainer) - let verifiedId: VerifiedId = try configuration.mapper.map(verifiableCredential) - return Result.success(verifiedId) + return Result.success(verifiableCredential) } catch { return Result.failure(error) } From 748d75b8c3b68025bfb7f5eb03c778ace3b3c04f Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 11:49:06 -0800 Subject: [PATCH 212/373] Add Error View to test app. --- .../project.pbxproj | 28 +++++++++++++++--- .../WalletLibraryDemo/Views/ErrorView.swift | 29 +++++++++++++++++++ 2 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 Demo/WalletLibraryDemo/WalletLibraryDemo/Views/ErrorView.swift diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo.xcodeproj/project.pbxproj b/Demo/WalletLibraryDemo/WalletLibraryDemo.xcodeproj/project.pbxproj index 9dd19dc7..abc4c029 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo.xcodeproj/project.pbxproj +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo.xcodeproj/project.pbxproj @@ -7,6 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 553CC08929A94686005A5FD6 /* ErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC08829A94686005A5FD6 /* ErrorView.swift */; }; + 553CC08B29A9469B005A5FD6 /* SuccessView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC08A29A9469B005A5FD6 /* SuccessView.swift */; }; + 553CC08D29A94A13005A5FD6 /* RequirementListCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC08C29A94A13005A5FD6 /* RequirementListCellView.swift */; }; 5585BDE129A047370059710B /* SampleViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585BDE029A047370059710B /* SampleViewModel.swift */; }; 5585BDE529A692E10059710B /* RequirementState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585BDE429A692E10059710B /* RequirementState.swift */; }; 5585BDFE29A6C4CA0059710B /* RequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585BDFD29A6C4CA0059710B /* RequestView.swift */; }; @@ -36,6 +39,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 553CC08829A94686005A5FD6 /* ErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorView.swift; sourceTree = ""; }; + 553CC08A29A9469B005A5FD6 /* SuccessView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuccessView.swift; sourceTree = ""; }; + 553CC08C29A94A13005A5FD6 /* RequirementListCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequirementListCellView.swift; sourceTree = ""; }; 5585BDE029A047370059710B /* SampleViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleViewModel.swift; sourceTree = ""; }; 5585BDE429A692E10059710B /* RequirementState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequirementState.swift; sourceTree = ""; }; 5585BDFD29A6C4CA0059710B /* RequestView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestView.swift; sourceTree = ""; }; @@ -63,6 +69,18 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 553CC08E29A94A40005A5FD6 /* Views */ = { + isa = PBXGroup; + children = ( + 553CC08829A94686005A5FD6 /* ErrorView.swift */, + 5585BDFD29A6C4CA0059710B /* RequestView.swift */, + 553CC08C29A94A13005A5FD6 /* RequirementListCellView.swift */, + 5585BDFF29A6C5070059710B /* RequirementView.swift */, + 553CC08A29A9469B005A5FD6 /* SuccessView.swift */, + ); + path = Views; + sourceTree = ""; + }; 55E3368B293E8F7500CD2ED7 = { isa = PBXGroup; children = ( @@ -83,13 +101,12 @@ 55E33696293E8F7500CD2ED7 /* WalletLibraryDemo */ = { isa = PBXGroup; children = ( - 55E33697293E8F7500CD2ED7 /* WalletLibraryDemoApp.swift */, + 553CC08E29A94A40005A5FD6 /* Views */, + 55E3369E293E8F7500CD2ED7 /* ContentView.swift */, 55E33699293E8F7500CD2ED7 /* Persistence.swift */, - 5585BDFD29A6C4CA0059710B /* RequestView.swift */, - 5585BDFF29A6C5070059710B /* RequirementView.swift */, 5585BDE429A692E10059710B /* RequirementState.swift */, 5585BDE029A047370059710B /* SampleViewModel.swift */, - 55E3369E293E8F7500CD2ED7 /* ContentView.swift */, + 55E33697293E8F7500CD2ED7 /* WalletLibraryDemoApp.swift */, 55E336A0293E8F7700CD2ED7 /* Assets.xcassets */, 55E336A2293E8F7700CD2ED7 /* WalletLibraryDemo.entitlements */, 55E3369B293E8F7500CD2ED7 /* WalletLibraryDemo.xcdatamodeld */, @@ -187,11 +204,14 @@ files = ( 5585BDFE29A6C4CA0059710B /* RequestView.swift in Sources */, 55E3369D293E8F7500CD2ED7 /* WalletLibraryDemo.xcdatamodeld in Sources */, + 553CC08B29A9469B005A5FD6 /* SuccessView.swift in Sources */, 55E33698293E8F7500CD2ED7 /* WalletLibraryDemoApp.swift in Sources */, 55E3369F293E8F7500CD2ED7 /* ContentView.swift in Sources */, 5585BE0029A6C5070059710B /* RequirementView.swift in Sources */, 5585BDE529A692E10059710B /* RequirementState.swift in Sources */, 55E3369A293E8F7500CD2ED7 /* Persistence.swift in Sources */, + 553CC08929A94686005A5FD6 /* ErrorView.swift in Sources */, + 553CC08D29A94A13005A5FD6 /* RequirementListCellView.swift in Sources */, 5585BDE129A047370059710B /* SampleViewModel.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/ErrorView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/ErrorView.swift new file mode 100644 index 00000000..5195cf39 --- /dev/null +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/ErrorView.swift @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import SwiftUI + +struct ErrorView: View { + + @EnvironmentObject var viewModel: ViewModel + + let errorMessage: String + + @Environment(\.dismiss) var dismiss + + var body: some View { + VStack { + Text(errorMessage) + .foregroundColor(.red) + Button { + viewModel.reset() + dismiss() + } label: { + Text("Reset") + } + } + } +} + From 8447c0175647ae454d97411752b93af34cdca0e4 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 11:49:27 -0800 Subject: [PATCH 213/373] Update request view. --- .../WalletLibraryDemo/RequestView.swift | 94 ------------------- .../WalletLibraryDemo/Views/RequestView.swift | 44 +++++++++ 2 files changed, 44 insertions(+), 94 deletions(-) delete mode 100644 Demo/WalletLibraryDemo/WalletLibraryDemo/RequestView.swift create mode 100644 Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequestView.swift diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/RequestView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/RequestView.swift deleted file mode 100644 index 0cb4759e..00000000 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/RequestView.swift +++ /dev/null @@ -1,94 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Microsoft Corporation. All rights reserved. -* Licensed under the MIT License. See License.txt in the project root for license information. -*--------------------------------------------------------------------------------------------*/ - -import SwiftUI - -struct RequestView: View { - - @EnvironmentObject var viewModel: ViewModel - - @Environment(\.dismiss) var dismiss - - var body: some View { - VStack { - if viewModel.isProgressViewShowing { - ProgressView() - } - else if let errorMessage = viewModel.errorMessage { - Text(errorMessage) - .foregroundColor(.red) - Button { - viewModel.reset() - dismiss() - } label: { - Text("Reset") - } - } - else if let successMessage = viewModel.successMessage { - Text(successMessage) - Button { - viewModel.reset() - dismiss() - } label: { - Text("Reset") - } - } - else if let issuedVerifiedId = viewModel.issuedVerifiedId { - VStack { - Text("\(issuedVerifiedId.id)") - .multilineTextAlignment(.center) - Spacer() - Text("Issued on: \(issuedVerifiedId.issuedOn.formatted())") - .multilineTextAlignment(.center) - Spacer() - Text("Expires on: \(issuedVerifiedId.expiresOn.formatted())") - .multilineTextAlignment(.center) - Spacer() - Text("Claims:") - .multilineTextAlignment(.center) - List(issuedVerifiedId.claims.indices, id: \.self) { index in - HStack { - Text("\(issuedVerifiedId.claims[index].id):") - Text(String(describing: issuedVerifiedId.claims[index].value)) - } - }.listStyle(.inset) - }.navigationTitle("Verified Id Successfully Issued!") - .navigationBarTitleDisplayMode(.inline) - } - else { - VStack { - List(viewModel.requirements) { requirement in - NavigationLink(destination: RequirementView(requirement: requirement)) { - HStack { - switch (requirement.status) { - case .valid: - Image(systemName: "checkmark.circle.fill") - .foregroundColor(.green) - case .invalid: - Image(systemName: "x.circle.fill") - .foregroundColor(.red) - case .missing: - Image(systemName: "circle.fill") - .foregroundColor(.accentColor) - } - Text(requirement.label) - } - } - }.listStyle(.inset) - Spacer() - Divider() - Button { - viewModel.complete() - } label: { - Text("Complete") - }.disabled(!viewModel.isCompleteButtonEnabled) - Spacer() - }.navigationTitle("Fulfill Requirements") - .navigationBarTitleDisplayMode(.inline) - } - } - } -} - diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequestView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequestView.swift new file mode 100644 index 00000000..9962ed09 --- /dev/null +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequestView.swift @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import SwiftUI + +struct RequestView: View { + + @EnvironmentObject var viewModel: ViewModel + + @Environment(\.dismiss) var dismiss + + var body: some View { + VStack { + if viewModel.isProgressViewShowing { + ProgressView() + } + else if let errorMessage = viewModel.errorMessage { + ErrorView(errorMessage: errorMessage) + } + else if viewModel.successMessage != nil || viewModel.issuedVerifiedId != nil { + SuccessView() + } else { + VStack { + List(viewModel.requirements) { requirement in + NavigationLink(destination: RequirementView(requirement: requirement)) { + RequirementListViewCell(requirement: requirement) + } + }.listStyle(.inset) + Spacer() + Divider() + Button { + viewModel.complete() + } label: { + Text("Complete") + }.disabled(!viewModel.isCompleteButtonEnabled) + Spacer() + }.navigationTitle("Fulfill Requirements") + .navigationBarTitleDisplayMode(.inline) + } + } + } +} From 1d56075b1e68e30c4df0a43a0ba68a6f6528b26f Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 11:49:37 -0800 Subject: [PATCH 214/373] Update requirements view. --- .../WalletLibraryDemo/{ => Views}/RequirementView.swift | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Demo/WalletLibraryDemo/WalletLibraryDemo/{ => Views}/RequirementView.swift (100%) diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/RequirementView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequirementView.swift similarity index 100% rename from Demo/WalletLibraryDemo/WalletLibraryDemo/RequirementView.swift rename to Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequirementView.swift From 22993a95f251daa0af55e04bf4ac2e1e1dabd90a Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 11:49:41 -0800 Subject: [PATCH 215/373] Create RequirementListCellView.swift --- .../Views/RequirementListCellView.swift | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequirementListCellView.swift diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequirementListCellView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequirementListCellView.swift new file mode 100644 index 00000000..c174da35 --- /dev/null +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequirementListCellView.swift @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import SwiftUI + +struct RequirementListViewCell: View { + + @StateObject var requirement: RequirementState + + var body: some View { + HStack { + switch (requirement.status) { + case .valid: + Image(systemName: "checkmark.circle.fill") + .foregroundColor(.green) + case .invalid: + Image(systemName: "x.circle.fill") + .foregroundColor(.red) + case .missing: + Image(systemName: "circle.fill") + .foregroundColor(.accentColor) + } + Text(requirement.label) + } + } +} + From d30ef371dcf620ea05c994df2169bd227b0b030b Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 11:49:56 -0800 Subject: [PATCH 216/373] Make RequirementState an ObservableObject. --- .../WalletLibraryDemo/RequirementState.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/RequirementState.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/RequirementState.swift index 01f0edbd..dc0f6274 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/RequirementState.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/RequirementState.swift @@ -17,11 +17,11 @@ enum RequirementStatus { case invalid } -class RequirementState: Identifiable { +class RequirementState: Identifiable, ObservableObject { - var label: String + @Published var label: String - var status: RequirementStatus + @Published var status: RequirementStatus let requirement: Requirement From 171d7c0810a4fb0ccfba7f98d257c42ea92d445a Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 11:49:59 -0800 Subject: [PATCH 217/373] Create SuccessView.swift --- .../WalletLibraryDemo/Views/SuccessView.swift | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 Demo/WalletLibraryDemo/WalletLibraryDemo/Views/SuccessView.swift diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/SuccessView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/SuccessView.swift new file mode 100644 index 00000000..9e583ceb --- /dev/null +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/SuccessView.swift @@ -0,0 +1,53 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import SwiftUI + +struct SuccessView: View { + + @EnvironmentObject var viewModel: ViewModel + + @Environment(\.dismiss) var dismiss + + var body: some View { + VStack { + if let successMessage = viewModel.successMessage { + Text(successMessage) + Button { + viewModel.reset() + dismiss() + } label: { + Text("Reset") + } + } + else if let issuedVerifiedId = viewModel.issuedVerifiedId { + VStack { + Text("\(issuedVerifiedId.id)") + .multilineTextAlignment(.center) + Spacer() + Text("Issued on: \(issuedVerifiedId.issuedOn.formatted())") + .multilineTextAlignment(.center) + Spacer() + if let expiresOn = issuedVerifiedId.expiresOn { + Text("Expires on: \(expiresOn.formatted())") + .multilineTextAlignment(.center) + Spacer() + } + Text("Claims:") + let claims = issuedVerifiedId.getClaims() + List(claims.indices, id: \.self) { index in + HStack { + let claim = claims[index] + Text("\(claim.id):") + Text(String(describing: claims[index].value)) + } + }.listStyle(.inset) + }.navigationTitle("Verified Id Successfully Issued!") + .navigationBarTitleDisplayMode(.inline) + } + } + } +} + From d057f81fcc064459b529a466c872b060bdfe6366 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 11:50:17 -0800 Subject: [PATCH 218/373] make getClaims a functions instead of property. --- .../VerifiableCredential/VerifiableCredential.swift | 8 ++++---- WalletLibrary/WalletLibrary/VerifiedId/VerifiedId.swift | 5 ++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift b/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift index e12d521e..4fa30b17 100644 --- a/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift +++ b/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift @@ -19,10 +19,6 @@ struct VerifiableCredential: VerifiedId { public let id: String - public private(set) lazy var claims: [VerifiedIdClaim] = { - return createClaims() - }() - public let expiresOn: Date? public let issuedOn: Date @@ -74,6 +70,10 @@ struct VerifiableCredential: VerifiedId { try container.encode(contract, forKey: .contract) } + public func getClaims() -> [VerifiedIdClaim] { + return createClaims() + } + private func createClaims() -> [VerifiedIdClaim] { guard let vcClaims = raw.content.vc?.credentialSubject else { diff --git a/WalletLibrary/WalletLibrary/VerifiedId/VerifiedId.swift b/WalletLibrary/WalletLibrary/VerifiedId/VerifiedId.swift index c2269c10..9b4bf9c0 100644 --- a/WalletLibrary/WalletLibrary/VerifiedId/VerifiedId.swift +++ b/WalletLibrary/WalletLibrary/VerifiedId/VerifiedId.swift @@ -13,13 +13,12 @@ public protocol VerifiedId: Codable { /// the id of the verified id. For example, this value would equal the jti of a Verifiable Credential. var id: String { get } - /// a list of claims within the verified id. - var claims: [VerifiedIdClaim] { mutating get } - /// date the verified id expires. var expiresOn: Date? { get } /// date the verified id was issued. var issuedOn: Date { get } + + func getClaims() -> [VerifiedIdClaim] } From 5532d85e7ee16043db5a9b9c3274385e1797632d Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 12:44:49 -0800 Subject: [PATCH 219/373] Update VerifiableCredential-SDK-iOS --- WalletLibrary/Submodules/VerifiableCredential-SDK-iOS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS b/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS index a1110b20..73c64e43 160000 --- a/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS +++ b/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS @@ -1 +1 @@ -Subproject commit a1110b20a39414144ac807c3a7b1a5fb64dce9a0 +Subproject commit 73c64e4301998d022808dc33eb7708ce7b24b5d9 From fb7d948e282212b3bc492e549adc489227d7912f Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 12:45:03 -0800 Subject: [PATCH 220/373] Add PinDescriptor mapping. --- .../WalletLibrary.xcodeproj/project.pbxproj | 8 +++++ .../Presentation/PinDescriptor+Mappable.swift | 31 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PinDescriptor+Mappable.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 43d57a6b..84f9780d 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -50,6 +50,8 @@ 553CC08429A8193E005A5FD6 /* VCNetworking.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 55E33704293FCE9C00CD2ED7 /* VCNetworking.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 553CC08529A81949005A5FD6 /* VCCrypto.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55E336FA293FCE8200CD2ED7 /* VCCrypto.framework */; platformFilter = ios; }; 553CC08629A81949005A5FD6 /* VCCrypto.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 55E336FA293FCE8200CD2ED7 /* VCCrypto.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 553CC09029A955E6005A5FD6 /* PinDescriptor+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC08F29A955E6005A5FD6 /* PinDescriptor+Mappable.swift */; }; + 553CC09229A957A3005A5FD6 /* PinDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC09129A957A3005A5FD6 /* PinDescriptor+MappableTests.swift */; }; 5585BDE729A6C38C0059710B /* InjectedIdToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585BDE629A6C38C0059710B /* InjectedIdToken.swift */; }; 5585BE0229A6D42B0059710B /* VerifiedIdType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585BE0129A6D42B0059710B /* VerifiedIdType.swift */; }; 559BD3032995993700BD61AC /* PresentationInputDescriptor+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */; }; @@ -334,6 +336,8 @@ 5534E687294A5F16005D0765 /* IdTokenDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IdTokenDescriptor+MappableTests.swift"; sourceTree = ""; }; 5534E68B294A8B8F005D0765 /* AccessTokenDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AccessTokenDescriptor+MappableTests.swift"; sourceTree = ""; }; 5534E6AC294AAFA6005D0765 /* PresentationDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationDescriptor+MappableTests.swift"; sourceTree = ""; }; + 553CC08F29A955E6005A5FD6 /* PinDescriptor+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PinDescriptor+Mappable.swift"; sourceTree = ""; }; + 553CC09129A957A3005A5FD6 /* PinDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PinDescriptor+MappableTests.swift"; sourceTree = ""; }; 5585BDE629A6C38C0059710B /* InjectedIdToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InjectedIdToken.swift; sourceTree = ""; }; 5585BE0129A6D42B0059710B /* VerifiedIdType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdType.swift; sourceTree = ""; }; 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationInputDescriptor+Mappable.swift"; sourceTree = ""; }; @@ -708,6 +712,7 @@ 559BD3042995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift */, 559BD3062995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift */, 559BD3082995ABE800BD61AC /* PresentationRequest+MappableTests.swift */, + 553CC09129A957A3005A5FD6 /* PinDescriptor+MappableTests.swift */, ); path = Presentation; sourceTree = ""; @@ -718,6 +723,7 @@ 55E2F089299576350008010D /* PresentationDefinition+Mappable.swift */, 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */, 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */, + 553CC08F29A955E6005A5FD6 /* PinDescriptor+Mappable.swift */, ); path = Presentation; sourceTree = ""; @@ -1368,6 +1374,7 @@ 5534E66E294A0F8F005D0765 /* Mapping.swift in Sources */, 559BD4B3299EDFB400BD61AC /* VerifiableCredential.swift in Sources */, 55E336D6293FA78C00CD2ED7 /* VerifiedIdClaim.swift in Sources */, + 553CC09029A955E6005A5FD6 /* PinDescriptor+Mappable.swift in Sources */, 55E337652943749600CD2ED7 /* AsyncWrapper.swift in Sources */, 550E3214298B11A5004E7716 /* Requirement.swift in Sources */, 559BD453299D3DD800BD61AC /* IssuanceRequest+Mappable.swift in Sources */, @@ -1445,6 +1452,7 @@ 559BD4F1299EEAEA00BD61AC /* PinRequirementTests.swift in Sources */, 559BD30F2996C1C500BD61AC /* MockLogConsumer.swift in Sources */, 55E2F076299555280008010D /* MockRequirement.swift in Sources */, + 553CC09229A957A3005A5FD6 /* PinDescriptor+MappableTests.swift in Sources */, 5534E68C294A8B8F005D0765 /* AccessTokenDescriptor+MappableTests.swift in Sources */, 55E2F077299555280008010D /* MockRequesterStyle.swift in Sources */, 552E50A0293E6AB200868F47 /* VerifiedIdClientTests.swift in Sources */, diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PinDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PinDescriptor+Mappable.swift new file mode 100644 index 00000000..89c11572 --- /dev/null +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PinDescriptor+Mappable.swift @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import VCEntities + +/** + * Errors thrown in Pin Descriptor Mappable extension. + */ +enum PinDescriptorError: Error { + case presentationDefinitionMissingInRequest +} + +/** + * An extension of the VCEntities.PinDescriptor class to be able + * to map PinDescriptor to PinRequirement. + */ +extension VCEntities.PinDescriptor: Mappable { + + func map(using mapper: Mapping) throws -> PinRequirement { + + let type = try getRequiredProperty(property: type, propertyName: "type") + + let pinRequirement = PinRequirement(required: true, + length: length, + type: type, + salt: salt) + return pinRequirement + } +} From 26afa387c6d2c84a59391fa4998643feb5e8020c Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 12:45:09 -0800 Subject: [PATCH 221/373] Update IssuanceResponseContainer+WalletLibrary.swift --- .../Entities/IssuanceResponseContainer+WalletLibrary.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+WalletLibrary.swift b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+WalletLibrary.swift index 6a143fde..f84f0155 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+WalletLibrary.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+WalletLibrary.swift @@ -17,6 +17,10 @@ enum IssuanceResponseContainerError: Error, Equatable { */ extension VCEntities.IssuanceResponseContainer { + struct Constants { + static let IdTokenHintKey = "https://self-issued.me" + } + init(from manifest: any RawManifest, input: VerifiedIdRequestInput) throws { guard let manifest = manifest as? IssuanceRequest else { @@ -62,7 +66,7 @@ extension VCEntities.IssuanceResponseContainer { private mutating func add(idTokenRequirement: IdTokenRequirement) throws { try idTokenRequirement.validate() - if idTokenRequirement.configuration.absoluteString == "https://self-issued.me" { + if idTokenRequirement.configuration.absoluteString == Constants.IdTokenHintKey { self.issuanceIdToken = idTokenRequirement.idToken } else { self.requestedIdTokenMap[idTokenRequirement.configuration.absoluteString] = idTokenRequirement.idToken From 6ac88fd264b73c635aaf5e1ab702958587ed64c5 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 12:45:23 -0800 Subject: [PATCH 222/373] Add PinDescriptor mapping to presentation request mapping. --- .../PresentationRequest+Mappable.swift | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+Mappable.swift index 7915dd42..c819637b 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+Mappable.swift @@ -26,7 +26,7 @@ extension VCEntities.PresentationRequest: Mappable { let requirement = try mapper.map(presentationDefinition) let rootOfTrust = try mapper.map(linkedDomainResult) - let injectedIdToken = createInjectedIdTokenIfExists() + let injectedIdToken = try createInjectedIdTokenIfExists(using: mapper) let clientName = content.registration?.clientName ?? "" let requesterStyle = OpenIdVerifierStyle(name: clientName) @@ -39,23 +39,18 @@ extension VCEntities.PresentationRequest: Mappable { return content } - private func createInjectedIdTokenIfExists() -> InjectedIdToken? { + private func createInjectedIdTokenIfExists(using mapper: Mapping) throws -> InjectedIdToken? { if let idTokenHint = content.idTokenHint { return InjectedIdToken(rawToken: idTokenHint, - pin: createPinRequirementIfExists()) + pin: try createPinRequirementIfExists(using: mapper)) } return nil } - private func createPinRequirementIfExists() -> PinRequirement? { - if let pin = content.pin, - let type = pin.type { - let pinRequirement = PinRequirement(required: true, - length: pin.length, - type: type, - salt: pin.salt) - return pinRequirement + private func createPinRequirementIfExists(using mapper: Mapping) throws -> PinRequirement? { + if let pin = content.pin { + return try mapper.map(pin) } return nil From 9d3fb3c6ab48f4a17b6d7c59f256bc2008c8fff1 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 12:45:28 -0800 Subject: [PATCH 223/373] Update IssuanceResponseContainerExtensionTests.swift --- ...uanceResponseContainerExtensionTests.swift | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Entities/IssuanceResponseContainerExtensionTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Entities/IssuanceResponseContainerExtensionTests.swift index cad9f5f5..7b4268e2 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Entities/IssuanceResponseContainerExtensionTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Entities/IssuanceResponseContainerExtensionTests.swift @@ -79,6 +79,34 @@ class IssuanceResponseContainerExtensionTests: XCTestCase { // Assert XCTAssertEqual(container.requestedIdTokenMap, [requirement.configuration.absoluteString: requirement.idToken]) + XCTAssertNil(container.issuanceIdToken) + XCTAssert(container.requestedAccessTokenMap.isEmpty) + XCTAssert(container.requestedSelfAttestedClaimMap.isEmpty) + XCTAssert(container.requestVCMap.isEmpty) + XCTAssertNil(container.issuancePin) + } + + func testAddRequirement_WithIdTokenHintRequirement_UpdateIdTokenHintValue() async throws { + // Arrange + let issuanceRequest = createMockIssuanceRequest() + let mockInput = VerifiedIdRequestURL(url: URL(string: "https://test.com")!) + var container = try IssuanceResponseContainer(from: issuanceRequest, input: mockInput) + + let requirement = IdTokenRequirement(encrypted: false, + required: true, + configuration: URL(string: "https://self-issued.me")!, + clientId: "", + redirectUri: "", + scope: "") + requirement.fulfill(with: "mock token") + + // Act + try container.add(requirement: requirement) + + // Assert + XCTAssertNotNil(container.issuanceIdToken) + XCTAssertEqual(container.issuanceIdToken, "mock token") + XCTAssert(container.requestedIdTokenMap.isEmpty) XCTAssert(container.requestedAccessTokenMap.isEmpty) XCTAssert(container.requestedSelfAttestedClaimMap.isEmpty) XCTAssert(container.requestVCMap.isEmpty) From 436157c4a78207b9fb39acb97674630827136f82 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 12:45:31 -0800 Subject: [PATCH 224/373] Create PinDescriptor+MappableTests.swift --- .../PinDescriptor+MappableTests.swift | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PinDescriptor+MappableTests.swift diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PinDescriptor+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PinDescriptor+MappableTests.swift new file mode 100644 index 00000000..b79c0219 --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PinDescriptor+MappableTests.swift @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import XCTest +import VCEntities +@testable import WalletLibrary + +class PinDescriptorMappingTests: XCTestCase { + + func testMap_WithNilType_ThrowsError() throws { + // Arrange + let mockMapper = MockMapper() + let pinDescriptor = PinDescriptor(type: nil, + length: 4, + hash: "hash", + salt: nil, + iterations: nil, + alg: nil) + + // Act + XCTAssertThrowsError(try mockMapper.map(pinDescriptor)) { error in + // Assert + XCTAssert(error is MappingError) + XCTAssertEqual(error as? MappingError, .PropertyNotPresent(property: "type", in: "PinDescriptor")) + } + } + + func testMap_WithNilSalt_ReturnsPinRequirement() throws { + // Arrange + let mockMapper = MockMapper() + let pinDescriptor = PinDescriptor(type: "mock type", + length: 4, + hash: "hash", + salt: nil, + iterations: nil, + alg: nil) + + // Act + let actualResult = try mockMapper.map(pinDescriptor) + + // Assert + XCTAssert(actualResult.required) + XCTAssertEqual(actualResult.type, "mock type") + XCTAssertEqual(actualResult.length, 4) + XCTAssertEqual(actualResult.salt, nil) + XCTAssertEqual(actualResult.pin, nil) + } + + func testMap_WithSalt_ReturnsPinRequirement() throws { + // Arrange + let mockMapper = MockMapper() + let pinDescriptor = PinDescriptor(type: "mock type", + length: 8, + hash: "hash", + salt: "mock salt", + iterations: nil, + alg: nil) + + // Act + let actualResult = try mockMapper.map(pinDescriptor) + + // Assert + XCTAssert(actualResult.required) + XCTAssertEqual(actualResult.type, "mock type") + XCTAssertEqual(actualResult.length, 8) + XCTAssertEqual(actualResult.salt, "mock salt") + XCTAssertEqual(actualResult.pin, nil) + } +} From 3aa173ab653bcae3656c2e246778d75cb8c4c3e6 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 12:45:36 -0800 Subject: [PATCH 225/373] Update PresentationRequest+MappableTests.swift --- .../PresentationRequest+MappableTests.swift | 169 +++++++++++++++--- 1 file changed, 144 insertions(+), 25 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+MappableTests.swift index 231ad19a..023162ec 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+MappableTests.swift @@ -14,12 +14,10 @@ class PresentationRequestMappingTests: XCTestCase { case expectedToBeThrown } - let mapper = Mapper() - func testMap_WithNoPresentationDefinitionPresent_ThrowsError() throws { // Arrange let mockRequestClaims = RequestedClaims(vpToken: RequestedVPToken(presentationDefinition: nil)) - let token = createPresentationRequestToken(with: mockRequestClaims, and: nil) + let token = createPresentationRequestToken(requestedClaims: mockRequestClaims, registration: nil) let linkedDomainResult = LinkedDomainResult.linkedDomainMissing let presentationRequest = PresentationRequest(from: token, @@ -39,7 +37,7 @@ class PresentationRequestMappingTests: XCTestCase { // Arrange let mockPresentationDefinition = PresentationDefinition(id: nil, inputDescriptors: nil, issuance: nil) let mockRequestClaims = RequestedClaims(vpToken: RequestedVPToken(presentationDefinition: mockPresentationDefinition)) - let token = createPresentationRequestToken(with: mockRequestClaims, and: nil) + let token = createPresentationRequestToken(requestedClaims: mockRequestClaims, registration: nil) let linkedDomainResult = LinkedDomainResult.linkedDomainMissing let presentationRequest = PresentationRequest(from: token, @@ -72,7 +70,7 @@ class PresentationRequestMappingTests: XCTestCase { issuanceOptions: []) let mockPresentationDefinition = PresentationDefinition(id: nil, inputDescriptors: nil, issuance: nil) let mockRequestClaims = RequestedClaims(vpToken: RequestedVPToken(presentationDefinition: mockPresentationDefinition)) - let token = createPresentationRequestToken(with: mockRequestClaims, and: nil) + let token = createPresentationRequestToken(requestedClaims: mockRequestClaims, registration: nil) let linkedDomainResult = LinkedDomainResult.linkedDomainMissing let presentationRequest = PresentationRequest(from: token, @@ -108,16 +106,16 @@ class PresentationRequestMappingTests: XCTestCase { let expectedVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, required: false, types: [], - purpose: nil, - issuanceOptions: []) + purpose: nil, + issuanceOptions: []) let mockPresentationDefinition = PresentationDefinition(id: nil, inputDescriptors: nil, issuance: nil) let mockRequestClaims = RequestedClaims(vpToken: RequestedVPToken(presentationDefinition: mockPresentationDefinition)) let mockRegistration = RegistrationClaims(clientName: nil, - clientPurpose: nil, - logoURI: nil, - subjectIdentifierTypesSupported: nil, - vpFormats: nil) - let token = createPresentationRequestToken(with: mockRequestClaims, and: mockRegistration) + clientPurpose: nil, + logoURI: nil, + subjectIdentifierTypesSupported: nil, + vpFormats: nil) + let token = createPresentationRequestToken(requestedClaims: mockRequestClaims, registration: mockRegistration) let expectedRootOfTrust = RootOfTrust(verified: false, source: "") let linkedDomainResult = LinkedDomainResult.linkedDomainMissing @@ -146,6 +144,7 @@ class PresentationRequestMappingTests: XCTestCase { XCTAssertIdentical(actualResult.requirement as AnyObject, expectedVerifiedIdRequirement as AnyObject) XCTAssertEqual(actualResult.rootOfTrust, expectedRootOfTrust) XCTAssertEqual(actualResult.style as? OpenIdVerifierStyle, expectedStyle) + XCTAssertNil(actualResult.injectedIdToken) } func testMap_WithClientNamePresent_ReturnVerifiedIdRequestContent() throws { @@ -153,18 +152,18 @@ class PresentationRequestMappingTests: XCTestCase { let mockRequesterName = "mockRequesterName235" let expectedStyle = OpenIdVerifierStyle(name: mockRequesterName) let expectedVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, - required: false, - types: [], - purpose: nil, - issuanceOptions: []) + required: false, + types: [], + purpose: nil, + issuanceOptions: []) let mockPresentationDefinition = PresentationDefinition(id: nil, inputDescriptors: nil, issuance: nil) let mockRequestClaims = RequestedClaims(vpToken: RequestedVPToken(presentationDefinition: mockPresentationDefinition)) let mockRegistration = RegistrationClaims(clientName: mockRequesterName, - clientPurpose: nil, - logoURI: nil, - subjectIdentifierTypesSupported: nil, - vpFormats: nil) - let token = createPresentationRequestToken(with: mockRequestClaims, and: mockRegistration) + clientPurpose: nil, + logoURI: nil, + subjectIdentifierTypesSupported: nil, + vpFormats: nil) + let token = createPresentationRequestToken(requestedClaims: mockRequestClaims, registration: mockRegistration) let expectedRootOfTrust = RootOfTrust(verified: false, source: "") let linkedDomainResult = LinkedDomainResult.linkedDomainMissing @@ -193,10 +192,130 @@ class PresentationRequestMappingTests: XCTestCase { XCTAssertIdentical(actualResult.requirement as AnyObject, expectedVerifiedIdRequirement as AnyObject) XCTAssertEqual(actualResult.rootOfTrust, expectedRootOfTrust) XCTAssertEqual(actualResult.style as? OpenIdVerifierStyle, expectedStyle) + XCTAssertNil(actualResult.injectedIdToken) + } + + func testMap_WithIdTokenHintWithoutPin_ReturnVerifiedIdRequestContentWithInjectedIdToken() throws { + // Arrange + let mockRequesterName = "mockRequesterName235" + let expectedStyle = OpenIdVerifierStyle(name: mockRequesterName) + let expectedVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, + required: false, + types: [], + purpose: nil, + issuanceOptions: []) + let mockPresentationDefinition = PresentationDefinition(id: nil, inputDescriptors: nil, issuance: nil) + let mockRequestClaims = RequestedClaims(vpToken: RequestedVPToken(presentationDefinition: mockPresentationDefinition)) + let mockRegistration = RegistrationClaims(clientName: mockRequesterName, + clientPurpose: nil, + logoURI: nil, + subjectIdentifierTypesSupported: nil, + vpFormats: nil) + let token = createPresentationRequestToken(requestedClaims: mockRequestClaims, + registration: mockRegistration, + idTokenHint: "mock idToken hint") + + let expectedRootOfTrust = RootOfTrust(verified: false, source: "") + let linkedDomainResult = LinkedDomainResult.linkedDomainMissing + let presentationRequest = PresentationRequest(from: token, + linkedDomainResult: linkedDomainResult) + + func mockResults(objectToBeMapped: Any) throws -> Any? { + + if objectToBeMapped is PresentationDefinition { + return expectedVerifiedIdRequirement + } + + if objectToBeMapped is LinkedDomainResult { + return expectedRootOfTrust + } + + return nil + } + + let mockMapper = MockMapper(mockResults: mockResults) + + // Act + let actualResult = try mockMapper.map(presentationRequest) + + // Act + XCTAssertIdentical(actualResult.requirement as AnyObject, expectedVerifiedIdRequirement as AnyObject) + XCTAssertEqual(actualResult.rootOfTrust, expectedRootOfTrust) + XCTAssertEqual(actualResult.style as? OpenIdVerifierStyle, expectedStyle) + XCTAssertEqual(actualResult.injectedIdToken?.rawToken, "mock idToken hint") + XCTAssertNil(actualResult.injectedIdToken?.pin) + } + + func testMap_WithIdTokenHintWithPin_ReturnVerifiedIdRequestContentWithInjectedIdToken() throws { + // Arrange + let mockRequesterName = "mockRequesterName235" + let expectedPinRequirement = PinRequirement(required: true, + length: 4, + type: "mock pin type", + salt: "mock salt") + let expectedStyle = OpenIdVerifierStyle(name: mockRequesterName) + let expectedVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, + required: false, + types: [], + purpose: nil, + issuanceOptions: []) + let mockPresentationDefinition = PresentationDefinition(id: nil, inputDescriptors: nil, issuance: nil) + let mockRequestClaims = RequestedClaims(vpToken: RequestedVPToken(presentationDefinition: mockPresentationDefinition)) + let mockRegistration = RegistrationClaims(clientName: mockRequesterName, + clientPurpose: nil, + logoURI: nil, + subjectIdentifierTypesSupported: nil, + vpFormats: nil) + let pinDescriptor = PinDescriptor(type: "mock pin type", + length: 4, + hash: "mock hash", + salt: "mock salt", + iterations: nil, + alg: nil) + let token = createPresentationRequestToken(requestedClaims: mockRequestClaims, + registration: mockRegistration, + idTokenHint: "mock idToken hint", + pin: pinDescriptor) + + let expectedRootOfTrust = RootOfTrust(verified: false, source: "") + let linkedDomainResult = LinkedDomainResult.linkedDomainMissing + let presentationRequest = PresentationRequest(from: token, + linkedDomainResult: linkedDomainResult) + + func mockResults(objectToBeMapped: Any) throws -> Any? { + + if objectToBeMapped is PresentationDefinition { + return expectedVerifiedIdRequirement + } + + if objectToBeMapped is LinkedDomainResult { + return expectedRootOfTrust + } + + if objectToBeMapped is PinDescriptor { + return expectedPinRequirement + } + + return nil + } + + let mockMapper = MockMapper(mockResults: mockResults) + + // Act + let actualResult = try mockMapper.map(presentationRequest) + + // Act + XCTAssertIdentical(actualResult.requirement as AnyObject, expectedVerifiedIdRequirement as AnyObject) + XCTAssertEqual(actualResult.rootOfTrust, expectedRootOfTrust) + XCTAssertEqual(actualResult.style as? OpenIdVerifierStyle, expectedStyle) + XCTAssertEqual(actualResult.injectedIdToken?.rawToken, "mock idToken hint") + XCTAssertIdentical(actualResult.injectedIdToken?.pin as AnyObject, expectedPinRequirement as AnyObject) } - private func createPresentationRequestToken(with requestedClaims: RequestedClaims?, - and registration: RegistrationClaims?) -> PresentationRequestToken { + private func createPresentationRequestToken(requestedClaims: RequestedClaims? = nil, + registration: RegistrationClaims? = nil, + idTokenHint: String? = nil, + pin: PinDescriptor? = nil) -> PresentationRequestToken { let presentationRequestTokenClaims = PresentationRequestClaims(jti: nil, clientID: nil, redirectURI: nil, @@ -208,10 +327,10 @@ class PresentationRequestMappingTests: XCTestCase { scope: nil, prompt: nil, registration: registration, - idTokenHint: nil, + idTokenHint: idTokenHint, iat: nil, exp: nil, - pin: nil) + pin: pin) return PresentationRequestToken(headers: Header(), content: presentationRequestTokenClaims)! } From edb6908f6fd1fb2986a254c122c9e2a40a80b7c8 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 14:25:56 -0800 Subject: [PATCH 226/373] Complete test cases for ContractIssuanceRequest. --- .../WalletLibrary.xcodeproj/project.pbxproj | 36 +++ .../ContractIssuanceRequestTests.swift | 212 ++++++++++++++++++ 2 files changed, 248 insertions(+) create mode 100644 WalletLibrary/WalletLibraryTests/Requests/RequestProtocols/Manifest/ContractIssuanceRequestTests.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 84f9780d..c244dc5b 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -52,6 +52,9 @@ 553CC08629A81949005A5FD6 /* VCCrypto.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 55E336FA293FCE8200CD2ED7 /* VCCrypto.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 553CC09029A955E6005A5FD6 /* PinDescriptor+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC08F29A955E6005A5FD6 /* PinDescriptor+Mappable.swift */; }; 553CC09229A957A3005A5FD6 /* PinDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC09129A957A3005A5FD6 /* PinDescriptor+MappableTests.swift */; }; + 553CC09629A96249005A5FD6 /* ContractIssuanceRequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC09529A96249005A5FD6 /* ContractIssuanceRequestTests.swift */; }; + 553CC09929A96C38005A5FD6 /* MockVerifiedId.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC09829A96C38005A5FD6 /* MockVerifiedId.swift */; }; + 553CC09B29A96ECD005A5FD6 /* MockIssuanceResponseContaining.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC09A29A96ECD005A5FD6 /* MockIssuanceResponseContaining.swift */; }; 5585BDE729A6C38C0059710B /* InjectedIdToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585BDE629A6C38C0059710B /* InjectedIdToken.swift */; }; 5585BE0229A6D42B0059710B /* VerifiedIdType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585BE0129A6D42B0059710B /* VerifiedIdType.swift */; }; 559BD3032995993700BD61AC /* PresentationInputDescriptor+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */; }; @@ -338,6 +341,9 @@ 5534E6AC294AAFA6005D0765 /* PresentationDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationDescriptor+MappableTests.swift"; sourceTree = ""; }; 553CC08F29A955E6005A5FD6 /* PinDescriptor+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PinDescriptor+Mappable.swift"; sourceTree = ""; }; 553CC09129A957A3005A5FD6 /* PinDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PinDescriptor+MappableTests.swift"; sourceTree = ""; }; + 553CC09529A96249005A5FD6 /* ContractIssuanceRequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContractIssuanceRequestTests.swift; sourceTree = ""; }; + 553CC09829A96C38005A5FD6 /* MockVerifiedId.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockVerifiedId.swift; sourceTree = ""; }; + 553CC09A29A96ECD005A5FD6 /* MockIssuanceResponseContaining.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockIssuanceResponseContaining.swift; sourceTree = ""; }; 5585BDE629A6C38C0059710B /* InjectedIdToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InjectedIdToken.swift; sourceTree = ""; }; 5585BE0129A6D42B0059710B /* VerifiedIdType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdType.swift; sourceTree = ""; }; 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationInputDescriptor+Mappable.swift"; sourceTree = ""; }; @@ -662,6 +668,30 @@ path = VCSDK; sourceTree = ""; }; + 553CC09329A96217005A5FD6 /* RequestProtocols */ = { + isa = PBXGroup; + children = ( + 553CC09429A96235005A5FD6 /* Manifest */, + ); + path = RequestProtocols; + sourceTree = ""; + }; + 553CC09429A96235005A5FD6 /* Manifest */ = { + isa = PBXGroup; + children = ( + 553CC09529A96249005A5FD6 /* ContractIssuanceRequestTests.swift */, + ); + path = Manifest; + sourceTree = ""; + }; + 553CC09729A96C29005A5FD6 /* VerifiedId */ = { + isa = PBXGroup; + children = ( + 553CC09829A96C38005A5FD6 /* MockVerifiedId.swift */, + ); + path = VerifiedId; + sourceTree = ""; + }; 559BD3A4299AD9A200BD61AC /* Manifest */ = { isa = PBXGroup; children = ( @@ -760,6 +790,7 @@ isa = PBXGroup; children = ( 559BD4F7299EED5700BD61AC /* MockRawManifest.swift */, + 553CC09A29A96ECD005A5FD6 /* MockIssuanceResponseContaining.swift */, ); path = Manifest; sourceTree = ""; @@ -767,6 +798,7 @@ 55A81BF02991BB13002C259A /* Requests */ = { isa = PBXGroup; children = ( + 553CC09329A96217005A5FD6 /* RequestProtocols */, 55E2F06D299553B40008010D /* Handlers */, 550A9193299402450014D030 /* Resolvers */, 559BD4E9299EEAA200BD61AC /* Requirements */, @@ -779,6 +811,7 @@ 55A81C0A2991BF6C002C259A /* Mocks */ = { isa = PBXGroup; children = ( + 553CC09729A96C29005A5FD6 /* VerifiedId */, 55E2F08229955E7B0008010D /* Utilities */, 55A81C0B2991BF73002C259A /* Requests */, ); @@ -1435,6 +1468,7 @@ 559BD3072995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift in Sources */, 559BD4EB299EEAC400BD61AC /* GroupRequirementTests.swift in Sources */, 55A81C072991BB2C002C259A /* RequestResolverFactoryTests.swift in Sources */, + 553CC09B29A96ECD005A5FD6 /* MockIssuanceResponseContaining.swift in Sources */, 559BD4EF299EEADF00BD61AC /* AccessTokenRequirementTests.swift in Sources */, 55A81C102991C211002C259A /* MockResolver.swift in Sources */, 559BD4E5299EEA3000BD61AC /* IssuanceResponseContainerExtensionTests.swift in Sources */, @@ -1444,6 +1478,7 @@ 559BD3052995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift in Sources */, 55A81C0D2991BF86002C259A /* MockInput.swift in Sources */, 559BD3AF299AF42C00BD61AC /* MockManifestResolver.swift in Sources */, + 553CC09929A96C38005A5FD6 /* MockVerifiedId.swift in Sources */, 559BD4F8299EED5700BD61AC /* MockRawManifest.swift in Sources */, 550A919829940DCE0014D030 /* MockOpenIdforVCResolver.swift in Sources */, 559BD459299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift in Sources */, @@ -1453,6 +1488,7 @@ 559BD30F2996C1C500BD61AC /* MockLogConsumer.swift in Sources */, 55E2F076299555280008010D /* MockRequirement.swift in Sources */, 553CC09229A957A3005A5FD6 /* PinDescriptor+MappableTests.swift in Sources */, + 553CC09629A96249005A5FD6 /* ContractIssuanceRequestTests.swift in Sources */, 5534E68C294A8B8F005D0765 /* AccessTokenDescriptor+MappableTests.swift in Sources */, 55E2F077299555280008010D /* MockRequesterStyle.swift in Sources */, 552E50A0293E6AB200868F47 /* VerifiedIdClientTests.swift in Sources */, diff --git a/WalletLibrary/WalletLibraryTests/Requests/RequestProtocols/Manifest/ContractIssuanceRequestTests.swift b/WalletLibrary/WalletLibraryTests/Requests/RequestProtocols/Manifest/ContractIssuanceRequestTests.swift new file mode 100644 index 00000000..e17c8f67 --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Requests/RequestProtocols/Manifest/ContractIssuanceRequestTests.swift @@ -0,0 +1,212 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import XCTest +import VCEntities +@testable import WalletLibrary + +class ContractIssuanceRequestTests: XCTestCase { + + enum ExpectedError: Error, Equatable { + case expectedToBeThrown + } + + func testIsSatisfied_WithInvalidRequirement_ThrowsError() async throws { + // Arrange + let mockStyle = MockRequesterStyle(requester: "mock requester") + let mockRootOfTrust = RootOfTrust(verified: true, source: "") + let mockMapper = MockMapper() + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) + let mockVCRequester = MockVerifiableCredentialRequester() + let mockIssuanceResponseContainer = MockIssuanceResponseContainer() + + let mockRequirement = MockRequirement(id: "mockRequirement324", + mockValidateCallback: { throw ExpectedError.expectedToBeThrown }) + + let content = VerifiedIdRequestContent(style: mockStyle, + requirement: mockRequirement, + rootOfTrust: mockRootOfTrust) + + let contractIssuanceRequest = ContractIssuanceRequest(content: content, + issuanceResponseContainer: mockIssuanceResponseContainer, + verifiableCredentialRequester: mockVCRequester, + configuration: configuration) + + // Act + let actualResult = contractIssuanceRequest.isSatisfied() + + // Assert + XCTAssertFalse(actualResult) + } + + func testIsSatisfied_WithOneInvalidRequirementofMultipleValidRequirements_ThrowsError() async throws { + // Arrange + let mockStyle = MockRequesterStyle(requester: "mock requester") + let mockRootOfTrust = RootOfTrust(verified: true, source: "") + let mockMapper = MockMapper() + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) + let mockVCRequester = MockVerifiableCredentialRequester() + let mockIssuanceResponseContainer = MockIssuanceResponseContainer() + + let invalidRequirement = MockRequirement(id: "mockRequirement324", + mockValidateCallback: { throw ExpectedError.expectedToBeThrown }) + let firstValidRequirement = MockRequirement(id: "mockRequirement634") + let secondValidRequirement = MockRequirement(id: "mockRequirement674") + let mockRequirement = GroupRequirement(required: true, + requirements: [firstValidRequirement, invalidRequirement, secondValidRequirement], + requirementOperator: .ALL) + + + let content = VerifiedIdRequestContent(style: mockStyle, + requirement: mockRequirement, + rootOfTrust: mockRootOfTrust) + + let contractIssuanceRequest = ContractIssuanceRequest(content: content, + issuanceResponseContainer: mockIssuanceResponseContainer, + verifiableCredentialRequester: mockVCRequester, + configuration: configuration) + + // Act + let actualResult = contractIssuanceRequest.isSatisfied() + + // Assert + XCTAssertFalse(actualResult) + } + + func testIsSatisfied_WithOneValidRequirement_ReturnsTrue() async throws { + // Arrange + let mockStyle = MockRequesterStyle(requester: "mock requester") + let mockRootOfTrust = RootOfTrust(verified: true, source: "") + let mockMapper = MockMapper() + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) + let mockVCRequester = MockVerifiableCredentialRequester() + let mockIssuanceResponseContainer = MockIssuanceResponseContainer() + + let validRequirement = MockRequirement(id: "mockRequirement324") + + + let content = VerifiedIdRequestContent(style: mockStyle, + requirement: validRequirement, + rootOfTrust: mockRootOfTrust) + + let contractIssuanceRequest = ContractIssuanceRequest(content: content, + issuanceResponseContainer: mockIssuanceResponseContainer, + verifiableCredentialRequester: mockVCRequester, + configuration: configuration) + + // Act + let actualResult = contractIssuanceRequest.isSatisfied() + + // Assert + XCTAssert(actualResult) + } + + func testIsSatisfied_WithMultipleValidRequirements_ReturnsTrue() async throws { + // Arrange + let mockStyle = MockRequesterStyle(requester: "mock requester") + let mockRootOfTrust = RootOfTrust(verified: true, source: "") + let mockMapper = MockMapper() + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) + let mockVCRequester = MockVerifiableCredentialRequester() + let mockIssuanceResponseContainer = MockIssuanceResponseContainer() + + let firstValidRequirement = MockRequirement(id: "mockRequirement634") + let secondValidRequirement = MockRequirement(id: "mockRequirement674") + let thirdValidRequirement = MockRequirement(id: "mockRequirement694") + let mockRequirement = GroupRequirement(required: true, + requirements: [firstValidRequirement, secondValidRequirement, thirdValidRequirement], + requirementOperator: .ALL) + + + let content = VerifiedIdRequestContent(style: mockStyle, + requirement: mockRequirement, + rootOfTrust: mockRootOfTrust) + + let contractIssuanceRequest = ContractIssuanceRequest(content: content, + issuanceResponseContainer: mockIssuanceResponseContainer, + verifiableCredentialRequester: mockVCRequester, + configuration: configuration) + + // Act + let actualResult = contractIssuanceRequest.isSatisfied() + + // Assert + XCTAssert(actualResult) + } + + func testComplete_WithSuccessfulIssuance_ReturnVerifiedId() async throws { + // Arrange + let expectedVerifiedId = MockVerifiedId(id: "mockVerifiedId", issuedOn: Date()) + let mockStyle = MockRequesterStyle(requester: "mock requester") + let mockRootOfTrust = RootOfTrust(verified: true, source: "") + let mockRequirement = MockRequirement(id: "mockRequirement634") + let mockMapper = MockMapper() + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) + let mockVCRequester = MockVerifiableCredentialRequester(sendRequestCallback: { _ in expectedVerifiedId }) + + func mockAddRequirementCallback(requirement: Requirement) throws { + XCTAssertEqual(requirement as? MockRequirement, mockRequirement) + } + + let mockIssuanceResponseContainer = MockIssuanceResponseContainer(mockAddRequirementCallback: mockAddRequirementCallback) + + let content = VerifiedIdRequestContent(style: mockStyle, + requirement: mockRequirement, + rootOfTrust: mockRootOfTrust) + + let contractIssuanceRequest = ContractIssuanceRequest(content: content, + issuanceResponseContainer: mockIssuanceResponseContainer, + verifiableCredentialRequester: mockVCRequester, + configuration: configuration) + + // Act + let actualResult = await contractIssuanceRequest.complete() + + // Assert + switch (actualResult) { + case .success(let verifiedId): + XCTAssertEqual(verifiedId as? MockVerifiedId, expectedVerifiedId) + case .failure(let error): + XCTFail(error.localizedDescription) + } + } + + func testComplete_WithUnsuccessfulIssuance_ReturnsError() async throws { + // Arrange + let mockStyle = MockRequesterStyle(requester: "mock requester") + let mockRootOfTrust = RootOfTrust(verified: true, source: "") + let mockRequirement = MockRequirement(id: "mockRequirement634") + let mockMapper = MockMapper() + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) + let mockVCRequester = MockVerifiableCredentialRequester(sendRequestCallback: { _ in throw ExpectedError.expectedToBeThrown }) + + func mockAddRequirementCallback(requirement: Requirement) throws { + XCTAssertEqual(requirement as? MockRequirement, mockRequirement) + } + + let mockIssuanceResponseContainer = MockIssuanceResponseContainer(mockAddRequirementCallback: mockAddRequirementCallback) + + let content = VerifiedIdRequestContent(style: mockStyle, + requirement: mockRequirement, + rootOfTrust: mockRootOfTrust) + + let contractIssuanceRequest = ContractIssuanceRequest(content: content, + issuanceResponseContainer: mockIssuanceResponseContainer, + verifiableCredentialRequester: mockVCRequester, + configuration: configuration) + + // Act + let actualResult = await contractIssuanceRequest.complete() + + // Assert + switch (actualResult) { + case .success(_): + XCTFail("Should have thrown an error.") + case .failure(let error): + XCTAssert(error is ExpectedError) + XCTAssertEqual(error as? ExpectedError, .expectedToBeThrown) + } + } +} From e148be1b850a5aa2024a902d0edc370c5c01b374 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 16:44:32 -0800 Subject: [PATCH 227/373] Add protocol for IssuanceResponseContaining. --- .../WalletLibrary.xcodeproj/project.pbxproj | 32 ++++++++++++++----- .../Manifest/IssuanceResponseContaining.swift | 12 +++++++ 2 files changed, 36 insertions(+), 8 deletions(-) create mode 100644 WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/IssuanceResponseContaining.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index c244dc5b..c2da2c96 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -55,6 +55,8 @@ 553CC09629A96249005A5FD6 /* ContractIssuanceRequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC09529A96249005A5FD6 /* ContractIssuanceRequestTests.swift */; }; 553CC09929A96C38005A5FD6 /* MockVerifiedId.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC09829A96C38005A5FD6 /* MockVerifiedId.swift */; }; 553CC09B29A96ECD005A5FD6 /* MockIssuanceResponseContaining.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC09A29A96ECD005A5FD6 /* MockIssuanceResponseContaining.swift */; }; + 553CC09D29A97181005A5FD6 /* IssuanceResponseContaining.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC09C29A97181005A5FD6 /* IssuanceResponseContaining.swift */; }; + 553CC0A029A98003005A5FD6 /* VerifiableCredentialTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC09F29A98003005A5FD6 /* VerifiableCredentialTests.swift */; }; 5585BDE729A6C38C0059710B /* InjectedIdToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585BDE629A6C38C0059710B /* InjectedIdToken.swift */; }; 5585BE0229A6D42B0059710B /* VerifiedIdType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585BE0129A6D42B0059710B /* VerifiedIdType.swift */; }; 559BD3032995993700BD61AC /* PresentationInputDescriptor+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */; }; @@ -78,7 +80,7 @@ 559BD457299D3E6000BD61AC /* AttestationsDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD456299D3E6000BD61AC /* AttestationsDescriptor+MappableTests.swift */; }; 559BD459299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD458299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift */; }; 559BD45B299D587200BD61AC /* Manifest2022IssuerStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD45A299D587200BD61AC /* Manifest2022IssuerStyle.swift */; }; - 559BD485299D8DCC00BD61AC /* VerifiableCredentialRequester.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD484299D8DCC00BD61AC /* VerifiableCredentialRequester.swift */; }; + 559BD485299D8DCC00BD61AC /* VerifiedIdRequester.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD484299D8DCC00BD61AC /* VerifiedIdRequester.swift */; }; 559BD49C299D913F00BD61AC /* IssuanceResponseContainer+WalletLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD49B299D913F00BD61AC /* IssuanceResponseContainer+WalletLibrary.swift */; }; 559BD4B3299EDFB400BD61AC /* VerifiableCredential.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4B2299EDFB400BD61AC /* VerifiableCredential.swift */; }; 559BD4B5299EE10800BD61AC /* IssuanceService+VerifiableCredentialRequester.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4B4299EE10800BD61AC /* IssuanceService+VerifiableCredentialRequester.swift */; }; @@ -88,7 +90,7 @@ 559BD4EF299EEADF00BD61AC /* AccessTokenRequirementTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4EE299EEADF00BD61AC /* AccessTokenRequirementTests.swift */; }; 559BD4F1299EEAEA00BD61AC /* PinRequirementTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4F0299EEAEA00BD61AC /* PinRequirementTests.swift */; }; 559BD4F3299EEAFD00BD61AC /* SelfAttestedClaimRequirementTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4F2299EEAFD00BD61AC /* SelfAttestedClaimRequirementTests.swift */; }; - 559BD4F5299EEC9500BD61AC /* MockVerifiableCredentialRequester.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4F4299EEC9500BD61AC /* MockVerifiableCredentialRequester.swift */; }; + 559BD4F5299EEC9500BD61AC /* MockVerifiedIdRequester.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4F4299EEC9500BD61AC /* MockVerifiedIdRequester.swift */; }; 559BD4F8299EED5700BD61AC /* MockRawManifest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4F7299EED5700BD61AC /* MockRawManifest.swift */; }; 55A81BCA2991AB82002C259A /* RequestResolving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BC92991AB82002C259A /* RequestResolving.swift */; }; 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE02991AB96002C259A /* RequestHandling.swift */; }; @@ -344,6 +346,8 @@ 553CC09529A96249005A5FD6 /* ContractIssuanceRequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContractIssuanceRequestTests.swift; sourceTree = ""; }; 553CC09829A96C38005A5FD6 /* MockVerifiedId.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockVerifiedId.swift; sourceTree = ""; }; 553CC09A29A96ECD005A5FD6 /* MockIssuanceResponseContaining.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockIssuanceResponseContaining.swift; sourceTree = ""; }; + 553CC09C29A97181005A5FD6 /* IssuanceResponseContaining.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssuanceResponseContaining.swift; sourceTree = ""; }; + 553CC09F29A98003005A5FD6 /* VerifiableCredentialTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiableCredentialTests.swift; sourceTree = ""; }; 5585BDE629A6C38C0059710B /* InjectedIdToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InjectedIdToken.swift; sourceTree = ""; }; 5585BE0129A6D42B0059710B /* VerifiedIdType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdType.swift; sourceTree = ""; }; 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationInputDescriptor+Mappable.swift"; sourceTree = ""; }; @@ -367,7 +371,7 @@ 559BD456299D3E6000BD61AC /* AttestationsDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttestationsDescriptor+MappableTests.swift"; sourceTree = ""; }; 559BD458299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SelfIssuedClaimsDescriptor+MappableTests.swift"; sourceTree = ""; }; 559BD45A299D587200BD61AC /* Manifest2022IssuerStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Manifest2022IssuerStyle.swift; sourceTree = ""; }; - 559BD484299D8DCC00BD61AC /* VerifiableCredentialRequester.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiableCredentialRequester.swift; sourceTree = ""; }; + 559BD484299D8DCC00BD61AC /* VerifiedIdRequester.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdRequester.swift; sourceTree = ""; }; 559BD49B299D913F00BD61AC /* IssuanceResponseContainer+WalletLibrary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceResponseContainer+WalletLibrary.swift"; sourceTree = ""; }; 559BD4B2299EDFB400BD61AC /* VerifiableCredential.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiableCredential.swift; sourceTree = ""; }; 559BD4B4299EE10800BD61AC /* IssuanceService+VerifiableCredentialRequester.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceService+VerifiableCredentialRequester.swift"; sourceTree = ""; }; @@ -377,7 +381,7 @@ 559BD4EE299EEADF00BD61AC /* AccessTokenRequirementTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessTokenRequirementTests.swift; sourceTree = ""; }; 559BD4F0299EEAEA00BD61AC /* PinRequirementTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinRequirementTests.swift; sourceTree = ""; }; 559BD4F2299EEAFD00BD61AC /* SelfAttestedClaimRequirementTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelfAttestedClaimRequirementTests.swift; sourceTree = ""; }; - 559BD4F4299EEC9500BD61AC /* MockVerifiableCredentialRequester.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockVerifiableCredentialRequester.swift; sourceTree = ""; }; + 559BD4F4299EEC9500BD61AC /* MockVerifiedIdRequester.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockVerifiedIdRequester.swift; sourceTree = ""; }; 559BD4F7299EED5700BD61AC /* MockRawManifest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRawManifest.swift; sourceTree = ""; }; 55A81BC92991AB82002C259A /* RequestResolving.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolving.swift; sourceTree = ""; }; 55A81BE02991AB96002C259A /* RequestHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestHandling.swift; sourceTree = ""; }; @@ -553,6 +557,7 @@ 5534E67C294A392F005D0765 /* Extensions */, 55A81BF02991BB13002C259A /* Requests */, 55E3376929478C3300CD2ED7 /* Utilities */, + 553CC09E29A97FD7005A5FD6 /* VerifiedId */, 559BD30C2996C12500BD61AC /* VerifiedIdClientBuilderTests.swift */, 552E509F293E6AB200868F47 /* VerifiedIdClientTests.swift */, ); @@ -692,11 +697,19 @@ path = VerifiedId; sourceTree = ""; }; + 553CC09E29A97FD7005A5FD6 /* VerifiedId */ = { + isa = PBXGroup; + children = ( + 553CC09F29A98003005A5FD6 /* VerifiableCredentialTests.swift */, + ); + path = VerifiedId; + sourceTree = ""; + }; 559BD3A4299AD9A200BD61AC /* Manifest */ = { isa = PBXGroup; children = ( 559BD3A5299AD9BD00BD61AC /* ManifestResolver.swift */, - 559BD484299D8DCC00BD61AC /* VerifiableCredentialRequester.swift */, + 553CC09C29A97181005A5FD6 /* IssuanceResponseContaining.swift */, ); path = Manifest; sourceTree = ""; @@ -831,7 +844,7 @@ 550A913C29930D970014D030 /* MockRawRequest.swift */, 559BD3102996D3CE00BD61AC /* MockVerifiedIdRequest.swift */, 559BD3AE299AF42C00BD61AC /* MockManifestResolver.swift */, - 559BD4F4299EEC9500BD61AC /* MockVerifiableCredentialRequester.swift */, + 559BD4F4299EEC9500BD61AC /* MockVerifiedIdRequester.swift */, ); path = Requests; sourceTree = ""; @@ -1071,6 +1084,7 @@ 55A81BC92991AB82002C259A /* RequestResolving.swift */, 55E2F08029955A960008010D /* RequestType.swift */, 550E320F298B113C004E7716 /* VerifiedIdRequest.swift */, + 559BD484299D8DCC00BD61AC /* VerifiedIdRequester.swift */, 550E3211298B1150004E7716 /* VerifiedIdRequestInput.swift */, ); path = Requests; @@ -1430,7 +1444,7 @@ 55E3370C293FD61E00CD2ED7 /* SelfAttestedClaimRequirement.swift in Sources */, 55E3376829437E3500CD2ED7 /* OpenIdForVCResolver.swift in Sources */, 55A81BE52991B2E5002C259A /* RequestResolverFactory.swift in Sources */, - 559BD485299D8DCC00BD61AC /* VerifiableCredentialRequester.swift in Sources */, + 559BD485299D8DCC00BD61AC /* VerifiedIdRequester.swift in Sources */, 55E2F086299573C30008010D /* OpenIdVerifierStyle.swift in Sources */, 559BD49C299D913F00BD61AC /* IssuanceResponseContainer+WalletLibrary.swift in Sources */, 559BD414299D37B100BD61AC /* SelfIssuedClaimsDescriptor+Mappable.swift in Sources */, @@ -1441,6 +1455,7 @@ 5534E672294A19D5005D0765 /* IdTokenDescriptor+Mappable.swift in Sources */, 5534E677294A1FA0005D0765 /* AccessTokenDescriptor+Mappable.swift in Sources */, 55E2F08A299576350008010D /* PresentationDefinition+Mappable.swift in Sources */, + 553CC09D29A97181005A5FD6 /* IssuanceResponseContaining.swift in Sources */, 550A919E29940ED70014D030 /* OpenIdRawRequest.swift in Sources */, 55E337BC2948B5FE00CD2ED7 /* PresentationService+OpenIdForVC.swift in Sources */, 55E336D4293FA75300CD2ED7 /* VerifiedId.swift in Sources */, @@ -1464,6 +1479,7 @@ 5534E6AD294AAFA6005D0765 /* PresentationDescriptor+MappableTests.swift in Sources */, 559BD4ED299EEAD100BD61AC /* IdTokenRequirementTests.swift in Sources */, 559BD455299D3E4800BD61AC /* IssuanceRequest+MappableTests.swift in Sources */, + 553CC0A029A98003005A5FD6 /* VerifiableCredentialTests.swift in Sources */, 55A81C132991C829002C259A /* MockHandler.swift in Sources */, 559BD3072995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift in Sources */, 559BD4EB299EEAC400BD61AC /* GroupRequirementTests.swift in Sources */, @@ -1497,7 +1513,7 @@ 559BD3112996D3CE00BD61AC /* MockVerifiedIdRequest.swift in Sources */, 550A913D29930D970014D030 /* MockRawRequest.swift in Sources */, 559BD30B2995B1B500BD61AC /* LinkedDomainResult+MappableTests.swift in Sources */, - 559BD4F5299EEC9500BD61AC /* MockVerifiableCredentialRequester.swift in Sources */, + 559BD4F5299EEC9500BD61AC /* MockVerifiedIdRequester.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/IssuanceResponseContaining.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/IssuanceResponseContaining.swift new file mode 100644 index 00000000..4a982975 --- /dev/null +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/IssuanceResponseContaining.swift @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +/** + * Protocol defines the behavior of collecting requirements to send to requester. + * For example, it is used as a wrapper to wrap the VC SDK get contract method. + */ +protocol IssuanceResponseContaining { + mutating func add(requirement: Requirement) throws +} From 1fdddfdf1f5280c165d75508352a21f5952598d9 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 16:44:39 -0800 Subject: [PATCH 228/373] Update IssuanceResponseContainer+WalletLibrary.swift --- .../Entities/IssuanceResponseContainer+WalletLibrary.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+WalletLibrary.swift b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+WalletLibrary.swift index f84f0155..488ea1fd 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+WalletLibrary.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+WalletLibrary.swift @@ -15,7 +15,7 @@ enum IssuanceResponseContainerError: Error, Equatable { * An extension of the VCEntities.IssuanceResponseContainer class * to convert Requirements to mappings in IssuanceResponseContainer. */ -extension VCEntities.IssuanceResponseContainer { +extension VCEntities.IssuanceResponseContainer: IssuanceResponseContaining { struct Constants { static let IdTokenHintKey = "https://self-issued.me" From 1f1ed098d2c916cdca35c344ba12b07fece868ae Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 16:44:57 -0800 Subject: [PATCH 229/373] Update naming. --- .../IssuanceService+VerifiableCredentialRequester.swift | 4 ++-- .../Protocols/Requests/Manifest/ManifestResolver.swift | 2 -- ...eCredentialRequester.swift => VerifiedIdRequester.swift} | 6 +++--- 3 files changed, 5 insertions(+), 7 deletions(-) rename WalletLibrary/WalletLibrary/Protocols/Requests/{Manifest/VerifiableCredentialRequester.swift => VerifiedIdRequester.swift} (78%) diff --git a/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+VerifiableCredentialRequester.swift b/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+VerifiableCredentialRequester.swift index 447801bc..6b89bd3c 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+VerifiableCredentialRequester.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+VerifiableCredentialRequester.swift @@ -13,11 +13,11 @@ enum IssuanceServiceVCRequesterError: Error { * An extension of the VCServices.IssuanceService class * that wraps send method with a generic send method that conforms to VerifiableCredentialRequester protocol. */ -extension IssuanceService: VerifiableCredentialRequester { +extension IssuanceService: VerifiedIdRequester { /// Sends the issuance response container and if successful, returns a Verifiable Credential. /// If unsuccessful, throws an error. - func send(request: Request) async throws -> VerifiableCredential { + func send(request: Request) async throws -> VerifiedId { guard let issuanceResponseContainer = request as? IssuanceResponseContainer else { let requestType = String(describing: request.self) diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/ManifestResolver.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/ManifestResolver.swift index c97cdd4e..b60c5afc 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/ManifestResolver.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/ManifestResolver.swift @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import VCEntities - /** * Protocol defines the behavior of taking a url and resolving a manifest. * For example, it is used as a wrapper to wrap the VC SDK get contract method. diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/VerifiableCredentialRequester.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequester.swift similarity index 78% rename from WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/VerifiableCredentialRequester.swift rename to WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequester.swift index 4d38db10..543a0646 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/VerifiableCredentialRequester.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequester.swift @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ /** - * Protocol defines the behavior of given generic request, requests a Verifiable Credential from an issuer. + * Protocol defines the behavior of given generic request, requests a Verified Id from an issuer. * For example, it is used as a wrapper to wrap the VC SDK send response method. */ -protocol VerifiableCredentialRequester { +protocol VerifiedIdRequester { /// Given generic request, requests a raw Verified Id from an issuer. - func send(request: Request) async throws -> VerifiableCredential + func send(request: Request) async throws -> VerifiedId } From 59c724459371d5f56c926b75d5ec02e2fc54ee9b Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 16:45:16 -0800 Subject: [PATCH 230/373] Update naming. --- .../Handlers/OpenIdRequestHandler.swift | 26 +++++-------------- .../Manifest/ContractIssuanceRequest.swift | 12 ++++----- .../Requirements/GroupRequirement.swift | 2 +- 3 files changed, 14 insertions(+), 26 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift index d3391519..115ca7ba 100644 --- a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift +++ b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift @@ -21,12 +21,12 @@ struct OpenIdRequestHandler: RequestHandling { private let manifestResolver: ManifestResolver - private let verifiableCredentialRequester: VerifiableCredentialRequester + private let verifiableCredentialRequester: VerifiedIdRequester /// TODO: post private preview, manifest resolving and verified id requester will be handled by processors. init(configuration: LibraryConfiguration, manifestResolver: ManifestResolver, - verifiableCredentialRequester: VerifiableCredentialRequester) { + verifiableCredentialRequester: VerifiedIdRequester) { self.configuration = configuration self.manifestResolver = manifestResolver self.verifiableCredentialRequester = verifiableCredentialRequester @@ -62,29 +62,17 @@ struct OpenIdRequestHandler: RequestHandling { let issuanceResponseContainer = try IssuanceResponseContainer(from: rawContract, input: issuanceOption) var issuanceRequestContent = try configuration.mapper.map(rawContract) - repopulateIssuanceRequirementsIfInjectedIdTokenExists(presentationRequestContent: requestContent, - issuanceRequestContent: &issuanceRequestContent) + + if let injectedIdToken = requestContent.injectedIdToken { + issuanceRequestContent.addRequirement(from: injectedIdToken) + } return ContractIssuanceRequest(content: issuanceRequestContent, issuanceResponseContainer: issuanceResponseContainer, - verifiableCredentialRequester: verifiableCredentialRequester, + verifiedIdRequester: verifiableCredentialRequester, configuration: configuration) } - private func repopulateIssuanceRequirementsIfInjectedIdTokenExists(presentationRequestContent: VerifiedIdRequestContent, - issuanceRequestContent: inout VerifiedIdRequestContent) { - if let injectedIdToken = presentationRequestContent.injectedIdToken, - let idTokenRequirement = issuanceRequestContent.requirement as? IdTokenRequirement { - idTokenRequirement.fulfill(with: injectedIdToken.rawToken) - if let pinRequirement = injectedIdToken.pin { - let groupRequirement = GroupRequirement(required: true, - requirements: [idTokenRequirement, pinRequirement], - requirementOperator: .ALL) - issuanceRequestContent.requirement = groupRequirement - } - } - } - private func handlePresentationRequest(from requestContent: VerifiedIdRequestContent) throws -> any VerifiedIdPresentationRequest { return OpenIdPresentationRequest(content: requestContent, configuration: configuration) } diff --git a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift index 3201e142..eb53dc80 100644 --- a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift +++ b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift @@ -17,21 +17,21 @@ class ContractIssuanceRequest: VerifiedIdIssuanceRequest { public let rootOfTrust: RootOfTrust - private let verifiableCredentialRequester: VerifiableCredentialRequester + private let verifiedIdRequester: VerifiedIdRequester private let configuration: LibraryConfiguration - private var responseContainer: IssuanceResponseContainer + private var responseContainer: IssuanceResponseContaining init(content: VerifiedIdRequestContent, - issuanceResponseContainer: IssuanceResponseContainer, - verifiableCredentialRequester: VerifiableCredentialRequester, + issuanceResponseContainer: IssuanceResponseContaining, + verifiedIdRequester: VerifiedIdRequester, configuration: LibraryConfiguration) { self.style = content.style self.requirement = content.requirement self.rootOfTrust = content.rootOfTrust self.responseContainer = issuanceResponseContainer - self.verifiableCredentialRequester = verifiableCredentialRequester + self.verifiedIdRequester = verifiedIdRequester self.configuration = configuration } @@ -47,7 +47,7 @@ class ContractIssuanceRequest: VerifiedIdIssuanceRequest { public func complete() async -> Result { do { try self.responseContainer.add(requirement: requirement) - let verifiableCredential = try await verifiableCredentialRequester.send(request: responseContainer) + let verifiableCredential = try await verifiedIdRequester.send(request: responseContainer) return Result.success(verifiableCredential) } catch { return Result.failure(error) diff --git a/WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift b/WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift index 4ea3e554..63314075 100644 --- a/WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift +++ b/WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift @@ -23,7 +23,7 @@ public class GroupRequirement: Requirement { /// If the requirement is required or not. public let required: Bool - public let requirements: [Requirement] + internal(set) public var requirements: [Requirement] public let requirementOperator: GroupRequirementOperator From 8a02caf4ba62fa9d018578d3838e129720d73aa4 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 16:45:27 -0800 Subject: [PATCH 231/373] Add mocks. --- .../MockIssuanceResponseContaining.swift | 19 ++++++++++++ .../MockVerifiableCredentialRequester.swift | 30 ------------------- .../Requests/MockVerifiedIdRequester.swift | 29 ++++++++++++++++++ .../Mocks/VerifiedId/MockVerifiedId.swift | 19 ++++++++++++ 4 files changed, 67 insertions(+), 30 deletions(-) create mode 100644 WalletLibrary/WalletLibraryTests/Mocks/Requests/Manifest/MockIssuanceResponseContaining.swift delete mode 100644 WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiableCredentialRequester.swift create mode 100644 WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiedIdRequester.swift create mode 100644 WalletLibrary/WalletLibraryTests/Mocks/VerifiedId/MockVerifiedId.swift diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/Manifest/MockIssuanceResponseContaining.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/Manifest/MockIssuanceResponseContaining.swift new file mode 100644 index 00000000..95b73045 --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Mocks/Requests/Manifest/MockIssuanceResponseContaining.swift @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +@testable import WalletLibrary + +struct MockIssuanceResponseContainer: IssuanceResponseContaining { + + private let mockAddRequirementCallback: ((Requirement) throws -> Void)? + + init(mockAddRequirementCallback: ((Requirement) throws -> Void)? = nil) { + self.mockAddRequirementCallback = mockAddRequirementCallback + } + + mutating func add(requirement: Requirement) throws { + try mockAddRequirementCallback?(requirement) + } +} diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiableCredentialRequester.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiableCredentialRequester.swift deleted file mode 100644 index 6a39796d..00000000 --- a/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiableCredentialRequester.swift +++ /dev/null @@ -1,30 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Microsoft Corporation. All rights reserved. -* Licensed under the MIT License. See License.txt in the project root for license information. -*--------------------------------------------------------------------------------------------*/ - -@testable import WalletLibrary - -class MockVerifiableCredentialRequester: VerifiableCredentialRequester { - - enum MockVerifiableCredentialRequestError: Error { - case missingCallback - } - - let sendRequestCallback: ((String) throws -> WalletLibrary.VerifiableCredential)? - - init(sendRequestCallback: ((String) throws -> WalletLibrary.VerifiableCredential)? = nil) { - self.sendRequestCallback = sendRequestCallback - } - - func send(request: Request) async throws -> WalletLibrary.VerifiableCredential { - - if let sendRequestCallback = sendRequestCallback, - let request = request as? String { - return try sendRequestCallback(request) - } - - throw MockVerifiableCredentialRequestError.missingCallback - } - -} diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiedIdRequester.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiedIdRequester.swift new file mode 100644 index 00000000..1d3e2fcb --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiedIdRequester.swift @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +@testable import WalletLibrary + +class MockVerifiedIdRequester: VerifiedIdRequester { + + enum MockVerifiedIdRequesterError: Error { + case missingCallback + } + + let sendRequestCallback: ((String) throws -> VerifiedId)? + + init(sendRequestCallback: ((String) throws -> VerifiedId)? = nil) { + self.sendRequestCallback = sendRequestCallback + } + + func send(request: Request) async throws -> VerifiedId { + + if let sendRequestCallback = sendRequestCallback { + return try sendRequestCallback((request as? String) ?? "") + } + + throw MockVerifiedIdRequesterError.missingCallback + } + +} diff --git a/WalletLibrary/WalletLibraryTests/Mocks/VerifiedId/MockVerifiedId.swift b/WalletLibrary/WalletLibraryTests/Mocks/VerifiedId/MockVerifiedId.swift new file mode 100644 index 00000000..bdf5b3a9 --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Mocks/VerifiedId/MockVerifiedId.swift @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +@testable import WalletLibrary + +struct MockVerifiedId: VerifiedId, Equatable { + + var id: String + + var expiresOn: Date? + + var issuedOn: Date + + func getClaims() -> [VerifiedIdClaim] { + return [] + } +} From 73adf67178c590efc4461e6169aa86d225d5d5a5 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 16:45:37 -0800 Subject: [PATCH 232/373] Update naming. --- .../Handlers/OpenIdRequestHandlerTests.swift | 74 +++++++++++++++++-- .../ContractIssuanceRequestTests.swift | 24 +++--- .../OpenIdURLRequestResolverTests.swift | 2 +- 3 files changed, 79 insertions(+), 21 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift index 23ecc18a..f5d006f0 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift @@ -39,7 +39,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, manifestResolver: MockManifestResolver(), - verifiableCredentialRequester: MockVerifiableCredentialRequester()) + verifiableCredentialRequester: MockVerifiedIdRequester()) // Act let actualRequest = try await handler.handleRequest(from: mockRawRequest) @@ -68,7 +68,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, manifestResolver: MockManifestResolver(), - verifiableCredentialRequester: MockVerifiableCredentialRequester()) + verifiableCredentialRequester: MockVerifiedIdRequester()) // Act do { @@ -104,7 +104,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, manifestResolver: MockManifestResolver(), - verifiableCredentialRequester: MockVerifiableCredentialRequester()) + verifiableCredentialRequester: MockVerifiedIdRequester()) // Act do { @@ -144,7 +144,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, manifestResolver: MockManifestResolver(), - verifiableCredentialRequester: MockVerifiableCredentialRequester()) + verifiableCredentialRequester: MockVerifiedIdRequester()) // Act do { @@ -184,7 +184,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, manifestResolver: MockManifestResolver(), - verifiableCredentialRequester: MockVerifiableCredentialRequester()) + verifiableCredentialRequester: MockVerifiedIdRequester()) // Act do { @@ -229,7 +229,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, manifestResolver: MockManifestResolver(mockGetRequestCallback: mockResolveContract), - verifiableCredentialRequester: MockVerifiableCredentialRequester()) + verifiableCredentialRequester: MockVerifiedIdRequester()) // Act do { @@ -278,7 +278,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, manifestResolver: MockManifestResolver(mockGetRequestCallback: mockResolveContract), - verifiableCredentialRequester: MockVerifiableCredentialRequester()) + verifiableCredentialRequester: MockVerifiedIdRequester()) // Act do { @@ -334,7 +334,65 @@ class OpenIdRequestHandlerTests: XCTestCase { let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, manifestResolver: MockManifestResolver(mockGetRequestCallback: mockResolveContract), - verifiableCredentialRequester: MockVerifiableCredentialRequester()) + verifiableCredentialRequester: MockVerifiedIdRequester()) + + // Act + let actualRequest = try await handler.handleRequest(from: mockRawRequest) + + // Assert + XCTAssert(actualRequest is ContractIssuanceRequest) + XCTAssertEqual(actualRequest.style as? MockRequesterStyle, expectedIssuanceStyle) + XCTAssertEqual(actualRequest.requirement as? MockRequirement, expectedIssuanceRequirement) + XCTAssertEqual(actualRequest.rootOfTrust.source, expectedIssuanceRootOfTrust.source) + XCTAssert(actualRequest.rootOfTrust.verified) + } + + func testHandleIssuanceRequest_WithValidContractAndInjectedIdToken_ReturnsVerifiedIdIssuanceRequest() async throws { + + // Arrange + let issuanceOptionURL = URL(string: "https://test.com")! + let expectedPresentationStyle = MockRequesterStyle(requester: "mock requester") + let expectedPresentationRequirement = VerifiedIdRequirement(encrypted: false, + required: true, + types: [], + purpose: nil, + issuanceOptions: [VerifiedIdRequestURL(url: issuanceOptionURL)]) + let expectedPresentationRootOfTrust = RootOfTrust(verified: true, source: "mock source") + let expectedInjectedIdToken = InjectedIdToken(rawToken: "mock idToken hint", pin: nil) + let expectedPresentationContent = VerifiedIdRequestContent(style: expectedPresentationStyle, + requirement: expectedPresentationRequirement, + rootOfTrust: expectedPresentationRootOfTrust, + injectedIdToken: expectedInjectedIdToken) + + let expectedIssuanceStyle = MockRequesterStyle(requester: "mock issuer") + let expectedIssuanceRequirement = MockRequirement(id: "mockRequirement23535") + let expectedIssuanceRootOfTrust = RootOfTrust(verified: true, source: "mock issuer source") + let expectedIssuanceContent = VerifiedIdRequestContent(style: expectedIssuanceStyle, + requirement: expectedIssuanceRequirement, + rootOfTrust: expectedIssuanceRootOfTrust) + + func mockResults(objectToBeMapped: Any) throws -> Any? { + if objectToBeMapped is MockOpenIdRawRequest { + return expectedPresentationContent + } + + if objectToBeMapped is IssuanceRequest { + return expectedIssuanceContent + } + + return nil + } + + func mockResolveContract(url: String) throws -> any RawManifest { + return createMockIssuanceRequest() + } + + let mockMapper = MockMapper(mockResults: mockResults) + let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) + let handler = OpenIdRequestHandler(configuration: configuration, + manifestResolver: MockManifestResolver(mockGetRequestCallback: mockResolveContract), + verifiableCredentialRequester: MockVerifiedIdRequester()) // Act let actualRequest = try await handler.handleRequest(from: mockRawRequest) diff --git a/WalletLibrary/WalletLibraryTests/Requests/RequestProtocols/Manifest/ContractIssuanceRequestTests.swift b/WalletLibrary/WalletLibraryTests/Requests/RequestProtocols/Manifest/ContractIssuanceRequestTests.swift index e17c8f67..9f337ccc 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/RequestProtocols/Manifest/ContractIssuanceRequestTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/RequestProtocols/Manifest/ContractIssuanceRequestTests.swift @@ -19,7 +19,7 @@ class ContractIssuanceRequestTests: XCTestCase { let mockRootOfTrust = RootOfTrust(verified: true, source: "") let mockMapper = MockMapper() let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) - let mockVCRequester = MockVerifiableCredentialRequester() + let mockVCRequester = MockVerifiedIdRequester() let mockIssuanceResponseContainer = MockIssuanceResponseContainer() let mockRequirement = MockRequirement(id: "mockRequirement324", @@ -31,7 +31,7 @@ class ContractIssuanceRequestTests: XCTestCase { let contractIssuanceRequest = ContractIssuanceRequest(content: content, issuanceResponseContainer: mockIssuanceResponseContainer, - verifiableCredentialRequester: mockVCRequester, + verifiedIdRequester: mockVCRequester, configuration: configuration) // Act @@ -47,7 +47,7 @@ class ContractIssuanceRequestTests: XCTestCase { let mockRootOfTrust = RootOfTrust(verified: true, source: "") let mockMapper = MockMapper() let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) - let mockVCRequester = MockVerifiableCredentialRequester() + let mockVCRequester = MockVerifiedIdRequester() let mockIssuanceResponseContainer = MockIssuanceResponseContainer() let invalidRequirement = MockRequirement(id: "mockRequirement324", @@ -65,7 +65,7 @@ class ContractIssuanceRequestTests: XCTestCase { let contractIssuanceRequest = ContractIssuanceRequest(content: content, issuanceResponseContainer: mockIssuanceResponseContainer, - verifiableCredentialRequester: mockVCRequester, + verifiedIdRequester: mockVCRequester, configuration: configuration) // Act @@ -81,7 +81,7 @@ class ContractIssuanceRequestTests: XCTestCase { let mockRootOfTrust = RootOfTrust(verified: true, source: "") let mockMapper = MockMapper() let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) - let mockVCRequester = MockVerifiableCredentialRequester() + let mockVCRequester = MockVerifiedIdRequester() let mockIssuanceResponseContainer = MockIssuanceResponseContainer() let validRequirement = MockRequirement(id: "mockRequirement324") @@ -93,7 +93,7 @@ class ContractIssuanceRequestTests: XCTestCase { let contractIssuanceRequest = ContractIssuanceRequest(content: content, issuanceResponseContainer: mockIssuanceResponseContainer, - verifiableCredentialRequester: mockVCRequester, + verifiedIdRequester: mockVCRequester, configuration: configuration) // Act @@ -109,7 +109,7 @@ class ContractIssuanceRequestTests: XCTestCase { let mockRootOfTrust = RootOfTrust(verified: true, source: "") let mockMapper = MockMapper() let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) - let mockVCRequester = MockVerifiableCredentialRequester() + let mockVCRequester = MockVerifiedIdRequester() let mockIssuanceResponseContainer = MockIssuanceResponseContainer() let firstValidRequirement = MockRequirement(id: "mockRequirement634") @@ -126,7 +126,7 @@ class ContractIssuanceRequestTests: XCTestCase { let contractIssuanceRequest = ContractIssuanceRequest(content: content, issuanceResponseContainer: mockIssuanceResponseContainer, - verifiableCredentialRequester: mockVCRequester, + verifiedIdRequester: mockVCRequester, configuration: configuration) // Act @@ -144,7 +144,7 @@ class ContractIssuanceRequestTests: XCTestCase { let mockRequirement = MockRequirement(id: "mockRequirement634") let mockMapper = MockMapper() let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) - let mockVCRequester = MockVerifiableCredentialRequester(sendRequestCallback: { _ in expectedVerifiedId }) + let mockVCRequester = MockVerifiedIdRequester(sendRequestCallback: { _ in expectedVerifiedId }) func mockAddRequirementCallback(requirement: Requirement) throws { XCTAssertEqual(requirement as? MockRequirement, mockRequirement) @@ -158,7 +158,7 @@ class ContractIssuanceRequestTests: XCTestCase { let contractIssuanceRequest = ContractIssuanceRequest(content: content, issuanceResponseContainer: mockIssuanceResponseContainer, - verifiableCredentialRequester: mockVCRequester, + verifiedIdRequester: mockVCRequester, configuration: configuration) // Act @@ -180,7 +180,7 @@ class ContractIssuanceRequestTests: XCTestCase { let mockRequirement = MockRequirement(id: "mockRequirement634") let mockMapper = MockMapper() let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) - let mockVCRequester = MockVerifiableCredentialRequester(sendRequestCallback: { _ in throw ExpectedError.expectedToBeThrown }) + let mockVCRequester = MockVerifiedIdRequester(sendRequestCallback: { _ in throw ExpectedError.expectedToBeThrown }) func mockAddRequirementCallback(requirement: Requirement) throws { XCTAssertEqual(requirement as? MockRequirement, mockRequirement) @@ -194,7 +194,7 @@ class ContractIssuanceRequestTests: XCTestCase { let contractIssuanceRequest = ContractIssuanceRequest(content: content, issuanceResponseContainer: mockIssuanceResponseContainer, - verifiableCredentialRequester: mockVCRequester, + verifiedIdRequester: mockVCRequester, configuration: configuration) // Act diff --git a/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift index 70430fa5..9964c84c 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift @@ -115,7 +115,7 @@ class OpenIdURLRequestResolverTests: XCTestCase { let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: Mapper()) let mockHandler = OpenIdRequestHandler(configuration: configuration, manifestResolver: MockManifestResolver(), - verifiableCredentialRequester: MockVerifiableCredentialRequester()) + verifiableCredentialRequester: MockVerifiedIdRequester()) let resolver = OpenIdURLRequestResolver(openIdResolver: MockOpenIdForVCResolver(), configuration: configuration) // Act From 0fbdd8eaefba47c05bd51e74fed96b89a4cf8ff7 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 16:45:45 -0800 Subject: [PATCH 233/373] Update VerifiedIdRequestContent.swift --- .../Requests/VerifiedIdRequestContent.swift | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift b/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift index bf6ab220..f0613d11 100644 --- a/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift +++ b/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift @@ -6,7 +6,8 @@ /** * Contents in a Verified Id Request. * This object is used to map protocol specific requests to common request object. - * TODO: make this object extensibility to be able to add VerifiedIdStyle. + * TODO: make this object extensible by separating Presentation with Issuance esp. for InjectedIdToken logic. + * TODO: add VerifiedIdStyle to Issuance Content. */ struct VerifiedIdRequestContent { @@ -27,4 +28,33 @@ struct VerifiedIdRequestContent { self.rootOfTrust = rootOfTrust self.injectedIdToken = injectedIdToken } + + mutating func addRequirement(from injectedIdToken: InjectedIdToken) { + switch (requirement) { + case var groupRequirement as GroupRequirement: + repopulateGroupRequirementIfInjectedIdTokenExists(injectedIdToken: injectedIdToken, + groupRequirement: &groupRequirement) + case var idTokenRequirement as IdTokenRequirement: + idTokenRequirement.fulfill(with: injectedIdToken.rawToken) + if let pinRequirement = injectedIdToken.pin { + requirement = GroupRequirement(required: false, + requirements: [idTokenRequirement, pinRequirement], + requirementOperator: .ALL) + } + default: + return + } + } + + private func repopulateGroupRequirementIfInjectedIdTokenExists(injectedIdToken: InjectedIdToken, + groupRequirement: inout GroupRequirement) { + for requirement in groupRequirement.requirements { + if var idTokenRequirement = requirement as? IdTokenRequirement { + idTokenRequirement.fulfill(with: injectedIdToken.rawToken) + if let pinRequirement = injectedIdToken.pin { + groupRequirement.requirements.append(pinRequirement) + } + } + } + } } From 63f74baae38da6aa39b0036f52f313b143f53366 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 16:45:51 -0800 Subject: [PATCH 234/373] Update VerifiableCredential.swift --- .../VerifiedId/VerifiableCredential/VerifiableCredential.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift b/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift index 4fa30b17..9df70a4d 100644 --- a/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift +++ b/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift @@ -53,7 +53,7 @@ struct VerifiableCredential: VerifiedId { case raw, contract } - init(from decoder: Decoder) throws { + public init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) let rawToken = try values.decode(String.self, forKey: .raw) guard let raw = VCEntities.VerifiableCredential(from: rawToken) else { From cf6ebd0433fc99f781685120f37d4165ce63f178 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 16:45:54 -0800 Subject: [PATCH 235/373] Update VerifiedId.swift --- WalletLibrary/WalletLibrary/VerifiedId/VerifiedId.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/WalletLibrary/WalletLibrary/VerifiedId/VerifiedId.swift b/WalletLibrary/WalletLibrary/VerifiedId/VerifiedId.swift index 9b4bf9c0..b01d5ea9 100644 --- a/WalletLibrary/WalletLibrary/VerifiedId/VerifiedId.swift +++ b/WalletLibrary/WalletLibrary/VerifiedId/VerifiedId.swift @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import Foundation - /** * The Verified Id Data Model. */ From b28594b8bcabc8c8b0c9ad1c19be140ee85a4fd1 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 16:45:59 -0800 Subject: [PATCH 236/373] Update VerifiedIdClaim.swift --- WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdClaim.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdClaim.swift b/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdClaim.swift index 5a3a7ae4..85847249 100644 --- a/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdClaim.swift +++ b/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdClaim.swift @@ -7,10 +7,10 @@ * A mapping of the claims contained within a Verified Id. */ public struct VerifiedIdClaim { + /// id of the claim. For example, within a VC, it is the key value of credentialSubject. public let id: String /// the value of the claim. public let value: Any } - From c588da2021d73ea10bae6c72e91a45b101e214e2 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 16:46:02 -0800 Subject: [PATCH 237/373] Create VerifiableCredentialTests.swift --- .../VerifiableCredentialTests.swift | 215 ++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 WalletLibrary/WalletLibraryTests/VerifiedId/VerifiableCredentialTests.swift diff --git a/WalletLibrary/WalletLibraryTests/VerifiedId/VerifiableCredentialTests.swift b/WalletLibrary/WalletLibraryTests/VerifiedId/VerifiableCredentialTests.swift new file mode 100644 index 00000000..d54a0013 --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/VerifiedId/VerifiableCredentialTests.swift @@ -0,0 +1,215 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import XCTest +import VCEntities +import VCToken +@testable import WalletLibrary + +class VerifiableCredentialTests: XCTestCase { + + func testInit_WithValidInput_CreatesVerifiableCredential() async throws { + // Arrange + let mockVerifiableCredential = createVCEntitiesVC() + let mockContract = createMockSignedContract() + + // Act + let actualResult = try WalletLibrary.VerifiableCredential(raw: mockVerifiableCredential, from: mockContract.content) + + // Assert + XCTAssertEqual(try actualResult.raw.serialize(), try mockVerifiableCredential.serialize()) + XCTAssertEqual(actualResult.contract, mockContract.content) + XCTAssertEqual(actualResult.expiresOn, Date(timeIntervalSince1970: 0)) + XCTAssertEqual(actualResult.issuedOn, Date(timeIntervalSince1970: 0)) + XCTAssertEqual(actualResult.id, actualResult.id) + + } + + private func createMockSignedContract(attestations: AttestationsDescriptor? = nil) -> SignedContract { + let mockCardDisplay = CardDisplayDescriptor(title: "mock title", + issuedBy: "mock issuer", + backgroundColor: "mock background color", + textColor: "mock text color", + logo: nil, + cardDescription: "mock description") + let mockConsentDisplay = ConsentDisplayDescriptor(title: nil, + instructions: "mock purpose") + let mockDisplayDescriptor = DisplayDescriptor(id: nil, + locale: nil, + contract: nil, + card: mockCardDisplay, + consent: mockConsentDisplay, + claims: [:]) + let mockContractInputDescriptor = ContractInputDescriptor(credentialIssuer: "mock credential issuer", + issuer: "mock issuer", + attestations: attestations) + let mockContract = Contract(id: "mockContract", + display: mockDisplayDescriptor, + input: mockContractInputDescriptor) + + return SignedContract(headers: Header(), content: mockContract)! + } + + private func createVCEntitiesVC(expectedJti: String? = "1234", + expectedIat: Double? = 0, + expectedExp: Double? = 0, + expectedClaims: [String: String] = [:]) -> VCEntities.VerifiableCredential { + let claims = VCClaims(jti: expectedJti, + iss: "", + sub: "", + iat: expectedIat, + exp: expectedExp, + vc: VerifiableCredentialDescriptor(context: nil, + type: nil, + credentialSubject: expectedClaims)) + return VCEntities.VerifiableCredential(headers: Header(), content: claims)! + } + + func testInit_WithMissingJtiOnVC_ThrowsError() async throws { + // Arrange + let mockVerifiableCredential = createVCEntitiesVC(expectedJti: nil) + let mockContract = createMockSignedContract() + + // Act + XCTAssertThrowsError(try VerifiableCredential(raw: mockVerifiableCredential, from: mockContract.content)) { error in + // Assert + XCTAssert(error is VerifiableCredentialMappingError) + XCTAssertEqual(error as? VerifiableCredentialMappingError, .missingJtiInVerifiableCredential) + } + } + + func testInit_WithMissingIatOnVC_ThrowsError() async throws { + // Arrange + let mockVerifiableCredential = createVCEntitiesVC(expectedIat: nil) + let mockContract = createMockSignedContract() + + // Act + XCTAssertThrowsError(try VerifiableCredential(raw: mockVerifiableCredential, from: mockContract.content)) { error in + // Assert + XCTAssert(error is VerifiableCredentialMappingError) + XCTAssertEqual(error as? VerifiableCredentialMappingError, .missingIssuedOnValueInVerifiableCredential) + } + } + + func testInit_WithNilExpOnVC_CreatesVerifiableCredential() async throws { + // Arrange + let mockVerifiableCredential = createVCEntitiesVC(expectedExp: nil) + let mockContract = createMockSignedContract() + + // Act + let actualResult = try WalletLibrary.VerifiableCredential(raw: mockVerifiableCredential, from: mockContract.content) + + // Assert + XCTAssertEqual(try actualResult.raw.serialize(), try mockVerifiableCredential.serialize()) + XCTAssertEqual(actualResult.contract, mockContract.content) + XCTAssertNil(actualResult.expiresOn) + XCTAssertEqual(actualResult.issuedOn, Date(timeIntervalSince1970: 0)) + XCTAssertEqual(actualResult.id, actualResult.id) + } + + func testGetClaims_WithMissingCredentialSubjectInVC_ReturnsEmptyList() async throws { + // Arrange + let mockVerifiableCredential = createVCEntitiesVC() + let mockContract = createMockSignedContract() + let verifiableCredential = try VerifiableCredential(raw: mockVerifiableCredential, + from: mockContract.content) + + // Act + let actualResult = verifiableCredential.getClaims() + + // Assert + XCTAssert(actualResult.isEmpty) + } + + func testGetClaims_WithNoLabelsInContract_ReturnsClaims() async throws { + // Arrange + let expectedValue1 = "mockValue1" + let expectedValue2 = "mockValue2" + let expectedClaim1 = VerifiedIdClaim(id: "mockKey1", value: expectedValue1) + let expectedClaim2 = VerifiedIdClaim(id: "mockKey2", value: expectedValue2) + let expectedResult = [expectedClaim1, expectedClaim2] + let mockVCClaimDictionary = ["mockKey1": expectedValue1, "mockKey2": expectedValue2] + let mockVerifiableCredential = createVCEntitiesVC(expectedClaims: mockVCClaimDictionary) + let mockContract = createMockSignedContract() + let verifiableCredential = try VerifiableCredential(raw: mockVerifiableCredential, + from: mockContract.content) + + // Act + let actualResult = verifiableCredential.getClaims() + + // Assert + areClaimsEqual(result: actualResult[0], expected: expectedClaim1) + areClaimsEqual(result: actualResult[1], expected: expectedClaim2) + } + + private func areClaimsEqual(result: VerifiedIdClaim, expected: VerifiedIdClaim) { + XCTAssertEqual(result.id, expected.id) + XCTAssertEqual(result.value as! String, expected.value as! String) + } + + func testGetClaims_WithLabelsInContract_ReturnsClaims() async throws { + // Arrange + + // Act + + // Assert + } + + func testGetClaims_WithOneClaimWithNoLabelInContract_ReturnsClaims() async throws { + // Arrange + + // Act + + // Assert + } + + func testEncode_WhenUnableToSerializeVCToken_ThrowsError() async throws { + // Arrange + + // Act + + // Assert + } + + func testEncode_WhenUnableToSerializeContract_ThrowsError() async throws { + // Arrange + + // Act + + // Assert + } + + func testEncode_WithValidInput_ReturnsEncodedVerifiableCredential() async throws { + // Arrange + + // Act + + // Assert + } + + func testDecode_WithInvalidRawToken_ThrowsError() async throws { + // Arrange + + // Act + + // Assert + } + + func testDecode_WithInvalidContract_ThrowsError() async throws { + // Arrange + + // Act + + // Assert + } + + func testDecode_WithValidInput_CreatesVerifiableCredential() async throws { + // Arrange + + // Act + + // Assert + } +} From 7da70d7554d0705cb650a8ce7caf02332a4f86b6 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 24 Feb 2023 16:46:43 -0800 Subject: [PATCH 238/373] Update VerifiableCredential-SDK-iOS --- WalletLibrary/Submodules/VerifiableCredential-SDK-iOS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS b/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS index 73c64e43..ad27b0f3 160000 --- a/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS +++ b/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS @@ -1 +1 @@ -Subproject commit 73c64e4301998d022808dc33eb7708ce7b24b5d9 +Subproject commit ad27b0f39b5f34dc2c9a47852d7abcd6810607b6 From 2bcdc0d65427e62b7f6512fc4892d96cfc30e18a Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 09:24:37 -0800 Subject: [PATCH 239/373] Update VerifiableCredentialTests.swift --- .../VerifiableCredentialTests.swift | 194 ++++++++++-------- 1 file changed, 114 insertions(+), 80 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/VerifiedId/VerifiableCredentialTests.swift b/WalletLibrary/WalletLibraryTests/VerifiedId/VerifiableCredentialTests.swift index d54a0013..9ca928f5 100644 --- a/WalletLibrary/WalletLibraryTests/VerifiedId/VerifiableCredentialTests.swift +++ b/WalletLibrary/WalletLibraryTests/VerifiedId/VerifiableCredentialTests.swift @@ -16,64 +16,24 @@ class VerifiableCredentialTests: XCTestCase { let mockContract = createMockSignedContract() // Act - let actualResult = try WalletLibrary.VerifiableCredential(raw: mockVerifiableCredential, from: mockContract.content) + let actualResult = try WalletLibrary.VerifiableCredential(raw: mockVerifiableCredential, from: mockContract) // Assert XCTAssertEqual(try actualResult.raw.serialize(), try mockVerifiableCredential.serialize()) - XCTAssertEqual(actualResult.contract, mockContract.content) + XCTAssertEqual(actualResult.contract, mockContract) XCTAssertEqual(actualResult.expiresOn, Date(timeIntervalSince1970: 0)) XCTAssertEqual(actualResult.issuedOn, Date(timeIntervalSince1970: 0)) XCTAssertEqual(actualResult.id, actualResult.id) } - private func createMockSignedContract(attestations: AttestationsDescriptor? = nil) -> SignedContract { - let mockCardDisplay = CardDisplayDescriptor(title: "mock title", - issuedBy: "mock issuer", - backgroundColor: "mock background color", - textColor: "mock text color", - logo: nil, - cardDescription: "mock description") - let mockConsentDisplay = ConsentDisplayDescriptor(title: nil, - instructions: "mock purpose") - let mockDisplayDescriptor = DisplayDescriptor(id: nil, - locale: nil, - contract: nil, - card: mockCardDisplay, - consent: mockConsentDisplay, - claims: [:]) - let mockContractInputDescriptor = ContractInputDescriptor(credentialIssuer: "mock credential issuer", - issuer: "mock issuer", - attestations: attestations) - let mockContract = Contract(id: "mockContract", - display: mockDisplayDescriptor, - input: mockContractInputDescriptor) - - return SignedContract(headers: Header(), content: mockContract)! - } - - private func createVCEntitiesVC(expectedJti: String? = "1234", - expectedIat: Double? = 0, - expectedExp: Double? = 0, - expectedClaims: [String: String] = [:]) -> VCEntities.VerifiableCredential { - let claims = VCClaims(jti: expectedJti, - iss: "", - sub: "", - iat: expectedIat, - exp: expectedExp, - vc: VerifiableCredentialDescriptor(context: nil, - type: nil, - credentialSubject: expectedClaims)) - return VCEntities.VerifiableCredential(headers: Header(), content: claims)! - } - func testInit_WithMissingJtiOnVC_ThrowsError() async throws { // Arrange let mockVerifiableCredential = createVCEntitiesVC(expectedJti: nil) let mockContract = createMockSignedContract() // Act - XCTAssertThrowsError(try VerifiableCredential(raw: mockVerifiableCredential, from: mockContract.content)) { error in + XCTAssertThrowsError(try VerifiableCredential(raw: mockVerifiableCredential, from: mockContract)) { error in // Assert XCTAssert(error is VerifiableCredentialMappingError) XCTAssertEqual(error as? VerifiableCredentialMappingError, .missingJtiInVerifiableCredential) @@ -86,7 +46,7 @@ class VerifiableCredentialTests: XCTestCase { let mockContract = createMockSignedContract() // Act - XCTAssertThrowsError(try VerifiableCredential(raw: mockVerifiableCredential, from: mockContract.content)) { error in + XCTAssertThrowsError(try VerifiableCredential(raw: mockVerifiableCredential, from: mockContract)) { error in // Assert XCTAssert(error is VerifiableCredentialMappingError) XCTAssertEqual(error as? VerifiableCredentialMappingError, .missingIssuedOnValueInVerifiableCredential) @@ -99,11 +59,11 @@ class VerifiableCredentialTests: XCTestCase { let mockContract = createMockSignedContract() // Act - let actualResult = try WalletLibrary.VerifiableCredential(raw: mockVerifiableCredential, from: mockContract.content) + let actualResult = try WalletLibrary.VerifiableCredential(raw: mockVerifiableCredential, from: mockContract) // Assert XCTAssertEqual(try actualResult.raw.serialize(), try mockVerifiableCredential.serialize()) - XCTAssertEqual(actualResult.contract, mockContract.content) + XCTAssertEqual(actualResult.contract, mockContract) XCTAssertNil(actualResult.expiresOn) XCTAssertEqual(actualResult.issuedOn, Date(timeIntervalSince1970: 0)) XCTAssertEqual(actualResult.id, actualResult.id) @@ -114,7 +74,7 @@ class VerifiableCredentialTests: XCTestCase { let mockVerifiableCredential = createVCEntitiesVC() let mockContract = createMockSignedContract() let verifiableCredential = try VerifiableCredential(raw: mockVerifiableCredential, - from: mockContract.content) + from: mockContract) // Act let actualResult = verifiableCredential.getClaims() @@ -129,59 +89,109 @@ class VerifiableCredentialTests: XCTestCase { let expectedValue2 = "mockValue2" let expectedClaim1 = VerifiedIdClaim(id: "mockKey1", value: expectedValue1) let expectedClaim2 = VerifiedIdClaim(id: "mockKey2", value: expectedValue2) - let expectedResult = [expectedClaim1, expectedClaim2] let mockVCClaimDictionary = ["mockKey1": expectedValue1, "mockKey2": expectedValue2] let mockVerifiableCredential = createVCEntitiesVC(expectedClaims: mockVCClaimDictionary) let mockContract = createMockSignedContract() let verifiableCredential = try VerifiableCredential(raw: mockVerifiableCredential, - from: mockContract.content) + from: mockContract) // Act let actualResult = verifiableCredential.getClaims() // Assert - areClaimsEqual(result: actualResult[0], expected: expectedClaim1) - areClaimsEqual(result: actualResult[1], expected: expectedClaim2) - } - - private func areClaimsEqual(result: VerifiedIdClaim, expected: VerifiedIdClaim) { - XCTAssertEqual(result.id, expected.id) - XCTAssertEqual(result.value as! String, expected.value as! String) + XCTAssertEqual(actualResult.count, 2) + XCTAssert(actualResult.contains { + areClaimsEqual(result: $0, expected: expectedClaim1) + }) + XCTAssert(actualResult.contains { + areClaimsEqual(result: $0, expected: expectedClaim2) + }) } func testGetClaims_WithLabelsInContract_ReturnsClaims() async throws { // Arrange - + let expectedValue1 = "mockValue1" + let expectedClaim1 = VerifiedIdClaim(id: "MockLabel1", value: expectedValue1) + let mockVCClaimDictionary = ["mockKey1": expectedValue1] + let mockVerifiableCredential = createVCEntitiesVC(expectedClaims: mockVCClaimDictionary) + let expectedClaimLabel1 = ClaimDisplayDescriptor(type: "String", + label: "MockLabel1") + let expectedClaimLabels = ["vc.credentialSubject.mockKey1": expectedClaimLabel1] + let mockContract = createMockSignedContract(claims: expectedClaimLabels) + let verifiableCredential = try VerifiableCredential(raw: mockVerifiableCredential, + from: mockContract) + // Act + let actualResult = verifiableCredential.getClaims() // Assert + XCTAssertEqual(actualResult.count, 1) + XCTAssert(actualResult.contains { + areClaimsEqual(result: $0, expected: VerifiedIdClaim(id: "MockLabel1", value: expectedValue1)) + }) } func testGetClaims_WithOneClaimWithNoLabelInContract_ReturnsClaims() async throws { // Arrange + let expectedValue1 = "mockValue1" + let expectedValue2 = "mockValue2" + + let mockVCClaimDictionary = ["mockKey1": expectedValue1, "mockKey2": expectedValue2] + let mockVerifiableCredential = createVCEntitiesVC(expectedClaims: mockVCClaimDictionary) + let expectedClaimLabel1 = ClaimDisplayDescriptor(type: "String", label: "MockLabel1") + let expectedClaimLabels = ["vc.credentialSubject.mockKey1": expectedClaimLabel1] + let mockContract = createMockSignedContract(claims: expectedClaimLabels) + + let verifiableCredential = try VerifiableCredential(raw: mockVerifiableCredential, + from: mockContract) + // Act + let actualResult = verifiableCredential.getClaims() // Assert + XCTAssertEqual(actualResult.count, 2) + XCTAssert(actualResult.contains { + areClaimsEqual(result: $0, expected: VerifiedIdClaim(id: "MockLabel1", value: expectedValue1)) + }) + XCTAssert(actualResult.contains { + areClaimsEqual(result: $0, expected: VerifiedIdClaim(id: "mockKey2", value: expectedValue2)) + }) } - func testEncode_WhenUnableToSerializeVCToken_ThrowsError() async throws { + func testDecode_WhenUnableToSerializeVCToken_ThrowsError() async throws { // Arrange + let decoder = JSONDecoder() + let encoder = JSONEncoder() + let mockVC = MockVerifiableCredential(raw: "unserializableToken", contract: createMockSignedContract()) + let encodedMockVC = try encoder.encode(mockVC) // Act - - // Assert + XCTAssertThrowsError(try decoder.decode(VerifiableCredential.self, from: encodedMockVC)) { error in + // Assert + XCTAssert(error is VerifiableCredentialMappingError) + XCTAssertEqual(error as? VerifiableCredentialMappingError, .unableToDecodeRawVerifiableCredentialToken) + } } - func testEncode_WhenUnableToSerializeContract_ThrowsError() async throws { + func testDecode_WithValidInput_ReturnsVerifiableCredential() async throws { // Arrange + let decoder = JSONDecoder() + let encoder = JSONEncoder() + let expectedSerializedVC = try createVCEntitiesVC().serialize() + let expectedContract = createMockSignedContract() + let mockVC = MockVerifiableCredential(raw: expectedSerializedVC, contract: expectedContract) + let encodedMockVC = try encoder.encode(mockVC) // Act + let actualResult = try decoder.decode(VerifiableCredential.self, from: encodedMockVC) // Assert + XCTAssertEqual(try actualResult.raw.serialize(), expectedSerializedVC) + XCTAssertEqual(actualResult.contract, expectedContract) } - func testEncode_WithValidInput_ReturnsEncodedVerifiableCredential() async throws { + func testEncode_WithValidInput_CreatesEncodedVerifiableCredential() async throws { // Arrange // Act @@ -189,27 +199,51 @@ class VerifiableCredentialTests: XCTestCase { // Assert } - func testDecode_WithInvalidRawToken_ThrowsError() async throws { - // Arrange - - // Act - - // Assert + private func createMockSignedContract(claims: [String: ClaimDisplayDescriptor] = [:]) -> Contract { + let mockCardDisplay = CardDisplayDescriptor(title: "mock title", + issuedBy: "mock issuer", + backgroundColor: "mock background color", + textColor: "mock text color", + logo: nil, + cardDescription: "mock description") + let mockConsentDisplay = ConsentDisplayDescriptor(title: nil, + instructions: "mock purpose") + let mockDisplayDescriptor = DisplayDescriptor(id: nil, + locale: nil, + contract: nil, + card: mockCardDisplay, + consent: mockConsentDisplay, + claims: claims) + let mockContractInputDescriptor = ContractInputDescriptor(credentialIssuer: "mock credential issuer", + issuer: "mock issuer", + attestations: nil) + return Contract(id: "mockContract", + display: mockDisplayDescriptor, + input: mockContractInputDescriptor) } - func testDecode_WithInvalidContract_ThrowsError() async throws { - // Arrange - - // Act - - // Assert + private func createVCEntitiesVC(expectedJti: String? = "1234", + expectedIat: Double? = 0, + expectedExp: Double? = 0, + expectedClaims: [String: String] = [:]) -> VCEntities.VerifiableCredential { + let claims = VCClaims(jti: expectedJti, + iss: "", + sub: "", + iat: expectedIat, + exp: expectedExp, + vc: VerifiableCredentialDescriptor(context: [], + type: [], + credentialSubject: expectedClaims)) + return VCEntities.VerifiableCredential(headers: Header(), content: claims)! } - func testDecode_WithValidInput_CreatesVerifiableCredential() async throws { - // Arrange - - // Act - - // Assert + private func areClaimsEqual(result: VerifiedIdClaim, expected: VerifiedIdClaim) -> Bool { + return (result.id == expected.id) && (result.value as! String == expected.value as! String) } } + +struct MockVerifiableCredential: Codable { + let raw: String + + let contract: Contract +} From 0bd2bc8dba6a79bc7388f854c13bd2b54d8b7c58 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 11:03:04 -0800 Subject: [PATCH 240/373] Update InjectedIdToken.swift --- .../RequestProtocols/OpenId/InjectedIdToken.swift | 9 --------- 1 file changed, 9 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/InjectedIdToken.swift b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/InjectedIdToken.swift index e5cae21e..436c581a 100644 --- a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/InjectedIdToken.swift +++ b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/InjectedIdToken.swift @@ -4,19 +4,10 @@ *--------------------------------------------------------------------------------------------*/ /** -<<<<<<<< HEAD:WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/InjectedIdToken.swift * An object that represents an id token that is injected from a presentation request into an issuance request. */ struct InjectedIdToken { let rawToken: String let pin: PinRequirement? -======== - * Protocol defines the behavior of taking a url and resolving a manifest. - * For example, it is used as a wrapper to wrap the VC SDK get contract method. - */ -protocol ManifestResolver { - /// Fetches and validates the manifest. - func resolve(with url: URL) async throws -> any RawManifest ->>>>>>>> dev:WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/ManifestResolver.swift } From b445df6ff6dc7e0502de3d9f54acb40a8f8cd10a Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 11:47:04 -0800 Subject: [PATCH 241/373] Add IdTokenHint logic to IssuanceResponseContainer extension. --- .../WalletLibrary.xcodeproj/project.pbxproj | 102 ++++++++++++++++-- ...uanceResponseContainer+WalletLibrary.swift | 13 ++- 2 files changed, 103 insertions(+), 12 deletions(-) diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 519ed79f..c2da2c96 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -42,6 +42,23 @@ 5534E688294A5F16005D0765 /* IdTokenDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5534E687294A5F16005D0765 /* IdTokenDescriptor+MappableTests.swift */; }; 5534E68C294A8B8F005D0765 /* AccessTokenDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5534E68B294A8B8F005D0765 /* AccessTokenDescriptor+MappableTests.swift */; }; 5534E6AD294AAFA6005D0765 /* PresentationDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5534E6AC294AAFA6005D0765 /* PresentationDescriptor+MappableTests.swift */; }; + 553CC06A29A818F7005A5FD6 /* PromiseKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55E33679293E7B5F00CD2ED7 /* PromiseKit.framework */; }; + 553CC06B29A818F7005A5FD6 /* PromiseKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 55E33679293E7B5F00CD2ED7 /* PromiseKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 553CC08129A81931005A5FD6 /* VCToken.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55E336F1293FCE5E00CD2ED7 /* VCToken.framework */; platformFilter = ios; }; + 553CC08229A81931005A5FD6 /* VCToken.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 55E336F1293FCE5E00CD2ED7 /* VCToken.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 553CC08329A8193E005A5FD6 /* VCNetworking.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55E33704293FCE9C00CD2ED7 /* VCNetworking.framework */; platformFilter = ios; }; + 553CC08429A8193E005A5FD6 /* VCNetworking.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 55E33704293FCE9C00CD2ED7 /* VCNetworking.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 553CC08529A81949005A5FD6 /* VCCrypto.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55E336FA293FCE8200CD2ED7 /* VCCrypto.framework */; platformFilter = ios; }; + 553CC08629A81949005A5FD6 /* VCCrypto.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 55E336FA293FCE8200CD2ED7 /* VCCrypto.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 553CC09029A955E6005A5FD6 /* PinDescriptor+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC08F29A955E6005A5FD6 /* PinDescriptor+Mappable.swift */; }; + 553CC09229A957A3005A5FD6 /* PinDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC09129A957A3005A5FD6 /* PinDescriptor+MappableTests.swift */; }; + 553CC09629A96249005A5FD6 /* ContractIssuanceRequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC09529A96249005A5FD6 /* ContractIssuanceRequestTests.swift */; }; + 553CC09929A96C38005A5FD6 /* MockVerifiedId.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC09829A96C38005A5FD6 /* MockVerifiedId.swift */; }; + 553CC09B29A96ECD005A5FD6 /* MockIssuanceResponseContaining.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC09A29A96ECD005A5FD6 /* MockIssuanceResponseContaining.swift */; }; + 553CC09D29A97181005A5FD6 /* IssuanceResponseContaining.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC09C29A97181005A5FD6 /* IssuanceResponseContaining.swift */; }; + 553CC0A029A98003005A5FD6 /* VerifiableCredentialTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC09F29A98003005A5FD6 /* VerifiableCredentialTests.swift */; }; + 5585BDE729A6C38C0059710B /* InjectedIdToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585BDE629A6C38C0059710B /* InjectedIdToken.swift */; }; + 5585BE0229A6D42B0059710B /* VerifiedIdType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585BE0129A6D42B0059710B /* VerifiedIdType.swift */; }; 559BD3032995993700BD61AC /* PresentationInputDescriptor+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */; }; 559BD3052995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3042995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift */; }; 559BD3072995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3062995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift */; }; @@ -63,7 +80,7 @@ 559BD457299D3E6000BD61AC /* AttestationsDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD456299D3E6000BD61AC /* AttestationsDescriptor+MappableTests.swift */; }; 559BD459299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD458299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift */; }; 559BD45B299D587200BD61AC /* Manifest2022IssuerStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD45A299D587200BD61AC /* Manifest2022IssuerStyle.swift */; }; - 559BD485299D8DCC00BD61AC /* VerifiableCredentialRequester.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD484299D8DCC00BD61AC /* VerifiableCredentialRequester.swift */; }; + 559BD485299D8DCC00BD61AC /* VerifiedIdRequester.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD484299D8DCC00BD61AC /* VerifiedIdRequester.swift */; }; 559BD49C299D913F00BD61AC /* IssuanceResponseContainer+WalletLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD49B299D913F00BD61AC /* IssuanceResponseContainer+WalletLibrary.swift */; }; 559BD4B3299EDFB400BD61AC /* VerifiableCredential.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4B2299EDFB400BD61AC /* VerifiableCredential.swift */; }; 559BD4B5299EE10800BD61AC /* IssuanceService+VerifiableCredentialRequester.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4B4299EE10800BD61AC /* IssuanceService+VerifiableCredentialRequester.swift */; }; @@ -73,7 +90,7 @@ 559BD4EF299EEADF00BD61AC /* AccessTokenRequirementTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4EE299EEADF00BD61AC /* AccessTokenRequirementTests.swift */; }; 559BD4F1299EEAEA00BD61AC /* PinRequirementTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4F0299EEAEA00BD61AC /* PinRequirementTests.swift */; }; 559BD4F3299EEAFD00BD61AC /* SelfAttestedClaimRequirementTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4F2299EEAFD00BD61AC /* SelfAttestedClaimRequirementTests.swift */; }; - 559BD4F5299EEC9500BD61AC /* MockVerifiableCredentialRequester.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4F4299EEC9500BD61AC /* MockVerifiableCredentialRequester.swift */; }; + 559BD4F5299EEC9500BD61AC /* MockVerifiedIdRequester.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4F4299EEC9500BD61AC /* MockVerifiedIdRequester.swift */; }; 559BD4F8299EED5700BD61AC /* MockRawManifest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD4F7299EED5700BD61AC /* MockRawManifest.swift */; }; 55A81BCA2991AB82002C259A /* RequestResolving.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BC92991AB82002C259A /* RequestResolving.swift */; }; 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81BE02991AB96002C259A /* RequestHandling.swift */; }; @@ -97,7 +114,6 @@ 55E2F0882995743D0008010D /* LinkedDomainResult+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F0872995743D0008010D /* LinkedDomainResult+Mappable.swift */; }; 55E2F08A299576350008010D /* PresentationDefinition+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F089299576350008010D /* PresentationDefinition+Mappable.swift */; }; 55E2F08C299576730008010D /* GroupRequirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F08B299576730008010D /* GroupRequirement.swift */; }; - 55E33684293E7B6600CD2ED7 /* PromiseKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 55E33679293E7B5F00CD2ED7 /* PromiseKit.framework */; }; 55E336D1293FA6F400CD2ED7 /* VerifiedIdRequirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E336D0293FA6F400CD2ED7 /* VerifiedIdRequirement.swift */; }; 55E336D4293FA75300CD2ED7 /* VerifiedId.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E336D3293FA75300CD2ED7 /* VerifiedId.swift */; }; 55E336D6293FA78C00CD2ED7 /* VerifiedIdClaim.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E336D5293FA78C00CD2ED7 /* VerifiedIdClaim.swift */; }; @@ -275,8 +291,12 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( + 553CC06B29A818F7005A5FD6 /* PromiseKit.framework in Embed Frameworks */, 5534E5AD2948B8C8005D0765 /* VCEntities.framework in Embed Frameworks */, + 553CC08629A81949005A5FD6 /* VCCrypto.framework in Embed Frameworks */, 5534E5982948B8C2005D0765 /* VCServices.framework in Embed Frameworks */, + 553CC08429A8193E005A5FD6 /* VCNetworking.framework in Embed Frameworks */, + 553CC08229A81931005A5FD6 /* VCToken.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -321,6 +341,15 @@ 5534E687294A5F16005D0765 /* IdTokenDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IdTokenDescriptor+MappableTests.swift"; sourceTree = ""; }; 5534E68B294A8B8F005D0765 /* AccessTokenDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AccessTokenDescriptor+MappableTests.swift"; sourceTree = ""; }; 5534E6AC294AAFA6005D0765 /* PresentationDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationDescriptor+MappableTests.swift"; sourceTree = ""; }; + 553CC08F29A955E6005A5FD6 /* PinDescriptor+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PinDescriptor+Mappable.swift"; sourceTree = ""; }; + 553CC09129A957A3005A5FD6 /* PinDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PinDescriptor+MappableTests.swift"; sourceTree = ""; }; + 553CC09529A96249005A5FD6 /* ContractIssuanceRequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContractIssuanceRequestTests.swift; sourceTree = ""; }; + 553CC09829A96C38005A5FD6 /* MockVerifiedId.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockVerifiedId.swift; sourceTree = ""; }; + 553CC09A29A96ECD005A5FD6 /* MockIssuanceResponseContaining.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockIssuanceResponseContaining.swift; sourceTree = ""; }; + 553CC09C29A97181005A5FD6 /* IssuanceResponseContaining.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssuanceResponseContaining.swift; sourceTree = ""; }; + 553CC09F29A98003005A5FD6 /* VerifiableCredentialTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiableCredentialTests.swift; sourceTree = ""; }; + 5585BDE629A6C38C0059710B /* InjectedIdToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InjectedIdToken.swift; sourceTree = ""; }; + 5585BE0129A6D42B0059710B /* VerifiedIdType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdType.swift; sourceTree = ""; }; 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationInputDescriptor+Mappable.swift"; sourceTree = ""; }; 559BD3042995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationDefinition+MappableTests.swift"; sourceTree = ""; }; 559BD3062995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationInputDescriptor+MappableTests.swift"; sourceTree = ""; }; @@ -342,7 +371,7 @@ 559BD456299D3E6000BD61AC /* AttestationsDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttestationsDescriptor+MappableTests.swift"; sourceTree = ""; }; 559BD458299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SelfIssuedClaimsDescriptor+MappableTests.swift"; sourceTree = ""; }; 559BD45A299D587200BD61AC /* Manifest2022IssuerStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Manifest2022IssuerStyle.swift; sourceTree = ""; }; - 559BD484299D8DCC00BD61AC /* VerifiableCredentialRequester.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiableCredentialRequester.swift; sourceTree = ""; }; + 559BD484299D8DCC00BD61AC /* VerifiedIdRequester.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdRequester.swift; sourceTree = ""; }; 559BD49B299D913F00BD61AC /* IssuanceResponseContainer+WalletLibrary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceResponseContainer+WalletLibrary.swift"; sourceTree = ""; }; 559BD4B2299EDFB400BD61AC /* VerifiableCredential.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiableCredential.swift; sourceTree = ""; }; 559BD4B4299EE10800BD61AC /* IssuanceService+VerifiableCredentialRequester.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "IssuanceService+VerifiableCredentialRequester.swift"; sourceTree = ""; }; @@ -352,7 +381,7 @@ 559BD4EE299EEADF00BD61AC /* AccessTokenRequirementTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessTokenRequirementTests.swift; sourceTree = ""; }; 559BD4F0299EEAEA00BD61AC /* PinRequirementTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinRequirementTests.swift; sourceTree = ""; }; 559BD4F2299EEAFD00BD61AC /* SelfAttestedClaimRequirementTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelfAttestedClaimRequirementTests.swift; sourceTree = ""; }; - 559BD4F4299EEC9500BD61AC /* MockVerifiableCredentialRequester.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockVerifiableCredentialRequester.swift; sourceTree = ""; }; + 559BD4F4299EEC9500BD61AC /* MockVerifiedIdRequester.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockVerifiedIdRequester.swift; sourceTree = ""; }; 559BD4F7299EED5700BD61AC /* MockRawManifest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRawManifest.swift; sourceTree = ""; }; 55A81BC92991AB82002C259A /* RequestResolving.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestResolving.swift; sourceTree = ""; }; 55A81BE02991AB96002C259A /* RequestHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestHandling.swift; sourceTree = ""; }; @@ -397,9 +426,12 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 553CC06A29A818F7005A5FD6 /* PromiseKit.framework in Frameworks */, 5534E5AC2948B8C8005D0765 /* VCEntities.framework in Frameworks */, - 55E33684293E7B6600CD2ED7 /* PromiseKit.framework in Frameworks */, + 553CC08529A81949005A5FD6 /* VCCrypto.framework in Frameworks */, 5534E5972948B8C2005D0765 /* VCServices.framework in Frameworks */, + 553CC08329A8193E005A5FD6 /* VCNetworking.framework in Frameworks */, + 553CC08129A81931005A5FD6 /* VCToken.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -525,6 +557,7 @@ 5534E67C294A392F005D0765 /* Extensions */, 55A81BF02991BB13002C259A /* Requests */, 55E3376929478C3300CD2ED7 /* Utilities */, + 553CC09E29A97FD7005A5FD6 /* VerifiedId */, 559BD30C2996C12500BD61AC /* VerifiedIdClientBuilderTests.swift */, 552E509F293E6AB200868F47 /* VerifiedIdClientTests.swift */, ); @@ -640,11 +673,43 @@ path = VCSDK; sourceTree = ""; }; + 553CC09329A96217005A5FD6 /* RequestProtocols */ = { + isa = PBXGroup; + children = ( + 553CC09429A96235005A5FD6 /* Manifest */, + ); + path = RequestProtocols; + sourceTree = ""; + }; + 553CC09429A96235005A5FD6 /* Manifest */ = { + isa = PBXGroup; + children = ( + 553CC09529A96249005A5FD6 /* ContractIssuanceRequestTests.swift */, + ); + path = Manifest; + sourceTree = ""; + }; + 553CC09729A96C29005A5FD6 /* VerifiedId */ = { + isa = PBXGroup; + children = ( + 553CC09829A96C38005A5FD6 /* MockVerifiedId.swift */, + ); + path = VerifiedId; + sourceTree = ""; + }; + 553CC09E29A97FD7005A5FD6 /* VerifiedId */ = { + isa = PBXGroup; + children = ( + 553CC09F29A98003005A5FD6 /* VerifiableCredentialTests.swift */, + ); + path = VerifiedId; + sourceTree = ""; + }; 559BD3A4299AD9A200BD61AC /* Manifest */ = { isa = PBXGroup; children = ( 559BD3A5299AD9BD00BD61AC /* ManifestResolver.swift */, - 559BD484299D8DCC00BD61AC /* VerifiableCredentialRequester.swift */, + 553CC09C29A97181005A5FD6 /* IssuanceResponseContaining.swift */, ); path = Manifest; sourceTree = ""; @@ -690,6 +755,7 @@ 559BD3042995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift */, 559BD3062995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift */, 559BD3082995ABE800BD61AC /* PresentationRequest+MappableTests.swift */, + 553CC09129A957A3005A5FD6 /* PinDescriptor+MappableTests.swift */, ); path = Presentation; sourceTree = ""; @@ -700,6 +766,7 @@ 55E2F089299576350008010D /* PresentationDefinition+Mappable.swift */, 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */, 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */, + 553CC08F29A955E6005A5FD6 /* PinDescriptor+Mappable.swift */, ); path = Presentation; sourceTree = ""; @@ -736,6 +803,7 @@ isa = PBXGroup; children = ( 559BD4F7299EED5700BD61AC /* MockRawManifest.swift */, + 553CC09A29A96ECD005A5FD6 /* MockIssuanceResponseContaining.swift */, ); path = Manifest; sourceTree = ""; @@ -743,6 +811,7 @@ 55A81BF02991BB13002C259A /* Requests */ = { isa = PBXGroup; children = ( + 553CC09329A96217005A5FD6 /* RequestProtocols */, 55E2F06D299553B40008010D /* Handlers */, 550A9193299402450014D030 /* Resolvers */, 559BD4E9299EEAA200BD61AC /* Requirements */, @@ -755,6 +824,7 @@ 55A81C0A2991BF6C002C259A /* Mocks */ = { isa = PBXGroup; children = ( + 553CC09729A96C29005A5FD6 /* VerifiedId */, 55E2F08229955E7B0008010D /* Utilities */, 55A81C0B2991BF73002C259A /* Requests */, ); @@ -774,7 +844,7 @@ 550A913C29930D970014D030 /* MockRawRequest.swift */, 559BD3102996D3CE00BD61AC /* MockVerifiedIdRequest.swift */, 559BD3AE299AF42C00BD61AC /* MockManifestResolver.swift */, - 559BD4F4299EEC9500BD61AC /* MockVerifiableCredentialRequester.swift */, + 559BD4F4299EEC9500BD61AC /* MockVerifiedIdRequester.swift */, ); path = Requests; sourceTree = ""; @@ -833,6 +903,7 @@ children = ( 55E2F07C2995561E0008010D /* OpenIdPresentationRequest.swift */, 55E2F085299573C30008010D /* OpenIdVerifierStyle.swift */, + 5585BDE629A6C38C0059710B /* InjectedIdToken.swift */, ); path = OpenId; sourceTree = ""; @@ -895,6 +966,7 @@ 559BD4B1299EDF9E00BD61AC /* VerifiableCredential */, 55E336D3293FA75300CD2ED7 /* VerifiedId.swift */, 55E336D5293FA78C00CD2ED7 /* VerifiedIdClaim.swift */, + 5585BE0129A6D42B0059710B /* VerifiedIdType.swift */, ); path = VerifiedId; sourceTree = ""; @@ -1012,6 +1084,7 @@ 55A81BC92991AB82002C259A /* RequestResolving.swift */, 55E2F08029955A960008010D /* RequestType.swift */, 550E320F298B113C004E7716 /* VerifiedIdRequest.swift */, + 559BD484299D8DCC00BD61AC /* VerifiedIdRequester.swift */, 550E3211298B1150004E7716 /* VerifiedIdRequestInput.swift */, ); path = Requests; @@ -1341,12 +1414,14 @@ 55E2F07F29955A3B0008010D /* VerifiedIdRequestContent.swift in Sources */, 550A916F299310D40014D030 /* OpenIdRequestHandler.swift in Sources */, 55E3370A293FD3E000CD2ED7 /* IdTokenRequirement.swift in Sources */, + 5585BDE729A6C38C0059710B /* InjectedIdToken.swift in Sources */, 550E320C298B0F30004E7716 /* WalletLibraryLogConsumer.swift in Sources */, 550E3217298B1236004E7716 /* RequesterStyle.swift in Sources */, 55E336D1293FA6F400CD2ED7 /* VerifiedIdRequirement.swift in Sources */, 5534E66E294A0F8F005D0765 /* Mapping.swift in Sources */, 559BD4B3299EDFB400BD61AC /* VerifiableCredential.swift in Sources */, 55E336D6293FA78C00CD2ED7 /* VerifiedIdClaim.swift in Sources */, + 553CC09029A955E6005A5FD6 /* PinDescriptor+Mappable.swift in Sources */, 55E337652943749600CD2ED7 /* AsyncWrapper.swift in Sources */, 550E3214298B11A5004E7716 /* Requirement.swift in Sources */, 559BD453299D3DD800BD61AC /* IssuanceRequest+Mappable.swift in Sources */, @@ -1357,6 +1432,7 @@ 550E320E298B0FAC004E7716 /* WalletLibraryLogger.swift in Sources */, 559BD3032995993700BD61AC /* PresentationInputDescriptor+Mappable.swift in Sources */, 55E337B52948B4B000CD2ED7 /* PinRequirement.swift in Sources */, + 5585BE0229A6D42B0059710B /* VerifiedIdType.swift in Sources */, 559BD3A6299AD9BD00BD61AC /* ManifestResolver.swift in Sources */, 550A9170299310D40014D030 /* OpenIdURLRequestResolver.swift in Sources */, 5534E669294A0C3F005D0765 /* Mapper.swift in Sources */, @@ -1368,7 +1444,7 @@ 55E3370C293FD61E00CD2ED7 /* SelfAttestedClaimRequirement.swift in Sources */, 55E3376829437E3500CD2ED7 /* OpenIdForVCResolver.swift in Sources */, 55A81BE52991B2E5002C259A /* RequestResolverFactory.swift in Sources */, - 559BD485299D8DCC00BD61AC /* VerifiableCredentialRequester.swift in Sources */, + 559BD485299D8DCC00BD61AC /* VerifiedIdRequester.swift in Sources */, 55E2F086299573C30008010D /* OpenIdVerifierStyle.swift in Sources */, 559BD49C299D913F00BD61AC /* IssuanceResponseContainer+WalletLibrary.swift in Sources */, 559BD414299D37B100BD61AC /* SelfIssuedClaimsDescriptor+Mappable.swift in Sources */, @@ -1379,6 +1455,7 @@ 5534E672294A19D5005D0765 /* IdTokenDescriptor+Mappable.swift in Sources */, 5534E677294A1FA0005D0765 /* AccessTokenDescriptor+Mappable.swift in Sources */, 55E2F08A299576350008010D /* PresentationDefinition+Mappable.swift in Sources */, + 553CC09D29A97181005A5FD6 /* IssuanceResponseContaining.swift in Sources */, 550A919E29940ED70014D030 /* OpenIdRawRequest.swift in Sources */, 55E337BC2948B5FE00CD2ED7 /* PresentationService+OpenIdForVC.swift in Sources */, 55E336D4293FA75300CD2ED7 /* VerifiedId.swift in Sources */, @@ -1402,10 +1479,12 @@ 5534E6AD294AAFA6005D0765 /* PresentationDescriptor+MappableTests.swift in Sources */, 559BD4ED299EEAD100BD61AC /* IdTokenRequirementTests.swift in Sources */, 559BD455299D3E4800BD61AC /* IssuanceRequest+MappableTests.swift in Sources */, + 553CC0A029A98003005A5FD6 /* VerifiableCredentialTests.swift in Sources */, 55A81C132991C829002C259A /* MockHandler.swift in Sources */, 559BD3072995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift in Sources */, 559BD4EB299EEAC400BD61AC /* GroupRequirementTests.swift in Sources */, 55A81C072991BB2C002C259A /* RequestResolverFactoryTests.swift in Sources */, + 553CC09B29A96ECD005A5FD6 /* MockIssuanceResponseContaining.swift in Sources */, 559BD4EF299EEADF00BD61AC /* AccessTokenRequirementTests.swift in Sources */, 55A81C102991C211002C259A /* MockResolver.swift in Sources */, 559BD4E5299EEA3000BD61AC /* IssuanceResponseContainerExtensionTests.swift in Sources */, @@ -1415,6 +1494,7 @@ 559BD3052995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift in Sources */, 55A81C0D2991BF86002C259A /* MockInput.swift in Sources */, 559BD3AF299AF42C00BD61AC /* MockManifestResolver.swift in Sources */, + 553CC09929A96C38005A5FD6 /* MockVerifiedId.swift in Sources */, 559BD4F8299EED5700BD61AC /* MockRawManifest.swift in Sources */, 550A919829940DCE0014D030 /* MockOpenIdforVCResolver.swift in Sources */, 559BD459299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift in Sources */, @@ -1423,6 +1503,8 @@ 559BD4F1299EEAEA00BD61AC /* PinRequirementTests.swift in Sources */, 559BD30F2996C1C500BD61AC /* MockLogConsumer.swift in Sources */, 55E2F076299555280008010D /* MockRequirement.swift in Sources */, + 553CC09229A957A3005A5FD6 /* PinDescriptor+MappableTests.swift in Sources */, + 553CC09629A96249005A5FD6 /* ContractIssuanceRequestTests.swift in Sources */, 5534E68C294A8B8F005D0765 /* AccessTokenDescriptor+MappableTests.swift in Sources */, 55E2F077299555280008010D /* MockRequesterStyle.swift in Sources */, 552E50A0293E6AB200868F47 /* VerifiedIdClientTests.swift in Sources */, @@ -1431,7 +1513,7 @@ 559BD3112996D3CE00BD61AC /* MockVerifiedIdRequest.swift in Sources */, 550A913D29930D970014D030 /* MockRawRequest.swift in Sources */, 559BD30B2995B1B500BD61AC /* LinkedDomainResult+MappableTests.swift in Sources */, - 559BD4F5299EEC9500BD61AC /* MockVerifiableCredentialRequester.swift in Sources */, + 559BD4F5299EEC9500BD61AC /* MockVerifiedIdRequester.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+WalletLibrary.swift b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+WalletLibrary.swift index 2b50e548..488ea1fd 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+WalletLibrary.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Entities/IssuanceResponseContainer+WalletLibrary.swift @@ -15,7 +15,11 @@ enum IssuanceResponseContainerError: Error, Equatable { * An extension of the VCEntities.IssuanceResponseContainer class * to convert Requirements to mappings in IssuanceResponseContainer. */ -extension VCEntities.IssuanceResponseContainer { +extension VCEntities.IssuanceResponseContainer: IssuanceResponseContaining { + + struct Constants { + static let IdTokenHintKey = "https://self-issued.me" + } init(from manifest: any RawManifest, input: VerifiedIdRequestInput) throws { @@ -61,7 +65,12 @@ extension VCEntities.IssuanceResponseContainer { private mutating func add(idTokenRequirement: IdTokenRequirement) throws { try idTokenRequirement.validate() - self.requestedIdTokenMap[idTokenRequirement.configuration.absoluteString] = idTokenRequirement.idToken + + if idTokenRequirement.configuration.absoluteString == Constants.IdTokenHintKey { + self.issuanceIdToken = idTokenRequirement.idToken + } else { + self.requestedIdTokenMap[idTokenRequirement.configuration.absoluteString] = idTokenRequirement.idToken + } } private mutating func add(accessTokenRequirement: AccessTokenRequirement) throws { From 79ce63486f498e64234ca6ad15412770b6b6c881 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 11:47:11 -0800 Subject: [PATCH 242/373] Update IdTokenDescriptor+Mappable.swift --- .../Mappings/VCSDK/Issuance/IdTokenDescriptor+Mappable.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IdTokenDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IdTokenDescriptor+Mappable.swift index 77693215..bb53bdea 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IdTokenDescriptor+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IdTokenDescriptor+Mappable.swift @@ -20,7 +20,6 @@ extension VCEntities.IdTokenDescriptor: Mappable { } let redirectUri = try getRequiredProperty(property: redirectURI, propertyName: "redirectURI") - let scope = try getRequiredProperty(property: scope, propertyName: "scope") return IdTokenRequirement(encrypted: encrypted ?? false, required: idTokenRequired ?? false, From 7448c6e50d8ea10beb3f016a31eff6e7595efec1 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 11:47:20 -0800 Subject: [PATCH 243/373] Create PinDescriptor+Mappable.swift --- .../Presentation/PinDescriptor+Mappable.swift | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PinDescriptor+Mappable.swift diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PinDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PinDescriptor+Mappable.swift new file mode 100644 index 00000000..89c11572 --- /dev/null +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PinDescriptor+Mappable.swift @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import VCEntities + +/** + * Errors thrown in Pin Descriptor Mappable extension. + */ +enum PinDescriptorError: Error { + case presentationDefinitionMissingInRequest +} + +/** + * An extension of the VCEntities.PinDescriptor class to be able + * to map PinDescriptor to PinRequirement. + */ +extension VCEntities.PinDescriptor: Mappable { + + func map(using mapper: Mapping) throws -> PinRequirement { + + let type = try getRequiredProperty(property: type, propertyName: "type") + + let pinRequirement = PinRequirement(required: true, + length: length, + type: type, + salt: salt) + return pinRequirement + } +} From 12cfd548f917d2b62d6f13eeea699883a844a91f Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 11:47:39 -0800 Subject: [PATCH 244/373] Add IdTokenHint logic to presentation request extension. --- .../PresentationRequest+Mappable.swift | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+Mappable.swift index 76b43b46..c819637b 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+Mappable.swift @@ -26,14 +26,33 @@ extension VCEntities.PresentationRequest: Mappable { let requirement = try mapper.map(presentationDefinition) let rootOfTrust = try mapper.map(linkedDomainResult) + let injectedIdToken = try createInjectedIdTokenIfExists(using: mapper) let clientName = content.registration?.clientName ?? "" let requesterStyle = OpenIdVerifierStyle(name: clientName) let content = VerifiedIdRequestContent(style: requesterStyle, requirement: requirement, - rootOfTrust: rootOfTrust) + rootOfTrust: rootOfTrust, + injectedIdToken: injectedIdToken) return content } + + private func createInjectedIdTokenIfExists(using mapper: Mapping) throws -> InjectedIdToken? { + if let idTokenHint = content.idTokenHint { + return InjectedIdToken(rawToken: idTokenHint, + pin: try createPinRequirementIfExists(using: mapper)) + } + + return nil + } + + private func createPinRequirementIfExists(using mapper: Mapping) throws -> PinRequirement? { + if let pin = content.pin { + return try mapper.map(pin) + } + + return nil + } } From ff856e36829be6af55b74880b397fadc7fc54037 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 11:47:48 -0800 Subject: [PATCH 245/373] Update IssuanceService+VerifiableCredentialRequester.swift --- .../IssuanceService+VerifiableCredentialRequester.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+VerifiableCredentialRequester.swift b/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+VerifiableCredentialRequester.swift index 0113af49..6b89bd3c 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+VerifiableCredentialRequester.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Services/IssuanceService+VerifiableCredentialRequester.swift @@ -13,11 +13,11 @@ enum IssuanceServiceVCRequesterError: Error { * An extension of the VCServices.IssuanceService class * that wraps send method with a generic send method that conforms to VerifiableCredentialRequester protocol. */ -extension IssuanceService: VerifiableCredentialRequester { +extension IssuanceService: VerifiedIdRequester { /// Sends the issuance response container and if successful, returns a Verifiable Credential. /// If unsuccessful, throws an error. - func send(request: Request) async throws -> VerifiableCredential { + func send(request: Request) async throws -> VerifiedId { guard let issuanceResponseContainer = request as? IssuanceResponseContainer else { let requestType = String(describing: request.self) @@ -28,8 +28,8 @@ extension IssuanceService: VerifiableCredentialRequester { self.send(response: issuanceResponseContainer) }() - let verifiableCredential = VerifiableCredential(raw: rawVerifiableCredential, - from: issuanceResponseContainer.contract) + let verifiableCredential = try VerifiableCredential(raw: rawVerifiableCredential, + from: issuanceResponseContainer.contract) return verifiableCredential } } From 152961158f880286e5545bed05fc452f1544f083 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 11:47:54 -0800 Subject: [PATCH 246/373] Create IssuanceResponseContaining.swift --- .../Manifest/IssuanceResponseContaining.swift | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/IssuanceResponseContaining.swift diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/IssuanceResponseContaining.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/IssuanceResponseContaining.swift new file mode 100644 index 00000000..4a982975 --- /dev/null +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/IssuanceResponseContaining.swift @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +/** + * Protocol defines the behavior of collecting requirements to send to requester. + * For example, it is used as a wrapper to wrap the VC SDK get contract method. + */ +protocol IssuanceResponseContaining { + mutating func add(requirement: Requirement) throws +} From 3cdcdc72fe7f7474a155c1957159d36e9294a5c3 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 11:47:57 -0800 Subject: [PATCH 247/373] Update ManifestResolver.swift --- .../Protocols/Requests/Manifest/ManifestResolver.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/ManifestResolver.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/ManifestResolver.swift index c97cdd4e..b60c5afc 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/ManifestResolver.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/ManifestResolver.swift @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import VCEntities - /** * Protocol defines the behavior of taking a url and resolving a manifest. * For example, it is used as a wrapper to wrap the VC SDK get contract method. From 46f80c556d5ed9afbca93421c7ae30fd8beb1001 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 11:48:16 -0800 Subject: [PATCH 248/373] Create VerifiedIdRequester protocol. --- .../Protocols/Requests/VerifiedIdRequest.swift | 2 +- .../Protocols/Requests/VerifiedIdRequester.swift | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequester.swift diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequest.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequest.swift index f8e4d347..023e3aa7 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequest.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequest.swift @@ -38,4 +38,4 @@ public protocol VerifiedIdIssuanceRequest: VerifiedIdRequest where T == Verified /** * Internal Protocol that represents a Presentation Request. */ -protocol VerifiedIdPresentationRequest: VerifiedIdRequest where T == Void { } +public protocol VerifiedIdPresentationRequest: VerifiedIdRequest where T == Void { } diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequester.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequester.swift new file mode 100644 index 00000000..543a0646 --- /dev/null +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequester.swift @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +/** + * Protocol defines the behavior of given generic request, requests a Verified Id from an issuer. + * For example, it is used as a wrapper to wrap the VC SDK send response method. + */ +protocol VerifiedIdRequester { + + /// Given generic request, requests a raw Verified Id from an issuer. + func send(request: Request) async throws -> VerifiedId +} From 3db52cc74d9fe79fa14bc9e7e170c62698c1959a Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 11:48:32 -0800 Subject: [PATCH 249/373] Add IdTokenHint logic to OpenIdRequestHandler. --- .../Requests/Handlers/OpenIdRequestHandler.swift | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift index 54d221c1..115ca7ba 100644 --- a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift +++ b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift @@ -21,12 +21,12 @@ struct OpenIdRequestHandler: RequestHandling { private let manifestResolver: ManifestResolver - private let verifiableCredentialRequester: VerifiableCredentialRequester + private let verifiableCredentialRequester: VerifiedIdRequester /// TODO: post private preview, manifest resolving and verified id requester will be handled by processors. init(configuration: LibraryConfiguration, manifestResolver: ManifestResolver, - verifiableCredentialRequester: VerifiableCredentialRequester) { + verifiableCredentialRequester: VerifiedIdRequester) { self.configuration = configuration self.manifestResolver = manifestResolver self.verifiableCredentialRequester = verifiableCredentialRequester @@ -61,11 +61,15 @@ struct OpenIdRequestHandler: RequestHandling { let rawContract = try await manifestResolver.resolve(with: issuanceOption.url) let issuanceResponseContainer = try IssuanceResponseContainer(from: rawContract, input: issuanceOption) - /// TODO: add logic here to add PinRequirement to ContractIssuanceRequest if it exists. - let issuanceRequestContent = try configuration.mapper.map(rawContract) + var issuanceRequestContent = try configuration.mapper.map(rawContract) + + if let injectedIdToken = requestContent.injectedIdToken { + issuanceRequestContent.addRequirement(from: injectedIdToken) + } + return ContractIssuanceRequest(content: issuanceRequestContent, issuanceResponseContainer: issuanceResponseContainer, - verifiableCredentialRequester: verifiableCredentialRequester, + verifiedIdRequester: verifiableCredentialRequester, configuration: configuration) } From bedaa1849279a47d841090ed440f438326e88c9b Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 11:48:51 -0800 Subject: [PATCH 250/373] Add isSatisfied logic to ContractIssuanceRequest. --- .../Manifest/ContractIssuanceRequest.swift | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift index 7d1a7ae9..eb53dc80 100644 --- a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift +++ b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift @@ -17,35 +17,38 @@ class ContractIssuanceRequest: VerifiedIdIssuanceRequest { public let rootOfTrust: RootOfTrust - private let verifiableCredentialRequester: VerifiableCredentialRequester + private let verifiedIdRequester: VerifiedIdRequester private let configuration: LibraryConfiguration - private var responseContainer: IssuanceResponseContainer + private var responseContainer: IssuanceResponseContaining init(content: VerifiedIdRequestContent, - issuanceResponseContainer: IssuanceResponseContainer, - verifiableCredentialRequester: VerifiableCredentialRequester, + issuanceResponseContainer: IssuanceResponseContaining, + verifiedIdRequester: VerifiedIdRequester, configuration: LibraryConfiguration) { self.style = content.style self.requirement = content.requirement self.rootOfTrust = content.rootOfTrust self.responseContainer = issuanceResponseContainer - self.verifiableCredentialRequester = verifiableCredentialRequester + self.verifiedIdRequester = verifiedIdRequester self.configuration = configuration } public func isSatisfied() -> Bool { - /// TODO: implement. - return false + do { + try requirement.validate() + return true + } catch { + return false + } } public func complete() async -> Result { do { try self.responseContainer.add(requirement: requirement) - let verifiableCredential = try await verifiableCredentialRequester.send(request: responseContainer) - let verifiedId: VerifiedId = try configuration.mapper.map(verifiableCredential) - return Result.success(verifiedId) + let verifiableCredential = try await verifiedIdRequester.send(request: responseContainer) + return Result.success(verifiableCredential) } catch { return Result.failure(error) } From 46a4e1e415c2784071d4ae6c7b95dfd2f0c67a25 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 11:50:41 -0800 Subject: [PATCH 251/373] Update VerifiableCredential-SDK-iOS --- WalletLibrary/Submodules/VerifiableCredential-SDK-iOS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS b/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS index a1110b20..ad27b0f3 160000 --- a/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS +++ b/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS @@ -1 +1 @@ -Subproject commit a1110b20a39414144ac807c3a7b1a5fb64dce9a0 +Subproject commit ad27b0f39b5f34dc2c9a47852d7abcd6810607b6 From ad29d9924bc7b16dcad12f3bede247d2fe2b3c8f Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 11:50:45 -0800 Subject: [PATCH 252/373] Create InjectedIdToken.swift --- .../RequestProtocols/OpenId/InjectedIdToken.swift | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/InjectedIdToken.swift diff --git a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/InjectedIdToken.swift b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/InjectedIdToken.swift new file mode 100644 index 00000000..436c581a --- /dev/null +++ b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/InjectedIdToken.swift @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +/** + * An object that represents an id token that is injected from a presentation request into an issuance request. + */ +struct InjectedIdToken { + let rawToken: String + + let pin: PinRequirement? +} From f7681ab1cd944863f45a20243adc4ebc04fa217b Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 11:50:56 -0800 Subject: [PATCH 253/373] Update GroupRequirement.swift --- .../WalletLibrary/Requests/Requirements/GroupRequirement.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift b/WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift index 4ea3e554..63314075 100644 --- a/WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift +++ b/WalletLibrary/WalletLibrary/Requests/Requirements/GroupRequirement.swift @@ -23,7 +23,7 @@ public class GroupRequirement: Requirement { /// If the requirement is required or not. public let required: Bool - public let requirements: [Requirement] + internal(set) public var requirements: [Requirement] public let requirementOperator: GroupRequirementOperator From bbafc7cf815659a24525f3da86e92b04335d18da Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 11:51:01 -0800 Subject: [PATCH 254/373] Update IdTokenRequirement.swift --- .../Requests/Requirements/IdTokenRequirement.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Requirements/IdTokenRequirement.swift b/WalletLibrary/WalletLibrary/Requests/Requirements/IdTokenRequirement.swift index 651833e1..729cd1fb 100644 --- a/WalletLibrary/WalletLibrary/Requests/Requirements/IdTokenRequirement.swift +++ b/WalletLibrary/WalletLibrary/Requests/Requirements/IdTokenRequirement.swift @@ -28,7 +28,7 @@ public class IdTokenRequirement: Requirement { public let redirectUri: String /// The scope used to get the id token through an authentication library. - public let scope: String + public let scope: String? /// The nonce acts as an extra level of security and is used as an additional property /// within the id token request through an authentication library. The nonce will be placed within @@ -43,7 +43,7 @@ public class IdTokenRequirement: Requirement { configuration: URL, clientId: String, redirectUri: String, - scope: String) { + scope: String?) { self.encrypted = encrypted self.required = required self.configuration = configuration From a7196c703ec9b4e561e37fb3fc4cfe5a70f3b93f Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 11:51:05 -0800 Subject: [PATCH 255/373] Update VerifiedIdRequestContent.swift --- .../Requests/VerifiedIdRequestContent.swift | 46 ++++++++++++++++++- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift b/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift index 547f5696..f0613d11 100644 --- a/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift +++ b/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift @@ -6,13 +6,55 @@ /** * Contents in a Verified Id Request. * This object is used to map protocol specific requests to common request object. - * TODO: make this object extensibility to be able to add VerifiedIdStyle. + * TODO: make this object extensible by separating Presentation with Issuance esp. for InjectedIdToken logic. + * TODO: add VerifiedIdStyle to Issuance Content. */ struct VerifiedIdRequestContent { let style: RequesterStyle - let requirement: Requirement + var requirement: Requirement let rootOfTrust: RootOfTrust + + let injectedIdToken: InjectedIdToken? + + init(style: RequesterStyle, + requirement: Requirement, + rootOfTrust: RootOfTrust, + injectedIdToken: InjectedIdToken? = nil) { + self.style = style + self.requirement = requirement + self.rootOfTrust = rootOfTrust + self.injectedIdToken = injectedIdToken + } + + mutating func addRequirement(from injectedIdToken: InjectedIdToken) { + switch (requirement) { + case var groupRequirement as GroupRequirement: + repopulateGroupRequirementIfInjectedIdTokenExists(injectedIdToken: injectedIdToken, + groupRequirement: &groupRequirement) + case var idTokenRequirement as IdTokenRequirement: + idTokenRequirement.fulfill(with: injectedIdToken.rawToken) + if let pinRequirement = injectedIdToken.pin { + requirement = GroupRequirement(required: false, + requirements: [idTokenRequirement, pinRequirement], + requirementOperator: .ALL) + } + default: + return + } + } + + private func repopulateGroupRequirementIfInjectedIdTokenExists(injectedIdToken: InjectedIdToken, + groupRequirement: inout GroupRequirement) { + for requirement in groupRequirement.requirements { + if var idTokenRequirement = requirement as? IdTokenRequirement { + idTokenRequirement.fulfill(with: injectedIdToken.rawToken) + if let pinRequirement = injectedIdToken.pin { + groupRequirement.requirements.append(pinRequirement) + } + } + } + } } From 5426f72876446d3d460cae3660ad12923ddec470 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 11:51:24 -0800 Subject: [PATCH 256/373] Update mocks. --- .../MockIssuanceResponseContaining.swift | 19 ++++++++++++ .../Requests/MockVerifiedIdRequester.swift | 29 +++++++++++++++++++ .../Mocks/VerifiedId/MockVerifiedId.swift | 19 ++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 WalletLibrary/WalletLibraryTests/Mocks/Requests/Manifest/MockIssuanceResponseContaining.swift create mode 100644 WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiedIdRequester.swift create mode 100644 WalletLibrary/WalletLibraryTests/Mocks/VerifiedId/MockVerifiedId.swift diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/Manifest/MockIssuanceResponseContaining.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/Manifest/MockIssuanceResponseContaining.swift new file mode 100644 index 00000000..95b73045 --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Mocks/Requests/Manifest/MockIssuanceResponseContaining.swift @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +@testable import WalletLibrary + +struct MockIssuanceResponseContainer: IssuanceResponseContaining { + + private let mockAddRequirementCallback: ((Requirement) throws -> Void)? + + init(mockAddRequirementCallback: ((Requirement) throws -> Void)? = nil) { + self.mockAddRequirementCallback = mockAddRequirementCallback + } + + mutating func add(requirement: Requirement) throws { + try mockAddRequirementCallback?(requirement) + } +} diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiedIdRequester.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiedIdRequester.swift new file mode 100644 index 00000000..1d3e2fcb --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockVerifiedIdRequester.swift @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +@testable import WalletLibrary + +class MockVerifiedIdRequester: VerifiedIdRequester { + + enum MockVerifiedIdRequesterError: Error { + case missingCallback + } + + let sendRequestCallback: ((String) throws -> VerifiedId)? + + init(sendRequestCallback: ((String) throws -> VerifiedId)? = nil) { + self.sendRequestCallback = sendRequestCallback + } + + func send(request: Request) async throws -> VerifiedId { + + if let sendRequestCallback = sendRequestCallback { + return try sendRequestCallback((request as? String) ?? "") + } + + throw MockVerifiedIdRequesterError.missingCallback + } + +} diff --git a/WalletLibrary/WalletLibraryTests/Mocks/VerifiedId/MockVerifiedId.swift b/WalletLibrary/WalletLibraryTests/Mocks/VerifiedId/MockVerifiedId.swift new file mode 100644 index 00000000..bdf5b3a9 --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Mocks/VerifiedId/MockVerifiedId.swift @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +@testable import WalletLibrary + +struct MockVerifiedId: VerifiedId, Equatable { + + var id: String + + var expiresOn: Date? + + var issuedOn: Date + + func getClaims() -> [VerifiedIdClaim] { + return [] + } +} From b32e0615a72e3a679d43e2c81872d03ffbbe92f1 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 11:51:37 -0800 Subject: [PATCH 257/373] Add IdTokenHint logic to IssuanceResponseContainer extension tests. --- ...uanceResponseContainerExtensionTests.swift | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Entities/IssuanceResponseContainerExtensionTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Entities/IssuanceResponseContainerExtensionTests.swift index cad9f5f5..7b4268e2 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Entities/IssuanceResponseContainerExtensionTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Entities/IssuanceResponseContainerExtensionTests.swift @@ -79,6 +79,34 @@ class IssuanceResponseContainerExtensionTests: XCTestCase { // Assert XCTAssertEqual(container.requestedIdTokenMap, [requirement.configuration.absoluteString: requirement.idToken]) + XCTAssertNil(container.issuanceIdToken) + XCTAssert(container.requestedAccessTokenMap.isEmpty) + XCTAssert(container.requestedSelfAttestedClaimMap.isEmpty) + XCTAssert(container.requestVCMap.isEmpty) + XCTAssertNil(container.issuancePin) + } + + func testAddRequirement_WithIdTokenHintRequirement_UpdateIdTokenHintValue() async throws { + // Arrange + let issuanceRequest = createMockIssuanceRequest() + let mockInput = VerifiedIdRequestURL(url: URL(string: "https://test.com")!) + var container = try IssuanceResponseContainer(from: issuanceRequest, input: mockInput) + + let requirement = IdTokenRequirement(encrypted: false, + required: true, + configuration: URL(string: "https://self-issued.me")!, + clientId: "", + redirectUri: "", + scope: "") + requirement.fulfill(with: "mock token") + + // Act + try container.add(requirement: requirement) + + // Assert + XCTAssertNotNil(container.issuanceIdToken) + XCTAssertEqual(container.issuanceIdToken, "mock token") + XCTAssert(container.requestedIdTokenMap.isEmpty) XCTAssert(container.requestedAccessTokenMap.isEmpty) XCTAssert(container.requestedSelfAttestedClaimMap.isEmpty) XCTAssert(container.requestVCMap.isEmpty) From edea2931dfa744000c84f92e9296b5e24a7a44d5 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 11:51:42 -0800 Subject: [PATCH 258/373] Update IdTokenDescriptor+MappableTests.swift --- .../IdTokenDescriptor+MappableTests.swift | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/IdTokenDescriptor+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/IdTokenDescriptor+MappableTests.swift index af080b17..63913ae7 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/IdTokenDescriptor+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/IdTokenDescriptor+MappableTests.swift @@ -82,22 +82,6 @@ class IdTokenDescriptorMappingTests: XCTestCase { } } - func testMappingWithNoScopePresentError() throws { - let input = IdTokenDescriptor(encrypted: false, - claims: [], - idTokenRequired: false, - configuration: expectedConfiguration, - clientID: expectedClientId, - redirectURI: expectedRedirectUri, - scope: nil) - - XCTAssertThrowsError(try mapper.map(input)) { error in - XCTAssert(error is MappingError) - XCTAssertEqual(error as? MappingError, - .PropertyNotPresent(property: "scope", in: String(describing: IdTokenDescriptor.self))) - } - } - private func assertEqual(_ actual: IdTokenRequirement, _ expected: IdTokenRequirement) { XCTAssertEqual(actual.encrypted, expected.encrypted) XCTAssertEqual(actual.required, expected.required) From d6080e38f8c0ae5f6f8c0babc066370380a438bb Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 11:51:48 -0800 Subject: [PATCH 259/373] Create PinDescriptor+MappableTests.swift --- .../PinDescriptor+MappableTests.swift | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PinDescriptor+MappableTests.swift diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PinDescriptor+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PinDescriptor+MappableTests.swift new file mode 100644 index 00000000..b79c0219 --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PinDescriptor+MappableTests.swift @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import XCTest +import VCEntities +@testable import WalletLibrary + +class PinDescriptorMappingTests: XCTestCase { + + func testMap_WithNilType_ThrowsError() throws { + // Arrange + let mockMapper = MockMapper() + let pinDescriptor = PinDescriptor(type: nil, + length: 4, + hash: "hash", + salt: nil, + iterations: nil, + alg: nil) + + // Act + XCTAssertThrowsError(try mockMapper.map(pinDescriptor)) { error in + // Assert + XCTAssert(error is MappingError) + XCTAssertEqual(error as? MappingError, .PropertyNotPresent(property: "type", in: "PinDescriptor")) + } + } + + func testMap_WithNilSalt_ReturnsPinRequirement() throws { + // Arrange + let mockMapper = MockMapper() + let pinDescriptor = PinDescriptor(type: "mock type", + length: 4, + hash: "hash", + salt: nil, + iterations: nil, + alg: nil) + + // Act + let actualResult = try mockMapper.map(pinDescriptor) + + // Assert + XCTAssert(actualResult.required) + XCTAssertEqual(actualResult.type, "mock type") + XCTAssertEqual(actualResult.length, 4) + XCTAssertEqual(actualResult.salt, nil) + XCTAssertEqual(actualResult.pin, nil) + } + + func testMap_WithSalt_ReturnsPinRequirement() throws { + // Arrange + let mockMapper = MockMapper() + let pinDescriptor = PinDescriptor(type: "mock type", + length: 8, + hash: "hash", + salt: "mock salt", + iterations: nil, + alg: nil) + + // Act + let actualResult = try mockMapper.map(pinDescriptor) + + // Assert + XCTAssert(actualResult.required) + XCTAssertEqual(actualResult.type, "mock type") + XCTAssertEqual(actualResult.length, 8) + XCTAssertEqual(actualResult.salt, "mock salt") + XCTAssertEqual(actualResult.pin, nil) + } +} From 4224523b1eba92167868001c72a695a3ff43c1c8 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 11:52:15 -0800 Subject: [PATCH 260/373] Add IdTokenHint logic to presentation request extension tests. --- .../PresentationRequest+MappableTests.swift | 169 +++++++++++++++--- 1 file changed, 144 insertions(+), 25 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+MappableTests.swift index 231ad19a..023162ec 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+MappableTests.swift @@ -14,12 +14,10 @@ class PresentationRequestMappingTests: XCTestCase { case expectedToBeThrown } - let mapper = Mapper() - func testMap_WithNoPresentationDefinitionPresent_ThrowsError() throws { // Arrange let mockRequestClaims = RequestedClaims(vpToken: RequestedVPToken(presentationDefinition: nil)) - let token = createPresentationRequestToken(with: mockRequestClaims, and: nil) + let token = createPresentationRequestToken(requestedClaims: mockRequestClaims, registration: nil) let linkedDomainResult = LinkedDomainResult.linkedDomainMissing let presentationRequest = PresentationRequest(from: token, @@ -39,7 +37,7 @@ class PresentationRequestMappingTests: XCTestCase { // Arrange let mockPresentationDefinition = PresentationDefinition(id: nil, inputDescriptors: nil, issuance: nil) let mockRequestClaims = RequestedClaims(vpToken: RequestedVPToken(presentationDefinition: mockPresentationDefinition)) - let token = createPresentationRequestToken(with: mockRequestClaims, and: nil) + let token = createPresentationRequestToken(requestedClaims: mockRequestClaims, registration: nil) let linkedDomainResult = LinkedDomainResult.linkedDomainMissing let presentationRequest = PresentationRequest(from: token, @@ -72,7 +70,7 @@ class PresentationRequestMappingTests: XCTestCase { issuanceOptions: []) let mockPresentationDefinition = PresentationDefinition(id: nil, inputDescriptors: nil, issuance: nil) let mockRequestClaims = RequestedClaims(vpToken: RequestedVPToken(presentationDefinition: mockPresentationDefinition)) - let token = createPresentationRequestToken(with: mockRequestClaims, and: nil) + let token = createPresentationRequestToken(requestedClaims: mockRequestClaims, registration: nil) let linkedDomainResult = LinkedDomainResult.linkedDomainMissing let presentationRequest = PresentationRequest(from: token, @@ -108,16 +106,16 @@ class PresentationRequestMappingTests: XCTestCase { let expectedVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, required: false, types: [], - purpose: nil, - issuanceOptions: []) + purpose: nil, + issuanceOptions: []) let mockPresentationDefinition = PresentationDefinition(id: nil, inputDescriptors: nil, issuance: nil) let mockRequestClaims = RequestedClaims(vpToken: RequestedVPToken(presentationDefinition: mockPresentationDefinition)) let mockRegistration = RegistrationClaims(clientName: nil, - clientPurpose: nil, - logoURI: nil, - subjectIdentifierTypesSupported: nil, - vpFormats: nil) - let token = createPresentationRequestToken(with: mockRequestClaims, and: mockRegistration) + clientPurpose: nil, + logoURI: nil, + subjectIdentifierTypesSupported: nil, + vpFormats: nil) + let token = createPresentationRequestToken(requestedClaims: mockRequestClaims, registration: mockRegistration) let expectedRootOfTrust = RootOfTrust(verified: false, source: "") let linkedDomainResult = LinkedDomainResult.linkedDomainMissing @@ -146,6 +144,7 @@ class PresentationRequestMappingTests: XCTestCase { XCTAssertIdentical(actualResult.requirement as AnyObject, expectedVerifiedIdRequirement as AnyObject) XCTAssertEqual(actualResult.rootOfTrust, expectedRootOfTrust) XCTAssertEqual(actualResult.style as? OpenIdVerifierStyle, expectedStyle) + XCTAssertNil(actualResult.injectedIdToken) } func testMap_WithClientNamePresent_ReturnVerifiedIdRequestContent() throws { @@ -153,18 +152,18 @@ class PresentationRequestMappingTests: XCTestCase { let mockRequesterName = "mockRequesterName235" let expectedStyle = OpenIdVerifierStyle(name: mockRequesterName) let expectedVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, - required: false, - types: [], - purpose: nil, - issuanceOptions: []) + required: false, + types: [], + purpose: nil, + issuanceOptions: []) let mockPresentationDefinition = PresentationDefinition(id: nil, inputDescriptors: nil, issuance: nil) let mockRequestClaims = RequestedClaims(vpToken: RequestedVPToken(presentationDefinition: mockPresentationDefinition)) let mockRegistration = RegistrationClaims(clientName: mockRequesterName, - clientPurpose: nil, - logoURI: nil, - subjectIdentifierTypesSupported: nil, - vpFormats: nil) - let token = createPresentationRequestToken(with: mockRequestClaims, and: mockRegistration) + clientPurpose: nil, + logoURI: nil, + subjectIdentifierTypesSupported: nil, + vpFormats: nil) + let token = createPresentationRequestToken(requestedClaims: mockRequestClaims, registration: mockRegistration) let expectedRootOfTrust = RootOfTrust(verified: false, source: "") let linkedDomainResult = LinkedDomainResult.linkedDomainMissing @@ -193,10 +192,130 @@ class PresentationRequestMappingTests: XCTestCase { XCTAssertIdentical(actualResult.requirement as AnyObject, expectedVerifiedIdRequirement as AnyObject) XCTAssertEqual(actualResult.rootOfTrust, expectedRootOfTrust) XCTAssertEqual(actualResult.style as? OpenIdVerifierStyle, expectedStyle) + XCTAssertNil(actualResult.injectedIdToken) + } + + func testMap_WithIdTokenHintWithoutPin_ReturnVerifiedIdRequestContentWithInjectedIdToken() throws { + // Arrange + let mockRequesterName = "mockRequesterName235" + let expectedStyle = OpenIdVerifierStyle(name: mockRequesterName) + let expectedVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, + required: false, + types: [], + purpose: nil, + issuanceOptions: []) + let mockPresentationDefinition = PresentationDefinition(id: nil, inputDescriptors: nil, issuance: nil) + let mockRequestClaims = RequestedClaims(vpToken: RequestedVPToken(presentationDefinition: mockPresentationDefinition)) + let mockRegistration = RegistrationClaims(clientName: mockRequesterName, + clientPurpose: nil, + logoURI: nil, + subjectIdentifierTypesSupported: nil, + vpFormats: nil) + let token = createPresentationRequestToken(requestedClaims: mockRequestClaims, + registration: mockRegistration, + idTokenHint: "mock idToken hint") + + let expectedRootOfTrust = RootOfTrust(verified: false, source: "") + let linkedDomainResult = LinkedDomainResult.linkedDomainMissing + let presentationRequest = PresentationRequest(from: token, + linkedDomainResult: linkedDomainResult) + + func mockResults(objectToBeMapped: Any) throws -> Any? { + + if objectToBeMapped is PresentationDefinition { + return expectedVerifiedIdRequirement + } + + if objectToBeMapped is LinkedDomainResult { + return expectedRootOfTrust + } + + return nil + } + + let mockMapper = MockMapper(mockResults: mockResults) + + // Act + let actualResult = try mockMapper.map(presentationRequest) + + // Act + XCTAssertIdentical(actualResult.requirement as AnyObject, expectedVerifiedIdRequirement as AnyObject) + XCTAssertEqual(actualResult.rootOfTrust, expectedRootOfTrust) + XCTAssertEqual(actualResult.style as? OpenIdVerifierStyle, expectedStyle) + XCTAssertEqual(actualResult.injectedIdToken?.rawToken, "mock idToken hint") + XCTAssertNil(actualResult.injectedIdToken?.pin) + } + + func testMap_WithIdTokenHintWithPin_ReturnVerifiedIdRequestContentWithInjectedIdToken() throws { + // Arrange + let mockRequesterName = "mockRequesterName235" + let expectedPinRequirement = PinRequirement(required: true, + length: 4, + type: "mock pin type", + salt: "mock salt") + let expectedStyle = OpenIdVerifierStyle(name: mockRequesterName) + let expectedVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, + required: false, + types: [], + purpose: nil, + issuanceOptions: []) + let mockPresentationDefinition = PresentationDefinition(id: nil, inputDescriptors: nil, issuance: nil) + let mockRequestClaims = RequestedClaims(vpToken: RequestedVPToken(presentationDefinition: mockPresentationDefinition)) + let mockRegistration = RegistrationClaims(clientName: mockRequesterName, + clientPurpose: nil, + logoURI: nil, + subjectIdentifierTypesSupported: nil, + vpFormats: nil) + let pinDescriptor = PinDescriptor(type: "mock pin type", + length: 4, + hash: "mock hash", + salt: "mock salt", + iterations: nil, + alg: nil) + let token = createPresentationRequestToken(requestedClaims: mockRequestClaims, + registration: mockRegistration, + idTokenHint: "mock idToken hint", + pin: pinDescriptor) + + let expectedRootOfTrust = RootOfTrust(verified: false, source: "") + let linkedDomainResult = LinkedDomainResult.linkedDomainMissing + let presentationRequest = PresentationRequest(from: token, + linkedDomainResult: linkedDomainResult) + + func mockResults(objectToBeMapped: Any) throws -> Any? { + + if objectToBeMapped is PresentationDefinition { + return expectedVerifiedIdRequirement + } + + if objectToBeMapped is LinkedDomainResult { + return expectedRootOfTrust + } + + if objectToBeMapped is PinDescriptor { + return expectedPinRequirement + } + + return nil + } + + let mockMapper = MockMapper(mockResults: mockResults) + + // Act + let actualResult = try mockMapper.map(presentationRequest) + + // Act + XCTAssertIdentical(actualResult.requirement as AnyObject, expectedVerifiedIdRequirement as AnyObject) + XCTAssertEqual(actualResult.rootOfTrust, expectedRootOfTrust) + XCTAssertEqual(actualResult.style as? OpenIdVerifierStyle, expectedStyle) + XCTAssertEqual(actualResult.injectedIdToken?.rawToken, "mock idToken hint") + XCTAssertIdentical(actualResult.injectedIdToken?.pin as AnyObject, expectedPinRequirement as AnyObject) } - private func createPresentationRequestToken(with requestedClaims: RequestedClaims?, - and registration: RegistrationClaims?) -> PresentationRequestToken { + private func createPresentationRequestToken(requestedClaims: RequestedClaims? = nil, + registration: RegistrationClaims? = nil, + idTokenHint: String? = nil, + pin: PinDescriptor? = nil) -> PresentationRequestToken { let presentationRequestTokenClaims = PresentationRequestClaims(jti: nil, clientID: nil, redirectURI: nil, @@ -208,10 +327,10 @@ class PresentationRequestMappingTests: XCTestCase { scope: nil, prompt: nil, registration: registration, - idTokenHint: nil, + idTokenHint: idTokenHint, iat: nil, exp: nil, - pin: nil) + pin: pin) return PresentationRequestToken(headers: Header(), content: presentationRequestTokenClaims)! } From ef28ff02c4313a9d90f3f7c4cc29bb91adf20d1f Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 11:52:37 -0800 Subject: [PATCH 261/373] Make VerifiedId a protocol. --- .../VerifiableCredential.swift | 81 ++++++++++++++++++- .../WalletLibrary/VerifiedId/VerifiedId.swift | 19 ++--- .../VerifiedId/VerifiedIdClaim.swift | 6 +- .../VerifiedId/VerifiedIdType.swift | 8 ++ 4 files changed, 93 insertions(+), 21 deletions(-) create mode 100644 WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdType.swift diff --git a/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift b/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift index ba9da7fe..9df70a4d 100644 --- a/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift +++ b/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift @@ -5,22 +5,95 @@ import VCEntities +enum VerifiableCredentialMappingError: Error { + case missingJtiInVerifiableCredential + case unableToDecodeRawVerifiableCredentialToken + case missingIssuedOnValueInVerifiableCredential +} + /** * Verifiable Credential object contains the raw VC, and the contract that created the Verifiable Credential. * This object conforms to the Mappable protocol and maps VC claims and display contract to a Verified Id. */ -struct VerifiableCredential: Mappable { +struct VerifiableCredential: VerifiedId { + + public let id: String + + public let expiresOn: Date? + + public let issuedOn: Date let raw: VCEntities.VerifiableCredential let contract: Contract - init(raw: VCEntities.VerifiableCredential, from contract: Contract) { + init(raw: VCEntities.VerifiableCredential, from contract: Contract) throws { + + guard let issuedOn = raw.content.iat else { + throw VerifiableCredentialMappingError.missingIssuedOnValueInVerifiableCredential + } + + guard let id = raw.content.jti else { + throw VerifiableCredentialMappingError.missingJtiInVerifiableCredential + } + self.raw = raw self.contract = contract + self.issuedOn = Date(timeIntervalSince1970: issuedOn) + self.id = id + + if let expiresOn = raw.content.exp { + self.expiresOn = Date(timeIntervalSince1970: expiresOn) + } else { + self.expiresOn = nil + } + } + + enum CodingKeys: String, CodingKey { + case raw, contract + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + let rawToken = try values.decode(String.self, forKey: .raw) + guard let raw = VCEntities.VerifiableCredential(from: rawToken) else { + throw VerifiableCredentialMappingError.unableToDecodeRawVerifiableCredentialToken + } + let contract = try values.decode(Contract.self, forKey: .contract) + try self.init(raw: raw, from: contract) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + let serializedToken = try raw.serialize() + try container.encode(serializedToken, forKey: .raw) + try container.encode(contract, forKey: .contract) + } + + public func getClaims() -> [VerifiedIdClaim] { + return createClaims() } - func map(using mapper: Mapping) throws -> VerifiedId { - throw VerifiedIdClientError.TODO(message: "implement") + private func createClaims() -> [VerifiedIdClaim] { + + guard let vcClaims = raw.content.vc?.credentialSubject else { + return [] + } + + let claimLabels = contract.display.claims + + var verifiedIdClaims: [VerifiedIdClaim] = [] + /// TODO: add casting to correct type from contract. + for (claim, value) in vcClaims { + if let claimStyle = claimLabels["vc.credentialSubject.\(claim)"] { + verifiedIdClaims.append(VerifiedIdClaim(id: claimStyle.label, + value: value)) + } else { + verifiedIdClaims.append(VerifiedIdClaim(id: claim, + value: value)) + } + } + + return verifiedIdClaims } } diff --git a/WalletLibrary/WalletLibrary/VerifiedId/VerifiedId.swift b/WalletLibrary/WalletLibrary/VerifiedId/VerifiedId.swift index 9029c9a0..b01d5ea9 100644 --- a/WalletLibrary/WalletLibrary/VerifiedId/VerifiedId.swift +++ b/WalletLibrary/WalletLibrary/VerifiedId/VerifiedId.swift @@ -3,29 +3,20 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import Foundation - /** * The Verified Id Data Model. */ -public struct VerifiedId { +public protocol VerifiedId: Codable { /// the id of the verified id. For example, this value would equal the jti of a Verifiable Credential. - public let id: String - - /// the type of the verified id. For example, Verifiable Credential would be the type of a VC. - public let type: String - - /// a list of claims within the verified id. - public let claims: [VerifiedIdClaim] + var id: String { get } /// date the verified id expires. - public let expiresOn: Date + var expiresOn: Date? { get } /// date the verified id was issued. - public let issuedOn: Date + var issuedOn: Date { get } - /// the raw representation of the verified id. - let raw: String + func getClaims() -> [VerifiedIdClaim] } diff --git a/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdClaim.swift b/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdClaim.swift index 2e589108..85847249 100644 --- a/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdClaim.swift +++ b/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdClaim.swift @@ -7,10 +7,10 @@ * A mapping of the claims contained within a Verified Id. */ public struct VerifiedIdClaim { + /// id of the claim. For example, within a VC, it is the key value of credentialSubject. - let id: String + public let id: String /// the value of the claim. - let value: Any + public let value: Any } - diff --git a/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdType.swift b/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdType.swift new file mode 100644 index 00000000..ed6506f1 --- /dev/null +++ b/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdType.swift @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +public enum VerifiedIdType { + case verifiableCredential +} From b9415c93fe9960f4e56fb39daa160b2ec8434ff5 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 11:52:43 -0800 Subject: [PATCH 262/373] Update OpenIdRequestHandlerTests.swift --- .../Handlers/OpenIdRequestHandlerTests.swift | 74 +++++++++++++++++-- 1 file changed, 66 insertions(+), 8 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift index 23ecc18a..f5d006f0 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift @@ -39,7 +39,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, manifestResolver: MockManifestResolver(), - verifiableCredentialRequester: MockVerifiableCredentialRequester()) + verifiableCredentialRequester: MockVerifiedIdRequester()) // Act let actualRequest = try await handler.handleRequest(from: mockRawRequest) @@ -68,7 +68,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, manifestResolver: MockManifestResolver(), - verifiableCredentialRequester: MockVerifiableCredentialRequester()) + verifiableCredentialRequester: MockVerifiedIdRequester()) // Act do { @@ -104,7 +104,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, manifestResolver: MockManifestResolver(), - verifiableCredentialRequester: MockVerifiableCredentialRequester()) + verifiableCredentialRequester: MockVerifiedIdRequester()) // Act do { @@ -144,7 +144,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, manifestResolver: MockManifestResolver(), - verifiableCredentialRequester: MockVerifiableCredentialRequester()) + verifiableCredentialRequester: MockVerifiedIdRequester()) // Act do { @@ -184,7 +184,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, manifestResolver: MockManifestResolver(), - verifiableCredentialRequester: MockVerifiableCredentialRequester()) + verifiableCredentialRequester: MockVerifiedIdRequester()) // Act do { @@ -229,7 +229,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, manifestResolver: MockManifestResolver(mockGetRequestCallback: mockResolveContract), - verifiableCredentialRequester: MockVerifiableCredentialRequester()) + verifiableCredentialRequester: MockVerifiedIdRequester()) // Act do { @@ -278,7 +278,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, manifestResolver: MockManifestResolver(mockGetRequestCallback: mockResolveContract), - verifiableCredentialRequester: MockVerifiableCredentialRequester()) + verifiableCredentialRequester: MockVerifiedIdRequester()) // Act do { @@ -334,7 +334,65 @@ class OpenIdRequestHandlerTests: XCTestCase { let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, manifestResolver: MockManifestResolver(mockGetRequestCallback: mockResolveContract), - verifiableCredentialRequester: MockVerifiableCredentialRequester()) + verifiableCredentialRequester: MockVerifiedIdRequester()) + + // Act + let actualRequest = try await handler.handleRequest(from: mockRawRequest) + + // Assert + XCTAssert(actualRequest is ContractIssuanceRequest) + XCTAssertEqual(actualRequest.style as? MockRequesterStyle, expectedIssuanceStyle) + XCTAssertEqual(actualRequest.requirement as? MockRequirement, expectedIssuanceRequirement) + XCTAssertEqual(actualRequest.rootOfTrust.source, expectedIssuanceRootOfTrust.source) + XCTAssert(actualRequest.rootOfTrust.verified) + } + + func testHandleIssuanceRequest_WithValidContractAndInjectedIdToken_ReturnsVerifiedIdIssuanceRequest() async throws { + + // Arrange + let issuanceOptionURL = URL(string: "https://test.com")! + let expectedPresentationStyle = MockRequesterStyle(requester: "mock requester") + let expectedPresentationRequirement = VerifiedIdRequirement(encrypted: false, + required: true, + types: [], + purpose: nil, + issuanceOptions: [VerifiedIdRequestURL(url: issuanceOptionURL)]) + let expectedPresentationRootOfTrust = RootOfTrust(verified: true, source: "mock source") + let expectedInjectedIdToken = InjectedIdToken(rawToken: "mock idToken hint", pin: nil) + let expectedPresentationContent = VerifiedIdRequestContent(style: expectedPresentationStyle, + requirement: expectedPresentationRequirement, + rootOfTrust: expectedPresentationRootOfTrust, + injectedIdToken: expectedInjectedIdToken) + + let expectedIssuanceStyle = MockRequesterStyle(requester: "mock issuer") + let expectedIssuanceRequirement = MockRequirement(id: "mockRequirement23535") + let expectedIssuanceRootOfTrust = RootOfTrust(verified: true, source: "mock issuer source") + let expectedIssuanceContent = VerifiedIdRequestContent(style: expectedIssuanceStyle, + requirement: expectedIssuanceRequirement, + rootOfTrust: expectedIssuanceRootOfTrust) + + func mockResults(objectToBeMapped: Any) throws -> Any? { + if objectToBeMapped is MockOpenIdRawRequest { + return expectedPresentationContent + } + + if objectToBeMapped is IssuanceRequest { + return expectedIssuanceContent + } + + return nil + } + + func mockResolveContract(url: String) throws -> any RawManifest { + return createMockIssuanceRequest() + } + + let mockMapper = MockMapper(mockResults: mockResults) + let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) + let handler = OpenIdRequestHandler(configuration: configuration, + manifestResolver: MockManifestResolver(mockGetRequestCallback: mockResolveContract), + verifiableCredentialRequester: MockVerifiedIdRequester()) // Act let actualRequest = try await handler.handleRequest(from: mockRawRequest) From e618d0efa9673d3b74b17f5129c55dca49cc48a6 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 11:52:54 -0800 Subject: [PATCH 263/373] Update Resolver tests. --- .../ContractIssuanceRequestTests.swift | 212 ++++++++++++++++++ .../OpenIdURLRequestResolverTests.swift | 2 +- 2 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 WalletLibrary/WalletLibraryTests/Requests/RequestProtocols/Manifest/ContractIssuanceRequestTests.swift diff --git a/WalletLibrary/WalletLibraryTests/Requests/RequestProtocols/Manifest/ContractIssuanceRequestTests.swift b/WalletLibrary/WalletLibraryTests/Requests/RequestProtocols/Manifest/ContractIssuanceRequestTests.swift new file mode 100644 index 00000000..9f337ccc --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Requests/RequestProtocols/Manifest/ContractIssuanceRequestTests.swift @@ -0,0 +1,212 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import XCTest +import VCEntities +@testable import WalletLibrary + +class ContractIssuanceRequestTests: XCTestCase { + + enum ExpectedError: Error, Equatable { + case expectedToBeThrown + } + + func testIsSatisfied_WithInvalidRequirement_ThrowsError() async throws { + // Arrange + let mockStyle = MockRequesterStyle(requester: "mock requester") + let mockRootOfTrust = RootOfTrust(verified: true, source: "") + let mockMapper = MockMapper() + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) + let mockVCRequester = MockVerifiedIdRequester() + let mockIssuanceResponseContainer = MockIssuanceResponseContainer() + + let mockRequirement = MockRequirement(id: "mockRequirement324", + mockValidateCallback: { throw ExpectedError.expectedToBeThrown }) + + let content = VerifiedIdRequestContent(style: mockStyle, + requirement: mockRequirement, + rootOfTrust: mockRootOfTrust) + + let contractIssuanceRequest = ContractIssuanceRequest(content: content, + issuanceResponseContainer: mockIssuanceResponseContainer, + verifiedIdRequester: mockVCRequester, + configuration: configuration) + + // Act + let actualResult = contractIssuanceRequest.isSatisfied() + + // Assert + XCTAssertFalse(actualResult) + } + + func testIsSatisfied_WithOneInvalidRequirementofMultipleValidRequirements_ThrowsError() async throws { + // Arrange + let mockStyle = MockRequesterStyle(requester: "mock requester") + let mockRootOfTrust = RootOfTrust(verified: true, source: "") + let mockMapper = MockMapper() + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) + let mockVCRequester = MockVerifiedIdRequester() + let mockIssuanceResponseContainer = MockIssuanceResponseContainer() + + let invalidRequirement = MockRequirement(id: "mockRequirement324", + mockValidateCallback: { throw ExpectedError.expectedToBeThrown }) + let firstValidRequirement = MockRequirement(id: "mockRequirement634") + let secondValidRequirement = MockRequirement(id: "mockRequirement674") + let mockRequirement = GroupRequirement(required: true, + requirements: [firstValidRequirement, invalidRequirement, secondValidRequirement], + requirementOperator: .ALL) + + + let content = VerifiedIdRequestContent(style: mockStyle, + requirement: mockRequirement, + rootOfTrust: mockRootOfTrust) + + let contractIssuanceRequest = ContractIssuanceRequest(content: content, + issuanceResponseContainer: mockIssuanceResponseContainer, + verifiedIdRequester: mockVCRequester, + configuration: configuration) + + // Act + let actualResult = contractIssuanceRequest.isSatisfied() + + // Assert + XCTAssertFalse(actualResult) + } + + func testIsSatisfied_WithOneValidRequirement_ReturnsTrue() async throws { + // Arrange + let mockStyle = MockRequesterStyle(requester: "mock requester") + let mockRootOfTrust = RootOfTrust(verified: true, source: "") + let mockMapper = MockMapper() + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) + let mockVCRequester = MockVerifiedIdRequester() + let mockIssuanceResponseContainer = MockIssuanceResponseContainer() + + let validRequirement = MockRequirement(id: "mockRequirement324") + + + let content = VerifiedIdRequestContent(style: mockStyle, + requirement: validRequirement, + rootOfTrust: mockRootOfTrust) + + let contractIssuanceRequest = ContractIssuanceRequest(content: content, + issuanceResponseContainer: mockIssuanceResponseContainer, + verifiedIdRequester: mockVCRequester, + configuration: configuration) + + // Act + let actualResult = contractIssuanceRequest.isSatisfied() + + // Assert + XCTAssert(actualResult) + } + + func testIsSatisfied_WithMultipleValidRequirements_ReturnsTrue() async throws { + // Arrange + let mockStyle = MockRequesterStyle(requester: "mock requester") + let mockRootOfTrust = RootOfTrust(verified: true, source: "") + let mockMapper = MockMapper() + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) + let mockVCRequester = MockVerifiedIdRequester() + let mockIssuanceResponseContainer = MockIssuanceResponseContainer() + + let firstValidRequirement = MockRequirement(id: "mockRequirement634") + let secondValidRequirement = MockRequirement(id: "mockRequirement674") + let thirdValidRequirement = MockRequirement(id: "mockRequirement694") + let mockRequirement = GroupRequirement(required: true, + requirements: [firstValidRequirement, secondValidRequirement, thirdValidRequirement], + requirementOperator: .ALL) + + + let content = VerifiedIdRequestContent(style: mockStyle, + requirement: mockRequirement, + rootOfTrust: mockRootOfTrust) + + let contractIssuanceRequest = ContractIssuanceRequest(content: content, + issuanceResponseContainer: mockIssuanceResponseContainer, + verifiedIdRequester: mockVCRequester, + configuration: configuration) + + // Act + let actualResult = contractIssuanceRequest.isSatisfied() + + // Assert + XCTAssert(actualResult) + } + + func testComplete_WithSuccessfulIssuance_ReturnVerifiedId() async throws { + // Arrange + let expectedVerifiedId = MockVerifiedId(id: "mockVerifiedId", issuedOn: Date()) + let mockStyle = MockRequesterStyle(requester: "mock requester") + let mockRootOfTrust = RootOfTrust(verified: true, source: "") + let mockRequirement = MockRequirement(id: "mockRequirement634") + let mockMapper = MockMapper() + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) + let mockVCRequester = MockVerifiedIdRequester(sendRequestCallback: { _ in expectedVerifiedId }) + + func mockAddRequirementCallback(requirement: Requirement) throws { + XCTAssertEqual(requirement as? MockRequirement, mockRequirement) + } + + let mockIssuanceResponseContainer = MockIssuanceResponseContainer(mockAddRequirementCallback: mockAddRequirementCallback) + + let content = VerifiedIdRequestContent(style: mockStyle, + requirement: mockRequirement, + rootOfTrust: mockRootOfTrust) + + let contractIssuanceRequest = ContractIssuanceRequest(content: content, + issuanceResponseContainer: mockIssuanceResponseContainer, + verifiedIdRequester: mockVCRequester, + configuration: configuration) + + // Act + let actualResult = await contractIssuanceRequest.complete() + + // Assert + switch (actualResult) { + case .success(let verifiedId): + XCTAssertEqual(verifiedId as? MockVerifiedId, expectedVerifiedId) + case .failure(let error): + XCTFail(error.localizedDescription) + } + } + + func testComplete_WithUnsuccessfulIssuance_ReturnsError() async throws { + // Arrange + let mockStyle = MockRequesterStyle(requester: "mock requester") + let mockRootOfTrust = RootOfTrust(verified: true, source: "") + let mockRequirement = MockRequirement(id: "mockRequirement634") + let mockMapper = MockMapper() + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) + let mockVCRequester = MockVerifiedIdRequester(sendRequestCallback: { _ in throw ExpectedError.expectedToBeThrown }) + + func mockAddRequirementCallback(requirement: Requirement) throws { + XCTAssertEqual(requirement as? MockRequirement, mockRequirement) + } + + let mockIssuanceResponseContainer = MockIssuanceResponseContainer(mockAddRequirementCallback: mockAddRequirementCallback) + + let content = VerifiedIdRequestContent(style: mockStyle, + requirement: mockRequirement, + rootOfTrust: mockRootOfTrust) + + let contractIssuanceRequest = ContractIssuanceRequest(content: content, + issuanceResponseContainer: mockIssuanceResponseContainer, + verifiedIdRequester: mockVCRequester, + configuration: configuration) + + // Act + let actualResult = await contractIssuanceRequest.complete() + + // Assert + switch (actualResult) { + case .success(_): + XCTFail("Should have thrown an error.") + case .failure(let error): + XCTAssert(error is ExpectedError) + XCTAssertEqual(error as? ExpectedError, .expectedToBeThrown) + } + } +} diff --git a/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift index 70430fa5..9964c84c 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift @@ -115,7 +115,7 @@ class OpenIdURLRequestResolverTests: XCTestCase { let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: Mapper()) let mockHandler = OpenIdRequestHandler(configuration: configuration, manifestResolver: MockManifestResolver(), - verifiableCredentialRequester: MockVerifiableCredentialRequester()) + verifiableCredentialRequester: MockVerifiedIdRequester()) let resolver = OpenIdURLRequestResolver(openIdResolver: MockOpenIdForVCResolver(), configuration: configuration) // Act From d5f0b44693fb2fb2d3efd9d14d48c8100e3b2603 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 11:52:58 -0800 Subject: [PATCH 264/373] Create VerifiableCredentialTests.swift --- .../VerifiableCredentialTests.swift | 248 ++++++++++++++++++ 1 file changed, 248 insertions(+) create mode 100644 WalletLibrary/WalletLibraryTests/VerifiedId/VerifiableCredentialTests.swift diff --git a/WalletLibrary/WalletLibraryTests/VerifiedId/VerifiableCredentialTests.swift b/WalletLibrary/WalletLibraryTests/VerifiedId/VerifiableCredentialTests.swift new file mode 100644 index 00000000..071efed0 --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/VerifiedId/VerifiableCredentialTests.swift @@ -0,0 +1,248 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import XCTest +import VCEntities +import VCToken +@testable import WalletLibrary + +class VerifiableCredentialTests: XCTestCase { + + func testInit_WithValidInput_CreatesVerifiableCredential() async throws { + // Arrange + let mockVerifiableCredential = createVCEntitiesVC() + let mockContract = createMockSignedContract() + + // Act + let actualResult = try WalletLibrary.VerifiableCredential(raw: mockVerifiableCredential, from: mockContract) + + // Assert + XCTAssertEqual(try actualResult.raw.serialize(), try mockVerifiableCredential.serialize()) + XCTAssertEqual(actualResult.contract, mockContract) + XCTAssertEqual(actualResult.expiresOn, Date(timeIntervalSince1970: 0)) + XCTAssertEqual(actualResult.issuedOn, Date(timeIntervalSince1970: 0)) + XCTAssertEqual(actualResult.id, actualResult.id) + + } + + func testInit_WithMissingJtiOnVC_ThrowsError() async throws { + // Arrange + let mockVerifiableCredential = createVCEntitiesVC(expectedJti: nil) + let mockContract = createMockSignedContract() + + // Act + XCTAssertThrowsError(try VerifiableCredential(raw: mockVerifiableCredential, from: mockContract)) { error in + // Assert + XCTAssert(error is VerifiableCredentialMappingError) + XCTAssertEqual(error as? VerifiableCredentialMappingError, .missingJtiInVerifiableCredential) + } + } + + func testInit_WithMissingIatOnVC_ThrowsError() async throws { + // Arrange + let mockVerifiableCredential = createVCEntitiesVC(expectedIat: nil) + let mockContract = createMockSignedContract() + + // Act + XCTAssertThrowsError(try VerifiableCredential(raw: mockVerifiableCredential, from: mockContract)) { error in + // Assert + XCTAssert(error is VerifiableCredentialMappingError) + XCTAssertEqual(error as? VerifiableCredentialMappingError, .missingIssuedOnValueInVerifiableCredential) + } + } + + func testInit_WithNilExpOnVC_CreatesVerifiableCredential() async throws { + // Arrange + let mockVerifiableCredential = createVCEntitiesVC(expectedExp: nil) + let mockContract = createMockSignedContract() + + // Act + let actualResult = try WalletLibrary.VerifiableCredential(raw: mockVerifiableCredential, from: mockContract) + + // Assert + XCTAssertEqual(try actualResult.raw.serialize(), try mockVerifiableCredential.serialize()) + XCTAssertEqual(actualResult.contract, mockContract) + XCTAssertNil(actualResult.expiresOn) + XCTAssertEqual(actualResult.issuedOn, Date(timeIntervalSince1970: 0)) + XCTAssertEqual(actualResult.id, actualResult.id) + } + + func testGetClaims_WithMissingCredentialSubjectInVC_ReturnsEmptyList() async throws { + // Arrange + let mockVerifiableCredential = createVCEntitiesVC() + let mockContract = createMockSignedContract() + let verifiableCredential = try VerifiableCredential(raw: mockVerifiableCredential, + from: mockContract) + + // Act + let actualResult = verifiableCredential.getClaims() + + // Assert + XCTAssert(actualResult.isEmpty) + } + + func testGetClaims_WithNoLabelsInContract_ReturnsClaims() async throws { + // Arrange + let expectedValue1 = "mockValue1" + let expectedValue2 = "mockValue2" + let expectedClaim1 = VerifiedIdClaim(id: "mockKey1", value: expectedValue1) + let expectedClaim2 = VerifiedIdClaim(id: "mockKey2", value: expectedValue2) + let mockVCClaimDictionary = ["mockKey1": expectedValue1, "mockKey2": expectedValue2] + let mockVerifiableCredential = createVCEntitiesVC(expectedClaims: mockVCClaimDictionary) + let mockContract = createMockSignedContract() + let verifiableCredential = try VerifiableCredential(raw: mockVerifiableCredential, + from: mockContract) + + // Act + let actualResult = verifiableCredential.getClaims() + + // Assert + XCTAssertEqual(actualResult.count, 2) + XCTAssert(actualResult.contains { + areClaimsEqual(result: $0, expected: expectedClaim1) + }) + XCTAssert(actualResult.contains { + areClaimsEqual(result: $0, expected: expectedClaim2) + }) + } + + func testGetClaims_WithLabelsInContract_ReturnsClaims() async throws { + // Arrange + let expectedValue1 = "mockValue1" + let mockVCClaimDictionary = ["mockKey1": expectedValue1] + let mockVerifiableCredential = createVCEntitiesVC(expectedClaims: mockVCClaimDictionary) + let expectedClaimLabel1 = ClaimDisplayDescriptor(type: "String", + label: "MockLabel1") + let expectedClaimLabels = ["vc.credentialSubject.mockKey1": expectedClaimLabel1] + let mockContract = createMockSignedContract(claims: expectedClaimLabels) + let verifiableCredential = try VerifiableCredential(raw: mockVerifiableCredential, + from: mockContract) + + // Act + let actualResult = verifiableCredential.getClaims() + + // Assert + XCTAssertEqual(actualResult.count, 1) + XCTAssert(actualResult.contains { + areClaimsEqual(result: $0, expected: VerifiedIdClaim(id: "MockLabel1", value: expectedValue1)) + }) + } + + func testGetClaims_WithOneClaimWithNoLabelInContract_ReturnsClaims() async throws { + // Arrange + let expectedValue1 = "mockValue1" + let expectedValue2 = "mockValue2" + + let mockVCClaimDictionary = ["mockKey1": expectedValue1, "mockKey2": expectedValue2] + let mockVerifiableCredential = createVCEntitiesVC(expectedClaims: mockVCClaimDictionary) + + let expectedClaimLabel1 = ClaimDisplayDescriptor(type: "String", label: "MockLabel1") + let expectedClaimLabels = ["vc.credentialSubject.mockKey1": expectedClaimLabel1] + let mockContract = createMockSignedContract(claims: expectedClaimLabels) + + let verifiableCredential = try VerifiableCredential(raw: mockVerifiableCredential, + from: mockContract) + + // Act + let actualResult = verifiableCredential.getClaims() + + // Assert + XCTAssertEqual(actualResult.count, 2) + XCTAssert(actualResult.contains { + areClaimsEqual(result: $0, expected: VerifiedIdClaim(id: "MockLabel1", value: expectedValue1)) + }) + XCTAssert(actualResult.contains { + areClaimsEqual(result: $0, expected: VerifiedIdClaim(id: "mockKey2", value: expectedValue2)) + }) + } + + func testDecode_WhenUnableToSerializeVCToken_ThrowsError() async throws { + // Arrange + let decoder = JSONDecoder() + let encoder = JSONEncoder() + let mockVC = MockVerifiableCredential(raw: "unserializableToken", contract: createMockSignedContract()) + let encodedMockVC = try encoder.encode(mockVC) + + // Act + XCTAssertThrowsError(try decoder.decode(VerifiableCredential.self, from: encodedMockVC)) { error in + // Assert + XCTAssert(error is VerifiableCredentialMappingError) + XCTAssertEqual(error as? VerifiableCredentialMappingError, .unableToDecodeRawVerifiableCredentialToken) + } + } + + func testDecode_WithValidInput_ReturnsVerifiableCredential() async throws { + // Arrange + let decoder = JSONDecoder() + let encoder = JSONEncoder() + let expectedSerializedVC = try createVCEntitiesVC().serialize() + let expectedContract = createMockSignedContract() + let mockVC = MockVerifiableCredential(raw: expectedSerializedVC, contract: expectedContract) + let encodedMockVC = try encoder.encode(mockVC) + + // Act + let actualResult = try decoder.decode(VerifiableCredential.self, from: encodedMockVC) + + // Assert + XCTAssertEqual(try actualResult.raw.serialize(), expectedSerializedVC) + XCTAssertEqual(actualResult.contract, expectedContract) + } + + func testEncode_WithValidInput_CreatesEncodedVerifiableCredential() async throws { + // Arrange + + // Act + + // Assert + } + + private func createMockSignedContract(claims: [String: ClaimDisplayDescriptor] = [:]) -> Contract { + let mockCardDisplay = CardDisplayDescriptor(title: "mock title", + issuedBy: "mock issuer", + backgroundColor: "mock background color", + textColor: "mock text color", + logo: nil, + cardDescription: "mock description") + let mockConsentDisplay = ConsentDisplayDescriptor(title: nil, + instructions: "mock purpose") + let mockDisplayDescriptor = DisplayDescriptor(id: nil, + locale: nil, + contract: nil, + card: mockCardDisplay, + consent: mockConsentDisplay, + claims: claims) + let mockContractInputDescriptor = ContractInputDescriptor(credentialIssuer: "mock credential issuer", + issuer: "mock issuer", + attestations: nil) + return Contract(id: "mockContract", + display: mockDisplayDescriptor, + input: mockContractInputDescriptor) + } + + private func createVCEntitiesVC(expectedJti: String? = "1234", + expectedIat: Double? = 0, + expectedExp: Double? = 0, + expectedClaims: [String: String] = [:]) -> VCEntities.VerifiableCredential { + let claims = VCClaims(jti: expectedJti, + iss: "", + sub: "", + iat: expectedIat, + exp: expectedExp, + vc: VerifiableCredentialDescriptor(context: [], + type: [], + credentialSubject: expectedClaims)) + return VCEntities.VerifiableCredential(headers: Header(), content: claims)! + } + + private func areClaimsEqual(result: VerifiedIdClaim, expected: VerifiedIdClaim) -> Bool { + return (result.id == expected.id) && (result.value as! String == expected.value as! String) + } +} + +struct MockVerifiableCredential: Codable { + let raw: String + + let contract: Contract +} From ebed072657f26a1584fec1be90fe6587b0b3daef Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 12:05:44 -0800 Subject: [PATCH 265/373] Fix comment in IssuanceResponseContaining. --- .../Requests/Manifest/IssuanceResponseContaining.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/IssuanceResponseContaining.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/IssuanceResponseContaining.swift index 4a982975..ffacb65f 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/IssuanceResponseContaining.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/IssuanceResponseContaining.swift @@ -5,7 +5,7 @@ /** * Protocol defines the behavior of collecting requirements to send to requester. - * For example, it is used as a wrapper to wrap the VC SDK get contract method. + * For example, it is used as a wrapper to wrap the VC SDK issuance response container. */ protocol IssuanceResponseContaining { mutating func add(requirement: Requirement) throws From ce8a7cb86aea1ed3b092087e9a28f969ec3e5f04 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 12:05:55 -0800 Subject: [PATCH 266/373] Fix naming in ContractIssuanceRequest. --- .../RequestProtocols/Manifest/ContractIssuanceRequest.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift index eb53dc80..11edd9fb 100644 --- a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift +++ b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift @@ -47,8 +47,8 @@ class ContractIssuanceRequest: VerifiedIdIssuanceRequest { public func complete() async -> Result { do { try self.responseContainer.add(requirement: requirement) - let verifiableCredential = try await verifiedIdRequester.send(request: responseContainer) - return Result.success(verifiableCredential) + let verifiedId = try await verifiedIdRequester.send(request: responseContainer) + return Result.success(verifiedId) } catch { return Result.failure(error) } From a07e0aa5f4a12cefb48a837ec0ed32825d35d4f1 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 12:06:17 -0800 Subject: [PATCH 267/373] Fix error name in VerifiableCredentialError. --- .../VerifiableCredential.swift | 14 +++----- .../PresentationRequest+MappableTests.swift | 12 +++---- .../VerifiableCredentialTests.swift | 32 +++++++------------ 3 files changed, 23 insertions(+), 35 deletions(-) diff --git a/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift b/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift index 9df70a4d..6aac2b91 100644 --- a/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift +++ b/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift @@ -5,10 +5,10 @@ import VCEntities -enum VerifiableCredentialMappingError: Error { +enum VerifiableCredentialError: Error { + case missingIssuedOnValueInVerifiableCredential case missingJtiInVerifiableCredential case unableToDecodeRawVerifiableCredentialToken - case missingIssuedOnValueInVerifiableCredential } /** @@ -30,11 +30,11 @@ struct VerifiableCredential: VerifiedId { init(raw: VCEntities.VerifiableCredential, from contract: Contract) throws { guard let issuedOn = raw.content.iat else { - throw VerifiableCredentialMappingError.missingIssuedOnValueInVerifiableCredential + throw VerifiableCredentialError.missingIssuedOnValueInVerifiableCredential } guard let id = raw.content.jti else { - throw VerifiableCredentialMappingError.missingJtiInVerifiableCredential + throw VerifiableCredentialError.missingJtiInVerifiableCredential } self.raw = raw @@ -57,7 +57,7 @@ struct VerifiableCredential: VerifiedId { let values = try decoder.container(keyedBy: CodingKeys.self) let rawToken = try values.decode(String.self, forKey: .raw) guard let raw = VCEntities.VerifiableCredential(from: rawToken) else { - throw VerifiableCredentialMappingError.unableToDecodeRawVerifiableCredentialToken + throw VerifiableCredentialError.unableToDecodeRawVerifiableCredentialToken } let contract = try values.decode(Contract.self, forKey: .contract) try self.init(raw: raw, from: contract) @@ -71,10 +71,6 @@ struct VerifiableCredential: VerifiedId { } public func getClaims() -> [VerifiedIdClaim] { - return createClaims() - } - - private func createClaims() -> [VerifiedIdClaim] { guard let vcClaims = raw.content.vc?.credentialSubject else { return [] diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+MappableTests.swift index 023162ec..39aa52d7 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+MappableTests.swift @@ -64,10 +64,10 @@ class PresentationRequestMappingTests: XCTestCase { func testMap_WithInvalidRootOfTrust_ThrowsError() throws { // Arrange let expectedVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, - required: false, - types: [], - purpose: nil, - issuanceOptions: []) + required: false, + types: [], + purpose: nil, + issuanceOptions: []) let mockPresentationDefinition = PresentationDefinition(id: nil, inputDescriptors: nil, issuance: nil) let mockRequestClaims = RequestedClaims(vpToken: RequestedVPToken(presentationDefinition: mockPresentationDefinition)) let token = createPresentationRequestToken(requestedClaims: mockRequestClaims, registration: nil) @@ -104,8 +104,8 @@ class PresentationRequestMappingTests: XCTestCase { // Arrange let expectedStyle = OpenIdVerifierStyle(name: "") let expectedVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, - required: false, - types: [], + required: false, + types: [], purpose: nil, issuanceOptions: []) let mockPresentationDefinition = PresentationDefinition(id: nil, inputDescriptors: nil, issuance: nil) diff --git a/WalletLibrary/WalletLibraryTests/VerifiedId/VerifiableCredentialTests.swift b/WalletLibrary/WalletLibraryTests/VerifiedId/VerifiableCredentialTests.swift index 071efed0..4838d5c6 100644 --- a/WalletLibrary/WalletLibraryTests/VerifiedId/VerifiableCredentialTests.swift +++ b/WalletLibrary/WalletLibraryTests/VerifiedId/VerifiableCredentialTests.swift @@ -10,6 +10,12 @@ import VCToken class VerifiableCredentialTests: XCTestCase { + struct MockVerifiableCredential: Codable { + let raw: String + + let contract: Contract + } + func testInit_WithValidInput_CreatesVerifiableCredential() async throws { // Arrange let mockVerifiableCredential = createVCEntitiesVC() @@ -35,8 +41,8 @@ class VerifiableCredentialTests: XCTestCase { // Act XCTAssertThrowsError(try VerifiableCredential(raw: mockVerifiableCredential, from: mockContract)) { error in // Assert - XCTAssert(error is VerifiableCredentialMappingError) - XCTAssertEqual(error as? VerifiableCredentialMappingError, .missingJtiInVerifiableCredential) + XCTAssert(error is VerifiableCredentialError) + XCTAssertEqual(error as? VerifiableCredentialError, .missingJtiInVerifiableCredential) } } @@ -48,8 +54,8 @@ class VerifiableCredentialTests: XCTestCase { // Act XCTAssertThrowsError(try VerifiableCredential(raw: mockVerifiableCredential, from: mockContract)) { error in // Assert - XCTAssert(error is VerifiableCredentialMappingError) - XCTAssertEqual(error as? VerifiableCredentialMappingError, .missingIssuedOnValueInVerifiableCredential) + XCTAssert(error is VerifiableCredentialError) + XCTAssertEqual(error as? VerifiableCredentialError, .missingIssuedOnValueInVerifiableCredential) } } @@ -168,8 +174,8 @@ class VerifiableCredentialTests: XCTestCase { // Act XCTAssertThrowsError(try decoder.decode(VerifiableCredential.self, from: encodedMockVC)) { error in // Assert - XCTAssert(error is VerifiableCredentialMappingError) - XCTAssertEqual(error as? VerifiableCredentialMappingError, .unableToDecodeRawVerifiableCredentialToken) + XCTAssert(error is VerifiableCredentialError) + XCTAssertEqual(error as? VerifiableCredentialError, .unableToDecodeRawVerifiableCredentialToken) } } @@ -190,14 +196,6 @@ class VerifiableCredentialTests: XCTestCase { XCTAssertEqual(actualResult.contract, expectedContract) } - func testEncode_WithValidInput_CreatesEncodedVerifiableCredential() async throws { - // Arrange - - // Act - - // Assert - } - private func createMockSignedContract(claims: [String: ClaimDisplayDescriptor] = [:]) -> Contract { let mockCardDisplay = CardDisplayDescriptor(title: "mock title", issuedBy: "mock issuer", @@ -240,9 +238,3 @@ class VerifiableCredentialTests: XCTestCase { return (result.id == expected.id) && (result.value as! String == expected.value as! String) } } - -struct MockVerifiableCredential: Codable { - let raw: String - - let contract: Contract -} From a5dd7e465c1d8f7134f2a7962ae00b564ddea8a5 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 12:27:39 -0800 Subject: [PATCH 268/373] Update VerifiedIdRequester.swift --- .../WalletLibrary/Protocols/Requests/VerifiedIdRequester.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequester.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequester.swift index 543a0646..5d5e9dfe 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequester.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/VerifiedIdRequester.swift @@ -9,6 +9,6 @@ */ protocol VerifiedIdRequester { - /// Given generic request, requests a raw Verified Id from an issuer. + /// Given generic request, requests a raw Verified Id from an issuer. func send(request: Request) async throws -> VerifiedId } From 96188519b336a02f26ef7973c73ca38701b2b54d Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 12:28:22 -0800 Subject: [PATCH 269/373] Remove VerifiedIdType. --- WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj | 4 ---- .../WalletLibrary/VerifiedId/VerifiedIdType.swift | 8 -------- 2 files changed, 12 deletions(-) delete mode 100644 WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdType.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index c2da2c96..71de2bb5 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -58,7 +58,6 @@ 553CC09D29A97181005A5FD6 /* IssuanceResponseContaining.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC09C29A97181005A5FD6 /* IssuanceResponseContaining.swift */; }; 553CC0A029A98003005A5FD6 /* VerifiableCredentialTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC09F29A98003005A5FD6 /* VerifiableCredentialTests.swift */; }; 5585BDE729A6C38C0059710B /* InjectedIdToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585BDE629A6C38C0059710B /* InjectedIdToken.swift */; }; - 5585BE0229A6D42B0059710B /* VerifiedIdType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585BE0129A6D42B0059710B /* VerifiedIdType.swift */; }; 559BD3032995993700BD61AC /* PresentationInputDescriptor+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */; }; 559BD3052995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3042995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift */; }; 559BD3072995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3062995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift */; }; @@ -349,7 +348,6 @@ 553CC09C29A97181005A5FD6 /* IssuanceResponseContaining.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssuanceResponseContaining.swift; sourceTree = ""; }; 553CC09F29A98003005A5FD6 /* VerifiableCredentialTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiableCredentialTests.swift; sourceTree = ""; }; 5585BDE629A6C38C0059710B /* InjectedIdToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InjectedIdToken.swift; sourceTree = ""; }; - 5585BE0129A6D42B0059710B /* VerifiedIdType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdType.swift; sourceTree = ""; }; 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationInputDescriptor+Mappable.swift"; sourceTree = ""; }; 559BD3042995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationDefinition+MappableTests.swift"; sourceTree = ""; }; 559BD3062995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationInputDescriptor+MappableTests.swift"; sourceTree = ""; }; @@ -966,7 +964,6 @@ 559BD4B1299EDF9E00BD61AC /* VerifiableCredential */, 55E336D3293FA75300CD2ED7 /* VerifiedId.swift */, 55E336D5293FA78C00CD2ED7 /* VerifiedIdClaim.swift */, - 5585BE0129A6D42B0059710B /* VerifiedIdType.swift */, ); path = VerifiedId; sourceTree = ""; @@ -1432,7 +1429,6 @@ 550E320E298B0FAC004E7716 /* WalletLibraryLogger.swift in Sources */, 559BD3032995993700BD61AC /* PresentationInputDescriptor+Mappable.swift in Sources */, 55E337B52948B4B000CD2ED7 /* PinRequirement.swift in Sources */, - 5585BE0229A6D42B0059710B /* VerifiedIdType.swift in Sources */, 559BD3A6299AD9BD00BD61AC /* ManifestResolver.swift in Sources */, 550A9170299310D40014D030 /* OpenIdURLRequestResolver.swift in Sources */, 5534E669294A0C3F005D0765 /* Mapper.swift in Sources */, diff --git a/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdType.swift b/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdType.swift deleted file mode 100644 index ed6506f1..00000000 --- a/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdType.swift +++ /dev/null @@ -1,8 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Microsoft Corporation. All rights reserved. -* Licensed under the MIT License. See License.txt in the project root for license information. -*--------------------------------------------------------------------------------------------*/ - -public enum VerifiedIdType { - case verifiableCredential -} From 4073ce82a3e9fb5691c5941fc68ab92a1955c7d5 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 28 Feb 2023 13:12:45 -0800 Subject: [PATCH 270/373] Update SampleViewModel naming. --- Demo/WalletLibraryDemo/WalletLibraryDemo/ContentView.swift | 2 +- .../WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift | 2 +- .../WalletLibraryDemo/WalletLibraryDemo/Views/ErrorView.swift | 2 +- .../WalletLibraryDemo/Views/RequestView.swift | 2 +- .../WalletLibraryDemo/Views/RequirementView.swift | 2 +- .../WalletLibraryDemo/Views/SuccessView.swift | 2 +- .../WalletLibrary/Requests/VerifiedIdRequestContent.swift | 4 ++-- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/ContentView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/ContentView.swift index d6c05c3f..fc2912da 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/ContentView.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/ContentView.swift @@ -10,7 +10,7 @@ import WalletLibrary struct ContentView: View { @Environment(\.managedObjectContext) private var viewContext - @StateObject private var viewModel = ViewModel() + @StateObject private var viewModel = SampleViewModel() var body: some View { NavigationView { diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift index e9db42d8..a0994dfe 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift @@ -14,7 +14,7 @@ enum SampleViewModelError: String, Error { } -@MainActor class ViewModel: ObservableObject { +@MainActor class SampleViewModel: ObservableObject { /// The requirements to be gathered by the user. @Published var requirements: [RequirementState] = [] diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/ErrorView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/ErrorView.swift index 5195cf39..f1306b06 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/ErrorView.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/ErrorView.swift @@ -7,7 +7,7 @@ import SwiftUI struct ErrorView: View { - @EnvironmentObject var viewModel: ViewModel + @EnvironmentObject var viewModel: SampleViewModel let errorMessage: String diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequestView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequestView.swift index 9962ed09..fb3b85ef 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequestView.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequestView.swift @@ -7,7 +7,7 @@ import SwiftUI struct RequestView: View { - @EnvironmentObject var viewModel: ViewModel + @EnvironmentObject var viewModel: SampleViewModel @Environment(\.dismiss) var dismiss diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequirementView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequirementView.swift index 4072cd8a..314da61c 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequirementView.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequirementView.swift @@ -7,7 +7,7 @@ import SwiftUI struct RequirementView: View { - @EnvironmentObject var viewModel: ViewModel + @EnvironmentObject var viewModel: SampleViewModel @State private var userInput: String = "" diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/SuccessView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/SuccessView.swift index 9e583ceb..be3e7b7b 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/SuccessView.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/SuccessView.swift @@ -7,7 +7,7 @@ import SwiftUI struct SuccessView: View { - @EnvironmentObject var viewModel: ViewModel + @EnvironmentObject var viewModel: SampleViewModel @Environment(\.dismiss) var dismiss diff --git a/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift b/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift index f0613d11..d86ad605 100644 --- a/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift +++ b/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift @@ -34,7 +34,7 @@ struct VerifiedIdRequestContent { case var groupRequirement as GroupRequirement: repopulateGroupRequirementIfInjectedIdTokenExists(injectedIdToken: injectedIdToken, groupRequirement: &groupRequirement) - case var idTokenRequirement as IdTokenRequirement: + case let idTokenRequirement as IdTokenRequirement: idTokenRequirement.fulfill(with: injectedIdToken.rawToken) if let pinRequirement = injectedIdToken.pin { requirement = GroupRequirement(required: false, @@ -49,7 +49,7 @@ struct VerifiedIdRequestContent { private func repopulateGroupRequirementIfInjectedIdTokenExists(injectedIdToken: InjectedIdToken, groupRequirement: inout GroupRequirement) { for requirement in groupRequirement.requirements { - if var idTokenRequirement = requirement as? IdTokenRequirement { + if let idTokenRequirement = requirement as? IdTokenRequirement { idTokenRequirement.fulfill(with: injectedIdToken.rawToken) if let pinRequirement = injectedIdToken.pin { groupRequirement.requirements.append(pinRequirement) From 3356d5ad52fe1cfc03e3ebf5c14797ba3db8508d Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 7 Mar 2023 14:09:41 -0500 Subject: [PATCH 271/373] update vc sdk git commit. --- WalletLibrary/Submodules/VerifiableCredential-SDK-iOS | 2 +- .../Requests/Handlers/OpenIdRequestHandler.swift | 4 ++-- .../Requests/Handlers/OpenIdRequestHandlerTests.swift | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS b/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS index ad27b0f3..e1207f1c 160000 --- a/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS +++ b/WalletLibrary/Submodules/VerifiableCredential-SDK-iOS @@ -1 +1 @@ -Subproject commit ad27b0f39b5f34dc2c9a47852d7abcd6810607b6 +Subproject commit e1207f1c559e1751f4a3c1034d6b1802d70b5451 diff --git a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift index 115ca7ba..f9f56a1e 100644 --- a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift +++ b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift @@ -8,7 +8,7 @@ import VCEntities enum OpenIdRequestHandlerError: Error { case unsupportedRawRequestType case noIssuanceOptionsPresentToCreateIssuanceRequest - case unableToCaseRequirementToVerifiedIdRequirement + case unableToCastRequirementToVerifiedIdRequirement } /** @@ -51,7 +51,7 @@ struct OpenIdRequestHandler: RequestHandling { private func handleIssuanceRequest(from requestContent: VerifiedIdRequestContent) async throws -> any VerifiedIdIssuanceRequest { guard let verifiedIdRequirement = requestContent.requirement as? VerifiedIdRequirement else { - throw OpenIdRequestHandlerError.unableToCaseRequirementToVerifiedIdRequirement + throw OpenIdRequestHandlerError.unableToCastRequirementToVerifiedIdRequirement } guard let issuanceOption = verifiedIdRequirement.issuanceOptions.first as? VerifiedIdRequestURL else { diff --git a/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift index f5d006f0..e4759e92 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift @@ -113,7 +113,7 @@ class OpenIdRequestHandlerTests: XCTestCase { } catch { // Assert XCTAssert(error is OpenIdRequestHandlerError) - XCTAssertEqual(error as? OpenIdRequestHandlerError, OpenIdRequestHandlerError.unableToCaseRequirementToVerifiedIdRequirement) + XCTAssertEqual(error as? OpenIdRequestHandlerError, OpenIdRequestHandlerError.unableToCastRequirementToVerifiedIdRequirement) } } From 7d15b5768919943be79c00dd363b3989427e484a Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 7 Mar 2023 14:11:32 -0500 Subject: [PATCH 272/373] Remove Verified Id Type. --- WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj | 4 ---- .../WalletLibrary/VerifiedId/VerifiedIdType.swift | 8 -------- 2 files changed, 12 deletions(-) delete mode 100644 WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdType.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index c2da2c96..71de2bb5 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -58,7 +58,6 @@ 553CC09D29A97181005A5FD6 /* IssuanceResponseContaining.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC09C29A97181005A5FD6 /* IssuanceResponseContaining.swift */; }; 553CC0A029A98003005A5FD6 /* VerifiableCredentialTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC09F29A98003005A5FD6 /* VerifiableCredentialTests.swift */; }; 5585BDE729A6C38C0059710B /* InjectedIdToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585BDE629A6C38C0059710B /* InjectedIdToken.swift */; }; - 5585BE0229A6D42B0059710B /* VerifiedIdType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585BE0129A6D42B0059710B /* VerifiedIdType.swift */; }; 559BD3032995993700BD61AC /* PresentationInputDescriptor+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */; }; 559BD3052995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3042995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift */; }; 559BD3072995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3062995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift */; }; @@ -349,7 +348,6 @@ 553CC09C29A97181005A5FD6 /* IssuanceResponseContaining.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssuanceResponseContaining.swift; sourceTree = ""; }; 553CC09F29A98003005A5FD6 /* VerifiableCredentialTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiableCredentialTests.swift; sourceTree = ""; }; 5585BDE629A6C38C0059710B /* InjectedIdToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InjectedIdToken.swift; sourceTree = ""; }; - 5585BE0129A6D42B0059710B /* VerifiedIdType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdType.swift; sourceTree = ""; }; 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationInputDescriptor+Mappable.swift"; sourceTree = ""; }; 559BD3042995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationDefinition+MappableTests.swift"; sourceTree = ""; }; 559BD3062995ABD300BD61AC /* PresentationInputDescriptor+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationInputDescriptor+MappableTests.swift"; sourceTree = ""; }; @@ -966,7 +964,6 @@ 559BD4B1299EDF9E00BD61AC /* VerifiableCredential */, 55E336D3293FA75300CD2ED7 /* VerifiedId.swift */, 55E336D5293FA78C00CD2ED7 /* VerifiedIdClaim.swift */, - 5585BE0129A6D42B0059710B /* VerifiedIdType.swift */, ); path = VerifiedId; sourceTree = ""; @@ -1432,7 +1429,6 @@ 550E320E298B0FAC004E7716 /* WalletLibraryLogger.swift in Sources */, 559BD3032995993700BD61AC /* PresentationInputDescriptor+Mappable.swift in Sources */, 55E337B52948B4B000CD2ED7 /* PinRequirement.swift in Sources */, - 5585BE0229A6D42B0059710B /* VerifiedIdType.swift in Sources */, 559BD3A6299AD9BD00BD61AC /* ManifestResolver.swift in Sources */, 550A9170299310D40014D030 /* OpenIdURLRequestResolver.swift in Sources */, 5534E669294A0C3F005D0765 /* Mapper.swift in Sources */, diff --git a/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdType.swift b/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdType.swift deleted file mode 100644 index ed6506f1..00000000 --- a/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdType.swift +++ /dev/null @@ -1,8 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Microsoft Corporation. All rights reserved. -* Licensed under the MIT License. See License.txt in the project root for license information. -*--------------------------------------------------------------------------------------------*/ - -public enum VerifiedIdType { - case verifiableCredential -} From 109a4a5b8ca5041b946442ede705df12f869de91 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 15:26:44 -0500 Subject: [PATCH 273/373] Create extension for PresentationResponseContainer. --- .../WalletLibrary.xcodeproj/project.pbxproj | 8 +++ ...ationResponseContainer+WalletLibrary.swift | 67 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 WalletLibrary/WalletLibrary/Extensions/Entities/PresentationResponseContainer+WalletLibrary.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 71de2bb5..a22e85ab 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -57,6 +57,7 @@ 553CC09B29A96ECD005A5FD6 /* MockIssuanceResponseContaining.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC09A29A96ECD005A5FD6 /* MockIssuanceResponseContaining.swift */; }; 553CC09D29A97181005A5FD6 /* IssuanceResponseContaining.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC09C29A97181005A5FD6 /* IssuanceResponseContaining.swift */; }; 553CC0A029A98003005A5FD6 /* VerifiableCredentialTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC09F29A98003005A5FD6 /* VerifiableCredentialTests.swift */; }; + 55520FF129B8DAAA00092DE6 /* PresentationResponseContainer+WalletLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55520FF029B8DAAA00092DE6 /* PresentationResponseContainer+WalletLibrary.swift */; }; 5585BDE729A6C38C0059710B /* InjectedIdToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585BDE629A6C38C0059710B /* InjectedIdToken.swift */; }; 559BD3032995993700BD61AC /* PresentationInputDescriptor+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */; }; 559BD3052995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3042995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift */; }; @@ -100,6 +101,7 @@ 55A81C0D2991BF86002C259A /* MockInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81C0C2991BF86002C259A /* MockInput.swift */; }; 55A81C102991C211002C259A /* MockResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81C0F2991C211002C259A /* MockResolver.swift */; }; 55A81C132991C829002C259A /* MockHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81C122991C829002C259A /* MockHandler.swift */; }; + 55DECBD029B922D200D5C802 /* MockPresentationResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECBCF29B922D200D5C802 /* MockPresentationResponder.swift */; }; 55E2F06C299553300008010D /* PresentationRequest+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */; }; 55E2F06F299553B40008010D /* OpenIdRequestHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F06E299553B40008010D /* OpenIdRequestHandlerTests.swift */; }; 55E2F071299554110008010D /* LibraryConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F070299554110008010D /* LibraryConfiguration.swift */; }; @@ -347,6 +349,7 @@ 553CC09A29A96ECD005A5FD6 /* MockIssuanceResponseContaining.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockIssuanceResponseContaining.swift; sourceTree = ""; }; 553CC09C29A97181005A5FD6 /* IssuanceResponseContaining.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssuanceResponseContaining.swift; sourceTree = ""; }; 553CC09F29A98003005A5FD6 /* VerifiableCredentialTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiableCredentialTests.swift; sourceTree = ""; }; + 55520FF029B8DAAA00092DE6 /* PresentationResponseContainer+WalletLibrary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationResponseContainer+WalletLibrary.swift"; sourceTree = ""; }; 5585BDE629A6C38C0059710B /* InjectedIdToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InjectedIdToken.swift; sourceTree = ""; }; 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationInputDescriptor+Mappable.swift"; sourceTree = ""; }; 559BD3042995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationDefinition+MappableTests.swift"; sourceTree = ""; }; @@ -390,6 +393,7 @@ 55A81C0C2991BF86002C259A /* MockInput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockInput.swift; sourceTree = ""; }; 55A81C0F2991C211002C259A /* MockResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockResolver.swift; sourceTree = ""; }; 55A81C122991C829002C259A /* MockHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockHandler.swift; sourceTree = ""; }; + 55DECBCF29B922D200D5C802 /* MockPresentationResponder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockPresentationResponder.swift; sourceTree = ""; }; 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PresentationRequest+Mappable.swift"; sourceTree = ""; }; 55E2F06E299553B40008010D /* OpenIdRequestHandlerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenIdRequestHandlerTests.swift; sourceTree = ""; }; 55E2F070299554110008010D /* LibraryConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryConfiguration.swift; sourceTree = ""; }; @@ -491,6 +495,7 @@ 550A919A29940E9E0014D030 /* PresentationRequest+OpenIdRawRequest.swift */, 559BD3AC299AE6C000BD61AC /* IssuanceRequest+RawManifest.swift */, 559BD49B299D913F00BD61AC /* IssuanceResponseContainer+WalletLibrary.swift */, + 55520FF029B8DAAA00092DE6 /* PresentationResponseContainer+WalletLibrary.swift */, ); path = Entities; sourceTree = ""; @@ -843,6 +848,7 @@ 559BD3102996D3CE00BD61AC /* MockVerifiedIdRequest.swift */, 559BD3AE299AF42C00BD61AC /* MockManifestResolver.swift */, 559BD4F4299EEC9500BD61AC /* MockVerifiedIdRequester.swift */, + 55DECBCF29B922D200D5C802 /* MockPresentationResponder.swift */, ); path = Requests; sourceTree = ""; @@ -1421,6 +1427,7 @@ 553CC09029A955E6005A5FD6 /* PinDescriptor+Mappable.swift in Sources */, 55E337652943749600CD2ED7 /* AsyncWrapper.swift in Sources */, 550E3214298B11A5004E7716 /* Requirement.swift in Sources */, + 55520FF129B8DAAA00092DE6 /* PresentationResponseContainer+WalletLibrary.swift in Sources */, 559BD453299D3DD800BD61AC /* IssuanceRequest+Mappable.swift in Sources */, 550A9173299311A10014D030 /* VerifiedIdRequestURL.swift in Sources */, 550E3210298B113C004E7716 /* VerifiedIdRequest.swift in Sources */, @@ -1486,6 +1493,7 @@ 559BD4E5299EEA3000BD61AC /* IssuanceResponseContainerExtensionTests.swift in Sources */, 55A81C092991BB3A002C259A /* RequestHandlerFactoryTests.swift in Sources */, 559BD30D2996C12500BD61AC /* VerifiedIdClientBuilderTests.swift in Sources */, + 55DECBD029B922D200D5C802 /* MockPresentationResponder.swift in Sources */, 550A91952994025C0014D030 /* OpenIdURLRequestResolverTests.swift in Sources */, 559BD3052995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift in Sources */, 55A81C0D2991BF86002C259A /* MockInput.swift in Sources */, diff --git a/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationResponseContainer+WalletLibrary.swift b/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationResponseContainer+WalletLibrary.swift new file mode 100644 index 00000000..ebc56d1b --- /dev/null +++ b/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationResponseContainer+WalletLibrary.swift @@ -0,0 +1,67 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import VCEntities + +enum PresentationResponseError: Error, Equatable { + case unableToCastVCSDKPresentationRequestFromRawRequestOfType(String) + case unsupportedRequirementOfType(String) + case unableToCastVerifiedIdToVerifiableCredentialOfType(String) + case missingIdInVerifiedIdRequirement +} + +/** + * An extension of the VCEntities.PresentationResponseContainer class + * to convert Requirements to mappings in PresentationResponseContainer. + */ +extension VCEntities.PresentationResponseContainer: PresentationResponse { + + init(from request: any OpenIdRawRequest) throws { + + guard let request = request as? PresentationRequest else { + let requestType = String(describing: type(of: request)) + throw PresentationResponseError.unableToCastVCSDKPresentationRequestFromRawRequestOfType(requestType) + } + + try self.init(from: request) + } + + mutating func add(requirement: Requirement) throws { + switch (requirement) { + case let groupRequirement as GroupRequirement: + try add(groupRequirement: groupRequirement) + case let verifiedIdRequirement as VerifiedIdRequirement: + try add(verifiedIdRequirement: verifiedIdRequirement) + default: + let requirementType = String(describing: type(of: requirement)) + throw PresentationResponseError.unsupportedRequirementOfType(requirementType) + } + } + + private mutating func add(groupRequirement: GroupRequirement) throws { + try groupRequirement.validate() + for requirement in groupRequirement.requirements { + try add(requirement: requirement) + } + } + + private mutating func add(verifiedIdRequirement: VerifiedIdRequirement) throws { + try verifiedIdRequirement.validate() + + guard let verifiableCredential = verifiedIdRequirement.selectedVerifiedId as? WalletLibrary.VerifiableCredential else { + let verifiedIdType = String(describing: type(of: verifiedIdRequirement.selectedVerifiedId)) + throw PresentationResponseError.unableToCastVerifiedIdToVerifiableCredentialOfType(verifiedIdType) + } + + guard let id = verifiedIdRequirement.id else { + throw PresentationResponseError.missingIdInVerifiedIdRequirement + } + + let mapping = RequestedVerifiableCredentialMapping(id: id, + verifiableCredential: verifiableCredential.raw) + + self.requestVCMap.append(mapping) + } +} From 3207ca60ad99d909b734e00cefcec8686779b956 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 15:26:54 -0500 Subject: [PATCH 274/373] Update PresentationRequest+OpenIdRawRequest.swift --- .../PresentationRequest+OpenIdRawRequest.swift | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationRequest+OpenIdRawRequest.swift b/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationRequest+OpenIdRawRequest.swift index 33373b6d..01187e35 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationRequest+OpenIdRawRequest.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationRequest+OpenIdRawRequest.swift @@ -22,14 +22,4 @@ extension VCEntities.PresentationRequest: OpenIdRawRequest { return .Presentation } - - /// The raw representation of the request. - var raw: Data? { - do { - let serializedToken = try self.token.serialize() - return serializedToken.data(using: .utf8) - } catch { - return nil - } - } } From ee78dc7cf52b797f43b2fe8c3088fb9502c724b1 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 15:27:03 -0500 Subject: [PATCH 275/373] Update IssuanceRequest+Mappable.swift --- .../VCSDK/Issuance/IssuanceRequest+Mappable.swift | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+Mappable.swift index 1478f703..cc496758 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+Mappable.swift @@ -9,8 +9,8 @@ import VCEntities * An extension of the VCEntities.IssuanceRequest class. * TODO: Update Style to include VerifiedIdStyle and more requester style attributes. */ -extension VCEntities.IssuanceRequest: Mappable { - func map(using mapper: Mapping) throws -> VerifiedIdRequestContent { +extension VCEntities.IssuanceRequest: RawRequest, Mappable { + func map(using mapper: Mapping) throws -> IssuanceRequestContent { let attestations = try getRequiredProperty(property: content.input.attestations, propertyName: "attestations") @@ -18,8 +18,9 @@ extension VCEntities.IssuanceRequest: Mappable { let rootOfTrust = try mapper.map(linkedDomainResult) let issuerStyle = Manifest2022IssuerStyle(name: content.display.card.issuedBy) - return VerifiedIdRequestContent(style: issuerStyle, - requirement: requirement, - rootOfTrust: rootOfTrust) + return IssuanceRequestContent(style: issuerStyle, + requirement: requirement, + rootOfTrust: rootOfTrust, + raw: self) } } From 0acf33cf961380e61a1e4e3a47f4b33a79f31348 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 15:27:07 -0500 Subject: [PATCH 276/373] Update PresentationDescriptor+Mappable.swift --- .../VCSDK/Issuance/PresentationDescriptor+Mappable.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+Mappable.swift index 4939ed84..a0600a58 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+Mappable.swift @@ -22,7 +22,8 @@ extension VCEntities.PresentationDescriptor: Mappable { return nil } - return VerifiedIdRequirement(encrypted: encrypted ?? false, + return VerifiedIdRequirement(id: nil, + encrypted: encrypted ?? false, required: presentationRequired ?? false, types: [credentialType], purpose: nil, From 7455a0908892e6e5814a4e87b678c698c0e9efbd Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 15:27:38 -0500 Subject: [PATCH 277/373] Add id to VerifiedIdRequirement --- ...PresentationInputDescriptor+Mappable.swift | 5 +++- .../Requirements/VerifiedIdRequirement.swift | 26 ++++++++++++++----- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+Mappable.swift index ead5a6c2..70c1261c 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+Mappable.swift @@ -20,6 +20,8 @@ extension VCEntities.PresentationInputDescriptor: Mappable { func map(using mapper: Mapping) throws -> VerifiedIdRequirement { + let id = try getRequiredProperty(property: id, propertyName: "Input Descriptor Id") + guard let types = schema?.compactMap({ $0.uri }), !types.isEmpty else { throw PresentationInputDescriptorMappingError.noVerifiedIdTypeInPresentationInputDescriptor @@ -35,7 +37,8 @@ extension VCEntities.PresentationInputDescriptor: Mappable { return nil } - return VerifiedIdRequirement(encrypted: false, + return VerifiedIdRequirement(id: id, + encrypted: false, required: true, types: types, purpose: purpose, diff --git a/WalletLibrary/WalletLibrary/Requests/Requirements/VerifiedIdRequirement.swift b/WalletLibrary/WalletLibrary/Requests/Requirements/VerifiedIdRequirement.swift index 96b97c3c..82f2d6c7 100644 --- a/WalletLibrary/WalletLibrary/Requests/Requirements/VerifiedIdRequirement.swift +++ b/WalletLibrary/WalletLibrary/Requests/Requirements/VerifiedIdRequirement.swift @@ -11,10 +11,13 @@ public class VerifiedIdRequirement: Requirement { /// If requirement must be encrypted. let encrypted: Bool + /// Input descriptor id from the presentation request tied to verifiable credential requested. + let id: String? + /// If the requirement is required or not. public let required: Bool - /// The type of the verified Id required. + /// The types of the verified Id required. public let types: [String] /// The purpose for the verified Id, developer can display to the user if desired. @@ -23,16 +26,16 @@ public class VerifiedIdRequirement: Requirement { /// An optional property for information needed for issuance during presentation flow. public let issuanceOptions: [VerifiedIdRequestInput] - /// TODO: helper method that returns verified id that match the requirement from a list of verified ids. - public func getMatches(verifiedIds: [VerifiedId]) -> [VerifiedId] { - return [] - } + /// The verified id that fulfills the requirement. + var selectedVerifiedId: VerifiedId? - init(encrypted: Bool, + init(id: String? = nil, + encrypted: Bool, required: Bool, types: [String], purpose: String?, issuanceOptions: [VerifiedIdRequestInput]) { + self.id = id self.encrypted = encrypted self.required = required self.types = types @@ -40,6 +43,17 @@ public class VerifiedIdRequirement: Requirement { self.issuanceOptions = issuanceOptions } + /// TODO: helper method that returns verified id that match the requirement from a list of verified ids. + public func getMatches(verifiedIds: [VerifiedId]) -> [VerifiedId] { + return [] + } + + /// Fulfill requirement with a verified id. + /// TODO: do some validation to make sure verified id actually fulfills. + public func fulfill(with verifiedId: VerifiedId) { + selectedVerifiedId = verifiedId + } + public func validate() throws { throw VerifiedIdClientError.TODO(message: "implement validate") } From 13a3fa1ff44fcc91dee7fb4f404d7d2a20111670 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 15:27:49 -0500 Subject: [PATCH 278/373] Update PresentationRequest+Mappable.swift --- .../Presentation/PresentationRequest+Mappable.swift | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+Mappable.swift index c819637b..1d158f3f 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+Mappable.swift @@ -18,7 +18,7 @@ enum PresentationRequestMappingError: Error { */ extension VCEntities.PresentationRequest: Mappable { - func map(using mapper: Mapping) throws -> VerifiedIdRequestContent { + func map(using mapper: Mapping) throws -> PresentationRequestContent { guard let presentationDefinition = content.claims?.vpToken?.presentationDefinition else { throw PresentationRequestMappingError.presentationDefinitionMissingInRequest @@ -31,10 +31,11 @@ extension VCEntities.PresentationRequest: Mappable { let clientName = content.registration?.clientName ?? "" let requesterStyle = OpenIdVerifierStyle(name: clientName) - let content = VerifiedIdRequestContent(style: requesterStyle, - requirement: requirement, - rootOfTrust: rootOfTrust, - injectedIdToken: injectedIdToken) + let content = PresentationRequestContent(style: requesterStyle, + requirement: requirement, + rootOfTrust: rootOfTrust, + raw: self, + injectedIdToken: injectedIdToken) return content } From 75071a52defa3ed6ecefba1c4be42624246e0ccc Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 15:27:56 -0500 Subject: [PATCH 279/373] Update PresentationService+OpenIdForVC.swift --- .../PresentationService+OpenIdForVC.swift | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Services/PresentationService+OpenIdForVC.swift b/WalletLibrary/WalletLibrary/Extensions/Services/PresentationService+OpenIdForVC.swift index 8f4feab2..ebeecfec 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Services/PresentationService+OpenIdForVC.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Services/PresentationService+OpenIdForVC.swift @@ -6,6 +6,10 @@ import VCEntities import VCServices +enum PresentationServiceError: Error { + case unableToCastOpenIdForVCResponseToPresentationResponseContainer +} + /** * An extension of the VCServices.PresentationService class. */ @@ -20,9 +24,20 @@ extension PresentationService: OpenIdForVCResolver, OpenIdForVCResponder { /// Sends the presentation response container and if successful, returns void, /// If unsuccessful, throws an error. - func send(response: VCEntities.PresentationResponseContainer) async throws -> Void { + func send(response: PresentationResponse) async throws -> Void { + + guard let presentationResponseContainer = response as? PresentationResponseContainer else { + throw PresentationServiceError.unableToCastOpenIdForVCResponseToPresentationResponseContainer + } + let _ = try await AsyncWrapper().wrap { () in - self.send(response: response) + self.send(response: presentationResponseContainer) }() } } + +protocol PresentationResponse { + mutating func add(requirement: Requirement) throws +} + + From 9230730de294d55289529fe6bbff2148ef072967 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 15:28:02 -0500 Subject: [PATCH 280/373] Update OpenIdForVCResponder.swift --- .../Protocols/Requests/OpenIdForVC/OpenIdForVCResponder.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/OpenIdForVC/OpenIdForVCResponder.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/OpenIdForVC/OpenIdForVCResponder.swift index f2babc79..759fe52e 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/OpenIdForVC/OpenIdForVCResponder.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/OpenIdForVC/OpenIdForVCResponder.swift @@ -3,13 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import VCEntities - /** * Protocol is used as a wrapper to wrap the VC SDK send presentation response method. */ protocol OpenIdForVCResponder { /// Sends the presentation response container and if successful, returns void, /// If unsuccessful, throws an error. - func send(response: PresentationResponseContainer) async throws -> Void + func send(response: PresentationResponse) async throws -> Void } From ee29efcb79c09abcbe2339e782fe99fde7e913d4 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 15:28:22 -0500 Subject: [PATCH 281/373] Make OpenIdRawRequest conform to RawRequest. --- .../Protocols/Requests/RawRequests/OpenIdRawRequest.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/RawRequests/OpenIdRawRequest.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/RawRequests/OpenIdRawRequest.swift index 0c274831..21de262a 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/RawRequests/OpenIdRawRequest.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/RawRequests/OpenIdRawRequest.swift @@ -7,9 +7,7 @@ * Representation of a Raw Open Id Request. * Object that conforms to this protocol must be able to map to VerifiedIdRequestContent. */ -protocol OpenIdRawRequest: Mappable where T == VerifiedIdRequestContent { +protocol OpenIdRawRequest: RawRequest, Mappable where T == PresentationRequestContent { var type: RequestType { get } - - var raw: Data? { get } } From 7c0e7004ed4f9fc420158d1f1a3fa8471c0b787c Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 15:28:50 -0500 Subject: [PATCH 282/373] Make RawManifest conform to RawRequest. --- .../Protocols/Requests/RawRequests/RawManifest.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/RawRequests/RawManifest.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/RawRequests/RawManifest.swift index 405cb341..5e644b6d 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/RawRequests/RawManifest.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/RawRequests/RawManifest.swift @@ -7,5 +7,5 @@ * Representation of a Raw Contract. * Object that conforms to this protocol must be able to map to VerifiedIdRequestContent. */ -protocol RawManifest: Mappable where T == VerifiedIdRequestContent {} +protocol RawManifest: RawRequest, Mappable where T == IssuanceRequestContent {} From caf5d81be290f79ea977c3dfc14ec7fc176ee11c Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 15:29:06 -0500 Subject: [PATCH 283/373] Update OpenIdRequestHandler.swift --- .../Requests/Handlers/OpenIdRequestHandler.swift | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift index f9f56a1e..4379d8e5 100644 --- a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift +++ b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift @@ -19,15 +19,19 @@ struct OpenIdRequestHandler: RequestHandling { private let configuration: LibraryConfiguration + private let presentationRequestResponder: OpenIdForVCResponder + private let manifestResolver: ManifestResolver private let verifiableCredentialRequester: VerifiedIdRequester /// TODO: post private preview, manifest resolving and verified id requester will be handled by processors. init(configuration: LibraryConfiguration, + presentationRequestResponder: OpenIdForVCResponder, manifestResolver: ManifestResolver, verifiableCredentialRequester: VerifiedIdRequester) { self.configuration = configuration + self.presentationRequestResponder = presentationRequestResponder self.manifestResolver = manifestResolver self.verifiableCredentialRequester = verifiableCredentialRequester } @@ -48,7 +52,7 @@ struct OpenIdRequestHandler: RequestHandling { return try handlePresentationRequest(from: requestContent) } - private func handleIssuanceRequest(from requestContent: VerifiedIdRequestContent) async throws -> any VerifiedIdIssuanceRequest { + private func handleIssuanceRequest(from requestContent: PresentationRequestContent) async throws -> any VerifiedIdIssuanceRequest { guard let verifiedIdRequirement = requestContent.requirement as? VerifiedIdRequirement else { throw OpenIdRequestHandlerError.unableToCastRequirementToVerifiedIdRequirement @@ -73,7 +77,9 @@ struct OpenIdRequestHandler: RequestHandling { configuration: configuration) } - private func handlePresentationRequest(from requestContent: VerifiedIdRequestContent) throws -> any VerifiedIdPresentationRequest { - return OpenIdPresentationRequest(content: requestContent, configuration: configuration) + private func handlePresentationRequest(from requestContent: PresentationRequestContent) throws -> any VerifiedIdPresentationRequest { + return try OpenIdPresentationRequest(content: requestContent, + openIdResponder: presentationRequestResponder, + configuration: configuration) } } From e8e8a598164e6a305dccc83461f3ff7e2b018a1b Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 15:29:12 -0500 Subject: [PATCH 284/373] Update ContractIssuanceRequest.swift --- .../RequestProtocols/Manifest/ContractIssuanceRequest.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift index 11edd9fb..97e6c596 100644 --- a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift +++ b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift @@ -23,7 +23,7 @@ class ContractIssuanceRequest: VerifiedIdIssuanceRequest { private var responseContainer: IssuanceResponseContaining - init(content: VerifiedIdRequestContent, + init(content: IssuanceRequestContent, issuanceResponseContainer: IssuanceResponseContaining, verifiedIdRequester: VerifiedIdRequester, configuration: LibraryConfiguration) { From a320735af89420cf62b73592840d47fdb49eb882 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 15:29:33 -0500 Subject: [PATCH 285/373] Implement complete method for openIdPresentationRequest. --- .../OpenId/OpenIdPresentationRequest.swift | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdPresentationRequest.swift b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdPresentationRequest.swift index d5781e56..d5317db2 100644 --- a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdPresentationRequest.swift +++ b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdPresentationRequest.swift @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import VCEntities + /** * Presentation Requst that is Open Id specific. * TODO: we will need open id specific data to implement complete and cancel. @@ -15,12 +17,25 @@ class OpenIdPresentationRequest: VerifiedIdPresentationRequest { let rootOfTrust: RootOfTrust + private let raw: any OpenIdRawRequest + + private let responder: OpenIdForVCResponder + private let configuration: LibraryConfiguration - init(content: VerifiedIdRequestContent, configuration: LibraryConfiguration) { + init(content: PresentationRequestContent, + openIdResponder: OpenIdForVCResponder, + configuration: LibraryConfiguration) throws { + + guard let raw = content.raw as? any OpenIdRawRequest else { + throw VerifiedIdClientError.TODO(message: "test") + } + self.style = content.style self.requirement = content.requirement self.rootOfTrust = content.rootOfTrust + self.raw = raw + self.responder = openIdResponder self.configuration = configuration } @@ -30,7 +45,15 @@ class OpenIdPresentationRequest: VerifiedIdPresentationRequest { } func complete() async -> Result<(), Error> { - return Result.failure(VerifiedIdClientError.TODO(message: "implement")) + do { + + var response = try PresentationResponseContainer(from: raw) + try response.add(requirement: requirement) + try await responder.send(response: response) + return Result.success(()) + } catch { + return Result.failure(error) + } } func cancel(message: String?) -> Result { From 743e61cd6035ca4eb435de340b634a0844b2ecd2 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 15:29:36 -0500 Subject: [PATCH 286/373] Update VerifiedIdRequestContent.swift --- .../Requests/VerifiedIdRequestContent.swift | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift b/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift index d86ad605..0311ea28 100644 --- a/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift +++ b/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift @@ -9,7 +9,7 @@ * TODO: make this object extensible by separating Presentation with Issuance esp. for InjectedIdToken logic. * TODO: add VerifiedIdStyle to Issuance Content. */ -struct VerifiedIdRequestContent { +struct PresentationRequestContent { let style: RequesterStyle @@ -19,15 +19,30 @@ struct VerifiedIdRequestContent { let injectedIdToken: InjectedIdToken? + let raw: RawRequest + init(style: RequesterStyle, requirement: Requirement, rootOfTrust: RootOfTrust, + raw: RawRequest, injectedIdToken: InjectedIdToken? = nil) { self.style = style self.requirement = requirement self.rootOfTrust = rootOfTrust + self.raw = raw self.injectedIdToken = injectedIdToken } +} + +struct IssuanceRequestContent: RequestContent { + + let style: RequesterStyle + + var requirement: Requirement + + let rootOfTrust: RootOfTrust + + let raw: RawRequest mutating func addRequirement(from injectedIdToken: InjectedIdToken) { switch (requirement) { @@ -57,4 +72,21 @@ struct VerifiedIdRequestContent { } } } + +} + +protocol RawRequest { + +} + + +protocol RequestContent { + + var style: RequesterStyle { get } + + var requirement: Requirement { get } + + var rootOfTrust: RootOfTrust { get } + + var raw: RawRequest { get } } From 913a19b1d215046b79accb10f4b6b7458371ac8d Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 15:29:41 -0500 Subject: [PATCH 287/373] Update VerifiedIdClientBuilder.swift --- WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift b/WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift index 369d8d2e..d6f63d1d 100644 --- a/WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift +++ b/WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift @@ -51,7 +51,9 @@ public class VerifiedIdClientBuilder { private func registerSupportedRequestHandlers(with configuration: LibraryConfiguration) { let issuanceService = IssuanceService() + let presentationService = PresentationService() let openIdHandler = OpenIdRequestHandler(configuration: configuration, + presentationRequestResponder: presentationService, manifestResolver: issuanceService, verifiableCredentialRequester: issuanceService) requestHandlers.append(openIdHandler) From 0e23abeaa35149e2288dc3f50197e18c3d1d7c3e Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 15:29:57 -0500 Subject: [PATCH 288/373] Update tests to include id. --- .../AttestationsDescriptor+MappableTests.swift | 9 ++++++--- .../PresentationDefinition+MappableTests.swift | 11 ++++++----- ...entationInputDescriptor+MappableTests.swift | 18 +++++++++--------- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+MappableTests.swift index 2b127ef9..192558b1 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+MappableTests.swift @@ -122,7 +122,8 @@ class AttestationsDescriptorMappingTests: XCTestCase { func testMap_WithOnlyOneVerifiedIdRequirement_ReturnsRequirement() throws { // Arrange - let expectedVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, + let expectedVerifiedIdRequirement = VerifiedIdRequirement(id: "", + encrypted: false, required: true, types: [], purpose: nil, @@ -227,7 +228,8 @@ class AttestationsDescriptorMappingTests: XCTestCase { let expectedSelfAttestedClaimRequirement = SelfAttestedClaimRequirement(encrypted: false, required: true, claim: "mock claim") - let expectedVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, + let expectedVerifiedIdRequirement = VerifiedIdRequirement(id: "", + encrypted: false, required: true, types: [], purpose: nil, @@ -295,7 +297,8 @@ class AttestationsDescriptorMappingTests: XCTestCase { let expectedSelfAttestedClaimRequirement = SelfAttestedClaimRequirement(encrypted: false, required: true, claim: "mock claim") - let expectedVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, + let expectedVerifiedIdRequirement = VerifiedIdRequirement(id: "", + encrypted: false, required: true, types: [], purpose: nil, diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+MappableTests.swift index 48ba189d..4b0de116 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+MappableTests.swift @@ -44,11 +44,12 @@ class PresentationDefinitionMappingTests: XCTestCase { func testMap_WithOneInputDescriptorPresent_ReturnsVerifiedIdRequirement() throws { // Arrange - let expectedVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, - required: false, - types: [], - purpose: nil, - issuanceOptions: []) + let expectedVerifiedIdRequirement = VerifiedIdRequirement(id: "", + encrypted: false, + required: false, + types: [], + purpose: nil, + issuanceOptions: []) let inputDescriptor = PresentationInputDescriptor(id: nil, schema: nil, issuanceMetadata: nil, diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+MappableTests.swift index 08d00649..715c68a5 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+MappableTests.swift @@ -12,7 +12,7 @@ class PresentationInputDescriptorMappingTests: XCTestCase { func testMap_WithNilSchema_ThrowsError() throws { // Arrange let mockMapper = MockMapper() - let presentationInputDescriptor = PresentationInputDescriptor(id: nil, + let presentationInputDescriptor = PresentationInputDescriptor(id: "mockId", schema: nil, issuanceMetadata: nil, name: nil, @@ -31,7 +31,7 @@ class PresentationInputDescriptorMappingTests: XCTestCase { // Arrange let mockMapper = MockMapper() let schema = InputDescriptorSchema(uri: nil) - let presentationInputDescriptor = PresentationInputDescriptor(id: nil, + let presentationInputDescriptor = PresentationInputDescriptor(id: "mockId", schema: [schema], issuanceMetadata: nil, name: nil, @@ -49,7 +49,7 @@ class PresentationInputDescriptorMappingTests: XCTestCase { func testMap_WithNoVerifiedIdTypesPresent_ThrowsError() throws { // Arrange let mockMapper = MockMapper() - let presentationInputDescriptor = PresentationInputDescriptor(id: nil, + let presentationInputDescriptor = PresentationInputDescriptor(id: "mockId", schema: [], issuanceMetadata: nil, name: nil, @@ -73,7 +73,7 @@ class PresentationInputDescriptorMappingTests: XCTestCase { types: ["mockType"], purpose: nil, issuanceOptions: []) - let presentationInputDescriptor = PresentationInputDescriptor(id: nil, + let presentationInputDescriptor = PresentationInputDescriptor(id: "mockId", schema: [mockSchema], issuanceMetadata: nil, name: nil, @@ -105,7 +105,7 @@ class PresentationInputDescriptorMappingTests: XCTestCase { types: [firstType, secondType, thirdType], purpose: nil, issuanceOptions: []) - let presentationInputDescriptor = PresentationInputDescriptor(id: nil, + let presentationInputDescriptor = PresentationInputDescriptor(id: "mockId", schema: [firstSchema, secondSchema, thirdSchema], issuanceMetadata: nil, name: nil, @@ -132,7 +132,7 @@ class PresentationInputDescriptorMappingTests: XCTestCase { types: ["mockType"], purpose: nil, issuanceOptions: []) - let presentationInputDescriptor = PresentationInputDescriptor(id: nil, + let presentationInputDescriptor = PresentationInputDescriptor(id: "mockId", schema: [mockSchema], issuanceMetadata: nil, name: nil, @@ -161,7 +161,7 @@ class PresentationInputDescriptorMappingTests: XCTestCase { types: ["mockType"], purpose: nil, issuanceOptions: []) - let presentationInputDescriptor = PresentationInputDescriptor(id: nil, + let presentationInputDescriptor = PresentationInputDescriptor(id: "mockId", schema: [mockSchema], issuanceMetadata: [invalidIssuanceMetadata], name: nil, @@ -201,7 +201,7 @@ class PresentationInputDescriptorMappingTests: XCTestCase { issuanceOptions: [firstVerifiedIdRequestURL, secondVerifiedIdRequestURL]) let issuanceMetadatas = [firstValidIssuanceMetadata, invalidIssuanceMetadata, secondValidIssuanceMetadata] - let presentationInputDescriptor = PresentationInputDescriptor(id: nil, + let presentationInputDescriptor = PresentationInputDescriptor(id: "mockId", schema: [mockSchema], issuanceMetadata: issuanceMetadatas, name: nil, @@ -229,7 +229,7 @@ class PresentationInputDescriptorMappingTests: XCTestCase { types: ["mockType"], purpose: purpose, issuanceOptions: []) - let presentationInputDescriptor = PresentationInputDescriptor(id: nil, + let presentationInputDescriptor = PresentationInputDescriptor(id: "mockId", schema: [mockSchema], issuanceMetadata: nil, name: nil, From 0137bd1feeab8faa8caa5c3a2ec1e36d85324e6a Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 15:30:02 -0500 Subject: [PATCH 289/373] Update MockRawManifest.swift --- .../Mocks/Requests/Manifest/MockRawManifest.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/Manifest/MockRawManifest.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/Manifest/MockRawManifest.swift index 6f29330f..fd3fa948 100644 --- a/WalletLibrary/WalletLibraryTests/Mocks/Requests/Manifest/MockRawManifest.swift +++ b/WalletLibrary/WalletLibraryTests/Mocks/Requests/Manifest/MockRawManifest.swift @@ -13,7 +13,7 @@ struct MockRawManifest: RawManifest, Equatable { self.id = id } - func map(using mapper: Mapping) throws -> VerifiedIdRequestContent { + func map(using mapper: Mapping) throws -> IssuanceRequestContent { throw VerifiedIdClientError.TODO(message: "implement") } } From 3874a36f0bf0854bcb586b49a218942cfe5c1fce Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 15:30:08 -0500 Subject: [PATCH 290/373] Create MockPresentationResponder.swift --- .../Mocks/Requests/MockPresentationResponder.swift | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 WalletLibrary/WalletLibraryTests/Mocks/Requests/MockPresentationResponder.swift diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockPresentationResponder.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockPresentationResponder.swift new file mode 100644 index 00000000..499d6cbd --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockPresentationResponder.swift @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +@testable import WalletLibrary + +class MockPresentationResponder: OpenIdForVCResponder { + + func send(response: PresentationResponse) async throws { } +} From dabbe298daa60de2377c5ff0ac5d8e83bfc2e00b Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 15:30:21 -0500 Subject: [PATCH 291/373] Update MockRawRequest.swift --- .../WalletLibraryTests/Mocks/Requests/MockRawRequest.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockRawRequest.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockRawRequest.swift index c5aa1cd1..dafb9daf 100644 --- a/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockRawRequest.swift +++ b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockRawRequest.swift @@ -3,7 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -struct MockRawRequest { +@testable import WalletLibrary + +struct MockRawRequest: RawRequest { let raw: String From 063c046cc74fa2f0b3e7d33173b69bd28ce30ca9 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 15:30:29 -0500 Subject: [PATCH 292/373] Update MockOpenIdRawRequest.swift --- .../Mocks/Requests/OpenIdForVC/MockOpenIdRawRequest.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/OpenIdForVC/MockOpenIdRawRequest.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/OpenIdForVC/MockOpenIdRawRequest.swift index 25a4e26b..b922d100 100644 --- a/WalletLibrary/WalletLibraryTests/Mocks/Requests/OpenIdForVC/MockOpenIdRawRequest.swift +++ b/WalletLibrary/WalletLibraryTests/Mocks/Requests/OpenIdForVC/MockOpenIdRawRequest.swift @@ -16,7 +16,7 @@ struct MockOpenIdRawRequest: OpenIdRawRequest, Equatable { self.type = type } - func map(using mapper: Mapping) throws -> VerifiedIdRequestContent { + func map(using mapper: Mapping) throws -> PresentationRequestContent { throw VerifiedIdClientError.TODO(message: "not implemented") } } From a8a744b3749b476ca01011fc450f9749d5f016bd Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 15:30:35 -0500 Subject: [PATCH 293/373] Update MockResolver.swift --- .../Mocks/Requests/Resolvers/MockResolver.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/Resolvers/MockResolver.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/Resolvers/MockResolver.swift index f3811596..7c48adcc 100644 --- a/WalletLibrary/WalletLibraryTests/Mocks/Requests/Resolvers/MockResolver.swift +++ b/WalletLibrary/WalletLibraryTests/Mocks/Requests/Resolvers/MockResolver.swift @@ -33,7 +33,7 @@ class MockResolver: RequestResolving { canResolveUsingInput } - func resolve(input: VerifiedIdRequestInput) async throws -> MockRawRequest { + func resolve(input: VerifiedIdRequestInput) async throws -> RawRequest { guard let mockResolve = mockResolve else { throw MockResolverError.nilMockResolveMethod From 6a13c557f2791818cb7c522081b26bbd9556fbeb Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 15:30:43 -0500 Subject: [PATCH 294/373] Update tests. --- .../Handlers/OpenIdRequestHandlerTests.swift | 81 ++++++++++++------- .../ContractIssuanceRequestTests.swift | 56 +++++++------ .../OpenIdURLRequestResolverTests.swift | 1 + 3 files changed, 84 insertions(+), 54 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift index e4759e92..357c151d 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift @@ -22,9 +22,10 @@ class OpenIdRequestHandlerTests: XCTestCase { let expectedStyle = MockRequesterStyle(requester: "mock requester") let expectedRequirement = MockRequirement(id: "mockRequirement324") let expectedRootOfTrust = RootOfTrust(verified: true, source: "mock source") - let expectedContent = VerifiedIdRequestContent(style: expectedStyle, - requirement: expectedRequirement, - rootOfTrust: expectedRootOfTrust) + let expectedContent = PresentationRequestContent(style: expectedStyle, + requirement: expectedRequirement, + rootOfTrust: expectedRootOfTrust, + raw: MockOpenIdRawRequest(raw: nil)) func mockResults(objectToBeMapped: Any) throws -> Any? { if objectToBeMapped is MockOpenIdRawRequest { @@ -38,6 +39,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockRawRequest = MockOpenIdRawRequest(raw: Data()) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, + presentationRequestResponder: MockPresentationResponder(), manifestResolver: MockManifestResolver(), verifiableCredentialRequester: MockVerifiedIdRequester()) @@ -67,6 +69,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockRawRequest = MockOpenIdRawRequest(raw: Data()) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, + presentationRequestResponder: MockPresentationResponder(), manifestResolver: MockManifestResolver(), verifiableCredentialRequester: MockVerifiedIdRequester()) @@ -87,9 +90,10 @@ class OpenIdRequestHandlerTests: XCTestCase { let expectedStyle = MockRequesterStyle(requester: "mock requester") let expectedRequirement = MockRequirement(id: "test") let expectedRootOfTrust = RootOfTrust(verified: true, source: "mock source") - let expectedContent = VerifiedIdRequestContent(style: expectedStyle, - requirement: expectedRequirement, - rootOfTrust: expectedRootOfTrust) + let expectedContent = PresentationRequestContent(style: expectedStyle, + requirement: expectedRequirement, + rootOfTrust: expectedRootOfTrust, + raw: MockOpenIdRawRequest(raw: nil)) func mockResults(objectToBeMapped: Any) throws -> Any? { if objectToBeMapped is MockOpenIdRawRequest { @@ -103,6 +107,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, + presentationRequestResponder: MockPresentationResponder(), manifestResolver: MockManifestResolver(), verifiableCredentialRequester: MockVerifiedIdRequester()) @@ -127,9 +132,10 @@ class OpenIdRequestHandlerTests: XCTestCase { purpose: nil, issuanceOptions: []) let expectedRootOfTrust = RootOfTrust(verified: true, source: "mock source") - let expectedContent = VerifiedIdRequestContent(style: expectedStyle, - requirement: expectedRequirement, - rootOfTrust: expectedRootOfTrust) + let expectedContent = PresentationRequestContent(style: expectedStyle, + requirement: expectedRequirement, + rootOfTrust: expectedRootOfTrust, + raw: MockOpenIdRawRequest(raw: nil)) func mockResults(objectToBeMapped: Any) throws -> Any? { if objectToBeMapped is MockOpenIdRawRequest { @@ -143,6 +149,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, + presentationRequestResponder: MockPresentationResponder(), manifestResolver: MockManifestResolver(), verifiableCredentialRequester: MockVerifiedIdRequester()) @@ -167,9 +174,10 @@ class OpenIdRequestHandlerTests: XCTestCase { purpose: nil, issuanceOptions: [MockInput(mockData: "")]) let expectedRootOfTrust = RootOfTrust(verified: true, source: "mock source") - let expectedContent = VerifiedIdRequestContent(style: expectedStyle, - requirement: expectedRequirement, - rootOfTrust: expectedRootOfTrust) + let expectedContent = PresentationRequestContent(style: expectedStyle, + requirement: expectedRequirement, + rootOfTrust: expectedRootOfTrust, + raw: MockOpenIdRawRequest(raw: nil)) func mockResults(objectToBeMapped: Any) throws -> Any? { if objectToBeMapped is MockOpenIdRawRequest { @@ -183,6 +191,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, + presentationRequestResponder: MockPresentationResponder(), manifestResolver: MockManifestResolver(), verifiableCredentialRequester: MockVerifiedIdRequester()) @@ -208,9 +217,10 @@ class OpenIdRequestHandlerTests: XCTestCase { purpose: nil, issuanceOptions: [VerifiedIdRequestURL(url: issuanceOptionURL)]) let expectedRootOfTrust = RootOfTrust(verified: true, source: "mock source") - let expectedContent = VerifiedIdRequestContent(style: expectedStyle, - requirement: expectedRequirement, - rootOfTrust: expectedRootOfTrust) + let expectedContent = PresentationRequestContent(style: expectedStyle, + requirement: expectedRequirement, + rootOfTrust: expectedRootOfTrust, + raw: MockOpenIdRawRequest(raw: nil)) func mockResults(objectToBeMapped: Any) throws -> Any? { if objectToBeMapped is MockOpenIdRawRequest { @@ -228,6 +238,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, + presentationRequestResponder: MockPresentationResponder(), manifestResolver: MockManifestResolver(mockGetRequestCallback: mockResolveContract), verifiableCredentialRequester: MockVerifiedIdRequester()) @@ -253,9 +264,10 @@ class OpenIdRequestHandlerTests: XCTestCase { purpose: nil, issuanceOptions: [VerifiedIdRequestURL(url: issuanceOptionURL)]) let expectedRootOfTrust = RootOfTrust(verified: true, source: "mock source") - let expectedContent = VerifiedIdRequestContent(style: expectedStyle, - requirement: expectedRequirement, - rootOfTrust: expectedRootOfTrust) + let expectedContent = PresentationRequestContent(style: expectedStyle, + requirement: expectedRequirement, + rootOfTrust: expectedRootOfTrust, + raw: MockOpenIdRawRequest(raw: nil)) func mockResults(objectToBeMapped: Any) throws -> Any? { if objectToBeMapped is MockOpenIdRawRequest { @@ -277,6 +289,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, + presentationRequestResponder: MockPresentationResponder(), manifestResolver: MockManifestResolver(mockGetRequestCallback: mockResolveContract), verifiableCredentialRequester: MockVerifiedIdRequester()) @@ -302,16 +315,18 @@ class OpenIdRequestHandlerTests: XCTestCase { purpose: nil, issuanceOptions: [VerifiedIdRequestURL(url: issuanceOptionURL)]) let expectedPresentationRootOfTrust = RootOfTrust(verified: true, source: "mock source") - let expectedPresentationContent = VerifiedIdRequestContent(style: expectedPresentationStyle, - requirement: expectedPresentationRequirement, - rootOfTrust: expectedPresentationRootOfTrust) + let expectedPresentationContent = PresentationRequestContent(style: expectedPresentationStyle, + requirement: expectedPresentationRequirement, + rootOfTrust: expectedPresentationRootOfTrust, + raw: MockOpenIdRawRequest(raw: nil)) let expectedIssuanceStyle = MockRequesterStyle(requester: "mock issuer") let expectedIssuanceRequirement = MockRequirement(id: "mockRequirement23535") let expectedIssuanceRootOfTrust = RootOfTrust(verified: true, source: "mock issuer source") - let expectedIssuanceContent = VerifiedIdRequestContent(style: expectedIssuanceStyle, - requirement: expectedIssuanceRequirement, - rootOfTrust: expectedIssuanceRootOfTrust) + let expectedIssuanceContent = IssuanceRequestContent(style: expectedIssuanceStyle, + requirement: expectedIssuanceRequirement, + rootOfTrust: expectedIssuanceRootOfTrust, + raw: MockOpenIdRawRequest(raw: nil)) func mockResults(objectToBeMapped: Any) throws -> Any? { if objectToBeMapped is MockOpenIdRawRequest { @@ -333,6 +348,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, + presentationRequestResponder: MockPresentationResponder(), manifestResolver: MockManifestResolver(mockGetRequestCallback: mockResolveContract), verifiableCredentialRequester: MockVerifiedIdRequester()) @@ -359,17 +375,19 @@ class OpenIdRequestHandlerTests: XCTestCase { issuanceOptions: [VerifiedIdRequestURL(url: issuanceOptionURL)]) let expectedPresentationRootOfTrust = RootOfTrust(verified: true, source: "mock source") let expectedInjectedIdToken = InjectedIdToken(rawToken: "mock idToken hint", pin: nil) - let expectedPresentationContent = VerifiedIdRequestContent(style: expectedPresentationStyle, - requirement: expectedPresentationRequirement, - rootOfTrust: expectedPresentationRootOfTrust, - injectedIdToken: expectedInjectedIdToken) + let expectedPresentationContent = PresentationRequestContent(style: expectedPresentationStyle, + requirement: expectedPresentationRequirement, + rootOfTrust: expectedPresentationRootOfTrust, + raw: MockOpenIdRawRequest(raw: nil), + injectedIdToken: expectedInjectedIdToken) let expectedIssuanceStyle = MockRequesterStyle(requester: "mock issuer") let expectedIssuanceRequirement = MockRequirement(id: "mockRequirement23535") let expectedIssuanceRootOfTrust = RootOfTrust(verified: true, source: "mock issuer source") - let expectedIssuanceContent = VerifiedIdRequestContent(style: expectedIssuanceStyle, - requirement: expectedIssuanceRequirement, - rootOfTrust: expectedIssuanceRootOfTrust) + let expectedIssuanceContent = IssuanceRequestContent(style: expectedIssuanceStyle, + requirement: expectedIssuanceRequirement, + rootOfTrust: expectedIssuanceRootOfTrust, + raw: createMockIssuanceRequest()) func mockResults(objectToBeMapped: Any) throws -> Any? { if objectToBeMapped is MockOpenIdRawRequest { @@ -391,6 +409,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, + presentationRequestResponder: MockPresentationResponder(), manifestResolver: MockManifestResolver(mockGetRequestCallback: mockResolveContract), verifiableCredentialRequester: MockVerifiedIdRequester()) diff --git a/WalletLibrary/WalletLibraryTests/Requests/RequestProtocols/Manifest/ContractIssuanceRequestTests.swift b/WalletLibrary/WalletLibraryTests/Requests/RequestProtocols/Manifest/ContractIssuanceRequestTests.swift index 9f337ccc..d459d5c3 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/RequestProtocols/Manifest/ContractIssuanceRequestTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/RequestProtocols/Manifest/ContractIssuanceRequestTests.swift @@ -21,13 +21,15 @@ class ContractIssuanceRequestTests: XCTestCase { let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let mockVCRequester = MockVerifiedIdRequester() let mockIssuanceResponseContainer = MockIssuanceResponseContainer() + let mockRawManifest = MockRawManifest(id: "") let mockRequirement = MockRequirement(id: "mockRequirement324", mockValidateCallback: { throw ExpectedError.expectedToBeThrown }) - let content = VerifiedIdRequestContent(style: mockStyle, - requirement: mockRequirement, - rootOfTrust: mockRootOfTrust) + let content = IssuanceRequestContent(style: mockStyle, + requirement: mockRequirement, + rootOfTrust: mockRootOfTrust, + raw: mockRawManifest) let contractIssuanceRequest = ContractIssuanceRequest(content: content, issuanceResponseContainer: mockIssuanceResponseContainer, @@ -49,6 +51,7 @@ class ContractIssuanceRequestTests: XCTestCase { let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let mockVCRequester = MockVerifiedIdRequester() let mockIssuanceResponseContainer = MockIssuanceResponseContainer() + let mockRawManifest = MockRawManifest(id: "") let invalidRequirement = MockRequirement(id: "mockRequirement324", mockValidateCallback: { throw ExpectedError.expectedToBeThrown }) @@ -57,11 +60,12 @@ class ContractIssuanceRequestTests: XCTestCase { let mockRequirement = GroupRequirement(required: true, requirements: [firstValidRequirement, invalidRequirement, secondValidRequirement], requirementOperator: .ALL) - - let content = VerifiedIdRequestContent(style: mockStyle, - requirement: mockRequirement, - rootOfTrust: mockRootOfTrust) + + let content = IssuanceRequestContent(style: mockStyle, + requirement: mockRequirement, + rootOfTrust: mockRootOfTrust, + raw: mockRawManifest) let contractIssuanceRequest = ContractIssuanceRequest(content: content, issuanceResponseContainer: mockIssuanceResponseContainer, @@ -83,13 +87,14 @@ class ContractIssuanceRequestTests: XCTestCase { let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let mockVCRequester = MockVerifiedIdRequester() let mockIssuanceResponseContainer = MockIssuanceResponseContainer() + let mockRawManifest = MockRawManifest(id: "") let validRequirement = MockRequirement(id: "mockRequirement324") - - let content = VerifiedIdRequestContent(style: mockStyle, - requirement: validRequirement, - rootOfTrust: mockRootOfTrust) + let content = IssuanceRequestContent(style: mockStyle, + requirement: validRequirement, + rootOfTrust: mockRootOfTrust, + raw: mockRawManifest) let contractIssuanceRequest = ContractIssuanceRequest(content: content, issuanceResponseContainer: mockIssuanceResponseContainer, @@ -111,6 +116,7 @@ class ContractIssuanceRequestTests: XCTestCase { let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let mockVCRequester = MockVerifiedIdRequester() let mockIssuanceResponseContainer = MockIssuanceResponseContainer() + let mockRawManifest = MockRawManifest(id: "") let firstValidRequirement = MockRequirement(id: "mockRequirement634") let secondValidRequirement = MockRequirement(id: "mockRequirement674") @@ -118,11 +124,11 @@ class ContractIssuanceRequestTests: XCTestCase { let mockRequirement = GroupRequirement(required: true, requirements: [firstValidRequirement, secondValidRequirement, thirdValidRequirement], requirementOperator: .ALL) - - let content = VerifiedIdRequestContent(style: mockStyle, - requirement: mockRequirement, - rootOfTrust: mockRootOfTrust) + let content = IssuanceRequestContent(style: mockStyle, + requirement: mockRequirement, + rootOfTrust: mockRootOfTrust, + raw: mockRawManifest) let contractIssuanceRequest = ContractIssuanceRequest(content: content, issuanceResponseContainer: mockIssuanceResponseContainer, @@ -145,16 +151,18 @@ class ContractIssuanceRequestTests: XCTestCase { let mockMapper = MockMapper() let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let mockVCRequester = MockVerifiedIdRequester(sendRequestCallback: { _ in expectedVerifiedId }) + let mockRawManifest = MockRawManifest(id: "") func mockAddRequirementCallback(requirement: Requirement) throws { XCTAssertEqual(requirement as? MockRequirement, mockRequirement) } let mockIssuanceResponseContainer = MockIssuanceResponseContainer(mockAddRequirementCallback: mockAddRequirementCallback) - - let content = VerifiedIdRequestContent(style: mockStyle, - requirement: mockRequirement, - rootOfTrust: mockRootOfTrust) + + let content = IssuanceRequestContent(style: mockStyle, + requirement: mockRequirement, + rootOfTrust: mockRootOfTrust, + raw: mockRawManifest) let contractIssuanceRequest = ContractIssuanceRequest(content: content, issuanceResponseContainer: mockIssuanceResponseContainer, @@ -181,16 +189,18 @@ class ContractIssuanceRequestTests: XCTestCase { let mockMapper = MockMapper() let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let mockVCRequester = MockVerifiedIdRequester(sendRequestCallback: { _ in throw ExpectedError.expectedToBeThrown }) + let mockRawManifest = MockRawManifest(id: "") func mockAddRequirementCallback(requirement: Requirement) throws { XCTAssertEqual(requirement as? MockRequirement, mockRequirement) } let mockIssuanceResponseContainer = MockIssuanceResponseContainer(mockAddRequirementCallback: mockAddRequirementCallback) - - let content = VerifiedIdRequestContent(style: mockStyle, - requirement: mockRequirement, - rootOfTrust: mockRootOfTrust) + + let content = IssuanceRequestContent(style: mockStyle, + requirement: mockRequirement, + rootOfTrust: mockRootOfTrust, + raw: mockRawManifest) let contractIssuanceRequest = ContractIssuanceRequest(content: content, issuanceResponseContainer: mockIssuanceResponseContainer, diff --git a/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift index 9964c84c..b3c4c6eb 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift @@ -114,6 +114,7 @@ class OpenIdURLRequestResolverTests: XCTestCase { // Arrange let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: Mapper()) let mockHandler = OpenIdRequestHandler(configuration: configuration, + presentationRequestResponder: MockPresentationResponder(), manifestResolver: MockManifestResolver(), verifiableCredentialRequester: MockVerifiedIdRequester()) let resolver = OpenIdURLRequestResolver(openIdResolver: MockOpenIdForVCResolver(), configuration: configuration) From 4cced2fec9f3b3ecfa6f710fbbe1b33c23e85c6d Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 15:54:31 -0500 Subject: [PATCH 295/373] Remove raw value idea from content. --- .../Mappings/VCSDK/Issuance/IssuanceRequest+Mappable.swift | 3 +-- .../VCSDK/Presentation/PresentationRequest+Mappable.swift | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+Mappable.swift index cc496758..62f2b250 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+Mappable.swift @@ -20,7 +20,6 @@ extension VCEntities.IssuanceRequest: RawRequest, Mappable { return IssuanceRequestContent(style: issuerStyle, requirement: requirement, - rootOfTrust: rootOfTrust, - raw: self) + rootOfTrust: rootOfTrust) } } diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+Mappable.swift index 1d158f3f..2b2a8b77 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+Mappable.swift @@ -34,7 +34,6 @@ extension VCEntities.PresentationRequest: Mappable { let content = PresentationRequestContent(style: requesterStyle, requirement: requirement, rootOfTrust: rootOfTrust, - raw: self, injectedIdToken: injectedIdToken) return content From 0d9533a090745136bd5e0e31ca2f03c26b59989f Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 15:54:50 -0500 Subject: [PATCH 296/373] Update PresentationService+OpenIdForVC.swift --- .../Services/PresentationService+OpenIdForVC.swift | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Services/PresentationService+OpenIdForVC.swift b/WalletLibrary/WalletLibrary/Extensions/Services/PresentationService+OpenIdForVC.swift index ebeecfec..ffca0a6b 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Services/PresentationService+OpenIdForVC.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Services/PresentationService+OpenIdForVC.swift @@ -13,7 +13,7 @@ enum PresentationServiceError: Error { /** * An extension of the VCServices.PresentationService class. */ -extension PresentationService: OpenIdForVCResolver, OpenIdForVCResponder { +extension PresentationService: OpenIdForVCResolver, OpenIdResponder { /// Fetches and validates the presentation request. func getRequest(url: String) async throws -> any OpenIdRawRequest { @@ -35,9 +35,3 @@ extension PresentationService: OpenIdForVCResolver, OpenIdForVCResponder { }() } } - -protocol PresentationResponse { - mutating func add(requirement: Requirement) throws -} - - From eaa579a76e40336433b87d94a506ba0bf2b1250e Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 15:55:02 -0500 Subject: [PATCH 297/373] Rename to OpenIdResponder. --- .../{OpenIdForVCResponder.swift => OpenIdResponder.swift} | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) rename WalletLibrary/WalletLibrary/Protocols/Requests/OpenIdForVC/{OpenIdForVCResponder.swift => OpenIdResponder.swift} (93%) diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/OpenIdForVC/OpenIdForVCResponder.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/OpenIdForVC/OpenIdResponder.swift similarity index 93% rename from WalletLibrary/WalletLibrary/Protocols/Requests/OpenIdForVC/OpenIdForVCResponder.swift rename to WalletLibrary/WalletLibrary/Protocols/Requests/OpenIdForVC/OpenIdResponder.swift index 759fe52e..dcca73dd 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/OpenIdForVC/OpenIdForVCResponder.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/OpenIdForVC/OpenIdResponder.swift @@ -3,10 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import VCEntities + /** * Protocol is used as a wrapper to wrap the VC SDK send presentation response method. */ -protocol OpenIdForVCResponder { +protocol OpenIdResponder { /// Sends the presentation response container and if successful, returns void, /// If unsuccessful, throws an error. func send(response: PresentationResponse) async throws -> Void From e9b3601a7d299ca0f444db23baaccc56c84c5a90 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 15:55:09 -0500 Subject: [PATCH 298/373] Update OpenIdRequestHandler.swift --- .../Requests/Handlers/OpenIdRequestHandler.swift | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift index 4379d8e5..6983c957 100644 --- a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift +++ b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift @@ -19,7 +19,7 @@ struct OpenIdRequestHandler: RequestHandling { private let configuration: LibraryConfiguration - private let presentationRequestResponder: OpenIdForVCResponder + private let presentationRequestResponder: OpenIdResponder private let manifestResolver: ManifestResolver @@ -27,7 +27,7 @@ struct OpenIdRequestHandler: RequestHandling { /// TODO: post private preview, manifest resolving and verified id requester will be handled by processors. init(configuration: LibraryConfiguration, - presentationRequestResponder: OpenIdForVCResponder, + presentationRequestResponder: OpenIdResponder, manifestResolver: ManifestResolver, verifiableCredentialRequester: VerifiedIdRequester) { self.configuration = configuration @@ -49,7 +49,7 @@ struct OpenIdRequestHandler: RequestHandling { return try await handleIssuanceRequest(from: requestContent) } - return try handlePresentationRequest(from: requestContent) + return handlePresentationRequest(requestContent: requestContent, rawRequest: request) } private func handleIssuanceRequest(from requestContent: PresentationRequestContent) async throws -> any VerifiedIdIssuanceRequest { @@ -77,9 +77,11 @@ struct OpenIdRequestHandler: RequestHandling { configuration: configuration) } - private func handlePresentationRequest(from requestContent: PresentationRequestContent) throws -> any VerifiedIdPresentationRequest { - return try OpenIdPresentationRequest(content: requestContent, - openIdResponder: presentationRequestResponder, - configuration: configuration) + private func handlePresentationRequest(requestContent: PresentationRequestContent, + rawRequest: any OpenIdRawRequest) -> any VerifiedIdPresentationRequest { + return OpenIdPresentationRequest(content: requestContent, + rawRequest: rawRequest, + openIdResponder: presentationRequestResponder, + configuration: configuration) } } From d30f24fb85a8935e1c9bfbbe06ae54186d0ea4fb Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 15:55:27 -0500 Subject: [PATCH 299/373] Inject raw value into OpenIdPresentationRequest. --- .../OpenId/OpenIdPresentationRequest.swift | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdPresentationRequest.swift b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdPresentationRequest.swift index d5317db2..8ee0cf4e 100644 --- a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdPresentationRequest.swift +++ b/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdPresentationRequest.swift @@ -17,24 +17,21 @@ class OpenIdPresentationRequest: VerifiedIdPresentationRequest { let rootOfTrust: RootOfTrust - private let raw: any OpenIdRawRequest + private let rawRequest: any OpenIdRawRequest - private let responder: OpenIdForVCResponder + private let responder: OpenIdResponder private let configuration: LibraryConfiguration init(content: PresentationRequestContent, - openIdResponder: OpenIdForVCResponder, - configuration: LibraryConfiguration) throws { - - guard let raw = content.raw as? any OpenIdRawRequest else { - throw VerifiedIdClientError.TODO(message: "test") - } + rawRequest: any OpenIdRawRequest, + openIdResponder: OpenIdResponder, + configuration: LibraryConfiguration) { self.style = content.style self.requirement = content.requirement self.rootOfTrust = content.rootOfTrust - self.raw = raw + self.rawRequest = rawRequest self.responder = openIdResponder self.configuration = configuration } @@ -47,7 +44,7 @@ class OpenIdPresentationRequest: VerifiedIdPresentationRequest { func complete() async -> Result<(), Error> { do { - var response = try PresentationResponseContainer(from: raw) + var response = try PresentationResponseContainer(from: rawRequest) try response.add(requirement: requirement) try await responder.send(response: response) return Result.success(()) From 95d1c94508c0167e248d438d01b7f2c9246731e5 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 15:55:43 -0500 Subject: [PATCH 300/373] Remove raw value in content idea. --- .../WalletLibrary/Requests/VerifiedIdRequestContent.swift | 8 -------- 1 file changed, 8 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift b/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift index 0311ea28..a98099c5 100644 --- a/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift +++ b/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift @@ -19,17 +19,13 @@ struct PresentationRequestContent { let injectedIdToken: InjectedIdToken? - let raw: RawRequest - init(style: RequesterStyle, requirement: Requirement, rootOfTrust: RootOfTrust, - raw: RawRequest, injectedIdToken: InjectedIdToken? = nil) { self.style = style self.requirement = requirement self.rootOfTrust = rootOfTrust - self.raw = raw self.injectedIdToken = injectedIdToken } } @@ -42,8 +38,6 @@ struct IssuanceRequestContent: RequestContent { let rootOfTrust: RootOfTrust - let raw: RawRequest - mutating func addRequirement(from injectedIdToken: InjectedIdToken) { switch (requirement) { case var groupRequirement as GroupRequirement: @@ -87,6 +81,4 @@ protocol RequestContent { var requirement: Requirement { get } var rootOfTrust: RootOfTrust { get } - - var raw: RawRequest { get } } From 3c53dc16cfb7cc37fce3423bc56e4890e939e1df Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 15:55:53 -0500 Subject: [PATCH 301/373] fix tests. --- ...PresentationDefinition+MappableTests.swift | 3 +- .../Requests/MockPresentationResponder.swift | 2 +- .../Handlers/OpenIdRequestHandlerTests.swift | 30 +++++++------------ .../ContractIssuanceRequestTests.swift | 24 ++++----------- 4 files changed, 19 insertions(+), 40 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+MappableTests.swift index 4b0de116..ef7c0e92 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+MappableTests.swift @@ -81,7 +81,8 @@ class PresentationDefinitionMappingTests: XCTestCase { func testMap_WithMultipleInputDescriptorPresent_ReturnsGroupRequirement() throws { // Arrange - let mockVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, + let mockVerifiedIdRequirement = VerifiedIdRequirement(id: "mockId", + encrypted: false, required: false, types: [], purpose: nil, diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockPresentationResponder.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockPresentationResponder.swift index 499d6cbd..7949e1ad 100644 --- a/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockPresentationResponder.swift +++ b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockPresentationResponder.swift @@ -5,7 +5,7 @@ @testable import WalletLibrary -class MockPresentationResponder: OpenIdForVCResponder { +class MockPresentationResponder: OpenIdResponder { func send(response: PresentationResponse) async throws { } } diff --git a/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift index 357c151d..545f880d 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift @@ -24,8 +24,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let expectedRootOfTrust = RootOfTrust(verified: true, source: "mock source") let expectedContent = PresentationRequestContent(style: expectedStyle, requirement: expectedRequirement, - rootOfTrust: expectedRootOfTrust, - raw: MockOpenIdRawRequest(raw: nil)) + rootOfTrust: expectedRootOfTrust) func mockResults(objectToBeMapped: Any) throws -> Any? { if objectToBeMapped is MockOpenIdRawRequest { @@ -92,8 +91,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let expectedRootOfTrust = RootOfTrust(verified: true, source: "mock source") let expectedContent = PresentationRequestContent(style: expectedStyle, requirement: expectedRequirement, - rootOfTrust: expectedRootOfTrust, - raw: MockOpenIdRawRequest(raw: nil)) + rootOfTrust: expectedRootOfTrust) func mockResults(objectToBeMapped: Any) throws -> Any? { if objectToBeMapped is MockOpenIdRawRequest { @@ -134,8 +132,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let expectedRootOfTrust = RootOfTrust(verified: true, source: "mock source") let expectedContent = PresentationRequestContent(style: expectedStyle, requirement: expectedRequirement, - rootOfTrust: expectedRootOfTrust, - raw: MockOpenIdRawRequest(raw: nil)) + rootOfTrust: expectedRootOfTrust) func mockResults(objectToBeMapped: Any) throws -> Any? { if objectToBeMapped is MockOpenIdRawRequest { @@ -176,8 +173,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let expectedRootOfTrust = RootOfTrust(verified: true, source: "mock source") let expectedContent = PresentationRequestContent(style: expectedStyle, requirement: expectedRequirement, - rootOfTrust: expectedRootOfTrust, - raw: MockOpenIdRawRequest(raw: nil)) + rootOfTrust: expectedRootOfTrust) func mockResults(objectToBeMapped: Any) throws -> Any? { if objectToBeMapped is MockOpenIdRawRequest { @@ -219,8 +215,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let expectedRootOfTrust = RootOfTrust(verified: true, source: "mock source") let expectedContent = PresentationRequestContent(style: expectedStyle, requirement: expectedRequirement, - rootOfTrust: expectedRootOfTrust, - raw: MockOpenIdRawRequest(raw: nil)) + rootOfTrust: expectedRootOfTrust) func mockResults(objectToBeMapped: Any) throws -> Any? { if objectToBeMapped is MockOpenIdRawRequest { @@ -266,8 +261,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let expectedRootOfTrust = RootOfTrust(verified: true, source: "mock source") let expectedContent = PresentationRequestContent(style: expectedStyle, requirement: expectedRequirement, - rootOfTrust: expectedRootOfTrust, - raw: MockOpenIdRawRequest(raw: nil)) + rootOfTrust: expectedRootOfTrust) func mockResults(objectToBeMapped: Any) throws -> Any? { if objectToBeMapped is MockOpenIdRawRequest { @@ -317,16 +311,14 @@ class OpenIdRequestHandlerTests: XCTestCase { let expectedPresentationRootOfTrust = RootOfTrust(verified: true, source: "mock source") let expectedPresentationContent = PresentationRequestContent(style: expectedPresentationStyle, requirement: expectedPresentationRequirement, - rootOfTrust: expectedPresentationRootOfTrust, - raw: MockOpenIdRawRequest(raw: nil)) + rootOfTrust: expectedPresentationRootOfTrust) let expectedIssuanceStyle = MockRequesterStyle(requester: "mock issuer") let expectedIssuanceRequirement = MockRequirement(id: "mockRequirement23535") let expectedIssuanceRootOfTrust = RootOfTrust(verified: true, source: "mock issuer source") let expectedIssuanceContent = IssuanceRequestContent(style: expectedIssuanceStyle, requirement: expectedIssuanceRequirement, - rootOfTrust: expectedIssuanceRootOfTrust, - raw: MockOpenIdRawRequest(raw: nil)) + rootOfTrust: expectedIssuanceRootOfTrust) func mockResults(objectToBeMapped: Any) throws -> Any? { if objectToBeMapped is MockOpenIdRawRequest { @@ -378,7 +370,6 @@ class OpenIdRequestHandlerTests: XCTestCase { let expectedPresentationContent = PresentationRequestContent(style: expectedPresentationStyle, requirement: expectedPresentationRequirement, rootOfTrust: expectedPresentationRootOfTrust, - raw: MockOpenIdRawRequest(raw: nil), injectedIdToken: expectedInjectedIdToken) let expectedIssuanceStyle = MockRequesterStyle(requester: "mock issuer") @@ -386,8 +377,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let expectedIssuanceRootOfTrust = RootOfTrust(verified: true, source: "mock issuer source") let expectedIssuanceContent = IssuanceRequestContent(style: expectedIssuanceStyle, requirement: expectedIssuanceRequirement, - rootOfTrust: expectedIssuanceRootOfTrust, - raw: createMockIssuanceRequest()) + rootOfTrust: expectedIssuanceRootOfTrust) func mockResults(objectToBeMapped: Any) throws -> Any? { if objectToBeMapped is MockOpenIdRawRequest { @@ -445,7 +435,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockContract = Contract(id: "mockContract", display: mockDisplayDescriptor, input: mockContractInputDescriptor) - + let mockSignedContract = SignedContract(headers: Header(), content: mockContract)! return IssuanceRequest(from: mockSignedContract, linkedDomainResult: .linkedDomainMissing) } diff --git a/WalletLibrary/WalletLibraryTests/Requests/RequestProtocols/Manifest/ContractIssuanceRequestTests.swift b/WalletLibrary/WalletLibraryTests/Requests/RequestProtocols/Manifest/ContractIssuanceRequestTests.swift index d459d5c3..958059e8 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/RequestProtocols/Manifest/ContractIssuanceRequestTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/RequestProtocols/Manifest/ContractIssuanceRequestTests.swift @@ -21,15 +21,13 @@ class ContractIssuanceRequestTests: XCTestCase { let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let mockVCRequester = MockVerifiedIdRequester() let mockIssuanceResponseContainer = MockIssuanceResponseContainer() - let mockRawManifest = MockRawManifest(id: "") let mockRequirement = MockRequirement(id: "mockRequirement324", mockValidateCallback: { throw ExpectedError.expectedToBeThrown }) let content = IssuanceRequestContent(style: mockStyle, requirement: mockRequirement, - rootOfTrust: mockRootOfTrust, - raw: mockRawManifest) + rootOfTrust: mockRootOfTrust) let contractIssuanceRequest = ContractIssuanceRequest(content: content, issuanceResponseContainer: mockIssuanceResponseContainer, @@ -51,7 +49,6 @@ class ContractIssuanceRequestTests: XCTestCase { let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let mockVCRequester = MockVerifiedIdRequester() let mockIssuanceResponseContainer = MockIssuanceResponseContainer() - let mockRawManifest = MockRawManifest(id: "") let invalidRequirement = MockRequirement(id: "mockRequirement324", mockValidateCallback: { throw ExpectedError.expectedToBeThrown }) @@ -64,8 +61,7 @@ class ContractIssuanceRequestTests: XCTestCase { let content = IssuanceRequestContent(style: mockStyle, requirement: mockRequirement, - rootOfTrust: mockRootOfTrust, - raw: mockRawManifest) + rootOfTrust: mockRootOfTrust) let contractIssuanceRequest = ContractIssuanceRequest(content: content, issuanceResponseContainer: mockIssuanceResponseContainer, @@ -87,14 +83,12 @@ class ContractIssuanceRequestTests: XCTestCase { let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let mockVCRequester = MockVerifiedIdRequester() let mockIssuanceResponseContainer = MockIssuanceResponseContainer() - let mockRawManifest = MockRawManifest(id: "") let validRequirement = MockRequirement(id: "mockRequirement324") let content = IssuanceRequestContent(style: mockStyle, requirement: validRequirement, - rootOfTrust: mockRootOfTrust, - raw: mockRawManifest) + rootOfTrust: mockRootOfTrust) let contractIssuanceRequest = ContractIssuanceRequest(content: content, issuanceResponseContainer: mockIssuanceResponseContainer, @@ -116,7 +110,6 @@ class ContractIssuanceRequestTests: XCTestCase { let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let mockVCRequester = MockVerifiedIdRequester() let mockIssuanceResponseContainer = MockIssuanceResponseContainer() - let mockRawManifest = MockRawManifest(id: "") let firstValidRequirement = MockRequirement(id: "mockRequirement634") let secondValidRequirement = MockRequirement(id: "mockRequirement674") @@ -127,8 +120,7 @@ class ContractIssuanceRequestTests: XCTestCase { let content = IssuanceRequestContent(style: mockStyle, requirement: mockRequirement, - rootOfTrust: mockRootOfTrust, - raw: mockRawManifest) + rootOfTrust: mockRootOfTrust) let contractIssuanceRequest = ContractIssuanceRequest(content: content, issuanceResponseContainer: mockIssuanceResponseContainer, @@ -151,7 +143,6 @@ class ContractIssuanceRequestTests: XCTestCase { let mockMapper = MockMapper() let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let mockVCRequester = MockVerifiedIdRequester(sendRequestCallback: { _ in expectedVerifiedId }) - let mockRawManifest = MockRawManifest(id: "") func mockAddRequirementCallback(requirement: Requirement) throws { XCTAssertEqual(requirement as? MockRequirement, mockRequirement) @@ -161,8 +152,7 @@ class ContractIssuanceRequestTests: XCTestCase { let content = IssuanceRequestContent(style: mockStyle, requirement: mockRequirement, - rootOfTrust: mockRootOfTrust, - raw: mockRawManifest) + rootOfTrust: mockRootOfTrust) let contractIssuanceRequest = ContractIssuanceRequest(content: content, issuanceResponseContainer: mockIssuanceResponseContainer, @@ -189,7 +179,6 @@ class ContractIssuanceRequestTests: XCTestCase { let mockMapper = MockMapper() let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let mockVCRequester = MockVerifiedIdRequester(sendRequestCallback: { _ in throw ExpectedError.expectedToBeThrown }) - let mockRawManifest = MockRawManifest(id: "") func mockAddRequirementCallback(requirement: Requirement) throws { XCTAssertEqual(requirement as? MockRequirement, mockRequirement) @@ -199,8 +188,7 @@ class ContractIssuanceRequestTests: XCTestCase { let content = IssuanceRequestContent(style: mockStyle, requirement: mockRequirement, - rootOfTrust: mockRootOfTrust, - raw: mockRawManifest) + rootOfTrust: mockRootOfTrust) let contractIssuanceRequest = ContractIssuanceRequest(content: content, issuanceResponseContainer: mockIssuanceResponseContainer, From 9bbc8102b77ec2c44f6ffeebec67c241a831818f Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 15:55:59 -0500 Subject: [PATCH 302/373] Update project.pbxproj --- WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index a22e85ab..a5c0db70 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -11,7 +11,7 @@ 550A916F299310D40014D030 /* OpenIdRequestHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 550A9157299310D40014D030 /* OpenIdRequestHandler.swift */; }; 550A9170299310D40014D030 /* OpenIdURLRequestResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 550A916E299310D40014D030 /* OpenIdURLRequestResolver.swift */; }; 550A9173299311A10014D030 /* VerifiedIdRequestURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 550A9172299311A10014D030 /* VerifiedIdRequestURL.swift */; }; - 550A9192299400820014D030 /* OpenIdForVCResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 550A9191299400820014D030 /* OpenIdForVCResponder.swift */; }; + 550A9192299400820014D030 /* OpenIdResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 550A9191299400820014D030 /* OpenIdResponder.swift */; }; 550A91952994025C0014D030 /* OpenIdURLRequestResolverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 550A91942994025C0014D030 /* OpenIdURLRequestResolverTests.swift */; }; 550A919829940DCE0014D030 /* MockOpenIdforVCResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 550A919729940DCE0014D030 /* MockOpenIdforVCResolver.swift */; }; 550A919B29940E9E0014D030 /* PresentationRequest+OpenIdRawRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 550A919A29940E9E0014D030 /* PresentationRequest+OpenIdRawRequest.swift */; }; @@ -309,7 +309,7 @@ 550A9157299310D40014D030 /* OpenIdRequestHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenIdRequestHandler.swift; sourceTree = ""; }; 550A916E299310D40014D030 /* OpenIdURLRequestResolver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenIdURLRequestResolver.swift; sourceTree = ""; }; 550A9172299311A10014D030 /* VerifiedIdRequestURL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdRequestURL.swift; sourceTree = ""; }; - 550A9191299400820014D030 /* OpenIdForVCResponder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenIdForVCResponder.swift; sourceTree = ""; }; + 550A9191299400820014D030 /* OpenIdResponder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenIdResponder.swift; sourceTree = ""; }; 550A91942994025C0014D030 /* OpenIdURLRequestResolverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenIdURLRequestResolverTests.swift; sourceTree = ""; }; 550A919729940DCE0014D030 /* MockOpenIdforVCResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockOpenIdforVCResolver.swift; sourceTree = ""; }; 550A919A29940E9E0014D030 /* PresentationRequest+OpenIdRawRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationRequest+OpenIdRawRequest.swift"; sourceTree = ""; }; @@ -1063,7 +1063,7 @@ isa = PBXGroup; children = ( 55E3376729437E3500CD2ED7 /* OpenIdForVCResolver.swift */, - 550A9191299400820014D030 /* OpenIdForVCResponder.swift */, + 550A9191299400820014D030 /* OpenIdResponder.swift */, ); path = OpenIdForVC; sourceTree = ""; @@ -1454,7 +1454,7 @@ 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */, 55E2F071299554110008010D /* LibraryConfiguration.swift in Sources */, 550E3209298B0EA4004E7716 /* VerifiedIdClientBuilder.swift in Sources */, - 550A9192299400820014D030 /* OpenIdForVCResponder.swift in Sources */, + 550A9192299400820014D030 /* OpenIdResponder.swift in Sources */, 5534E672294A19D5005D0765 /* IdTokenDescriptor+Mappable.swift in Sources */, 5534E677294A1FA0005D0765 /* AccessTokenDescriptor+Mappable.swift in Sources */, 55E2F08A299576350008010D /* PresentationDefinition+Mappable.swift in Sources */, From 04027c658ae936feb2e35518da5d4088627f6165 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 16:03:00 -0500 Subject: [PATCH 303/373] Move some folders around. --- .../WalletLibrary.xcodeproj/project.pbxproj | 34 ++++++------------- .../RawManifest.swift | 0 .../OpenIdForVCResolver.swift | 0 .../OpenIdRawRequest.swift | 0 .../OpenIdResponder.swift | 0 .../Manifest/ContractIssuanceRequest.swift | 0 .../Manifest/Manifest2022IssuerStyle.swift | 0 .../OpenId/InjectedIdToken.swift | 0 .../OpenId/OpenIdPresentationRequest.swift | 0 .../OpenId/OpenIdVerifierStyle.swift | 0 10 files changed, 11 insertions(+), 23 deletions(-) rename WalletLibrary/WalletLibrary/Protocols/Requests/{RawRequests => Manifest}/RawManifest.swift (100%) rename WalletLibrary/WalletLibrary/Protocols/Requests/{OpenIdForVC => OpenId}/OpenIdForVCResolver.swift (100%) rename WalletLibrary/WalletLibrary/Protocols/Requests/{RawRequests => OpenId}/OpenIdRawRequest.swift (100%) rename WalletLibrary/WalletLibrary/Protocols/Requests/{OpenIdForVC => OpenId}/OpenIdResponder.swift (100%) rename WalletLibrary/WalletLibrary/Requests/{RequestProtocols => }/Manifest/ContractIssuanceRequest.swift (100%) rename WalletLibrary/WalletLibrary/Requests/{RequestProtocols => }/Manifest/Manifest2022IssuerStyle.swift (100%) rename WalletLibrary/WalletLibrary/Requests/{RequestProtocols => }/OpenId/InjectedIdToken.swift (100%) rename WalletLibrary/WalletLibrary/Requests/{RequestProtocols => }/OpenId/OpenIdPresentationRequest.swift (100%) rename WalletLibrary/WalletLibrary/Requests/{RequestProtocols => }/OpenId/OpenIdVerifierStyle.swift (100%) diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index a5c0db70..3a3dda9d 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -102,6 +102,7 @@ 55A81C102991C211002C259A /* MockResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81C0F2991C211002C259A /* MockResolver.swift */; }; 55A81C132991C829002C259A /* MockHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81C122991C829002C259A /* MockHandler.swift */; }; 55DECBD029B922D200D5C802 /* MockPresentationResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECBCF29B922D200D5C802 /* MockPresentationResponder.swift */; }; + 55DECBE729B92E3900D5C802 /* PresentationResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECBE629B92E3900D5C802 /* PresentationResponse.swift */; }; 55E2F06C299553300008010D /* PresentationRequest+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */; }; 55E2F06F299553B40008010D /* OpenIdRequestHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F06E299553B40008010D /* OpenIdRequestHandlerTests.swift */; }; 55E2F071299554110008010D /* LibraryConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F070299554110008010D /* LibraryConfiguration.swift */; }; @@ -394,6 +395,7 @@ 55A81C0F2991C211002C259A /* MockResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockResolver.swift; sourceTree = ""; }; 55A81C122991C829002C259A /* MockHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockHandler.swift; sourceTree = ""; }; 55DECBCF29B922D200D5C802 /* MockPresentationResponder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockPresentationResponder.swift; sourceTree = ""; }; + 55DECBE629B92E3900D5C802 /* PresentationResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentationResponse.swift; sourceTree = ""; }; 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PresentationRequest+Mappable.swift"; sourceTree = ""; }; 55E2F06E299553B40008010D /* OpenIdRequestHandlerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenIdRequestHandlerTests.swift; sourceTree = ""; }; 55E2F070299554110008010D /* LibraryConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryConfiguration.swift; sourceTree = ""; }; @@ -500,15 +502,6 @@ path = Entities; sourceTree = ""; }; - 550A919C29940ECA0014D030 /* RawRequests */ = { - isa = PBXGroup; - children = ( - 550A919D29940ED70014D030 /* OpenIdRawRequest.swift */, - 559BD3A7299ADE9C00BD61AC /* RawManifest.swift */, - ); - path = RawRequests; - sourceTree = ""; - }; 550E3215298B1224004E7716 /* Styles */ = { isa = PBXGroup; children = ( @@ -711,6 +704,7 @@ 559BD3A4299AD9A200BD61AC /* Manifest */ = { isa = PBXGroup; children = ( + 559BD3A7299ADE9C00BD61AC /* RawManifest.swift */, 559BD3A5299AD9BD00BD61AC /* ManifestResolver.swift */, 553CC09C29A97181005A5FD6 /* IssuanceResponseContaining.swift */, ); @@ -893,15 +887,6 @@ path = Styles; sourceTree = ""; }; - 55E2F07A2995561E0008010D /* RequestProtocols */ = { - isa = PBXGroup; - children = ( - 559BD3A9299ADEF300BD61AC /* Manifest */, - 55E2F07B2995561E0008010D /* OpenId */, - ); - path = RequestProtocols; - sourceTree = ""; - }; 55E2F07B2995561E0008010D /* OpenId */ = { isa = PBXGroup; children = ( @@ -1024,7 +1009,8 @@ 55E3370F293FDDF400CD2ED7 /* Requests */ = { isa = PBXGroup; children = ( - 55E2F07A2995561E0008010D /* RequestProtocols */, + 559BD3A9299ADEF300BD61AC /* Manifest */, + 55E2F07B2995561E0008010D /* OpenId */, 550A91712993117B0014D030 /* Input */, 550A9156299310D40014D030 /* Handlers */, 550A916D299310D40014D030 /* Resolvers */, @@ -1059,13 +1045,14 @@ path = Utilities; sourceTree = ""; }; - 55E3376629437E1000CD2ED7 /* OpenIdForVC */ = { + 55E3376629437E1000CD2ED7 /* OpenId */ = { isa = PBXGroup; children = ( 55E3376729437E3500CD2ED7 /* OpenIdForVCResolver.swift */, 550A9191299400820014D030 /* OpenIdResponder.swift */, + 550A919D29940ED70014D030 /* OpenIdRawRequest.swift */, ); - path = OpenIdForVC; + path = OpenId; sourceTree = ""; }; 55E3376929478C3300CD2ED7 /* Utilities */ = { @@ -1080,9 +1067,9 @@ isa = PBXGroup; children = ( 559BD3A4299AD9A200BD61AC /* Manifest */, - 550A919C29940ECA0014D030 /* RawRequests */, 55E337B72948B50800CD2ED7 /* Handlers */, - 55E3376629437E1000CD2ED7 /* OpenIdForVC */, + 55E3376629437E1000CD2ED7 /* OpenId */, + 55DECBE629B92E3900D5C802 /* PresentationResponse.swift */, 550E3213298B11A5004E7716 /* Requirement.swift */, 55A81BC92991AB82002C259A /* RequestResolving.swift */, 55E2F08029955A960008010D /* RequestType.swift */, @@ -1422,6 +1409,7 @@ 550E3217298B1236004E7716 /* RequesterStyle.swift in Sources */, 55E336D1293FA6F400CD2ED7 /* VerifiedIdRequirement.swift in Sources */, 5534E66E294A0F8F005D0765 /* Mapping.swift in Sources */, + 55DECBE729B92E3900D5C802 /* PresentationResponse.swift in Sources */, 559BD4B3299EDFB400BD61AC /* VerifiableCredential.swift in Sources */, 55E336D6293FA78C00CD2ED7 /* VerifiedIdClaim.swift in Sources */, 553CC09029A955E6005A5FD6 /* PinDescriptor+Mappable.swift in Sources */, diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/RawRequests/RawManifest.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/RawManifest.swift similarity index 100% rename from WalletLibrary/WalletLibrary/Protocols/Requests/RawRequests/RawManifest.swift rename to WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/RawManifest.swift diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/OpenIdForVC/OpenIdForVCResolver.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/OpenId/OpenIdForVCResolver.swift similarity index 100% rename from WalletLibrary/WalletLibrary/Protocols/Requests/OpenIdForVC/OpenIdForVCResolver.swift rename to WalletLibrary/WalletLibrary/Protocols/Requests/OpenId/OpenIdForVCResolver.swift diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/RawRequests/OpenIdRawRequest.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/OpenId/OpenIdRawRequest.swift similarity index 100% rename from WalletLibrary/WalletLibrary/Protocols/Requests/RawRequests/OpenIdRawRequest.swift rename to WalletLibrary/WalletLibrary/Protocols/Requests/OpenId/OpenIdRawRequest.swift diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/OpenIdForVC/OpenIdResponder.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/OpenId/OpenIdResponder.swift similarity index 100% rename from WalletLibrary/WalletLibrary/Protocols/Requests/OpenIdForVC/OpenIdResponder.swift rename to WalletLibrary/WalletLibrary/Protocols/Requests/OpenId/OpenIdResponder.swift diff --git a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift b/WalletLibrary/WalletLibrary/Requests/Manifest/ContractIssuanceRequest.swift similarity index 100% rename from WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/ContractIssuanceRequest.swift rename to WalletLibrary/WalletLibrary/Requests/Manifest/ContractIssuanceRequest.swift diff --git a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/Manifest2022IssuerStyle.swift b/WalletLibrary/WalletLibrary/Requests/Manifest/Manifest2022IssuerStyle.swift similarity index 100% rename from WalletLibrary/WalletLibrary/Requests/RequestProtocols/Manifest/Manifest2022IssuerStyle.swift rename to WalletLibrary/WalletLibrary/Requests/Manifest/Manifest2022IssuerStyle.swift diff --git a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/InjectedIdToken.swift b/WalletLibrary/WalletLibrary/Requests/OpenId/InjectedIdToken.swift similarity index 100% rename from WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/InjectedIdToken.swift rename to WalletLibrary/WalletLibrary/Requests/OpenId/InjectedIdToken.swift diff --git a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdPresentationRequest.swift b/WalletLibrary/WalletLibrary/Requests/OpenId/OpenIdPresentationRequest.swift similarity index 100% rename from WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdPresentationRequest.swift rename to WalletLibrary/WalletLibrary/Requests/OpenId/OpenIdPresentationRequest.swift diff --git a/WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdVerifierStyle.swift b/WalletLibrary/WalletLibrary/Requests/OpenId/OpenIdVerifierStyle.swift similarity index 100% rename from WalletLibrary/WalletLibrary/Requests/RequestProtocols/OpenId/OpenIdVerifierStyle.swift rename to WalletLibrary/WalletLibrary/Requests/OpenId/OpenIdVerifierStyle.swift From 31712453bda866cb1b6c86c9f7cd60fe89146b57 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 16:03:17 -0500 Subject: [PATCH 304/373] Update name of an error on PresentationResponseContainer. --- .../PresentationResponseContainer+WalletLibrary.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationResponseContainer+WalletLibrary.swift b/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationResponseContainer+WalletLibrary.swift index ebc56d1b..becfb793 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationResponseContainer+WalletLibrary.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationResponseContainer+WalletLibrary.swift @@ -8,7 +8,7 @@ import VCEntities enum PresentationResponseError: Error, Equatable { case unableToCastVCSDKPresentationRequestFromRawRequestOfType(String) case unsupportedRequirementOfType(String) - case unableToCastVerifiedIdToVerifiableCredentialOfType(String) + case unableToCastVerifableCredentialFromVerifiedIdOfType(String) case missingIdInVerifiedIdRequirement } @@ -52,7 +52,7 @@ extension VCEntities.PresentationResponseContainer: PresentationResponse { guard let verifiableCredential = verifiedIdRequirement.selectedVerifiedId as? WalletLibrary.VerifiableCredential else { let verifiedIdType = String(describing: type(of: verifiedIdRequirement.selectedVerifiedId)) - throw PresentationResponseError.unableToCastVerifiedIdToVerifiableCredentialOfType(verifiedIdType) + throw PresentationResponseError.unableToCastVerifableCredentialFromVerifiedIdOfType(verifiedIdType) } guard let id = verifiedIdRequirement.id else { From 0f2b258fe3cd6df507a251d59d0ea4a28a27c58f Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 16:03:20 -0500 Subject: [PATCH 305/373] Create PresentationResponse.swift --- .../Protocols/Requests/PresentationResponse.swift | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 WalletLibrary/WalletLibrary/Protocols/Requests/PresentationResponse.swift diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/PresentationResponse.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/PresentationResponse.swift new file mode 100644 index 00000000..807828bc --- /dev/null +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/PresentationResponse.swift @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +/** + * An object that describes a raw presense response and defines the behavior of adding a requirement to it. + * For example, a VCSDK.PresentationResponseContainer conforms to this protocol. + */ +protocol PresentationResponse { + mutating func add(requirement: Requirement) throws +} From 7b6956c269c548e35498bdbc9d5d2c3c338c669c Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 16:11:53 -0500 Subject: [PATCH 306/373] Remove RawRequest from RawManifest. --- .../WalletLibrary/Protocols/Requests/Manifest/RawManifest.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/RawManifest.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/RawManifest.swift index 5e644b6d..19f76947 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/RawManifest.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/Manifest/RawManifest.swift @@ -7,5 +7,5 @@ * Representation of a Raw Contract. * Object that conforms to this protocol must be able to map to VerifiedIdRequestContent. */ -protocol RawManifest: RawRequest, Mappable where T == IssuanceRequestContent {} +protocol RawManifest: Mappable where T == IssuanceRequestContent {} From d38bb1e7cc10adf1655d54b9403a9c91856d0872 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 16:12:06 -0500 Subject: [PATCH 307/373] Remove RawRequest idea from OpenIdRawRequest. --- .../Protocols/Requests/OpenId/OpenIdRawRequest.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/OpenId/OpenIdRawRequest.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/OpenId/OpenIdRawRequest.swift index 21de262a..fab5b1fd 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/OpenId/OpenIdRawRequest.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/OpenId/OpenIdRawRequest.swift @@ -7,7 +7,7 @@ * Representation of a Raw Open Id Request. * Object that conforms to this protocol must be able to map to VerifiedIdRequestContent. */ -protocol OpenIdRawRequest: RawRequest, Mappable where T == PresentationRequestContent { +protocol OpenIdRawRequest: Mappable where T == PresentationRequestContent { var type: RequestType { get } } From d2c64323ba295ce09c9a2d0eaf4a3ea236802bde Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 16:12:17 -0500 Subject: [PATCH 308/373] Fix comment in openIdResponder. --- .../Protocols/Requests/OpenId/OpenIdResponder.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/OpenId/OpenIdResponder.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/OpenId/OpenIdResponder.swift index dcca73dd..9731eef8 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/OpenId/OpenIdResponder.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/OpenId/OpenIdResponder.swift @@ -9,7 +9,7 @@ import VCEntities * Protocol is used as a wrapper to wrap the VC SDK send presentation response method. */ protocol OpenIdResponder { - /// Sends the presentation response container and if successful, returns void, + /// Sends the presentation response and if successful, returns void, /// If unsuccessful, throws an error. func send(response: PresentationResponse) async throws -> Void } From 34033de2e1bc64e5e5ddcc9d7788424995794914 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 16:12:29 -0500 Subject: [PATCH 309/373] Fix naming in OpenIdRequestHandler. --- .../Requests/Handlers/OpenIdRequestHandler.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift index 6983c957..49c0d465 100644 --- a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift +++ b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift @@ -19,11 +19,11 @@ struct OpenIdRequestHandler: RequestHandling { private let configuration: LibraryConfiguration - private let presentationRequestResponder: OpenIdResponder + private let openIdResponder: OpenIdResponder private let manifestResolver: ManifestResolver - private let verifiableCredentialRequester: VerifiedIdRequester + private let verifiedIdRequester: VerifiedIdRequester /// TODO: post private preview, manifest resolving and verified id requester will be handled by processors. init(configuration: LibraryConfiguration, @@ -31,9 +31,9 @@ struct OpenIdRequestHandler: RequestHandling { manifestResolver: ManifestResolver, verifiableCredentialRequester: VerifiedIdRequester) { self.configuration = configuration - self.presentationRequestResponder = presentationRequestResponder + self.openIdResponder = presentationRequestResponder self.manifestResolver = manifestResolver - self.verifiableCredentialRequester = verifiableCredentialRequester + self.verifiedIdRequester = verifiableCredentialRequester } /// Create a VeriifiedIdRequest based on the Open Id raw request given. @@ -73,7 +73,7 @@ struct OpenIdRequestHandler: RequestHandling { return ContractIssuanceRequest(content: issuanceRequestContent, issuanceResponseContainer: issuanceResponseContainer, - verifiedIdRequester: verifiableCredentialRequester, + verifiedIdRequester: verifiedIdRequester, configuration: configuration) } @@ -81,7 +81,7 @@ struct OpenIdRequestHandler: RequestHandling { rawRequest: any OpenIdRawRequest) -> any VerifiedIdPresentationRequest { return OpenIdPresentationRequest(content: requestContent, rawRequest: rawRequest, - openIdResponder: presentationRequestResponder, + openIdResponder: openIdResponder, configuration: configuration) } } From 1d362d5c2147532d41680b32a0c17fb5b7c18971 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 16:12:36 -0500 Subject: [PATCH 310/373] Update AttestationsDescriptor+MappableTests.swift --- ...AttestationsDescriptor+MappableTests.swift | 41 +++++++++---------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+MappableTests.swift index 192558b1..d648f85e 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+MappableTests.swift @@ -67,15 +67,15 @@ class AttestationsDescriptorMappingTests: XCTestCase { XCTAssertEqual(error as? MockError, .expectedToBeUnableToMapAccessTokenDescriptor) } } - + func testMap_WithOnlyOneIdTokenRequirement_ReturnsRequirement() throws { // Arrange let expectedIdTokenRequirement = IdTokenRequirement(encrypted: false, - required: true, - configuration: URL(string: "https://test.com")!, - clientId: "mock client id", - redirectUri: "mock redirect uri", - scope: "mock scope") + required: true, + configuration: URL(string: "https://test.com")!, + clientId: "mock client id", + redirectUri: "mock redirect uri", + scope: "mock scope") let mockIdTokenDescriptor = IdTokenDescriptor(claims: [], configuration: "mock config", clientID: "mock client id") let attestations = AttestationsDescriptor(idTokens: [mockIdTokenDescriptor]) @@ -122,8 +122,7 @@ class AttestationsDescriptorMappingTests: XCTestCase { func testMap_WithOnlyOneVerifiedIdRequirement_ReturnsRequirement() throws { // Arrange - let expectedVerifiedIdRequirement = VerifiedIdRequirement(id: "", - encrypted: false, + let expectedVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, required: true, types: [], purpose: nil, @@ -228,18 +227,17 @@ class AttestationsDescriptorMappingTests: XCTestCase { let expectedSelfAttestedClaimRequirement = SelfAttestedClaimRequirement(encrypted: false, required: true, claim: "mock claim") - let expectedVerifiedIdRequirement = VerifiedIdRequirement(id: "", - encrypted: false, + let expectedVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, required: true, types: [], purpose: nil, issuanceOptions: []) let expectedIdTokenRequirement = IdTokenRequirement(encrypted: false, - required: true, - configuration: URL(string: "https://test.com")!, - clientId: "mock client id", - redirectUri: "mock redirect uri", - scope: "mock scope") + required: true, + configuration: URL(string: "https://test.com")!, + clientId: "mock client id", + redirectUri: "mock redirect uri", + scope: "mock scope") let expectedAccessTokenRequirement = AccessTokenRequirement(encrypted: false, required: true, configuration: "mock configuration", @@ -297,18 +295,17 @@ class AttestationsDescriptorMappingTests: XCTestCase { let expectedSelfAttestedClaimRequirement = SelfAttestedClaimRequirement(encrypted: false, required: true, claim: "mock claim") - let expectedVerifiedIdRequirement = VerifiedIdRequirement(id: "", - encrypted: false, + let expectedVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, required: true, types: [], purpose: nil, issuanceOptions: []) let expectedIdTokenRequirement = IdTokenRequirement(encrypted: false, - required: true, - configuration: URL(string: "https://test.com")!, - clientId: "mock client id", - redirectUri: "mock redirect uri", - scope: "mock scope") + required: true, + configuration: URL(string: "https://test.com")!, + clientId: "mock client id", + redirectUri: "mock redirect uri", + scope: "mock scope") let expectedAccessTokenRequirement = AccessTokenRequirement(encrypted: false, required: true, configuration: "mock configuration", From 9312fbb199544aa1de0c62b8f0b59936473254f3 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 16:12:41 -0500 Subject: [PATCH 311/373] Update PresentationDefinition+MappableTests.swift --- .../Presentation/PresentationDefinition+MappableTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+MappableTests.swift index ef7c0e92..7d0f3c30 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+MappableTests.swift @@ -44,7 +44,7 @@ class PresentationDefinitionMappingTests: XCTestCase { func testMap_WithOneInputDescriptorPresent_ReturnsVerifiedIdRequirement() throws { // Arrange - let expectedVerifiedIdRequirement = VerifiedIdRequirement(id: "", + let expectedVerifiedIdRequirement = VerifiedIdRequirement(id: "mockId", encrypted: false, required: false, types: [], From f2b62554eaadc9fec8b2d864525633754bfd46eb Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 16:26:50 -0500 Subject: [PATCH 312/373] Move factories to different folder. --- .../Mappings/VCSDK/Issuance/IssuanceRequest+Mappable.swift | 2 +- .../VCSDK/Issuance/PresentationDescriptor+Mappable.swift | 1 + .../Requests/{ => Handlers}/RequestHandlerFactory.swift | 0 .../Requests/{ => Resolvers}/RequestResolverFactory.swift | 0 4 files changed, 2 insertions(+), 1 deletion(-) rename WalletLibrary/WalletLibrary/Requests/{ => Handlers}/RequestHandlerFactory.swift (100%) rename WalletLibrary/WalletLibrary/Requests/{ => Resolvers}/RequestResolverFactory.swift (100%) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+Mappable.swift index 62f2b250..e6095e33 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/IssuanceRequest+Mappable.swift @@ -9,7 +9,7 @@ import VCEntities * An extension of the VCEntities.IssuanceRequest class. * TODO: Update Style to include VerifiedIdStyle and more requester style attributes. */ -extension VCEntities.IssuanceRequest: RawRequest, Mappable { +extension VCEntities.IssuanceRequest: Mappable { func map(using mapper: Mapping) throws -> IssuanceRequestContent { let attestations = try getRequiredProperty(property: content.input.attestations, diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+Mappable.swift index a0600a58..737d8a8c 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+Mappable.swift @@ -22,6 +22,7 @@ extension VCEntities.PresentationDescriptor: Mappable { return nil } + /// VerifiedIdRequirements associated with issuance do not have an id. return VerifiedIdRequirement(id: nil, encrypted: encrypted ?? false, required: presentationRequired ?? false, diff --git a/WalletLibrary/WalletLibrary/Requests/RequestHandlerFactory.swift b/WalletLibrary/WalletLibrary/Requests/Handlers/RequestHandlerFactory.swift similarity index 100% rename from WalletLibrary/WalletLibrary/Requests/RequestHandlerFactory.swift rename to WalletLibrary/WalletLibrary/Requests/Handlers/RequestHandlerFactory.swift diff --git a/WalletLibrary/WalletLibrary/Requests/RequestResolverFactory.swift b/WalletLibrary/WalletLibrary/Requests/Resolvers/RequestResolverFactory.swift similarity index 100% rename from WalletLibrary/WalletLibrary/Requests/RequestResolverFactory.swift rename to WalletLibrary/WalletLibrary/Requests/Resolvers/RequestResolverFactory.swift From c7a199389fffa98c23da1d358abd14de0b9dd551 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 16:27:13 -0500 Subject: [PATCH 313/373] Split up Issuance and Presentation Request Content. --- .../WalletLibrary.xcodeproj/project.pbxproj | 16 +++++--- ...ent.swift => IssuanceRequestContent.swift} | 39 +------------------ .../Requests/PresentationRequestContent.swift | 29 ++++++++++++++ 3 files changed, 41 insertions(+), 43 deletions(-) rename WalletLibrary/WalletLibrary/Requests/{VerifiedIdRequestContent.swift => IssuanceRequestContent.swift} (69%) create mode 100644 WalletLibrary/WalletLibrary/Requests/PresentationRequestContent.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 3a3dda9d..967095b2 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -103,13 +103,14 @@ 55A81C132991C829002C259A /* MockHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55A81C122991C829002C259A /* MockHandler.swift */; }; 55DECBD029B922D200D5C802 /* MockPresentationResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECBCF29B922D200D5C802 /* MockPresentationResponder.swift */; }; 55DECBE729B92E3900D5C802 /* PresentationResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECBE629B92E3900D5C802 /* PresentationResponse.swift */; }; + 55DECBE929B934B900D5C802 /* IssuanceRequestContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECBE829B934B900D5C802 /* IssuanceRequestContent.swift */; }; 55E2F06C299553300008010D /* PresentationRequest+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */; }; 55E2F06F299553B40008010D /* OpenIdRequestHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F06E299553B40008010D /* OpenIdRequestHandlerTests.swift */; }; 55E2F071299554110008010D /* LibraryConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F070299554110008010D /* LibraryConfiguration.swift */; }; 55E2F076299555280008010D /* MockRequirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F073299555280008010D /* MockRequirement.swift */; }; 55E2F077299555280008010D /* MockRequesterStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F075299555280008010D /* MockRequesterStyle.swift */; }; 55E2F07D2995561E0008010D /* OpenIdPresentationRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F07C2995561E0008010D /* OpenIdPresentationRequest.swift */; }; - 55E2F07F29955A3B0008010D /* VerifiedIdRequestContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F07E29955A3B0008010D /* VerifiedIdRequestContent.swift */; }; + 55E2F07F29955A3B0008010D /* PresentationRequestContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F07E29955A3B0008010D /* PresentationRequestContent.swift */; }; 55E2F08129955A960008010D /* RequestType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F08029955A960008010D /* RequestType.swift */; }; 55E2F08429955E8A0008010D /* MockMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F08329955E8A0008010D /* MockMapper.swift */; }; 55E2F086299573C30008010D /* OpenIdVerifierStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F085299573C30008010D /* OpenIdVerifierStyle.swift */; }; @@ -396,13 +397,14 @@ 55A81C122991C829002C259A /* MockHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockHandler.swift; sourceTree = ""; }; 55DECBCF29B922D200D5C802 /* MockPresentationResponder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockPresentationResponder.swift; sourceTree = ""; }; 55DECBE629B92E3900D5C802 /* PresentationResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentationResponse.swift; sourceTree = ""; }; + 55DECBE829B934B900D5C802 /* IssuanceRequestContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssuanceRequestContent.swift; sourceTree = ""; }; 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PresentationRequest+Mappable.swift"; sourceTree = ""; }; 55E2F06E299553B40008010D /* OpenIdRequestHandlerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenIdRequestHandlerTests.swift; sourceTree = ""; }; 55E2F070299554110008010D /* LibraryConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryConfiguration.swift; sourceTree = ""; }; 55E2F073299555280008010D /* MockRequirement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockRequirement.swift; sourceTree = ""; }; 55E2F075299555280008010D /* MockRequesterStyle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockRequesterStyle.swift; sourceTree = ""; }; 55E2F07C2995561E0008010D /* OpenIdPresentationRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenIdPresentationRequest.swift; sourceTree = ""; }; - 55E2F07E29955A3B0008010D /* VerifiedIdRequestContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdRequestContent.swift; sourceTree = ""; }; + 55E2F07E29955A3B0008010D /* PresentationRequestContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentationRequestContent.swift; sourceTree = ""; }; 55E2F08029955A960008010D /* RequestType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestType.swift; sourceTree = ""; }; 55E2F08329955E8A0008010D /* MockMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockMapper.swift; sourceTree = ""; }; 55E2F085299573C30008010D /* OpenIdVerifierStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenIdVerifierStyle.swift; sourceTree = ""; }; @@ -453,6 +455,7 @@ 550A9156299310D40014D030 /* Handlers */ = { isa = PBXGroup; children = ( + 55A81BE62991B2F3002C259A /* RequestHandlerFactory.swift */, 550A9157299310D40014D030 /* OpenIdRequestHandler.swift */, ); path = Handlers; @@ -461,6 +464,7 @@ 550A916D299310D40014D030 /* Resolvers */ = { isa = PBXGroup; children = ( + 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */, 550A916E299310D40014D030 /* OpenIdURLRequestResolver.swift */, ); path = Resolvers; @@ -1016,9 +1020,8 @@ 550A916D299310D40014D030 /* Resolvers */, 55E336CF293FA6D900CD2ED7 /* Requirements */, 5534E5B42948FEB5005D0765 /* RootOfTrust.swift */, - 55A81BE42991B2E5002C259A /* RequestResolverFactory.swift */, - 55A81BE62991B2F3002C259A /* RequestHandlerFactory.swift */, - 55E2F07E29955A3B0008010D /* VerifiedIdRequestContent.swift */, + 55E2F07E29955A3B0008010D /* PresentationRequestContent.swift */, + 55DECBE829B934B900D5C802 /* IssuanceRequestContent.swift */, ); path = Requests; sourceTree = ""; @@ -1401,7 +1404,7 @@ 55E2F08129955A960008010D /* RequestType.swift in Sources */, 55A81BE72991B2F3002C259A /* RequestHandlerFactory.swift in Sources */, 559BD3A3299AD95200BD61AC /* IssuanceService+ManifestResolver.swift in Sources */, - 55E2F07F29955A3B0008010D /* VerifiedIdRequestContent.swift in Sources */, + 55E2F07F29955A3B0008010D /* PresentationRequestContent.swift in Sources */, 550A916F299310D40014D030 /* OpenIdRequestHandler.swift in Sources */, 55E3370A293FD3E000CD2ED7 /* IdTokenRequirement.swift in Sources */, 5585BDE729A6C38C0059710B /* InjectedIdToken.swift in Sources */, @@ -1447,6 +1450,7 @@ 5534E677294A1FA0005D0765 /* AccessTokenDescriptor+Mappable.swift in Sources */, 55E2F08A299576350008010D /* PresentationDefinition+Mappable.swift in Sources */, 553CC09D29A97181005A5FD6 /* IssuanceResponseContaining.swift in Sources */, + 55DECBE929B934B900D5C802 /* IssuanceRequestContent.swift in Sources */, 550A919E29940ED70014D030 /* OpenIdRawRequest.swift in Sources */, 55E337BC2948B5FE00CD2ED7 /* PresentationService+OpenIdForVC.swift in Sources */, 55E336D4293FA75300CD2ED7 /* VerifiedId.swift in Sources */, diff --git a/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift b/WalletLibrary/WalletLibrary/Requests/IssuanceRequestContent.swift similarity index 69% rename from WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift rename to WalletLibrary/WalletLibrary/Requests/IssuanceRequestContent.swift index a98099c5..d59c8c3c 100644 --- a/WalletLibrary/WalletLibrary/Requests/VerifiedIdRequestContent.swift +++ b/WalletLibrary/WalletLibrary/Requests/IssuanceRequestContent.swift @@ -4,33 +4,11 @@ *--------------------------------------------------------------------------------------------*/ /** - * Contents in a Verified Id Request. + * Contents in a Verified Id Issuance Request. * This object is used to map protocol specific requests to common request object. - * TODO: make this object extensible by separating Presentation with Issuance esp. for InjectedIdToken logic. * TODO: add VerifiedIdStyle to Issuance Content. */ -struct PresentationRequestContent { - - let style: RequesterStyle - - var requirement: Requirement - - let rootOfTrust: RootOfTrust - - let injectedIdToken: InjectedIdToken? - - init(style: RequesterStyle, - requirement: Requirement, - rootOfTrust: RootOfTrust, - injectedIdToken: InjectedIdToken? = nil) { - self.style = style - self.requirement = requirement - self.rootOfTrust = rootOfTrust - self.injectedIdToken = injectedIdToken - } -} - -struct IssuanceRequestContent: RequestContent { +struct IssuanceRequestContent { let style: RequesterStyle @@ -69,16 +47,3 @@ struct IssuanceRequestContent: RequestContent { } -protocol RawRequest { - -} - - -protocol RequestContent { - - var style: RequesterStyle { get } - - var requirement: Requirement { get } - - var rootOfTrust: RootOfTrust { get } -} diff --git a/WalletLibrary/WalletLibrary/Requests/PresentationRequestContent.swift b/WalletLibrary/WalletLibrary/Requests/PresentationRequestContent.swift new file mode 100644 index 00000000..ee220218 --- /dev/null +++ b/WalletLibrary/WalletLibrary/Requests/PresentationRequestContent.swift @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +/** + * Contents in a Verified Id Presentation Request. + * This object is used to map protocol specific requests to common request object. + */ +struct PresentationRequestContent { + + let style: RequesterStyle + + var requirement: Requirement + + let rootOfTrust: RootOfTrust + + let injectedIdToken: InjectedIdToken? + + init(style: RequesterStyle, + requirement: Requirement, + rootOfTrust: RootOfTrust, + injectedIdToken: InjectedIdToken? = nil) { + self.style = style + self.requirement = requirement + self.rootOfTrust = rootOfTrust + self.injectedIdToken = injectedIdToken + } +} From d5f123e2b302f892c3dfe1f5842e92d283adb151 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 16:27:22 -0500 Subject: [PATCH 314/373] Fix mock resolver. --- .../WalletLibraryTests/Mocks/Requests/MockRawRequest.swift | 4 +--- .../Mocks/Requests/Resolvers/MockResolver.swift | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockRawRequest.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockRawRequest.swift index dafb9daf..c5aa1cd1 100644 --- a/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockRawRequest.swift +++ b/WalletLibrary/WalletLibraryTests/Mocks/Requests/MockRawRequest.swift @@ -3,9 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -@testable import WalletLibrary - -struct MockRawRequest: RawRequest { +struct MockRawRequest { let raw: String diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/Resolvers/MockResolver.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/Resolvers/MockResolver.swift index 7c48adcc..f3811596 100644 --- a/WalletLibrary/WalletLibraryTests/Mocks/Requests/Resolvers/MockResolver.swift +++ b/WalletLibrary/WalletLibraryTests/Mocks/Requests/Resolvers/MockResolver.swift @@ -33,7 +33,7 @@ class MockResolver: RequestResolving { canResolveUsingInput } - func resolve(input: VerifiedIdRequestInput) async throws -> RawRequest { + func resolve(input: VerifiedIdRequestInput) async throws -> MockRawRequest { guard let mockResolve = mockResolve else { throw MockResolverError.nilMockResolveMethod From d468deca016dc5cf12b9bed6c74edff6e692abc5 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 18:01:02 -0500 Subject: [PATCH 315/373] Update name of param in OpenIdRequestHandler. --- .../Requests/Handlers/OpenIdRequestHandler.swift | 4 ++-- WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift index 49c0d465..40cdfa7a 100644 --- a/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift +++ b/WalletLibrary/WalletLibrary/Requests/Handlers/OpenIdRequestHandler.swift @@ -27,11 +27,11 @@ struct OpenIdRequestHandler: RequestHandling { /// TODO: post private preview, manifest resolving and verified id requester will be handled by processors. init(configuration: LibraryConfiguration, - presentationRequestResponder: OpenIdResponder, + openIdResponder: OpenIdResponder, manifestResolver: ManifestResolver, verifiableCredentialRequester: VerifiedIdRequester) { self.configuration = configuration - self.openIdResponder = presentationRequestResponder + self.openIdResponder = openIdResponder self.manifestResolver = manifestResolver self.verifiedIdRequester = verifiableCredentialRequester } diff --git a/WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift b/WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift index d6f63d1d..a13fb912 100644 --- a/WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift +++ b/WalletLibrary/WalletLibrary/VerifiedIdClientBuilder.swift @@ -53,7 +53,7 @@ public class VerifiedIdClientBuilder { let issuanceService = IssuanceService() let presentationService = PresentationService() let openIdHandler = OpenIdRequestHandler(configuration: configuration, - presentationRequestResponder: presentationService, + openIdResponder: presentationService, manifestResolver: issuanceService, verifiableCredentialRequester: issuanceService) requestHandlers.append(openIdHandler) From 0a38249b2162d443367177f66e3e6c5a82324d87 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 18:08:28 -0500 Subject: [PATCH 316/373] Move some files changes to new PR. --- .../WalletLibrary.xcodeproj/project.pbxproj | 4 -- ...PresentationRequest+OpenIdRawRequest.swift | 10 +++ ...ationResponseContainer+WalletLibrary.swift | 67 ------------------- .../PresentationDescriptor+Mappable.swift | 3 +- .../OpenId/OpenIdPresentationRequest.swift | 10 +-- .../Requirements/VerifiedIdRequirement.swift | 26 ++----- ...AttestationsDescriptor+MappableTests.swift | 32 ++++----- ...PresentationDefinition+MappableTests.swift | 14 ++-- ...ntationInputDescriptor+MappableTests.swift | 18 ++--- 9 files changed, 49 insertions(+), 135 deletions(-) delete mode 100644 WalletLibrary/WalletLibrary/Extensions/Entities/PresentationResponseContainer+WalletLibrary.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 967095b2..0b592145 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -57,7 +57,6 @@ 553CC09B29A96ECD005A5FD6 /* MockIssuanceResponseContaining.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC09A29A96ECD005A5FD6 /* MockIssuanceResponseContaining.swift */; }; 553CC09D29A97181005A5FD6 /* IssuanceResponseContaining.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC09C29A97181005A5FD6 /* IssuanceResponseContaining.swift */; }; 553CC0A029A98003005A5FD6 /* VerifiableCredentialTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC09F29A98003005A5FD6 /* VerifiableCredentialTests.swift */; }; - 55520FF129B8DAAA00092DE6 /* PresentationResponseContainer+WalletLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55520FF029B8DAAA00092DE6 /* PresentationResponseContainer+WalletLibrary.swift */; }; 5585BDE729A6C38C0059710B /* InjectedIdToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585BDE629A6C38C0059710B /* InjectedIdToken.swift */; }; 559BD3032995993700BD61AC /* PresentationInputDescriptor+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */; }; 559BD3052995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 559BD3042995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift */; }; @@ -351,7 +350,6 @@ 553CC09A29A96ECD005A5FD6 /* MockIssuanceResponseContaining.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockIssuanceResponseContaining.swift; sourceTree = ""; }; 553CC09C29A97181005A5FD6 /* IssuanceResponseContaining.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssuanceResponseContaining.swift; sourceTree = ""; }; 553CC09F29A98003005A5FD6 /* VerifiableCredentialTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiableCredentialTests.swift; sourceTree = ""; }; - 55520FF029B8DAAA00092DE6 /* PresentationResponseContainer+WalletLibrary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationResponseContainer+WalletLibrary.swift"; sourceTree = ""; }; 5585BDE629A6C38C0059710B /* InjectedIdToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InjectedIdToken.swift; sourceTree = ""; }; 559BD3022995993700BD61AC /* PresentationInputDescriptor+Mappable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationInputDescriptor+Mappable.swift"; sourceTree = ""; }; 559BD3042995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationDefinition+MappableTests.swift"; sourceTree = ""; }; @@ -501,7 +499,6 @@ 550A919A29940E9E0014D030 /* PresentationRequest+OpenIdRawRequest.swift */, 559BD3AC299AE6C000BD61AC /* IssuanceRequest+RawManifest.swift */, 559BD49B299D913F00BD61AC /* IssuanceResponseContainer+WalletLibrary.swift */, - 55520FF029B8DAAA00092DE6 /* PresentationResponseContainer+WalletLibrary.swift */, ); path = Entities; sourceTree = ""; @@ -1418,7 +1415,6 @@ 553CC09029A955E6005A5FD6 /* PinDescriptor+Mappable.swift in Sources */, 55E337652943749600CD2ED7 /* AsyncWrapper.swift in Sources */, 550E3214298B11A5004E7716 /* Requirement.swift in Sources */, - 55520FF129B8DAAA00092DE6 /* PresentationResponseContainer+WalletLibrary.swift in Sources */, 559BD453299D3DD800BD61AC /* IssuanceRequest+Mappable.swift in Sources */, 550A9173299311A10014D030 /* VerifiedIdRequestURL.swift in Sources */, 550E3210298B113C004E7716 /* VerifiedIdRequest.swift in Sources */, diff --git a/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationRequest+OpenIdRawRequest.swift b/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationRequest+OpenIdRawRequest.swift index 01187e35..33373b6d 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationRequest+OpenIdRawRequest.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationRequest+OpenIdRawRequest.swift @@ -22,4 +22,14 @@ extension VCEntities.PresentationRequest: OpenIdRawRequest { return .Presentation } + + /// The raw representation of the request. + var raw: Data? { + do { + let serializedToken = try self.token.serialize() + return serializedToken.data(using: .utf8) + } catch { + return nil + } + } } diff --git a/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationResponseContainer+WalletLibrary.swift b/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationResponseContainer+WalletLibrary.swift deleted file mode 100644 index becfb793..00000000 --- a/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationResponseContainer+WalletLibrary.swift +++ /dev/null @@ -1,67 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Microsoft Corporation. All rights reserved. -* Licensed under the MIT License. See License.txt in the project root for license information. -*--------------------------------------------------------------------------------------------*/ - -import VCEntities - -enum PresentationResponseError: Error, Equatable { - case unableToCastVCSDKPresentationRequestFromRawRequestOfType(String) - case unsupportedRequirementOfType(String) - case unableToCastVerifableCredentialFromVerifiedIdOfType(String) - case missingIdInVerifiedIdRequirement -} - -/** - * An extension of the VCEntities.PresentationResponseContainer class - * to convert Requirements to mappings in PresentationResponseContainer. - */ -extension VCEntities.PresentationResponseContainer: PresentationResponse { - - init(from request: any OpenIdRawRequest) throws { - - guard let request = request as? PresentationRequest else { - let requestType = String(describing: type(of: request)) - throw PresentationResponseError.unableToCastVCSDKPresentationRequestFromRawRequestOfType(requestType) - } - - try self.init(from: request) - } - - mutating func add(requirement: Requirement) throws { - switch (requirement) { - case let groupRequirement as GroupRequirement: - try add(groupRequirement: groupRequirement) - case let verifiedIdRequirement as VerifiedIdRequirement: - try add(verifiedIdRequirement: verifiedIdRequirement) - default: - let requirementType = String(describing: type(of: requirement)) - throw PresentationResponseError.unsupportedRequirementOfType(requirementType) - } - } - - private mutating func add(groupRequirement: GroupRequirement) throws { - try groupRequirement.validate() - for requirement in groupRequirement.requirements { - try add(requirement: requirement) - } - } - - private mutating func add(verifiedIdRequirement: VerifiedIdRequirement) throws { - try verifiedIdRequirement.validate() - - guard let verifiableCredential = verifiedIdRequirement.selectedVerifiedId as? WalletLibrary.VerifiableCredential else { - let verifiedIdType = String(describing: type(of: verifiedIdRequirement.selectedVerifiedId)) - throw PresentationResponseError.unableToCastVerifableCredentialFromVerifiedIdOfType(verifiedIdType) - } - - guard let id = verifiedIdRequirement.id else { - throw PresentationResponseError.missingIdInVerifiedIdRequirement - } - - let mapping = RequestedVerifiableCredentialMapping(id: id, - verifiableCredential: verifiableCredential.raw) - - self.requestVCMap.append(mapping) - } -} diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+Mappable.swift index 737d8a8c..a36c8b81 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+Mappable.swift @@ -23,8 +23,7 @@ extension VCEntities.PresentationDescriptor: Mappable { } /// VerifiedIdRequirements associated with issuance do not have an id. - return VerifiedIdRequirement(id: nil, - encrypted: encrypted ?? false, + return VerifiedIdRequirement(encrypted: encrypted ?? false, required: presentationRequired ?? false, types: [credentialType], purpose: nil, diff --git a/WalletLibrary/WalletLibrary/Requests/OpenId/OpenIdPresentationRequest.swift b/WalletLibrary/WalletLibrary/Requests/OpenId/OpenIdPresentationRequest.swift index 8ee0cf4e..b3454a7e 100644 --- a/WalletLibrary/WalletLibrary/Requests/OpenId/OpenIdPresentationRequest.swift +++ b/WalletLibrary/WalletLibrary/Requests/OpenId/OpenIdPresentationRequest.swift @@ -42,15 +42,7 @@ class OpenIdPresentationRequest: VerifiedIdPresentationRequest { } func complete() async -> Result<(), Error> { - do { - - var response = try PresentationResponseContainer(from: rawRequest) - try response.add(requirement: requirement) - try await responder.send(response: response) - return Result.success(()) - } catch { - return Result.failure(error) - } + return Result.failure(VerifiedIdClientError.TODO(message: "implement")) } func cancel(message: String?) -> Result { diff --git a/WalletLibrary/WalletLibrary/Requests/Requirements/VerifiedIdRequirement.swift b/WalletLibrary/WalletLibrary/Requests/Requirements/VerifiedIdRequirement.swift index 82f2d6c7..96b97c3c 100644 --- a/WalletLibrary/WalletLibrary/Requests/Requirements/VerifiedIdRequirement.swift +++ b/WalletLibrary/WalletLibrary/Requests/Requirements/VerifiedIdRequirement.swift @@ -11,13 +11,10 @@ public class VerifiedIdRequirement: Requirement { /// If requirement must be encrypted. let encrypted: Bool - /// Input descriptor id from the presentation request tied to verifiable credential requested. - let id: String? - /// If the requirement is required or not. public let required: Bool - /// The types of the verified Id required. + /// The type of the verified Id required. public let types: [String] /// The purpose for the verified Id, developer can display to the user if desired. @@ -26,16 +23,16 @@ public class VerifiedIdRequirement: Requirement { /// An optional property for information needed for issuance during presentation flow. public let issuanceOptions: [VerifiedIdRequestInput] - /// The verified id that fulfills the requirement. - var selectedVerifiedId: VerifiedId? + /// TODO: helper method that returns verified id that match the requirement from a list of verified ids. + public func getMatches(verifiedIds: [VerifiedId]) -> [VerifiedId] { + return [] + } - init(id: String? = nil, - encrypted: Bool, + init(encrypted: Bool, required: Bool, types: [String], purpose: String?, issuanceOptions: [VerifiedIdRequestInput]) { - self.id = id self.encrypted = encrypted self.required = required self.types = types @@ -43,17 +40,6 @@ public class VerifiedIdRequirement: Requirement { self.issuanceOptions = issuanceOptions } - /// TODO: helper method that returns verified id that match the requirement from a list of verified ids. - public func getMatches(verifiedIds: [VerifiedId]) -> [VerifiedId] { - return [] - } - - /// Fulfill requirement with a verified id. - /// TODO: do some validation to make sure verified id actually fulfills. - public func fulfill(with verifiedId: VerifiedId) { - selectedVerifiedId = verifiedId - } - public func validate() throws { throw VerifiedIdClientError.TODO(message: "implement validate") } diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+MappableTests.swift index d648f85e..2b127ef9 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+MappableTests.swift @@ -67,15 +67,15 @@ class AttestationsDescriptorMappingTests: XCTestCase { XCTAssertEqual(error as? MockError, .expectedToBeUnableToMapAccessTokenDescriptor) } } - + func testMap_WithOnlyOneIdTokenRequirement_ReturnsRequirement() throws { // Arrange let expectedIdTokenRequirement = IdTokenRequirement(encrypted: false, - required: true, - configuration: URL(string: "https://test.com")!, - clientId: "mock client id", - redirectUri: "mock redirect uri", - scope: "mock scope") + required: true, + configuration: URL(string: "https://test.com")!, + clientId: "mock client id", + redirectUri: "mock redirect uri", + scope: "mock scope") let mockIdTokenDescriptor = IdTokenDescriptor(claims: [], configuration: "mock config", clientID: "mock client id") let attestations = AttestationsDescriptor(idTokens: [mockIdTokenDescriptor]) @@ -233,11 +233,11 @@ class AttestationsDescriptorMappingTests: XCTestCase { purpose: nil, issuanceOptions: []) let expectedIdTokenRequirement = IdTokenRequirement(encrypted: false, - required: true, - configuration: URL(string: "https://test.com")!, - clientId: "mock client id", - redirectUri: "mock redirect uri", - scope: "mock scope") + required: true, + configuration: URL(string: "https://test.com")!, + clientId: "mock client id", + redirectUri: "mock redirect uri", + scope: "mock scope") let expectedAccessTokenRequirement = AccessTokenRequirement(encrypted: false, required: true, configuration: "mock configuration", @@ -301,11 +301,11 @@ class AttestationsDescriptorMappingTests: XCTestCase { purpose: nil, issuanceOptions: []) let expectedIdTokenRequirement = IdTokenRequirement(encrypted: false, - required: true, - configuration: URL(string: "https://test.com")!, - clientId: "mock client id", - redirectUri: "mock redirect uri", - scope: "mock scope") + required: true, + configuration: URL(string: "https://test.com")!, + clientId: "mock client id", + redirectUri: "mock redirect uri", + scope: "mock scope") let expectedAccessTokenRequirement = AccessTokenRequirement(encrypted: false, required: true, configuration: "mock configuration", diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+MappableTests.swift index 7d0f3c30..48ba189d 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+MappableTests.swift @@ -44,12 +44,11 @@ class PresentationDefinitionMappingTests: XCTestCase { func testMap_WithOneInputDescriptorPresent_ReturnsVerifiedIdRequirement() throws { // Arrange - let expectedVerifiedIdRequirement = VerifiedIdRequirement(id: "mockId", - encrypted: false, - required: false, - types: [], - purpose: nil, - issuanceOptions: []) + let expectedVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, + required: false, + types: [], + purpose: nil, + issuanceOptions: []) let inputDescriptor = PresentationInputDescriptor(id: nil, schema: nil, issuanceMetadata: nil, @@ -81,8 +80,7 @@ class PresentationDefinitionMappingTests: XCTestCase { func testMap_WithMultipleInputDescriptorPresent_ReturnsGroupRequirement() throws { // Arrange - let mockVerifiedIdRequirement = VerifiedIdRequirement(id: "mockId", - encrypted: false, + let mockVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, required: false, types: [], purpose: nil, diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+MappableTests.swift index 715c68a5..08d00649 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+MappableTests.swift @@ -12,7 +12,7 @@ class PresentationInputDescriptorMappingTests: XCTestCase { func testMap_WithNilSchema_ThrowsError() throws { // Arrange let mockMapper = MockMapper() - let presentationInputDescriptor = PresentationInputDescriptor(id: "mockId", + let presentationInputDescriptor = PresentationInputDescriptor(id: nil, schema: nil, issuanceMetadata: nil, name: nil, @@ -31,7 +31,7 @@ class PresentationInputDescriptorMappingTests: XCTestCase { // Arrange let mockMapper = MockMapper() let schema = InputDescriptorSchema(uri: nil) - let presentationInputDescriptor = PresentationInputDescriptor(id: "mockId", + let presentationInputDescriptor = PresentationInputDescriptor(id: nil, schema: [schema], issuanceMetadata: nil, name: nil, @@ -49,7 +49,7 @@ class PresentationInputDescriptorMappingTests: XCTestCase { func testMap_WithNoVerifiedIdTypesPresent_ThrowsError() throws { // Arrange let mockMapper = MockMapper() - let presentationInputDescriptor = PresentationInputDescriptor(id: "mockId", + let presentationInputDescriptor = PresentationInputDescriptor(id: nil, schema: [], issuanceMetadata: nil, name: nil, @@ -73,7 +73,7 @@ class PresentationInputDescriptorMappingTests: XCTestCase { types: ["mockType"], purpose: nil, issuanceOptions: []) - let presentationInputDescriptor = PresentationInputDescriptor(id: "mockId", + let presentationInputDescriptor = PresentationInputDescriptor(id: nil, schema: [mockSchema], issuanceMetadata: nil, name: nil, @@ -105,7 +105,7 @@ class PresentationInputDescriptorMappingTests: XCTestCase { types: [firstType, secondType, thirdType], purpose: nil, issuanceOptions: []) - let presentationInputDescriptor = PresentationInputDescriptor(id: "mockId", + let presentationInputDescriptor = PresentationInputDescriptor(id: nil, schema: [firstSchema, secondSchema, thirdSchema], issuanceMetadata: nil, name: nil, @@ -132,7 +132,7 @@ class PresentationInputDescriptorMappingTests: XCTestCase { types: ["mockType"], purpose: nil, issuanceOptions: []) - let presentationInputDescriptor = PresentationInputDescriptor(id: "mockId", + let presentationInputDescriptor = PresentationInputDescriptor(id: nil, schema: [mockSchema], issuanceMetadata: nil, name: nil, @@ -161,7 +161,7 @@ class PresentationInputDescriptorMappingTests: XCTestCase { types: ["mockType"], purpose: nil, issuanceOptions: []) - let presentationInputDescriptor = PresentationInputDescriptor(id: "mockId", + let presentationInputDescriptor = PresentationInputDescriptor(id: nil, schema: [mockSchema], issuanceMetadata: [invalidIssuanceMetadata], name: nil, @@ -201,7 +201,7 @@ class PresentationInputDescriptorMappingTests: XCTestCase { issuanceOptions: [firstVerifiedIdRequestURL, secondVerifiedIdRequestURL]) let issuanceMetadatas = [firstValidIssuanceMetadata, invalidIssuanceMetadata, secondValidIssuanceMetadata] - let presentationInputDescriptor = PresentationInputDescriptor(id: "mockId", + let presentationInputDescriptor = PresentationInputDescriptor(id: nil, schema: [mockSchema], issuanceMetadata: issuanceMetadatas, name: nil, @@ -229,7 +229,7 @@ class PresentationInputDescriptorMappingTests: XCTestCase { types: ["mockType"], purpose: purpose, issuanceOptions: []) - let presentationInputDescriptor = PresentationInputDescriptor(id: "mockId", + let presentationInputDescriptor = PresentationInputDescriptor(id: nil, schema: [mockSchema], issuanceMetadata: nil, name: nil, From 471d31efee58523811b5877135883a42f10d62f0 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 18:46:51 -0500 Subject: [PATCH 317/373] Create PresentationResponseContainer extension. --- .../WalletLibrary.xcodeproj/project.pbxproj | 4 ++ ...ationResponseContainer+WalletLibrary.swift | 53 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 WalletLibrary/WalletLibrary/Extensions/Entities/PresentationResponseContainer+WalletLibrary.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 0b592145..738630d4 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -103,6 +103,7 @@ 55DECBD029B922D200D5C802 /* MockPresentationResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECBCF29B922D200D5C802 /* MockPresentationResponder.swift */; }; 55DECBE729B92E3900D5C802 /* PresentationResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECBE629B92E3900D5C802 /* PresentationResponse.swift */; }; 55DECBE929B934B900D5C802 /* IssuanceRequestContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECBE829B934B900D5C802 /* IssuanceRequestContent.swift */; }; + 55DECBEB29B954F500D5C802 /* PresentationResponseContainer+WalletLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECBEA29B954F500D5C802 /* PresentationResponseContainer+WalletLibrary.swift */; }; 55E2F06C299553300008010D /* PresentationRequest+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */; }; 55E2F06F299553B40008010D /* OpenIdRequestHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F06E299553B40008010D /* OpenIdRequestHandlerTests.swift */; }; 55E2F071299554110008010D /* LibraryConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F070299554110008010D /* LibraryConfiguration.swift */; }; @@ -396,6 +397,7 @@ 55DECBCF29B922D200D5C802 /* MockPresentationResponder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockPresentationResponder.swift; sourceTree = ""; }; 55DECBE629B92E3900D5C802 /* PresentationResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentationResponse.swift; sourceTree = ""; }; 55DECBE829B934B900D5C802 /* IssuanceRequestContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssuanceRequestContent.swift; sourceTree = ""; }; + 55DECBEA29B954F500D5C802 /* PresentationResponseContainer+WalletLibrary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationResponseContainer+WalletLibrary.swift"; sourceTree = ""; }; 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PresentationRequest+Mappable.swift"; sourceTree = ""; }; 55E2F06E299553B40008010D /* OpenIdRequestHandlerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenIdRequestHandlerTests.swift; sourceTree = ""; }; 55E2F070299554110008010D /* LibraryConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryConfiguration.swift; sourceTree = ""; }; @@ -499,6 +501,7 @@ 550A919A29940E9E0014D030 /* PresentationRequest+OpenIdRawRequest.swift */, 559BD3AC299AE6C000BD61AC /* IssuanceRequest+RawManifest.swift */, 559BD49B299D913F00BD61AC /* IssuanceResponseContainer+WalletLibrary.swift */, + 55DECBEA29B954F500D5C802 /* PresentationResponseContainer+WalletLibrary.swift */, ); path = Entities; sourceTree = ""; @@ -1428,6 +1431,7 @@ 5534E669294A0C3F005D0765 /* Mapper.swift in Sources */, 550E320A298B0EA4004E7716 /* VerifiedIdClient.swift in Sources */, 559BD45B299D587200BD61AC /* Manifest2022IssuerStyle.swift in Sources */, + 55DECBEB29B954F500D5C802 /* PresentationResponseContainer+WalletLibrary.swift in Sources */, 559BD3AD299AE6C000BD61AC /* IssuanceRequest+RawManifest.swift in Sources */, 559BD3B2299B0A0400BD61AC /* AttestationsDescriptor+Mappable.swift in Sources */, 5534E64D294A0888005D0765 /* PresentationDescriptor+Mappable.swift in Sources */, diff --git a/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationResponseContainer+WalletLibrary.swift b/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationResponseContainer+WalletLibrary.swift new file mode 100644 index 00000000..e763e538 --- /dev/null +++ b/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationResponseContainer+WalletLibrary.swift @@ -0,0 +1,53 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import VCEntities + +enum PresentationResponseError: Error, Equatable { + case unableToCastVCSDKPresentationRequestFromRawRequestOfType(String) + case unsupportedRequirementOfType(String) + case unableToCastVerifableCredentialFromVerifiedIdOfType(String) + case missingIdInVerifiedIdRequirement +} + +/** + * An extension of the VCEntities.PresentationResponseContainer class + * to convert Requirements to mappings in PresentationResponseContainer. + */ +extension VCEntities.PresentationResponseContainer: PresentationResponse { + + init(from request: any OpenIdRawRequest) throws { + + guard let request = request as? PresentationRequest else { + let requestType = String(describing: type(of: request)) + throw PresentationResponseError.unableToCastVCSDKPresentationRequestFromRawRequestOfType(requestType) + } + + try self.init(from: request) + } + + mutating func add(requirement: Requirement) throws { + switch (requirement) { + case let groupRequirement as GroupRequirement: + try add(groupRequirement: groupRequirement) + case let verifiedIdRequirement as VerifiedIdRequirement: + try add(verifiedIdRequirement: verifiedIdRequirement) + default: + let requirementType = String(describing: type(of: requirement)) + throw PresentationResponseError.unsupportedRequirementOfType(requirementType) + } + } + + private mutating func add(groupRequirement: GroupRequirement) throws { + try groupRequirement.validate() + for requirement in groupRequirement.requirements { + try add(requirement: requirement) + } + } + + private mutating func add(verifiedIdRequirement: VerifiedIdRequirement) throws { + throw VerifiedIdClientError.TODO(message: "Implement in next PR.") + } +} From 02fac99dd37e2c6dfcf60dde2222174cd5e94d15 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 18:47:08 -0500 Subject: [PATCH 318/373] Remove code to be added in next pr. --- .../VCSDK/Issuance/PresentationDescriptor+Mappable.swift | 1 - .../Presentation/PresentationInputDescriptor+Mappable.swift | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+Mappable.swift index a36c8b81..4939ed84 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+Mappable.swift @@ -22,7 +22,6 @@ extension VCEntities.PresentationDescriptor: Mappable { return nil } - /// VerifiedIdRequirements associated with issuance do not have an id. return VerifiedIdRequirement(encrypted: encrypted ?? false, required: presentationRequired ?? false, types: [credentialType], diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+Mappable.swift index 70c1261c..ead5a6c2 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+Mappable.swift @@ -20,8 +20,6 @@ extension VCEntities.PresentationInputDescriptor: Mappable { func map(using mapper: Mapping) throws -> VerifiedIdRequirement { - let id = try getRequiredProperty(property: id, propertyName: "Input Descriptor Id") - guard let types = schema?.compactMap({ $0.uri }), !types.isEmpty else { throw PresentationInputDescriptorMappingError.noVerifiedIdTypeInPresentationInputDescriptor @@ -37,8 +35,7 @@ extension VCEntities.PresentationInputDescriptor: Mappable { return nil } - return VerifiedIdRequirement(id: id, - encrypted: false, + return VerifiedIdRequirement(encrypted: false, required: true, types: types, purpose: purpose, From 0cedd821b72ef38dad18970cb2e35652e0a3e054 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Wed, 8 Mar 2023 18:47:16 -0500 Subject: [PATCH 319/373] Fix tests. --- .../Handlers/OpenIdRequestHandlerTests.swift | 18 +++++++++--------- .../OpenIdURLRequestResolverTests.swift | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift index 545f880d..66267dc3 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift @@ -38,7 +38,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockRawRequest = MockOpenIdRawRequest(raw: Data()) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, - presentationRequestResponder: MockPresentationResponder(), + openIdResponder: MockPresentationResponder(), manifestResolver: MockManifestResolver(), verifiableCredentialRequester: MockVerifiedIdRequester()) @@ -68,7 +68,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockRawRequest = MockOpenIdRawRequest(raw: Data()) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, - presentationRequestResponder: MockPresentationResponder(), + openIdResponder: MockPresentationResponder(), manifestResolver: MockManifestResolver(), verifiableCredentialRequester: MockVerifiedIdRequester()) @@ -105,7 +105,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, - presentationRequestResponder: MockPresentationResponder(), + openIdResponder: MockPresentationResponder(), manifestResolver: MockManifestResolver(), verifiableCredentialRequester: MockVerifiedIdRequester()) @@ -146,7 +146,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, - presentationRequestResponder: MockPresentationResponder(), + openIdResponder: MockPresentationResponder(), manifestResolver: MockManifestResolver(), verifiableCredentialRequester: MockVerifiedIdRequester()) @@ -187,7 +187,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, - presentationRequestResponder: MockPresentationResponder(), + openIdResponder: MockPresentationResponder(), manifestResolver: MockManifestResolver(), verifiableCredentialRequester: MockVerifiedIdRequester()) @@ -233,7 +233,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, - presentationRequestResponder: MockPresentationResponder(), + openIdResponder: MockPresentationResponder(), manifestResolver: MockManifestResolver(mockGetRequestCallback: mockResolveContract), verifiableCredentialRequester: MockVerifiedIdRequester()) @@ -283,7 +283,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, - presentationRequestResponder: MockPresentationResponder(), + openIdResponder: MockPresentationResponder(), manifestResolver: MockManifestResolver(mockGetRequestCallback: mockResolveContract), verifiableCredentialRequester: MockVerifiedIdRequester()) @@ -340,7 +340,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, - presentationRequestResponder: MockPresentationResponder(), + openIdResponder: MockPresentationResponder(), manifestResolver: MockManifestResolver(mockGetRequestCallback: mockResolveContract), verifiableCredentialRequester: MockVerifiedIdRequester()) @@ -399,7 +399,7 @@ class OpenIdRequestHandlerTests: XCTestCase { let mockRawRequest = MockOpenIdRawRequest(raw: Data(), type: .Issuance) let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: mockMapper) let handler = OpenIdRequestHandler(configuration: configuration, - presentationRequestResponder: MockPresentationResponder(), + openIdResponder: MockPresentationResponder(), manifestResolver: MockManifestResolver(mockGetRequestCallback: mockResolveContract), verifiableCredentialRequester: MockVerifiedIdRequester()) diff --git a/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift index b3c4c6eb..0bf5514a 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/Resolvers/OpenIdURLRequestResolverTests.swift @@ -114,7 +114,7 @@ class OpenIdURLRequestResolverTests: XCTestCase { // Arrange let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: Mapper()) let mockHandler = OpenIdRequestHandler(configuration: configuration, - presentationRequestResponder: MockPresentationResponder(), + openIdResponder: MockPresentationResponder(), manifestResolver: MockManifestResolver(), verifiableCredentialRequester: MockVerifiedIdRequester()) let resolver = OpenIdURLRequestResolver(openIdResolver: MockOpenIdForVCResolver(), configuration: configuration) From 9f574d501b518febbe56ec2130e93ca0352927e9 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Mar 2023 10:35:43 -0500 Subject: [PATCH 320/373] Add placeholder files for extension and content tests. --- .../WalletLibrary.xcodeproj/project.pbxproj | 8 ++ ...ationResponseContainerExtensionTests.swift | 88 +++++++++++++++++++ .../IssuanceRequestContentTests.swift | 67 ++++++++++++++ 3 files changed, 163 insertions(+) create mode 100644 WalletLibrary/WalletLibraryTests/Extensions/Entities/PresentationResponseContainerExtensionTests.swift create mode 100644 WalletLibrary/WalletLibraryTests/Requests/IssuanceRequestContentTests.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 738630d4..87f324f6 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -104,6 +104,8 @@ 55DECBE729B92E3900D5C802 /* PresentationResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECBE629B92E3900D5C802 /* PresentationResponse.swift */; }; 55DECBE929B934B900D5C802 /* IssuanceRequestContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECBE829B934B900D5C802 /* IssuanceRequestContent.swift */; }; 55DECBEB29B954F500D5C802 /* PresentationResponseContainer+WalletLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECBEA29B954F500D5C802 /* PresentationResponseContainer+WalletLibrary.swift */; }; + 55DECBED29BA29FB00D5C802 /* PresentationResponseContainerExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECBEC29BA29FB00D5C802 /* PresentationResponseContainerExtensionTests.swift */; }; + 55DECBEF29BA2BEE00D5C802 /* IssuanceRequestContentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECBEE29BA2BEE00D5C802 /* IssuanceRequestContentTests.swift */; }; 55E2F06C299553300008010D /* PresentationRequest+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */; }; 55E2F06F299553B40008010D /* OpenIdRequestHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F06E299553B40008010D /* OpenIdRequestHandlerTests.swift */; }; 55E2F071299554110008010D /* LibraryConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F070299554110008010D /* LibraryConfiguration.swift */; }; @@ -398,6 +400,8 @@ 55DECBE629B92E3900D5C802 /* PresentationResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentationResponse.swift; sourceTree = ""; }; 55DECBE829B934B900D5C802 /* IssuanceRequestContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssuanceRequestContent.swift; sourceTree = ""; }; 55DECBEA29B954F500D5C802 /* PresentationResponseContainer+WalletLibrary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationResponseContainer+WalletLibrary.swift"; sourceTree = ""; }; + 55DECBEC29BA29FB00D5C802 /* PresentationResponseContainerExtensionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentationResponseContainerExtensionTests.swift; sourceTree = ""; }; + 55DECBEE29BA2BEE00D5C802 /* IssuanceRequestContentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssuanceRequestContentTests.swift; sourceTree = ""; }; 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PresentationRequest+Mappable.swift"; sourceTree = ""; }; 55E2F06E299553B40008010D /* OpenIdRequestHandlerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenIdRequestHandlerTests.swift; sourceTree = ""; }; 55E2F070299554110008010D /* LibraryConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryConfiguration.swift; sourceTree = ""; }; @@ -784,6 +788,7 @@ isa = PBXGroup; children = ( 559BD4E4299EEA3000BD61AC /* IssuanceResponseContainerExtensionTests.swift */, + 55DECBEC29BA29FB00D5C802 /* PresentationResponseContainerExtensionTests.swift */, ); path = Entities; sourceTree = ""; @@ -818,6 +823,7 @@ 559BD4E9299EEAA200BD61AC /* Requirements */, 55A81C062991BB2C002C259A /* RequestResolverFactoryTests.swift */, 55A81C082991BB3A002C259A /* RequestHandlerFactoryTests.swift */, + 55DECBEE29BA2BEE00D5C802 /* IssuanceRequestContentTests.swift */, ); path = Requests; sourceTree = ""; @@ -1480,6 +1486,7 @@ 559BD4EB299EEAC400BD61AC /* GroupRequirementTests.swift in Sources */, 55A81C072991BB2C002C259A /* RequestResolverFactoryTests.swift in Sources */, 553CC09B29A96ECD005A5FD6 /* MockIssuanceResponseContaining.swift in Sources */, + 55DECBEF29BA2BEE00D5C802 /* IssuanceRequestContentTests.swift in Sources */, 559BD4EF299EEADF00BD61AC /* AccessTokenRequirementTests.swift in Sources */, 55A81C102991C211002C259A /* MockResolver.swift in Sources */, 559BD4E5299EEA3000BD61AC /* IssuanceResponseContainerExtensionTests.swift in Sources */, @@ -1489,6 +1496,7 @@ 550A91952994025C0014D030 /* OpenIdURLRequestResolverTests.swift in Sources */, 559BD3052995AB8F00BD61AC /* PresentationDefinition+MappableTests.swift in Sources */, 55A81C0D2991BF86002C259A /* MockInput.swift in Sources */, + 55DECBED29BA29FB00D5C802 /* PresentationResponseContainerExtensionTests.swift in Sources */, 559BD3AF299AF42C00BD61AC /* MockManifestResolver.swift in Sources */, 553CC09929A96C38005A5FD6 /* MockVerifiedId.swift in Sources */, 559BD4F8299EED5700BD61AC /* MockRawManifest.swift in Sources */, diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Entities/PresentationResponseContainerExtensionTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Entities/PresentationResponseContainerExtensionTests.swift new file mode 100644 index 00000000..b0ca4b68 --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Extensions/Entities/PresentationResponseContainerExtensionTests.swift @@ -0,0 +1,88 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import XCTest +import VCEntities +import VCToken +@testable import WalletLibrary + +class PresentationResponseContainerExtensionTests: XCTestCase { + + enum MockError: Error { + case expectedInvalidRequirement + case secondExpectedInvalidRequirement + case thirdExpectedInvalidRequirement + } + + func testInit_WithInvalidOpenIdRawRequest_ThrowsError() throws { + + // Arrange + let mockRawRequest = MockOpenIdRawRequest(raw: nil) + + // Act + XCTAssertThrowsError(try PresentationResponseContainer(rawRequest: mockRawRequest)) { error in + // Assert + XCTAssert(error is PresentationResponseError) + XCTAssertEqual(error as? PresentationResponseError, PresentationResponseError.unableToCastVCSDKPresentationRequestFromRawRequestOfType("MockOpenIdRawRequest")) + } + } + + func testInit_WithValidInput_ReturnPresentationResponseContainer() throws { + // Arrange + let rawPresentationRequest = createPresentationRequest() + + // Act + let result = try PresentationResponseContainer(rawRequest: rawPresentationRequest) + + // Assert + XCTAssertEqual(result.audienceDid, "expectedAudienceDid") + XCTAssertEqual(result.audienceUrl, "expectedAudienceUrl") + XCTAssertEqual(result.nonce, "expectedNonce") + XCTAssert(result.requestVCMap.isEmpty) + } + + func testAddRequirement_WithInvalidRequirementType_ThrowsError() async throws { + // Arrange + let mockPresentationRequest = createPresentationRequest() + let invalidRequirement = MockRequirement(id: "mockRequirement") + var presentationResponse = try PresentationResponseContainer(rawRequest: mockPresentationRequest) + + // Act + XCTAssertThrowsError(try presentationResponse.add(requirement: invalidRequirement)) { error in + // Assert + XCTAssert(error is PresentationResponseError) + XCTAssertEqual(error as? PresentationResponseError, + PresentationResponseError.unsupportedRequirementOfType("MockRequirement")) + } + } + + func testAddRequirement_WithVerifiedIdRequirement_AddsVCToMap() async throws { + // TODO + } + + func testAddRequirement_WithMultipleVerifiedIdRequirements_AddsVCsToMap() async throws { + // TODO + } + + private func createPresentationRequest() -> PresentationRequest { + let mockClaims = PresentationRequestClaims(jti: "", + clientID: "expectedAudienceDid", + redirectURI: "expectedAudienceUrl", + responseMode: "", + responseType: "", + claims: nil, + state: "", + nonce: "expectedNonce", + scope: "", + prompt: "", + registration: nil, + iat: nil, + exp: nil, + pin: nil) + let mockToken = PresentationRequestToken(headers: Header(), content: mockClaims)! + let rawPresentationRequest = PresentationRequest(from: mockToken, linkedDomainResult: LinkedDomainResult.linkedDomainMissing) + return rawPresentationRequest + } +} diff --git a/WalletLibrary/WalletLibraryTests/Requests/IssuanceRequestContentTests.swift b/WalletLibrary/WalletLibraryTests/Requests/IssuanceRequestContentTests.swift new file mode 100644 index 00000000..fe8c511f --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Requests/IssuanceRequestContentTests.swift @@ -0,0 +1,67 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import XCTest +@testable import WalletLibrary + +class IssuanceRequestContentTests: XCTestCase { + + func testAddInjecteIdToken_WithIdTokenRequirementAndNoPin_CreatesGroupRequirement() async throws { + + // Arrange + let requirement = SelfAttestedClaimRequirement(encrypted: false, + required: true, + claim: "") + + // Act + XCTAssertThrowsError(try requirement.validate()) { error in + // Assert + XCTAssert(error is SelfAttestedClaimRequirementError) + XCTAssertEqual(error as? SelfAttestedClaimRequirementError, .selfAttestedClaimRequirementHasNotBeenFulfilled) + } + } + + func testAddInjecteIdToken_WithIdTokenRequirementAndPin_CreatesGroupRequirement() async throws { + + // Arrange + let requirement = SelfAttestedClaimRequirement(encrypted: false, + required: true, + claim: "") + requirement.fulfill(with: "mock value") + + // Act / Assert + XCTAssertNoThrow(try requirement.validate()) + } + + func testAddInjecteIdToken_WithGroupRequirementAndNoPin_AddsIdTokenRequirement() async throws { + + // Arrange + let requirement = SelfAttestedClaimRequirement(encrypted: false, + required: true, + claim: "") + let mockValue = "mock value" + + // Act + requirement.fulfill(with: mockValue) + + // Assert + XCTAssertEqual(requirement.value, mockValue) + } + + func testAddInjecteIdToken_WithGroupRequirementAndPin_AddsIdTokenAndPinRequirement() async throws { + + // Arrange + let requirement = SelfAttestedClaimRequirement(encrypted: false, + required: true, + claim: "") + let mockValue = "mock value" + + // Act + requirement.fulfill(with: mockValue) + + // Assert + XCTAssertEqual(requirement.value, mockValue) + } +} From 88fa3015c351b11cd4b32beac6669b349737df2c Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Mar 2023 10:36:00 -0500 Subject: [PATCH 321/373] Update PresentationResponseContainer+WalletLibrary.swift --- .../PresentationResponseContainer+WalletLibrary.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationResponseContainer+WalletLibrary.swift b/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationResponseContainer+WalletLibrary.swift index e763e538..efc7650d 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationResponseContainer+WalletLibrary.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationResponseContainer+WalletLibrary.swift @@ -18,14 +18,14 @@ enum PresentationResponseError: Error, Equatable { */ extension VCEntities.PresentationResponseContainer: PresentationResponse { - init(from request: any OpenIdRawRequest) throws { + init(rawRequest: any OpenIdRawRequest) throws { - guard let request = request as? PresentationRequest else { - let requestType = String(describing: type(of: request)) + guard let presentationRequest = rawRequest as? VCEntities.PresentationRequest else { + let requestType = String(describing: type(of: rawRequest)) throw PresentationResponseError.unableToCastVCSDKPresentationRequestFromRawRequestOfType(requestType) } - try self.init(from: request) + try self.init(from: presentationRequest) } mutating func add(requirement: Requirement) throws { From 9ec9826730ef8cf4368c45fe7d6bb040583b31dc Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Mar 2023 10:36:03 -0500 Subject: [PATCH 322/373] Update IssuanceRequestContent.swift --- .../WalletLibrary/Requests/IssuanceRequestContent.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/IssuanceRequestContent.swift b/WalletLibrary/WalletLibrary/Requests/IssuanceRequestContent.swift index d59c8c3c..35bd521f 100644 --- a/WalletLibrary/WalletLibrary/Requests/IssuanceRequestContent.swift +++ b/WalletLibrary/WalletLibrary/Requests/IssuanceRequestContent.swift @@ -44,6 +44,4 @@ struct IssuanceRequestContent { } } } - } - From 463f6d38cc2a7e91a59cb41ca39691f56fa6c8fb Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Mar 2023 11:12:33 -0500 Subject: [PATCH 323/373] Complete test cases for issuance request content. --- .../Requests/IssuanceRequestContent.swift | 20 ++- .../IssuanceRequestContentTests.swift | 150 ++++++++++++++---- 2 files changed, 136 insertions(+), 34 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/IssuanceRequestContent.swift b/WalletLibrary/WalletLibrary/Requests/IssuanceRequestContent.swift index 35bd521f..41fa7420 100644 --- a/WalletLibrary/WalletLibrary/Requests/IssuanceRequestContent.swift +++ b/WalletLibrary/WalletLibrary/Requests/IssuanceRequestContent.swift @@ -10,6 +10,10 @@ */ struct IssuanceRequestContent { + private struct Constants { + static let IdTokenHintKey = "https://self-issued.me" + } + let style: RequesterStyle var requirement: Requirement @@ -20,21 +24,29 @@ struct IssuanceRequestContent { switch (requirement) { case var groupRequirement as GroupRequirement: repopulateGroupRequirementIfInjectedIdTokenExists(injectedIdToken: injectedIdToken, - groupRequirement: &groupRequirement) + groupRequirement: groupRequirement) case let idTokenRequirement as IdTokenRequirement: + addInjectedIdTokenHintToIdTokenRequirement(injectedIdToken: injectedIdToken, + idTokenRequirement: idTokenRequirement) + default: + return + } + } + + private mutating func addInjectedIdTokenHintToIdTokenRequirement(injectedIdToken: InjectedIdToken, + idTokenRequirement: IdTokenRequirement) { + if idTokenRequirement.configuration.absoluteString == Constants.IdTokenHintKey { idTokenRequirement.fulfill(with: injectedIdToken.rawToken) if let pinRequirement = injectedIdToken.pin { requirement = GroupRequirement(required: false, requirements: [idTokenRequirement, pinRequirement], requirementOperator: .ALL) } - default: - return } } private func repopulateGroupRequirementIfInjectedIdTokenExists(injectedIdToken: InjectedIdToken, - groupRequirement: inout GroupRequirement) { + groupRequirement: GroupRequirement) { for requirement in groupRequirement.requirements { if let idTokenRequirement = requirement as? IdTokenRequirement { idTokenRequirement.fulfill(with: injectedIdToken.rawToken) diff --git a/WalletLibrary/WalletLibraryTests/Requests/IssuanceRequestContentTests.swift b/WalletLibrary/WalletLibraryTests/Requests/IssuanceRequestContentTests.swift index fe8c511f..0261e5b6 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/IssuanceRequestContentTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/IssuanceRequestContentTests.swift @@ -8,60 +8,150 @@ import XCTest class IssuanceRequestContentTests: XCTestCase { - func testAddInjecteIdToken_WithIdTokenRequirementAndNoPin_CreatesGroupRequirement() async throws { + func testAddInjectedIdToken_WithIdTokenRequirementAndNoPin_AddIdTokenToRequirement() async throws { // Arrange - let requirement = SelfAttestedClaimRequirement(encrypted: false, - required: true, - claim: "") + let idTokenRequirement = IdTokenRequirement(encrypted: false, + required: false, + configuration: URL(string: "https://self-issued.me")!, + clientId: "mockClientId", + redirectUri: "mockRedirectUri", + scope: nil) + var issuanceRequestContent = IssuanceRequestContent(style: Manifest2022IssuerStyle(name: "mockIssuerName"), + requirement: idTokenRequirement, + rootOfTrust: RootOfTrust(verified: false, source: nil)) + let injectedIdToken = InjectedIdToken(rawToken: "mockRawToken", pin: nil) // Act - XCTAssertThrowsError(try requirement.validate()) { error in - // Assert - XCTAssert(error is SelfAttestedClaimRequirementError) - XCTAssertEqual(error as? SelfAttestedClaimRequirementError, .selfAttestedClaimRequirementHasNotBeenFulfilled) - } + issuanceRequestContent.addRequirement(from: injectedIdToken) + + // Assert + XCTAssert(issuanceRequestContent.requirement is IdTokenRequirement) + XCTAssertEqual((issuanceRequestContent.requirement as? IdTokenRequirement)?.idToken, injectedIdToken.rawToken) + + } + + func testAddInjectedIdToken_WithIdTokenRequirementWithInvalidConfigurationValue_DoesNothing() async throws { + + // Arrange + let idTokenRequirement = IdTokenRequirement(encrypted: false, + required: false, + configuration: URL(string: "https://invalidConfiguration.me")!, + clientId: "mockClientId", + redirectUri: "mockRedirectUri", + scope: nil) + var issuanceRequestContent = IssuanceRequestContent(style: Manifest2022IssuerStyle(name: "mockIssuerName"), + requirement: idTokenRequirement, + rootOfTrust: RootOfTrust(verified: false, source: nil)) + let injectedIdToken = InjectedIdToken(rawToken: "mockRawToken", pin: nil) + + // Act + issuanceRequestContent.addRequirement(from: injectedIdToken) + + // Assert + XCTAssert(issuanceRequestContent.requirement is IdTokenRequirement) + XCTAssertNil((issuanceRequestContent.requirement as? IdTokenRequirement)?.idToken) } - func testAddInjecteIdToken_WithIdTokenRequirementAndPin_CreatesGroupRequirement() async throws { + func testAddInjectedIdToken_WithIdTokenRequirementAndPin_CreatesGroupRequirement() async throws { // Arrange - let requirement = SelfAttestedClaimRequirement(encrypted: false, - required: true, - claim: "") - requirement.fulfill(with: "mock value") + let idTokenRequirement = IdTokenRequirement(encrypted: false, + required: false, + configuration: URL(string: "https://self-issued.me")!, + clientId: "mockClientId", + redirectUri: "mockRedirectUri", + scope: nil) + var issuanceRequestContent = IssuanceRequestContent(style: Manifest2022IssuerStyle(name: "mockIssuerName"), + requirement: idTokenRequirement, + rootOfTrust: RootOfTrust(verified: false, source: nil)) + let pinRequirement = PinRequirement(required: false, + length: 4, + type: "numeric", + salt: nil) + let injectedIdToken = InjectedIdToken(rawToken: "mockRawToken", pin: pinRequirement) - // Act / Assert - XCTAssertNoThrow(try requirement.validate()) + // Act + issuanceRequestContent.addRequirement(from: injectedIdToken) + + // Assert + XCTAssert(issuanceRequestContent.requirement is GroupRequirement) + XCTAssertEqual((issuanceRequestContent.requirement as? GroupRequirement)?.requirements.count, 2) + XCTAssertIdentical((issuanceRequestContent.requirement as? GroupRequirement)?.requirements.first as AnyObject, + idTokenRequirement as AnyObject) + XCTAssertEqual(((issuanceRequestContent.requirement as? GroupRequirement)?.requirements.first as? IdTokenRequirement)?.idToken, + injectedIdToken.rawToken) + XCTAssertIdentical((issuanceRequestContent.requirement as? GroupRequirement)?.requirements[1] as AnyObject, + pinRequirement as AnyObject) } - func testAddInjecteIdToken_WithGroupRequirementAndNoPin_AddsIdTokenRequirement() async throws { + func testAddInjectedIdToken_WithGroupRequirementAndNoPin_AddsIdTokenRequirement() async throws { // Arrange - let requirement = SelfAttestedClaimRequirement(encrypted: false, - required: true, - claim: "") - let mockValue = "mock value" + let mockRequirement = MockRequirement(id: "mockRequirement") + let idTokenRequirement = IdTokenRequirement(encrypted: false, + required: false, + configuration: URL(string: "https://self-issued.me")!, + clientId: "mockClientId", + redirectUri: "mockRedirectUri", + scope: nil) + let groupRequirement = GroupRequirement(required: false, + requirements: [mockRequirement, idTokenRequirement], + requirementOperator: .ALL) + var issuanceRequestContent = IssuanceRequestContent(style: Manifest2022IssuerStyle(name: "mockIssuerName"), + requirement: groupRequirement, + rootOfTrust: RootOfTrust(verified: false, source: nil)) + let injectedIdToken = InjectedIdToken(rawToken: "mockRawToken", pin: nil) // Act - requirement.fulfill(with: mockValue) + issuanceRequestContent.addRequirement(from: injectedIdToken) // Assert - XCTAssertEqual(requirement.value, mockValue) + XCTAssert(issuanceRequestContent.requirement is GroupRequirement) + XCTAssertEqual((issuanceRequestContent.requirement as? GroupRequirement)?.requirements.count, 2) + XCTAssertEqual((issuanceRequestContent.requirement as? GroupRequirement)?.requirements.first as? MockRequirement, + mockRequirement) + XCTAssertIdentical((issuanceRequestContent.requirement as? GroupRequirement)?.requirements[1] as AnyObject, + idTokenRequirement as AnyObject) + XCTAssertEqual(((issuanceRequestContent.requirement as? GroupRequirement)?.requirements[1] as? IdTokenRequirement)?.idToken, + injectedIdToken.rawToken) } - func testAddInjecteIdToken_WithGroupRequirementAndPin_AddsIdTokenAndPinRequirement() async throws { + func testAddInjectedIdToken_WithGroupRequirementAndPin_AddsIdTokenAndPinRequirement() async throws { // Arrange - let requirement = SelfAttestedClaimRequirement(encrypted: false, - required: true, - claim: "") - let mockValue = "mock value" + let mockRequirement = MockRequirement(id: "mockRequirement") + let idTokenRequirement = IdTokenRequirement(encrypted: false, + required: false, + configuration: URL(string: "https://self-issued.me")!, + clientId: "mockClientId", + redirectUri: "mockRedirectUri", + scope: nil) + let groupRequirement = GroupRequirement(required: false, + requirements: [mockRequirement, idTokenRequirement], + requirementOperator: .ALL) + var issuanceRequestContent = IssuanceRequestContent(style: Manifest2022IssuerStyle(name: "mockIssuerName"), + requirement: groupRequirement, + rootOfTrust: RootOfTrust(verified: false, source: nil)) + let pinRequirement = PinRequirement(required: false, + length: 4, + type: "numeric", + salt: nil) + let injectedIdToken = InjectedIdToken(rawToken: "mockRawToken", pin: pinRequirement) // Act - requirement.fulfill(with: mockValue) + issuanceRequestContent.addRequirement(from: injectedIdToken) // Assert - XCTAssertEqual(requirement.value, mockValue) + XCTAssert(issuanceRequestContent.requirement is GroupRequirement) + XCTAssertEqual((issuanceRequestContent.requirement as? GroupRequirement)?.requirements.count, 3) + XCTAssertEqual((issuanceRequestContent.requirement as? GroupRequirement)?.requirements.first as? MockRequirement, + mockRequirement) + XCTAssertIdentical((issuanceRequestContent.requirement as? GroupRequirement)?.requirements[1] as AnyObject, + idTokenRequirement as AnyObject) + XCTAssertEqual(((issuanceRequestContent.requirement as? GroupRequirement)?.requirements[1] as? IdTokenRequirement)?.idToken, + injectedIdToken.rawToken) + XCTAssertIdentical((issuanceRequestContent.requirement as? GroupRequirement)?.requirements[2] as AnyObject, + pinRequirement as AnyObject) } } From db429d368b88ee9a4138ac3f6e57f05d5ab5b150 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Mar 2023 12:29:24 -0500 Subject: [PATCH 324/373] fix nits before PR. --- .../Requests/OpenId/OpenIdPresentationRequest.swift | 2 -- .../Entities/PresentationResponseContainerExtensionTests.swift | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/OpenId/OpenIdPresentationRequest.swift b/WalletLibrary/WalletLibrary/Requests/OpenId/OpenIdPresentationRequest.swift index b3454a7e..3acec84b 100644 --- a/WalletLibrary/WalletLibrary/Requests/OpenId/OpenIdPresentationRequest.swift +++ b/WalletLibrary/WalletLibrary/Requests/OpenId/OpenIdPresentationRequest.swift @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import VCEntities - /** * Presentation Requst that is Open Id specific. * TODO: we will need open id specific data to implement complete and cancel. diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Entities/PresentationResponseContainerExtensionTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Entities/PresentationResponseContainerExtensionTests.swift index b0ca4b68..379ad6c5 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Entities/PresentationResponseContainerExtensionTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Entities/PresentationResponseContainerExtensionTests.swift @@ -25,7 +25,8 @@ class PresentationResponseContainerExtensionTests: XCTestCase { XCTAssertThrowsError(try PresentationResponseContainer(rawRequest: mockRawRequest)) { error in // Assert XCTAssert(error is PresentationResponseError) - XCTAssertEqual(error as? PresentationResponseError, PresentationResponseError.unableToCastVCSDKPresentationRequestFromRawRequestOfType("MockOpenIdRawRequest")) + XCTAssertEqual(error as? PresentationResponseError, + PresentationResponseError.unableToCastVCSDKPresentationRequestFromRawRequestOfType("MockOpenIdRawRequest")) } } From 558e1a04a13b33125f1a98244175c77667abf77d Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Mar 2023 18:35:56 -0500 Subject: [PATCH 325/373] Implement methods in VerifiedIdRequirement. --- .../Requirements/VerifiedIdRequirement.swift | 77 +++++++++++++++++-- 1 file changed, 71 insertions(+), 6 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Requirements/VerifiedIdRequirement.swift b/WalletLibrary/WalletLibrary/Requests/Requirements/VerifiedIdRequirement.swift index 96b97c3c..1183dde9 100644 --- a/WalletLibrary/WalletLibrary/Requests/Requirements/VerifiedIdRequirement.swift +++ b/WalletLibrary/WalletLibrary/Requests/Requirements/VerifiedIdRequirement.swift @@ -3,6 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +enum VerifiedIdRequirementError: Error { + case verifiedIdDoesNotMeetConstraints + case requirementHasNotBeenFulfilled +} /** * Information to describe Verified IDs required. */ @@ -23,24 +27,85 @@ public class VerifiedIdRequirement: Requirement { /// An optional property for information needed for issuance during presentation flow. public let issuanceOptions: [VerifiedIdRequestInput] - /// TODO: helper method that returns verified id that match the requirement from a list of verified ids. - public func getMatches(verifiedIds: [VerifiedId]) -> [VerifiedId] { - return [] - } + /// Optional id of requirement defined by the request. + let id: String? + + /// Constraint that define how the requirement can be satisfied. + let constraint: VerifiedIdConstraint + + /// The verified id that was selected. + var selectedVerifiedId: VerifiedId? init(encrypted: Bool, required: Bool, types: [String], purpose: String?, - issuanceOptions: [VerifiedIdRequestInput]) { + issuanceOptions: [VerifiedIdRequestInput], + id: String?, + constraint: VerifiedIdConstraint) { self.encrypted = encrypted self.required = required self.types = types self.purpose = purpose self.issuanceOptions = issuanceOptions + self.id = id + self.constraint = constraint } public func validate() throws { - throw VerifiedIdClientError.TODO(message: "implement validate") + guard let selectedVerifiedId = self.selectedVerifiedId else { + throw VerifiedIdRequirementError.requirementHasNotBeenFulfilled + } + + guard constraint.doesMatch(verifiedId: selectedVerifiedId) else { + throw VerifiedIdRequirementError.verifiedIdDoesNotMeetConstraints + } + } + + public func getMatches(verifiedIds: [VerifiedId]) -> [VerifiedId] { + return verifiedIds.filter { + constraint.doesMatch(verifiedId: $0) + } + } + + public func fulfill(with verifiedId: VerifiedId) throws { + if constraint.doesMatch(verifiedId: verifiedId) { + self.selectedVerifiedId = verifiedId + return + } + + throw VerifiedIdRequirementError.verifiedIdDoesNotMeetConstraints + } +} + +protocol VerifiedIdConstraint { + func doesMatch(verifiedId: VerifiedId) -> Bool + + func doesMatch(verifiedId: VerifiedId) throws +} + +enum GroupConstraintOperator { + case ANY + case ALL +} + +struct VerifiedIdGroupConstraint: VerifiedIdConstraint { + + let constraints: [VerifiedIdConstraint] + + let constraintOperator: GroupConstraintOperator + + init(constraints: [VerifiedIdConstraint], + constraintOperator: GroupConstraintOperator) { + self.constraints = constraints + self.constraintOperator = constraintOperator + } + + func doesMatch(verifiedId: VerifiedId) -> Bool { + return false + } + + func doesMatch(verifiedId: VerifiedId) throws { + throw VerifiedIdClientError.TODO(message: "implement") } } From 4552f0271440ca0374d54118074bae6561c7f62e Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Mar 2023 18:36:03 -0500 Subject: [PATCH 326/373] Update IssuanceRequestContent.swift --- .../WalletLibrary/Requests/IssuanceRequestContent.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/IssuanceRequestContent.swift b/WalletLibrary/WalletLibrary/Requests/IssuanceRequestContent.swift index 41fa7420..a8f3fe66 100644 --- a/WalletLibrary/WalletLibrary/Requests/IssuanceRequestContent.swift +++ b/WalletLibrary/WalletLibrary/Requests/IssuanceRequestContent.swift @@ -16,13 +16,13 @@ struct IssuanceRequestContent { let style: RequesterStyle - var requirement: Requirement + private(set) var requirement: Requirement let rootOfTrust: RootOfTrust mutating func addRequirement(from injectedIdToken: InjectedIdToken) { switch (requirement) { - case var groupRequirement as GroupRequirement: + case let groupRequirement as GroupRequirement: repopulateGroupRequirementIfInjectedIdTokenExists(injectedIdToken: injectedIdToken, groupRequirement: groupRequirement) case let idTokenRequirement as IdTokenRequirement: From 814137c7b1ab04577c1210430fe035ed927c4745 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Thu, 9 Mar 2023 18:36:35 -0500 Subject: [PATCH 327/373] Add id and constraints to VerifiedIdRequirement instances. --- .../PresentationDescriptor+Mappable.swift | 4 ++- ...PresentationInputDescriptor+Mappable.swift | 7 ++++- ...AttestationsDescriptor+MappableTests.swift | 15 ++++++++--- ...PresentationDescriptor+MappableTests.swift | 4 ++- ...PresentationDefinition+MappableTests.swift | 16 ++++++++---- ...ntationInputDescriptor+MappableTests.swift | 24 ++++++++++++----- .../PresentationRequest+MappableTests.swift | 25 ++++++++++++++---- .../Handlers/OpenIdRequestHandlerTests.swift | 26 ++++++++++++++----- 8 files changed, 93 insertions(+), 28 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+Mappable.swift index 4939ed84..0df3942c 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+Mappable.swift @@ -26,6 +26,8 @@ extension VCEntities.PresentationDescriptor: Mappable { required: presentationRequired ?? false, types: [credentialType], purpose: nil, - issuanceOptions: issuanceOptions ?? []) + issuanceOptions: issuanceOptions ?? [], + id: nil, + constraint: VerifiedIdGroupConstraint(constraints: [], constraintOperator: .ALL)) } } diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+Mappable.swift index ead5a6c2..814952af 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+Mappable.swift @@ -35,10 +35,15 @@ extension VCEntities.PresentationInputDescriptor: Mappable { return nil } + // TODO + let constraint = VerifiedIdGroupConstraint(constraints: [], constraintOperator: .ALL) + return VerifiedIdRequirement(encrypted: false, required: true, types: types, purpose: purpose, - issuanceOptions: issuanceOptions ?? []) + issuanceOptions: issuanceOptions ?? [], + id: id, + constraint: constraint) } } diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+MappableTests.swift index 2b127ef9..466c4f7e 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+MappableTests.swift @@ -126,7 +126,10 @@ class AttestationsDescriptorMappingTests: XCTestCase { required: true, types: [], purpose: nil, - issuanceOptions: []) + issuanceOptions: [], + id: nil, + constraint: VerifiedIdGroupConstraint(constraints: [], + constraintOperator: .ALL)) let mockPresentationDescriptor = PresentationDescriptor(claims: [], credentialType: "mock type") let attestations = AttestationsDescriptor(presentations: [mockPresentationDescriptor]) @@ -231,7 +234,10 @@ class AttestationsDescriptorMappingTests: XCTestCase { required: true, types: [], purpose: nil, - issuanceOptions: []) + issuanceOptions: [], + id: nil, + constraint: VerifiedIdGroupConstraint(constraints: [], + constraintOperator: .ALL)) let expectedIdTokenRequirement = IdTokenRequirement(encrypted: false, required: true, configuration: URL(string: "https://test.com")!, @@ -299,7 +305,10 @@ class AttestationsDescriptorMappingTests: XCTestCase { required: true, types: [], purpose: nil, - issuanceOptions: []) + issuanceOptions: [], + id: nil, + constraint: VerifiedIdGroupConstraint(constraints: [], + constraintOperator: .ALL)) let expectedIdTokenRequirement = IdTokenRequirement(encrypted: false, required: true, configuration: URL(string: "https://test.com")!, diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+MappableTests.swift index dd6b7fa8..5e7c8793 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+MappableTests.swift @@ -112,7 +112,9 @@ class PresentationDescriptorMappingTests: XCTestCase { required: required ?? false, types: [credentialType], purpose: nil, - issuanceOptions: expectedIssuanceOptions ?? []) + issuanceOptions: expectedIssuanceOptions ?? [], + id: nil, + constraint: VerifiedIdGroupConstraint(constraints: [], constraintOperator: .ALL)) return (input, expectedResult) } } diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+MappableTests.swift index 48ba189d..cf4dc9b9 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+MappableTests.swift @@ -45,10 +45,13 @@ class PresentationDefinitionMappingTests: XCTestCase { // Arrange let expectedVerifiedIdRequirement = VerifiedIdRequirement(encrypted: false, - required: false, - types: [], - purpose: nil, - issuanceOptions: []) + required: false, + types: [], + purpose: nil, + issuanceOptions: [], + id: nil, + constraint: VerifiedIdGroupConstraint(constraints: [], + constraintOperator: .ALL)) let inputDescriptor = PresentationInputDescriptor(id: nil, schema: nil, issuanceMetadata: nil, @@ -84,7 +87,10 @@ class PresentationDefinitionMappingTests: XCTestCase { required: false, types: [], purpose: nil, - issuanceOptions: []) + issuanceOptions: [], + id: nil, + constraint: VerifiedIdGroupConstraint(constraints: [], + constraintOperator: .ALL)) let firstMockInputDescriptor = PresentationInputDescriptor(id: nil, schema: nil, issuanceMetadata: nil, diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+MappableTests.swift index 08d00649..4f3c611e 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+MappableTests.swift @@ -72,7 +72,9 @@ class PresentationInputDescriptorMappingTests: XCTestCase { required: true, types: ["mockType"], purpose: nil, - issuanceOptions: []) + issuanceOptions: [], + id: nil, + constraint: VerifiedIdGroupConstraint(constraints: [], constraintOperator: .ALL)) let presentationInputDescriptor = PresentationInputDescriptor(id: nil, schema: [mockSchema], issuanceMetadata: nil, @@ -104,7 +106,9 @@ class PresentationInputDescriptorMappingTests: XCTestCase { required: true, types: [firstType, secondType, thirdType], purpose: nil, - issuanceOptions: []) + issuanceOptions: [], + id: nil, + constraint: VerifiedIdGroupConstraint(constraints: [], constraintOperator: .ALL)) let presentationInputDescriptor = PresentationInputDescriptor(id: nil, schema: [firstSchema, secondSchema, thirdSchema], issuanceMetadata: nil, @@ -131,7 +135,9 @@ class PresentationInputDescriptorMappingTests: XCTestCase { required: true, types: ["mockType"], purpose: nil, - issuanceOptions: []) + issuanceOptions: [], + id: nil, + constraint: VerifiedIdGroupConstraint(constraints: [], constraintOperator: .ALL)) let presentationInputDescriptor = PresentationInputDescriptor(id: nil, schema: [mockSchema], issuanceMetadata: nil, @@ -160,7 +166,9 @@ class PresentationInputDescriptorMappingTests: XCTestCase { required: true, types: ["mockType"], purpose: nil, - issuanceOptions: []) + issuanceOptions: [], + id: nil, + constraint: VerifiedIdGroupConstraint(constraints: [], constraintOperator: .ALL)) let presentationInputDescriptor = PresentationInputDescriptor(id: nil, schema: [mockSchema], issuanceMetadata: [invalidIssuanceMetadata], @@ -198,7 +206,9 @@ class PresentationInputDescriptorMappingTests: XCTestCase { required: true, types: ["mockType"], purpose: nil, - issuanceOptions: [firstVerifiedIdRequestURL, secondVerifiedIdRequestURL]) + issuanceOptions: [firstVerifiedIdRequestURL, secondVerifiedIdRequestURL], + id: nil, + constraint: VerifiedIdGroupConstraint(constraints: [], constraintOperator: .ALL)) let issuanceMetadatas = [firstValidIssuanceMetadata, invalidIssuanceMetadata, secondValidIssuanceMetadata] let presentationInputDescriptor = PresentationInputDescriptor(id: nil, @@ -228,7 +238,9 @@ class PresentationInputDescriptorMappingTests: XCTestCase { required: true, types: ["mockType"], purpose: purpose, - issuanceOptions: []) + issuanceOptions: [], + id: nil, + constraint: VerifiedIdGroupConstraint(constraints: [], constraintOperator: .ALL)) let presentationInputDescriptor = PresentationInputDescriptor(id: nil, schema: [mockSchema], issuanceMetadata: nil, diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+MappableTests.swift index 39aa52d7..f6cd45ed 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+MappableTests.swift @@ -67,7 +67,10 @@ class PresentationRequestMappingTests: XCTestCase { required: false, types: [], purpose: nil, - issuanceOptions: []) + issuanceOptions: [], + id: nil, + constraint: VerifiedIdGroupConstraint(constraints: [], + constraintOperator: .ALL)) let mockPresentationDefinition = PresentationDefinition(id: nil, inputDescriptors: nil, issuance: nil) let mockRequestClaims = RequestedClaims(vpToken: RequestedVPToken(presentationDefinition: mockPresentationDefinition)) let token = createPresentationRequestToken(requestedClaims: mockRequestClaims, registration: nil) @@ -107,7 +110,10 @@ class PresentationRequestMappingTests: XCTestCase { required: false, types: [], purpose: nil, - issuanceOptions: []) + issuanceOptions: [], + id: nil, + constraint: VerifiedIdGroupConstraint(constraints: [], + constraintOperator: .ALL)) let mockPresentationDefinition = PresentationDefinition(id: nil, inputDescriptors: nil, issuance: nil) let mockRequestClaims = RequestedClaims(vpToken: RequestedVPToken(presentationDefinition: mockPresentationDefinition)) let mockRegistration = RegistrationClaims(clientName: nil, @@ -155,7 +161,10 @@ class PresentationRequestMappingTests: XCTestCase { required: false, types: [], purpose: nil, - issuanceOptions: []) + issuanceOptions: [], + id: nil, + constraint: VerifiedIdGroupConstraint(constraints: [], + constraintOperator: .ALL)) let mockPresentationDefinition = PresentationDefinition(id: nil, inputDescriptors: nil, issuance: nil) let mockRequestClaims = RequestedClaims(vpToken: RequestedVPToken(presentationDefinition: mockPresentationDefinition)) let mockRegistration = RegistrationClaims(clientName: mockRequesterName, @@ -203,7 +212,10 @@ class PresentationRequestMappingTests: XCTestCase { required: false, types: [], purpose: nil, - issuanceOptions: []) + issuanceOptions: [], + id: nil, + constraint: VerifiedIdGroupConstraint(constraints: [], + constraintOperator: .ALL)) let mockPresentationDefinition = PresentationDefinition(id: nil, inputDescriptors: nil, issuance: nil) let mockRequestClaims = RequestedClaims(vpToken: RequestedVPToken(presentationDefinition: mockPresentationDefinition)) let mockRegistration = RegistrationClaims(clientName: mockRequesterName, @@ -258,7 +270,10 @@ class PresentationRequestMappingTests: XCTestCase { required: false, types: [], purpose: nil, - issuanceOptions: []) + issuanceOptions: [], + id: nil, + constraint: VerifiedIdGroupConstraint(constraints: [], + constraintOperator: .ALL)) let mockPresentationDefinition = PresentationDefinition(id: nil, inputDescriptors: nil, issuance: nil) let mockRequestClaims = RequestedClaims(vpToken: RequestedVPToken(presentationDefinition: mockPresentationDefinition)) let mockRegistration = RegistrationClaims(clientName: mockRequesterName, diff --git a/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift index 66267dc3..f8715e83 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift @@ -128,7 +128,9 @@ class OpenIdRequestHandlerTests: XCTestCase { required: true, types: [], purpose: nil, - issuanceOptions: []) + issuanceOptions: [], + id: nil, + constraint: VerifiedIdGroupConstraint(constraints: [], constraintOperator: .ALL)) let expectedRootOfTrust = RootOfTrust(verified: true, source: "mock source") let expectedContent = PresentationRequestContent(style: expectedStyle, requirement: expectedRequirement, @@ -169,7 +171,9 @@ class OpenIdRequestHandlerTests: XCTestCase { required: true, types: [], purpose: nil, - issuanceOptions: [MockInput(mockData: "")]) + issuanceOptions: [MockInput(mockData: "")], + id: nil, + constraint: VerifiedIdGroupConstraint(constraints: [], constraintOperator: .ALL)) let expectedRootOfTrust = RootOfTrust(verified: true, source: "mock source") let expectedContent = PresentationRequestContent(style: expectedStyle, requirement: expectedRequirement, @@ -211,7 +215,9 @@ class OpenIdRequestHandlerTests: XCTestCase { required: true, types: [], purpose: nil, - issuanceOptions: [VerifiedIdRequestURL(url: issuanceOptionURL)]) + issuanceOptions: [VerifiedIdRequestURL(url: issuanceOptionURL)], + id: nil, + constraint: VerifiedIdGroupConstraint(constraints: [], constraintOperator: .ALL)) let expectedRootOfTrust = RootOfTrust(verified: true, source: "mock source") let expectedContent = PresentationRequestContent(style: expectedStyle, requirement: expectedRequirement, @@ -257,7 +263,9 @@ class OpenIdRequestHandlerTests: XCTestCase { required: true, types: [], purpose: nil, - issuanceOptions: [VerifiedIdRequestURL(url: issuanceOptionURL)]) + issuanceOptions: [VerifiedIdRequestURL(url: issuanceOptionURL)], + id: nil, + constraint: VerifiedIdGroupConstraint(constraints: [], constraintOperator: .ALL)) let expectedRootOfTrust = RootOfTrust(verified: true, source: "mock source") let expectedContent = PresentationRequestContent(style: expectedStyle, requirement: expectedRequirement, @@ -307,7 +315,10 @@ class OpenIdRequestHandlerTests: XCTestCase { required: true, types: [], purpose: nil, - issuanceOptions: [VerifiedIdRequestURL(url: issuanceOptionURL)]) + issuanceOptions: [VerifiedIdRequestURL(url: issuanceOptionURL)], + id: nil, + constraint: VerifiedIdGroupConstraint(constraints: [], + constraintOperator: .ALL)) let expectedPresentationRootOfTrust = RootOfTrust(verified: true, source: "mock source") let expectedPresentationContent = PresentationRequestContent(style: expectedPresentationStyle, requirement: expectedPresentationRequirement, @@ -364,7 +375,10 @@ class OpenIdRequestHandlerTests: XCTestCase { required: true, types: [], purpose: nil, - issuanceOptions: [VerifiedIdRequestURL(url: issuanceOptionURL)]) + issuanceOptions: [VerifiedIdRequestURL(url: issuanceOptionURL)], + id: nil, + constraint: VerifiedIdGroupConstraint(constraints: [], + constraintOperator: .ALL)) let expectedPresentationRootOfTrust = RootOfTrust(verified: true, source: "mock source") let expectedInjectedIdToken = InjectedIdToken(rawToken: "mock idToken hint", pin: nil) let expectedPresentationContent = PresentationRequestContent(style: expectedPresentationStyle, From 3b0b2b7edf465c71152e58b823eaafc07ac0174c Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 10 Mar 2023 10:58:07 -0500 Subject: [PATCH 328/373] Add type constraint to PresentationDescriptor. --- .../VCSDK/Issuance/PresentationDescriptor+Mappable.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+Mappable.swift index 0df3942c..2d70b473 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+Mappable.swift @@ -22,12 +22,15 @@ extension VCEntities.PresentationDescriptor: Mappable { return nil } + let constraint = VCTypeConstraint(type: credentialType) + + /// TODO: do we need types in VerifiedIdRequirement anymore? We display it in Auth App. return VerifiedIdRequirement(encrypted: encrypted ?? false, required: presentationRequired ?? false, types: [credentialType], purpose: nil, issuanceOptions: issuanceOptions ?? [], id: nil, - constraint: VerifiedIdGroupConstraint(constraints: [], constraintOperator: .ALL)) + constraint: constraint) } } From 6bee9a7270c41e0bdba8c69e3b8779c51c480774 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 10 Mar 2023 10:58:23 -0500 Subject: [PATCH 329/373] Add type constraint to PresentationInputDescriptor. --- ...PresentationInputDescriptor+Mappable.swift | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+Mappable.swift index 814952af..34a74037 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+Mappable.swift @@ -35,8 +35,8 @@ extension VCEntities.PresentationInputDescriptor: Mappable { return nil } - // TODO - let constraint = VerifiedIdGroupConstraint(constraints: [], constraintOperator: .ALL) + /// TODO: support presentation exchange constraints. + let constraint = getTypeConstraint(from: types) return VerifiedIdRequirement(encrypted: false, required: true, @@ -46,4 +46,19 @@ extension VCEntities.PresentationInputDescriptor: Mappable { id: id, constraint: constraint) } + + private func getTypeConstraint(from requestedTypes: [String]) -> VerifiedIdConstraint { + + if requestedTypes.count == 1, let onlyType = requestedTypes.first { + return VCTypeConstraint(type: onlyType) + } + + var typeConstraints: [VCTypeConstraint] = [] + for type in requestedTypes { + typeConstraints.append(VCTypeConstraint(type: type)) + } + + /// TODO: Is this an ANY or ALL operation? + return VerifiedIdGroupConstraint(constraints: typeConstraints, constraintOperator: .ANY) + } } From ac7068e32fe5a43b49f79f1405ac80beff069010 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 10 Mar 2023 10:58:46 -0500 Subject: [PATCH 330/373] Create type constraint. --- .../Requirements/VerifiedIdRequirement.swift | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Requirements/VerifiedIdRequirement.swift b/WalletLibrary/WalletLibrary/Requests/Requirements/VerifiedIdRequirement.swift index 1183dde9..32cfc578 100644 --- a/WalletLibrary/WalletLibrary/Requests/Requirements/VerifiedIdRequirement.swift +++ b/WalletLibrary/WalletLibrary/Requests/Requirements/VerifiedIdRequirement.swift @@ -7,6 +7,7 @@ enum VerifiedIdRequirementError: Error { case verifiedIdDoesNotMeetConstraints case requirementHasNotBeenFulfilled } + /** * Information to describe Verified IDs required. */ @@ -30,7 +31,7 @@ public class VerifiedIdRequirement: Requirement { /// Optional id of requirement defined by the request. let id: String? - /// Constraint that define how the requirement can be satisfied. + /// Constraint defines how the requirement can be fulfilled. let constraint: VerifiedIdConstraint /// The verified id that was selected. @@ -81,6 +82,7 @@ public class VerifiedIdRequirement: Requirement { protocol VerifiedIdConstraint { func doesMatch(verifiedId: VerifiedId) -> Bool + /// TODO: do we want a method that gives an explicit reason why it is not a match? func doesMatch(verifiedId: VerifiedId) throws } @@ -109,3 +111,20 @@ struct VerifiedIdGroupConstraint: VerifiedIdConstraint { throw VerifiedIdClientError.TODO(message: "implement") } } + +struct VCTypeConstraint: VerifiedIdConstraint { + + let type: String + + func doesMatch(verifiedId: VerifiedId) -> Bool { + guard let verifiableCredential = verifiedId as? VerifiableCredential else { + return false + } + + return verifiableCredential.types.contains(where: { $0 == type }) + } + + func doesMatch(verifiedId: VerifiedId) throws { + throw VerifiedIdClientError.TODO(message: "implement") + } +} From 5cb11b3cb736c179ada0fb621fa2ba455d3d4e26 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 10 Mar 2023 10:59:03 -0500 Subject: [PATCH 331/373] Add type property to VerifiableCredential. --- .../VerifiedId/VerifiableCredential/VerifiableCredential.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift b/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift index 6aac2b91..357b4fe8 100644 --- a/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift +++ b/WalletLibrary/WalletLibrary/VerifiedId/VerifiableCredential/VerifiableCredential.swift @@ -23,6 +23,8 @@ struct VerifiableCredential: VerifiedId { public let issuedOn: Date + let types: [String] + let raw: VCEntities.VerifiableCredential let contract: Contract @@ -41,6 +43,7 @@ struct VerifiableCredential: VerifiedId { self.contract = contract self.issuedOn = Date(timeIntervalSince1970: issuedOn) self.id = id + self.types = raw.content.vc?.type ?? [] if let expiresOn = raw.content.exp { self.expiresOn = Date(timeIntervalSince1970: expiresOn) From 559cdc5a6a3ddef28ec0a4d93740fa2bb3721bcf Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Mar 2023 14:52:11 -0700 Subject: [PATCH 332/373] Add VerifiedIdConstraint protocol. --- .../WalletLibrary.xcodeproj/project.pbxproj | 28 +++++++++++++++++++ .../Constraints/VerifiedIdConstraint.swift | 15 ++++++++++ 2 files changed, 43 insertions(+) create mode 100644 WalletLibrary/WalletLibrary/Protocols/Requests/Constraints/VerifiedIdConstraint.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 87f324f6..71bed1b3 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -106,6 +106,9 @@ 55DECBEB29B954F500D5C802 /* PresentationResponseContainer+WalletLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECBEA29B954F500D5C802 /* PresentationResponseContainer+WalletLibrary.swift */; }; 55DECBED29BA29FB00D5C802 /* PresentationResponseContainerExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECBEC29BA29FB00D5C802 /* PresentationResponseContainerExtensionTests.swift */; }; 55DECBEF29BA2BEE00D5C802 /* IssuanceRequestContentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECBEE29BA2BEE00D5C802 /* IssuanceRequestContentTests.swift */; }; + 55DECC2B29BB8C8C00D5C802 /* VerifiedIdConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECC2A29BB8C8C00D5C802 /* VerifiedIdConstraint.swift */; }; + 55DECC2E29BB8DA800D5C802 /* VerifiedIdGroupConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECC2D29BB8DA800D5C802 /* VerifiedIdGroupConstraint.swift */; }; + 55DECC3029BB8E5C00D5C802 /* VCTypeConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECC2F29BB8E5C00D5C802 /* VCTypeConstraint.swift */; }; 55E2F06C299553300008010D /* PresentationRequest+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */; }; 55E2F06F299553B40008010D /* OpenIdRequestHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F06E299553B40008010D /* OpenIdRequestHandlerTests.swift */; }; 55E2F071299554110008010D /* LibraryConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F070299554110008010D /* LibraryConfiguration.swift */; }; @@ -402,6 +405,9 @@ 55DECBEA29B954F500D5C802 /* PresentationResponseContainer+WalletLibrary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PresentationResponseContainer+WalletLibrary.swift"; sourceTree = ""; }; 55DECBEC29BA29FB00D5C802 /* PresentationResponseContainerExtensionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentationResponseContainerExtensionTests.swift; sourceTree = ""; }; 55DECBEE29BA2BEE00D5C802 /* IssuanceRequestContentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssuanceRequestContentTests.swift; sourceTree = ""; }; + 55DECC2A29BB8C8C00D5C802 /* VerifiedIdConstraint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdConstraint.swift; sourceTree = ""; }; + 55DECC2D29BB8DA800D5C802 /* VerifiedIdGroupConstraint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdGroupConstraint.swift; sourceTree = ""; }; + 55DECC2F29BB8E5C00D5C802 /* VCTypeConstraint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VCTypeConstraint.swift; sourceTree = ""; }; 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PresentationRequest+Mappable.swift"; sourceTree = ""; }; 55E2F06E299553B40008010D /* OpenIdRequestHandlerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenIdRequestHandlerTests.swift; sourceTree = ""; }; 55E2F070299554110008010D /* LibraryConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryConfiguration.swift; sourceTree = ""; }; @@ -873,6 +879,23 @@ path = Handlers; sourceTree = ""; }; + 55DECC1429BB8C7800D5C802 /* Constraints */ = { + isa = PBXGroup; + children = ( + 55DECC2A29BB8C8C00D5C802 /* VerifiedIdConstraint.swift */, + ); + path = Constraints; + sourceTree = ""; + }; + 55DECC2C29BB8D9100D5C802 /* Constraints */ = { + isa = PBXGroup; + children = ( + 55DECC2D29BB8DA800D5C802 /* VerifiedIdGroupConstraint.swift */, + 55DECC2F29BB8E5C00D5C802 /* VCTypeConstraint.swift */, + ); + path = Constraints; + sourceTree = ""; + }; 55E2F06D299553B40008010D /* Handlers */ = { isa = PBXGroup; children = ( @@ -1019,6 +1042,7 @@ 55E3370F293FDDF400CD2ED7 /* Requests */ = { isa = PBXGroup; children = ( + 55DECC2C29BB8D9100D5C802 /* Constraints */, 559BD3A9299ADEF300BD61AC /* Manifest */, 55E2F07B2995561E0008010D /* OpenId */, 550A91712993117B0014D030 /* Input */, @@ -1075,6 +1099,7 @@ 55E337B62948B4F900CD2ED7 /* Requests */ = { isa = PBXGroup; children = ( + 55DECC1429BB8C7800D5C802 /* Constraints */, 559BD3A4299AD9A200BD61AC /* Manifest */, 55E337B72948B50800CD2ED7 /* Handlers */, 55E3376629437E1000CD2ED7 /* OpenId */, @@ -1405,12 +1430,14 @@ 55E2F08C299576730008010D /* GroupRequirement.swift in Sources */, 550E3212298B1150004E7716 /* VerifiedIdRequestInput.swift in Sources */, 550A919B29940E9E0014D030 /* PresentationRequest+OpenIdRawRequest.swift in Sources */, + 55DECC2E29BB8DA800D5C802 /* VerifiedIdGroupConstraint.swift in Sources */, 5534E667294A0B6C005D0765 /* Mappable.swift in Sources */, 559BD3A8299ADE9C00BD61AC /* RawManifest.swift in Sources */, 55E2F08129955A960008010D /* RequestType.swift in Sources */, 55A81BE72991B2F3002C259A /* RequestHandlerFactory.swift in Sources */, 559BD3A3299AD95200BD61AC /* IssuanceService+ManifestResolver.swift in Sources */, 55E2F07F29955A3B0008010D /* PresentationRequestContent.swift in Sources */, + 55DECC2B29BB8C8C00D5C802 /* VerifiedIdConstraint.swift in Sources */, 550A916F299310D40014D030 /* OpenIdRequestHandler.swift in Sources */, 55E3370A293FD3E000CD2ED7 /* IdTokenRequirement.swift in Sources */, 5585BDE729A6C38C0059710B /* InjectedIdToken.swift in Sources */, @@ -1451,6 +1478,7 @@ 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */, 55E2F071299554110008010D /* LibraryConfiguration.swift in Sources */, 550E3209298B0EA4004E7716 /* VerifiedIdClientBuilder.swift in Sources */, + 55DECC3029BB8E5C00D5C802 /* VCTypeConstraint.swift in Sources */, 550A9192299400820014D030 /* OpenIdResponder.swift in Sources */, 5534E672294A19D5005D0765 /* IdTokenDescriptor+Mappable.swift in Sources */, 5534E677294A1FA0005D0765 /* AccessTokenDescriptor+Mappable.swift in Sources */, diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/Constraints/VerifiedIdConstraint.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/Constraints/VerifiedIdConstraint.swift new file mode 100644 index 00000000..4744bfbd --- /dev/null +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/Constraints/VerifiedIdConstraint.swift @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +/** + * A constraint on a requirement that defines a behavior of determining whether a VerifiedId matches + * matches that constraint or not. + */ +protocol VerifiedIdConstraint { + func doesMatch(verifiedId: VerifiedId) -> Bool + + /// TODO: do we want a method that gives an explicit reason why it is not a match? + func doesMatch(verifiedId: VerifiedId) throws +} From 1d26ad37c6a4b2dad274dacef03ad0c0309f8470 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Mar 2023 14:52:16 -0700 Subject: [PATCH 333/373] Create VCTypeConstraint.swift --- .../Constraints/VCTypeConstraint.swift | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 WalletLibrary/WalletLibrary/Requests/Constraints/VCTypeConstraint.swift diff --git a/WalletLibrary/WalletLibrary/Requests/Constraints/VCTypeConstraint.swift b/WalletLibrary/WalletLibrary/Requests/Constraints/VCTypeConstraint.swift new file mode 100644 index 00000000..99a4c484 --- /dev/null +++ b/WalletLibrary/WalletLibrary/Requests/Constraints/VCTypeConstraint.swift @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +struct VCTypeConstraint: VerifiedIdConstraint { + + let type: String + + func doesMatch(verifiedId: VerifiedId) -> Bool { + guard let verifiableCredential = verifiedId as? VerifiableCredential else { + return false + } + + return verifiableCredential.types.contains(where: { $0 == type }) + } + + func doesMatch(verifiedId: VerifiedId) throws { + throw VerifiedIdClientError.TODO(message: "implement") + } +} From 40a95c8c613acea9d8d39bde201a6ee56dd521e8 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Mar 2023 14:52:18 -0700 Subject: [PATCH 334/373] Create VerifiedIdGroupConstraint.swift --- .../VerifiedIdGroupConstraint.swift | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 WalletLibrary/WalletLibrary/Requests/Constraints/VerifiedIdGroupConstraint.swift diff --git a/WalletLibrary/WalletLibrary/Requests/Constraints/VerifiedIdGroupConstraint.swift b/WalletLibrary/WalletLibrary/Requests/Constraints/VerifiedIdGroupConstraint.swift new file mode 100644 index 00000000..1e0f0351 --- /dev/null +++ b/WalletLibrary/WalletLibrary/Requests/Constraints/VerifiedIdGroupConstraint.swift @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +enum GroupConstraintOperator { + case ANY + case ALL +} + +/** + * A group of constraints. + */ +struct VerifiedIdGroupConstraint: VerifiedIdConstraint { + + let constraints: [VerifiedIdConstraint] + + let constraintOperator: GroupConstraintOperator + + init(constraints: [VerifiedIdConstraint], + constraintOperator: GroupConstraintOperator) { + self.constraints = constraints + self.constraintOperator = constraintOperator + } + + /// If operator is equal to ANY, one constraint in constraints list must match. + /// If operator is equal to ALL, all constraints must match. + func doesMatch(verifiedId: VerifiedId) -> Bool { + return false + } + + func doesMatch(verifiedId: VerifiedId) throws { + throw VerifiedIdClientError.TODO(message: "implement") + } +} From f9d55446d4b4dea98956297cb8c3116159dd5031 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Mar 2023 14:52:42 -0700 Subject: [PATCH 335/373] Implement isSatisfied and complete on openid presentation request. --- .../OpenId/OpenIdPresentationRequest.swift | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/OpenId/OpenIdPresentationRequest.swift b/WalletLibrary/WalletLibrary/Requests/OpenId/OpenIdPresentationRequest.swift index 3acec84b..2c297b78 100644 --- a/WalletLibrary/WalletLibrary/Requests/OpenId/OpenIdPresentationRequest.swift +++ b/WalletLibrary/WalletLibrary/Requests/OpenId/OpenIdPresentationRequest.swift @@ -3,9 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import VCEntities + /** * Presentation Requst that is Open Id specific. - * TODO: we will need open id specific data to implement complete and cancel. */ class OpenIdPresentationRequest: VerifiedIdPresentationRequest { @@ -34,13 +35,25 @@ class OpenIdPresentationRequest: VerifiedIdPresentationRequest { self.configuration = configuration } - /// TODO: implement. func isSatisfied() -> Bool { - return false + do { + try requirement.validate() + return true + } catch { + /// TODO: log error. + return false + } } func complete() async -> Result<(), Error> { - return Result.failure(VerifiedIdClientError.TODO(message: "implement")) + do { + var response = try PresentationResponseContainer(rawRequest: rawRequest) + try response.add(requirement: requirement) + try await responder.send(response: response) + return Result.success(()) + } catch { + return Result.failure(error) + } } func cancel(message: String?) -> Result { From 2241e0a679ee7bd696ed63dce39133c4d99fb713 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Mar 2023 14:52:52 -0700 Subject: [PATCH 336/373] Update VerifiedIdRequirement.swift --- .../Requirements/VerifiedIdRequirement.swift | 56 +------------------ 1 file changed, 3 insertions(+), 53 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Requirements/VerifiedIdRequirement.swift b/WalletLibrary/WalletLibrary/Requests/Requirements/VerifiedIdRequirement.swift index 32cfc578..ec237900 100644 --- a/WalletLibrary/WalletLibrary/Requests/Requirements/VerifiedIdRequirement.swift +++ b/WalletLibrary/WalletLibrary/Requests/Requirements/VerifiedIdRequirement.swift @@ -70,61 +70,11 @@ public class VerifiedIdRequirement: Requirement { } public func fulfill(with verifiedId: VerifiedId) throws { - if constraint.doesMatch(verifiedId: verifiedId) { - self.selectedVerifiedId = verifiedId - return - } - throw VerifiedIdRequirementError.verifiedIdDoesNotMeetConstraints - } -} - -protocol VerifiedIdConstraint { - func doesMatch(verifiedId: VerifiedId) -> Bool - - /// TODO: do we want a method that gives an explicit reason why it is not a match? - func doesMatch(verifiedId: VerifiedId) throws -} - -enum GroupConstraintOperator { - case ANY - case ALL -} - -struct VerifiedIdGroupConstraint: VerifiedIdConstraint { - - let constraints: [VerifiedIdConstraint] - - let constraintOperator: GroupConstraintOperator - - init(constraints: [VerifiedIdConstraint], - constraintOperator: GroupConstraintOperator) { - self.constraints = constraints - self.constraintOperator = constraintOperator - } - - func doesMatch(verifiedId: VerifiedId) -> Bool { - return false - } - - func doesMatch(verifiedId: VerifiedId) throws { - throw VerifiedIdClientError.TODO(message: "implement") - } -} - -struct VCTypeConstraint: VerifiedIdConstraint { - - let type: String - - func doesMatch(verifiedId: VerifiedId) -> Bool { - guard let verifiableCredential = verifiedId as? VerifiableCredential else { - return false + guard constraint.doesMatch(verifiedId: verifiedId) else { + throw VerifiedIdRequirementError.verifiedIdDoesNotMeetConstraints } - return verifiableCredential.types.contains(where: { $0 == type }) - } - - func doesMatch(verifiedId: VerifiedId) throws { - throw VerifiedIdClientError.TODO(message: "implement") + self.selectedVerifiedId = verifiedId } } From cf17d1f7bd010c81953462c97b6018c19110fe52 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Mar 2023 15:09:39 -0700 Subject: [PATCH 337/373] Add unit tests for constraints mapping for issuance and presentation. --- .../PresentationDescriptor+Mappable.swift | 1 - ...PresentationDescriptor+MappableTests.swift | 3 ++ ...ntationInputDescriptor+MappableTests.swift | 29 +++++++++++++++++-- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+Mappable.swift index 2d70b473..08d5cdf4 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+Mappable.swift @@ -24,7 +24,6 @@ extension VCEntities.PresentationDescriptor: Mappable { let constraint = VCTypeConstraint(type: credentialType) - /// TODO: do we need types in VerifiedIdRequirement anymore? We display it in Auth App. return VerifiedIdRequirement(encrypted: encrypted ?? false, required: presentationRequired ?? false, types: [credentialType], diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+MappableTests.swift index 5e7c8793..d2d673f4 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+MappableTests.swift @@ -85,6 +85,9 @@ class PresentationDescriptorMappingTests: XCTestCase { XCTAssertEqual(actual.types, expected.types) XCTAssertEqual(actual.purpose, expected.purpose) XCTAssertEqual(actual.issuanceOptions as? [VerifiedIdRequestURL], expected.issuanceOptions as? [VerifiedIdRequestURL]) + XCTAssert(actual.constraint is VCTypeConstraint) + XCTAssertEqual((actual.constraint as? VCTypeConstraint)?.type, expected.types.first) + XCTAssertNil(actual.id) } private func setUpInput(encrypted: Bool? = false, diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+MappableTests.swift index 4f3c611e..5b28fa2d 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+MappableTests.swift @@ -91,6 +91,9 @@ class PresentationInputDescriptorMappingTests: XCTestCase { XCTAssertEqual(actualResult.types, expectedVerifiedIdRequest.types) XCTAssertEqual(actualResult.issuanceOptions as? [VerifiedIdRequestURL], []) XCTAssertEqual(actualResult.purpose, expectedVerifiedIdRequest.purpose) + XCTAssert(actualResult.constraint is VCTypeConstraint) + XCTAssertEqual((actualResult.constraint as? VCTypeConstraint)?.type, "mockType") + XCTAssertEqual(actualResult.id, expectedVerifiedIdRequest.id) } func testMap_WithMultipleTypesPresent_ReturnsVerifiedIdRequirement() throws { @@ -125,6 +128,13 @@ class PresentationInputDescriptorMappingTests: XCTestCase { XCTAssertEqual(actualResult.types, expectedVerifiedIdRequest.types) XCTAssertEqual(actualResult.issuanceOptions as? [VerifiedIdRequestURL], []) XCTAssertEqual(actualResult.purpose, expectedVerifiedIdRequest.purpose) + XCTAssert(actualResult.constraint is VerifiedIdGroupConstraint) + let constraints = (actualResult.constraint as? VerifiedIdGroupConstraint)!.constraints + XCTAssertEqual(constraints.count, 3) + XCTAssertEqual((constraints[0] as? VCTypeConstraint)?.type, firstType) + XCTAssertEqual((constraints[1] as? VCTypeConstraint)?.type, secondType) + XCTAssertEqual((constraints[2] as? VCTypeConstraint)?.type, thirdType) + XCTAssertEqual(actualResult.id, expectedVerifiedIdRequest.id) } func testMap_WithNoIssuanceMetadataPresent_ReturnsVerifiedIdRequirement() throws { @@ -154,12 +164,16 @@ class PresentationInputDescriptorMappingTests: XCTestCase { XCTAssertEqual(actualResult.types, expectedVerifiedIdRequest.types) XCTAssertEqual(actualResult.issuanceOptions as? [VerifiedIdRequestURL], []) XCTAssertEqual(actualResult.purpose, expectedVerifiedIdRequest.purpose) + XCTAssert(actualResult.constraint is VCTypeConstraint) + XCTAssertEqual((actualResult.constraint as? VCTypeConstraint)?.type, "mockType") + XCTAssertEqual(actualResult.id, expectedVerifiedIdRequest.id) } func testMap_WithInvalidContractFormatInIssuanceMetadata_ReturnsVerifiedIdRequirement() throws { // Arrange let mockMapper = MockMapper() let mockSchema = InputDescriptorSchema(uri: "mockType") + let mockId = "mockId" let invalidContract = "//|\\" let invalidIssuanceMetadata = IssuanceMetadata(contract: invalidContract, issuerDid: nil) let expectedVerifiedIdRequest = VerifiedIdRequirement(encrypted: false, @@ -167,9 +181,9 @@ class PresentationInputDescriptorMappingTests: XCTestCase { types: ["mockType"], purpose: nil, issuanceOptions: [], - id: nil, + id: mockId, constraint: VerifiedIdGroupConstraint(constraints: [], constraintOperator: .ALL)) - let presentationInputDescriptor = PresentationInputDescriptor(id: nil, + let presentationInputDescriptor = PresentationInputDescriptor(id: mockId, schema: [mockSchema], issuanceMetadata: [invalidIssuanceMetadata], name: nil, @@ -185,12 +199,16 @@ class PresentationInputDescriptorMappingTests: XCTestCase { XCTAssertEqual(actualResult.types, expectedVerifiedIdRequest.types) XCTAssertEqual(actualResult.issuanceOptions as? [VerifiedIdRequestURL], []) XCTAssertEqual(actualResult.purpose, expectedVerifiedIdRequest.purpose) + XCTAssert(actualResult.constraint is VCTypeConstraint) + XCTAssertEqual((actualResult.constraint as? VCTypeConstraint)?.type, "mockType") + XCTAssertEqual(actualResult.id, expectedVerifiedIdRequest.id) } func testMap_WithOneInvalidContractAndTwoValidContractsPresent_ReturnsVerifiedIdRequirement() throws { // Arrange let mockMapper = MockMapper() let mockSchema = InputDescriptorSchema(uri: "mockType") + let mockId = "mockId" let invalidContract = "//|\\" let firstValidContract = "https://mockcontract1.com" let secondValidContract = "https://mockcontract2.com" @@ -211,7 +229,7 @@ class PresentationInputDescriptorMappingTests: XCTestCase { constraint: VerifiedIdGroupConstraint(constraints: [], constraintOperator: .ALL)) let issuanceMetadatas = [firstValidIssuanceMetadata, invalidIssuanceMetadata, secondValidIssuanceMetadata] - let presentationInputDescriptor = PresentationInputDescriptor(id: nil, + let presentationInputDescriptor = PresentationInputDescriptor(id: mockId, schema: [mockSchema], issuanceMetadata: issuanceMetadatas, name: nil, @@ -227,6 +245,9 @@ class PresentationInputDescriptorMappingTests: XCTestCase { XCTAssertEqual(actualResult.types, expectedVerifiedIdRequest.types) XCTAssertEqual(actualResult.issuanceOptions as? [VerifiedIdRequestURL], [firstVerifiedIdRequestURL, secondVerifiedIdRequestURL]) XCTAssertEqual(actualResult.purpose, expectedVerifiedIdRequest.purpose) + XCTAssert(actualResult.constraint is VCTypeConstraint) + XCTAssertEqual((actualResult.constraint as? VCTypeConstraint)?.type, "mockType") + XCTAssertEqual(actualResult.id, mockId) } func testMap_WithPurposePresent_ReturnsVerifiedIdRequirement() throws { @@ -257,5 +278,7 @@ class PresentationInputDescriptorMappingTests: XCTestCase { XCTAssertEqual(actualResult.types, expectedVerifiedIdRequest.types) XCTAssertEqual(actualResult.issuanceOptions as? [VerifiedIdRequestURL], []) XCTAssertEqual(actualResult.purpose, expectedVerifiedIdRequest.purpose) + XCTAssert(actualResult.constraint is VCTypeConstraint) + XCTAssertEqual((actualResult.constraint as? VCTypeConstraint)?.type, "mockType") } } From 18e8898307663a8d8cb71c79f7e644a5726e66af Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Mar 2023 15:56:02 -0700 Subject: [PATCH 338/373] Add MockConstraint. --- .../WalletLibrary.xcodeproj/project.pbxproj | 28 +++++++++++++++++++ .../Requests/Constraints/MockConstraint.swift | 19 +++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 WalletLibrary/WalletLibraryTests/Mocks/Requests/Constraints/MockConstraint.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 71bed1b3..00a8a8de 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -109,6 +109,9 @@ 55DECC2B29BB8C8C00D5C802 /* VerifiedIdConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECC2A29BB8C8C00D5C802 /* VerifiedIdConstraint.swift */; }; 55DECC2E29BB8DA800D5C802 /* VerifiedIdGroupConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECC2D29BB8DA800D5C802 /* VerifiedIdGroupConstraint.swift */; }; 55DECC3029BB8E5C00D5C802 /* VCTypeConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECC2F29BB8E5C00D5C802 /* VCTypeConstraint.swift */; }; + 55DECC3329BFDB6200D5C802 /* VerifiedIdGroupConstraintTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECC3229BFDB6200D5C802 /* VerifiedIdGroupConstraintTests.swift */; }; + 55DECC3529BFDB7600D5C802 /* VCTypeConstraintTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECC3429BFDB7600D5C802 /* VCTypeConstraintTests.swift */; }; + 55DECC3829BFE14E00D5C802 /* MockConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECC3729BFE14E00D5C802 /* MockConstraint.swift */; }; 55E2F06C299553300008010D /* PresentationRequest+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */; }; 55E2F06F299553B40008010D /* OpenIdRequestHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F06E299553B40008010D /* OpenIdRequestHandlerTests.swift */; }; 55E2F071299554110008010D /* LibraryConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F070299554110008010D /* LibraryConfiguration.swift */; }; @@ -408,6 +411,9 @@ 55DECC2A29BB8C8C00D5C802 /* VerifiedIdConstraint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdConstraint.swift; sourceTree = ""; }; 55DECC2D29BB8DA800D5C802 /* VerifiedIdGroupConstraint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdGroupConstraint.swift; sourceTree = ""; }; 55DECC2F29BB8E5C00D5C802 /* VCTypeConstraint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VCTypeConstraint.swift; sourceTree = ""; }; + 55DECC3229BFDB6200D5C802 /* VerifiedIdGroupConstraintTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdGroupConstraintTests.swift; sourceTree = ""; }; + 55DECC3429BFDB7600D5C802 /* VCTypeConstraintTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VCTypeConstraintTests.swift; sourceTree = ""; }; + 55DECC3729BFE14E00D5C802 /* MockConstraint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockConstraint.swift; sourceTree = ""; }; 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PresentationRequest+Mappable.swift"; sourceTree = ""; }; 55E2F06E299553B40008010D /* OpenIdRequestHandlerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenIdRequestHandlerTests.swift; sourceTree = ""; }; 55E2F070299554110008010D /* LibraryConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryConfiguration.swift; sourceTree = ""; }; @@ -823,6 +829,7 @@ 55A81BF02991BB13002C259A /* Requests */ = { isa = PBXGroup; children = ( + 55DECC3129BFDB4E00D5C802 /* Constraints */, 553CC09329A96217005A5FD6 /* RequestProtocols */, 55E2F06D299553B40008010D /* Handlers */, 550A9193299402450014D030 /* Resolvers */, @@ -847,6 +854,7 @@ 55A81C0B2991BF73002C259A /* Requests */ = { isa = PBXGroup; children = ( + 55DECC3629BFE13500D5C802 /* Constraints */, 559BD4F6299EED4900BD61AC /* Manifest */, 55A81C112991C81A002C259A /* Handlers */, 550A919629940D810014D030 /* OpenIdForVC */, @@ -896,6 +904,23 @@ path = Constraints; sourceTree = ""; }; + 55DECC3129BFDB4E00D5C802 /* Constraints */ = { + isa = PBXGroup; + children = ( + 55DECC3229BFDB6200D5C802 /* VerifiedIdGroupConstraintTests.swift */, + 55DECC3429BFDB7600D5C802 /* VCTypeConstraintTests.swift */, + ); + path = Constraints; + sourceTree = ""; + }; + 55DECC3629BFE13500D5C802 /* Constraints */ = { + isa = PBXGroup; + children = ( + 55DECC3729BFE14E00D5C802 /* MockConstraint.swift */, + ); + path = Constraints; + sourceTree = ""; + }; 55E2F06D299553B40008010D /* Handlers */ = { isa = PBXGroup; children = ( @@ -1532,10 +1557,12 @@ 559BD459299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift in Sources */, 559BD3092995ABE800BD61AC /* PresentationRequest+MappableTests.swift in Sources */, 550A91A0299412790014D030 /* MockOpenIdRawRequest.swift in Sources */, + 55DECC3329BFDB6200D5C802 /* VerifiedIdGroupConstraintTests.swift in Sources */, 559BD4F1299EEAEA00BD61AC /* PinRequirementTests.swift in Sources */, 559BD30F2996C1C500BD61AC /* MockLogConsumer.swift in Sources */, 55E2F076299555280008010D /* MockRequirement.swift in Sources */, 553CC09229A957A3005A5FD6 /* PinDescriptor+MappableTests.swift in Sources */, + 55DECC3529BFDB7600D5C802 /* VCTypeConstraintTests.swift in Sources */, 553CC09629A96249005A5FD6 /* ContractIssuanceRequestTests.swift in Sources */, 5534E68C294A8B8F005D0765 /* AccessTokenDescriptor+MappableTests.swift in Sources */, 55E2F077299555280008010D /* MockRequesterStyle.swift in Sources */, @@ -1545,6 +1572,7 @@ 559BD3112996D3CE00BD61AC /* MockVerifiedIdRequest.swift in Sources */, 550A913D29930D970014D030 /* MockRawRequest.swift in Sources */, 559BD30B2995B1B500BD61AC /* LinkedDomainResult+MappableTests.swift in Sources */, + 55DECC3829BFE14E00D5C802 /* MockConstraint.swift in Sources */, 559BD4F5299EEC9500BD61AC /* MockVerifiedIdRequester.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/Constraints/MockConstraint.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/Constraints/MockConstraint.swift new file mode 100644 index 00000000..59e4d8ec --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Mocks/Requests/Constraints/MockConstraint.swift @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +@testable import WalletLibrary + +struct MockConstraint: VerifiedIdConstraint { + + let doesMatchResult: Bool + + init(doesMatchResult: Bool) { + self.doesMatchResult = doesMatchResult + } + + func doesMatch(verifiedId: WalletLibrary.VerifiedId) -> Bool { + return doesMatchResult + } +} From 23b7e747b06fca329dd9c32c9c7b327be42b3d68 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Mar 2023 15:56:22 -0700 Subject: [PATCH 339/373] Complete test cases for VCTypeConstraint. --- .../Constraints/VCTypeConstraintTests.swift | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 WalletLibrary/WalletLibraryTests/Requests/Constraints/VCTypeConstraintTests.swift diff --git a/WalletLibrary/WalletLibraryTests/Requests/Constraints/VCTypeConstraintTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Constraints/VCTypeConstraintTests.swift new file mode 100644 index 00000000..dd8246e2 --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Requests/Constraints/VCTypeConstraintTests.swift @@ -0,0 +1,101 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import XCTest +import VCEntities +import VCToken +@testable import WalletLibrary + +class VCTypeConstraintTests: XCTestCase { + + func testDoesMatch_WithUnsupportedVerifiedIdType_ReturnFalse() throws { + // Arrange + let constraint = VCTypeConstraint(type: "mockType") + let unsupportedVerifiedId = MockVerifiedId(id: "mock verified id", issuedOn: Date()) + + // Act + let result = constraint.doesMatch(verifiedId: unsupportedVerifiedId) + + // Assert + XCTAssertFalse(result) + } + + func testDoesMatch_WhenVCContainsType_ReturnTrue() throws { + // Arrange + let constraint = VCTypeConstraint(type: "mockType") + let mockRawVC = createVCEntitiesVC(expectedTypes: ["mockType"]) + let mockContract = createMockContract() + let verifiableCredential = try VerifiableCredential(raw: mockRawVC, from: mockContract) + + // Act + let result = constraint.doesMatch(verifiedId: verifiableCredential) + + // Assert + XCTAssertTrue(result) + } + + func testDoesMatch_WhenVCContainsMultipleTypes_ReturnTrue() throws { + // Arrange + let constraint = VCTypeConstraint(type: "mockType") + let mockRawVC = createVCEntitiesVC(expectedTypes: ["mockType", "unmatchingType"]) + let mockContract = createMockContract() + let verifiableCredential = try VerifiableCredential(raw: mockRawVC, from: mockContract) + + // Act + let result = constraint.doesMatch(verifiedId: verifiableCredential) + + // Assert + XCTAssertTrue(result) + } + + func testDoesMatch_WhenVCDoesNotContainType_ReturnFalse() throws { + // Arrange + let constraint = VCTypeConstraint(type: "mockType") + let mockRawVC = createVCEntitiesVC(expectedTypes: ["unmatchingType"]) + let mockContract = createMockContract() + let verifiableCredential = try VerifiableCredential(raw: mockRawVC, from: mockContract) + + // Act + let result = constraint.doesMatch(verifiedId: verifiableCredential) + + // Assert + XCTAssertFalse(result) + } + + private func createVCEntitiesVC(expectedTypes: [String]) -> VCEntities.VerifiableCredential { + let claims = VCClaims(jti: "", + iss: "", + sub: "", + iat: 0, + exp: 0, + vc: VerifiableCredentialDescriptor(context: [], + type: expectedTypes, + credentialSubject: [:])) + return VCEntities.VerifiableCredential(headers: Header(), content: claims)! + } + + private func createMockContract() -> Contract { + let mockCardDisplay = CardDisplayDescriptor(title: "mock title", + issuedBy: "mock issuer", + backgroundColor: "mock background color", + textColor: "mock text color", + logo: nil, + cardDescription: "mock description") + let mockConsentDisplay = ConsentDisplayDescriptor(title: nil, + instructions: "mock purpose") + let mockDisplayDescriptor = DisplayDescriptor(id: nil, + locale: nil, + contract: nil, + card: mockCardDisplay, + consent: mockConsentDisplay, + claims: [:]) + let mockContractInputDescriptor = ContractInputDescriptor(credentialIssuer: "mock credential issuer", + issuer: "mock issuer", + attestations: nil) + return Contract(id: "mockContract", + display: mockDisplayDescriptor, + input: mockContractInputDescriptor) + } +} From 396bb0e526f6c2e4402c1f5fd42faf77392de046 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Mar 2023 15:56:36 -0700 Subject: [PATCH 340/373] Complete test cases for group constraint. --- .../VerifiedIdGroupConstraintTests.swift | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 WalletLibrary/WalletLibraryTests/Requests/Constraints/VerifiedIdGroupConstraintTests.swift diff --git a/WalletLibrary/WalletLibraryTests/Requests/Constraints/VerifiedIdGroupConstraintTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Constraints/VerifiedIdGroupConstraintTests.swift new file mode 100644 index 00000000..0aad89b3 --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Requests/Constraints/VerifiedIdGroupConstraintTests.swift @@ -0,0 +1,68 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import XCTest +import VCEntities +import VCToken +@testable import WalletLibrary + +class VerifiedIdGroupConstraintTests: XCTestCase { + + func testDoesMatch_WithALLOperatorAndAllMatch_ReturnsTrue() throws { + // Arrange + let mockVerifiedId = MockVerifiedId(id: "mock verified id", issuedOn: Date()) + let firstConstraint = MockConstraint(doesMatchResult: true) + let secondConstraint = MockConstraint(doesMatchResult: true) + let thirdConstraint = MockConstraint(doesMatchResult: true) + let constraints = [firstConstraint, secondConstraint, thirdConstraint] + let groupConstraint = VerifiedIdGroupConstraint(constraints: constraints, + constraintOperator: .ALL) + + // Act / Assert + XCTAssert(groupConstraint.doesMatch(verifiedId: mockVerifiedId)) + } + + func testDoesMatch_WithALLOperatorAndOneDoesNotMatch_ReturnsFalse() throws { + // Arrange + let mockVerifiedId = MockVerifiedId(id: "mock verified id", issuedOn: Date()) + let firstConstraint = MockConstraint(doesMatchResult: true) + let secondConstraint = MockConstraint(doesMatchResult: false) + let thirdConstraint = MockConstraint(doesMatchResult: true) + let constraints = [firstConstraint, secondConstraint, thirdConstraint] + let groupConstraint = VerifiedIdGroupConstraint(constraints: constraints, + constraintOperator: .ALL) + + // Act / Assert + XCTAssertFalse(groupConstraint.doesMatch(verifiedId: mockVerifiedId)) + } + + func testDoesMatch_WithANYOperatorAndOneMatches_ReturnsTrue() throws { + // Arrange + let mockVerifiedId = MockVerifiedId(id: "mock verified id", issuedOn: Date()) + let firstConstraint = MockConstraint(doesMatchResult: false) + let secondConstraint = MockConstraint(doesMatchResult: false) + let thirdConstraint = MockConstraint(doesMatchResult: true) + let constraints = [firstConstraint, secondConstraint, thirdConstraint] + let groupConstraint = VerifiedIdGroupConstraint(constraints: constraints, + constraintOperator: .ANY) + + // Act / Assert + XCTAssert(groupConstraint.doesMatch(verifiedId: mockVerifiedId)) + } + + func testDoesMatch_WithANYOperatorAndNoneMatch_ReturnsFalse() throws { + // Arrange + let mockVerifiedId = MockVerifiedId(id: "mock verified id", issuedOn: Date()) + let firstConstraint = MockConstraint(doesMatchResult: false) + let secondConstraint = MockConstraint(doesMatchResult: false) + let thirdConstraint = MockConstraint(doesMatchResult: false) + let constraints = [firstConstraint, secondConstraint, thirdConstraint] + let groupConstraint = VerifiedIdGroupConstraint(constraints: constraints, + constraintOperator: .ANY) + + // Act / Assert + XCTAssertFalse(groupConstraint.doesMatch(verifiedId: mockVerifiedId)) + } +} From c68a260664eebc7e46517d7a13b9a496d9dc0c23 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Mar 2023 15:56:53 -0700 Subject: [PATCH 341/373] Update constraint protocol. --- .../Protocols/Requests/Constraints/VerifiedIdConstraint.swift | 3 --- .../WalletLibrary/Requests/Constraints/VCTypeConstraint.swift | 4 ---- 2 files changed, 7 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/Constraints/VerifiedIdConstraint.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/Constraints/VerifiedIdConstraint.swift index 4744bfbd..6bef50e6 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/Constraints/VerifiedIdConstraint.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/Constraints/VerifiedIdConstraint.swift @@ -9,7 +9,4 @@ */ protocol VerifiedIdConstraint { func doesMatch(verifiedId: VerifiedId) -> Bool - - /// TODO: do we want a method that gives an explicit reason why it is not a match? - func doesMatch(verifiedId: VerifiedId) throws } diff --git a/WalletLibrary/WalletLibrary/Requests/Constraints/VCTypeConstraint.swift b/WalletLibrary/WalletLibrary/Requests/Constraints/VCTypeConstraint.swift index 99a4c484..2cde7ea0 100644 --- a/WalletLibrary/WalletLibrary/Requests/Constraints/VCTypeConstraint.swift +++ b/WalletLibrary/WalletLibrary/Requests/Constraints/VCTypeConstraint.swift @@ -14,8 +14,4 @@ struct VCTypeConstraint: VerifiedIdConstraint { return verifiableCredential.types.contains(where: { $0 == type }) } - - func doesMatch(verifiedId: VerifiedId) throws { - throw VerifiedIdClientError.TODO(message: "implement") - } } From 54bdf81676674d144b52ec2684eca9833b417ebd Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Mar 2023 15:57:06 -0700 Subject: [PATCH 342/373] Implement doesMatch for group constraint. --- .../Constraints/VerifiedIdGroupConstraint.swift | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Requests/Constraints/VerifiedIdGroupConstraint.swift b/WalletLibrary/WalletLibrary/Requests/Constraints/VerifiedIdGroupConstraint.swift index 1e0f0351..805ec7dd 100644 --- a/WalletLibrary/WalletLibrary/Requests/Constraints/VerifiedIdGroupConstraint.swift +++ b/WalletLibrary/WalletLibrary/Requests/Constraints/VerifiedIdGroupConstraint.swift @@ -26,10 +26,15 @@ struct VerifiedIdGroupConstraint: VerifiedIdConstraint { /// If operator is equal to ANY, one constraint in constraints list must match. /// If operator is equal to ALL, all constraints must match. func doesMatch(verifiedId: VerifiedId) -> Bool { - return false - } - - func doesMatch(verifiedId: VerifiedId) throws { - throw VerifiedIdClientError.TODO(message: "implement") + switch constraintOperator { + case .ANY: + return constraints.contains { + $0.doesMatch(verifiedId: verifiedId) + } + case .ALL: + return constraints.allSatisfy { + $0.doesMatch(verifiedId: verifiedId) + } + } } } From 648b29cf628e4e6fdaefe37a85194a5a426cfaba Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Mar 2023 16:43:24 -0700 Subject: [PATCH 343/373] Create Helper for VC Creation. --- .../WalletLibrary.xcodeproj/project.pbxproj | 26 +++++---- .../MockVerifiableCredentialHelper.swift | 53 +++++++++++++++++++ 2 files changed, 70 insertions(+), 9 deletions(-) create mode 100644 WalletLibrary/WalletLibraryTests/Mocks/MockVerifiableCredentialHelper.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 00a8a8de..155c3de6 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -112,6 +112,8 @@ 55DECC3329BFDB6200D5C802 /* VerifiedIdGroupConstraintTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECC3229BFDB6200D5C802 /* VerifiedIdGroupConstraintTests.swift */; }; 55DECC3529BFDB7600D5C802 /* VCTypeConstraintTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECC3429BFDB7600D5C802 /* VCTypeConstraintTests.swift */; }; 55DECC3829BFE14E00D5C802 /* MockConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECC3729BFE14E00D5C802 /* MockConstraint.swift */; }; + 55DECC3D29BFE22900D5C802 /* OpenIdPresentationRequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECC3C29BFE22900D5C802 /* OpenIdPresentationRequestTests.swift */; }; + 55DECC3F29BFEACB00D5C802 /* MockVerifiableCredentialHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECC3E29BFEACB00D5C802 /* MockVerifiableCredentialHelper.swift */; }; 55E2F06C299553300008010D /* PresentationRequest+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */; }; 55E2F06F299553B40008010D /* OpenIdRequestHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F06E299553B40008010D /* OpenIdRequestHandlerTests.swift */; }; 55E2F071299554110008010D /* LibraryConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F070299554110008010D /* LibraryConfiguration.swift */; }; @@ -414,6 +416,8 @@ 55DECC3229BFDB6200D5C802 /* VerifiedIdGroupConstraintTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdGroupConstraintTests.swift; sourceTree = ""; }; 55DECC3429BFDB7600D5C802 /* VCTypeConstraintTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VCTypeConstraintTests.swift; sourceTree = ""; }; 55DECC3729BFE14E00D5C802 /* MockConstraint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockConstraint.swift; sourceTree = ""; }; + 55DECC3C29BFE22900D5C802 /* OpenIdPresentationRequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenIdPresentationRequestTests.swift; sourceTree = ""; }; + 55DECC3E29BFEACB00D5C802 /* MockVerifiableCredentialHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockVerifiableCredentialHelper.swift; sourceTree = ""; }; 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PresentationRequest+Mappable.swift"; sourceTree = ""; }; 55E2F06E299553B40008010D /* OpenIdRequestHandlerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenIdRequestHandlerTests.swift; sourceTree = ""; }; 55E2F070299554110008010D /* LibraryConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryConfiguration.swift; sourceTree = ""; }; @@ -689,14 +693,6 @@ path = VCSDK; sourceTree = ""; }; - 553CC09329A96217005A5FD6 /* RequestProtocols */ = { - isa = PBXGroup; - children = ( - 553CC09429A96235005A5FD6 /* Manifest */, - ); - path = RequestProtocols; - sourceTree = ""; - }; 553CC09429A96235005A5FD6 /* Manifest */ = { isa = PBXGroup; children = ( @@ -830,8 +826,9 @@ isa = PBXGroup; children = ( 55DECC3129BFDB4E00D5C802 /* Constraints */, - 553CC09329A96217005A5FD6 /* RequestProtocols */, 55E2F06D299553B40008010D /* Handlers */, + 553CC09429A96235005A5FD6 /* Manifest */, + 55DECC3B29BFE20E00D5C802 /* OpenId */, 550A9193299402450014D030 /* Resolvers */, 559BD4E9299EEAA200BD61AC /* Requirements */, 55A81C062991BB2C002C259A /* RequestResolverFactoryTests.swift */, @@ -847,6 +844,7 @@ 553CC09729A96C29005A5FD6 /* VerifiedId */, 55E2F08229955E7B0008010D /* Utilities */, 55A81C0B2991BF73002C259A /* Requests */, + 55DECC3E29BFEACB00D5C802 /* MockVerifiableCredentialHelper.swift */, ); path = Mocks; sourceTree = ""; @@ -921,6 +919,14 @@ path = Constraints; sourceTree = ""; }; + 55DECC3B29BFE20E00D5C802 /* OpenId */ = { + isa = PBXGroup; + children = ( + 55DECC3C29BFE22900D5C802 /* OpenIdPresentationRequestTests.swift */, + ); + path = OpenId; + sourceTree = ""; + }; 55E2F06D299553B40008010D /* Handlers */ = { isa = PBXGroup; children = ( @@ -1532,6 +1538,7 @@ 55E3376B29478C5000CD2ED7 /* AsyncWrapperTests.swift in Sources */, 5534E6AD294AAFA6005D0765 /* PresentationDescriptor+MappableTests.swift in Sources */, 559BD4ED299EEAD100BD61AC /* IdTokenRequirementTests.swift in Sources */, + 55DECC3D29BFE22900D5C802 /* OpenIdPresentationRequestTests.swift in Sources */, 559BD455299D3E4800BD61AC /* IssuanceRequest+MappableTests.swift in Sources */, 553CC0A029A98003005A5FD6 /* VerifiableCredentialTests.swift in Sources */, 55A81C132991C829002C259A /* MockHandler.swift in Sources */, @@ -1574,6 +1581,7 @@ 559BD30B2995B1B500BD61AC /* LinkedDomainResult+MappableTests.swift in Sources */, 55DECC3829BFE14E00D5C802 /* MockConstraint.swift in Sources */, 559BD4F5299EEC9500BD61AC /* MockVerifiedIdRequester.swift in Sources */, + 55DECC3F29BFEACB00D5C802 /* MockVerifiableCredentialHelper.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/WalletLibrary/WalletLibraryTests/Mocks/MockVerifiableCredentialHelper.swift b/WalletLibrary/WalletLibraryTests/Mocks/MockVerifiableCredentialHelper.swift new file mode 100644 index 00000000..d00d04e7 --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Mocks/MockVerifiableCredentialHelper.swift @@ -0,0 +1,53 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import VCEntities +import VCToken +@testable import WalletLibrary + +struct MockVerifiableCredentialHelper { + + func createMockVerifiableCredential(expectedTypes: [String] = []) -> WalletLibrary.VerifiableCredential { + let raw = createVCEntitiesVC(expectedTypes: expectedTypes) + let contract = createMockContract() + return try! VerifiableCredential(raw: raw, from: contract) + } + + func createVCEntitiesVC(expectedTypes: [String]) -> VCEntities.VerifiableCredential { + let claims = VCClaims(jti: "", + iss: "", + sub: "", + iat: 0, + exp: 0, + vc: VerifiableCredentialDescriptor(context: [], + type: expectedTypes, + credentialSubject: [:])) + return VCEntities.VerifiableCredential(headers: Header(), content: claims)! + } + + func createMockContract() -> Contract { + let mockCardDisplay = CardDisplayDescriptor(title: "mock title", + issuedBy: "mock issuer", + backgroundColor: "mock background color", + textColor: "mock text color", + logo: nil, + cardDescription: "mock description") + let mockConsentDisplay = ConsentDisplayDescriptor(title: nil, + instructions: "mock purpose") + let mockDisplayDescriptor = DisplayDescriptor(id: nil, + locale: nil, + contract: nil, + card: mockCardDisplay, + consent: mockConsentDisplay, + claims: [:]) + let mockContractInputDescriptor = ContractInputDescriptor(credentialIssuer: "mock credential issuer", + issuer: "mock issuer", + attestations: nil) + return Contract(id: "mockContract", + display: mockDisplayDescriptor, + input: mockContractInputDescriptor) + } + +} From 3e433a739ce19c40b2f8201efc742f53b0e6d91a Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Mar 2023 16:43:43 -0700 Subject: [PATCH 344/373] Implement add Verified id to Presentation Response. --- ...esentationResponseContainer+WalletLibrary.swift | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationResponseContainer+WalletLibrary.swift b/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationResponseContainer+WalletLibrary.swift index efc7650d..0a5ea21a 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationResponseContainer+WalletLibrary.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationResponseContainer+WalletLibrary.swift @@ -48,6 +48,18 @@ extension VCEntities.PresentationResponseContainer: PresentationResponse { } private mutating func add(verifiedIdRequirement: VerifiedIdRequirement) throws { - throw VerifiedIdClientError.TODO(message: "Implement in next PR.") + + guard let requirementId = verifiedIdRequirement.id else { + throw PresentationResponseError.missingIdInVerifiedIdRequirement + } + + guard let verifiableCredential = verifiedIdRequirement.selectedVerifiedId as? VerifiableCredential else { + let verifiedIdType = String(describing: type(of: verifiedIdRequirement.selectedVerifiedId)) + throw PresentationResponseError.unableToCastVerifableCredentialFromVerifiedIdOfType(verifiedIdType) + } + + let mapping = RequestedVerifiableCredentialMapping(id: requirementId, + verifiableCredential: verifiableCredential.raw) + self.requestVCMap.append(mapping) } } From 2fe01e146c851196fbfb7dad45615a8909ea9e58 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Mar 2023 16:43:50 -0700 Subject: [PATCH 345/373] Update VCTypeConstraintTests.swift --- .../Constraints/VCTypeConstraintTests.swift | 49 ++----------------- 1 file changed, 5 insertions(+), 44 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Requests/Constraints/VCTypeConstraintTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Constraints/VCTypeConstraintTests.swift index dd8246e2..8ab89922 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/Constraints/VCTypeConstraintTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/Constraints/VCTypeConstraintTests.swift @@ -10,6 +10,8 @@ import VCToken class VCTypeConstraintTests: XCTestCase { + let vcHelper = MockVerifiableCredentialHelper() + func testDoesMatch_WithUnsupportedVerifiedIdType_ReturnFalse() throws { // Arrange let constraint = VCTypeConstraint(type: "mockType") @@ -25,9 +27,7 @@ class VCTypeConstraintTests: XCTestCase { func testDoesMatch_WhenVCContainsType_ReturnTrue() throws { // Arrange let constraint = VCTypeConstraint(type: "mockType") - let mockRawVC = createVCEntitiesVC(expectedTypes: ["mockType"]) - let mockContract = createMockContract() - let verifiableCredential = try VerifiableCredential(raw: mockRawVC, from: mockContract) + let verifiableCredential = vcHelper.createMockVerifiableCredential(expectedTypes: ["mockType"]) // Act let result = constraint.doesMatch(verifiedId: verifiableCredential) @@ -39,9 +39,7 @@ class VCTypeConstraintTests: XCTestCase { func testDoesMatch_WhenVCContainsMultipleTypes_ReturnTrue() throws { // Arrange let constraint = VCTypeConstraint(type: "mockType") - let mockRawVC = createVCEntitiesVC(expectedTypes: ["mockType", "unmatchingType"]) - let mockContract = createMockContract() - let verifiableCredential = try VerifiableCredential(raw: mockRawVC, from: mockContract) + let verifiableCredential = vcHelper.createMockVerifiableCredential(expectedTypes: ["mockType", "unmatchingType"]) // Act let result = constraint.doesMatch(verifiedId: verifiableCredential) @@ -53,9 +51,7 @@ class VCTypeConstraintTests: XCTestCase { func testDoesMatch_WhenVCDoesNotContainType_ReturnFalse() throws { // Arrange let constraint = VCTypeConstraint(type: "mockType") - let mockRawVC = createVCEntitiesVC(expectedTypes: ["unmatchingType"]) - let mockContract = createMockContract() - let verifiableCredential = try VerifiableCredential(raw: mockRawVC, from: mockContract) + let verifiableCredential = vcHelper.createMockVerifiableCredential(expectedTypes: ["unmatchingType"]) // Act let result = constraint.doesMatch(verifiedId: verifiableCredential) @@ -63,39 +59,4 @@ class VCTypeConstraintTests: XCTestCase { // Assert XCTAssertFalse(result) } - - private func createVCEntitiesVC(expectedTypes: [String]) -> VCEntities.VerifiableCredential { - let claims = VCClaims(jti: "", - iss: "", - sub: "", - iat: 0, - exp: 0, - vc: VerifiableCredentialDescriptor(context: [], - type: expectedTypes, - credentialSubject: [:])) - return VCEntities.VerifiableCredential(headers: Header(), content: claims)! - } - - private func createMockContract() -> Contract { - let mockCardDisplay = CardDisplayDescriptor(title: "mock title", - issuedBy: "mock issuer", - backgroundColor: "mock background color", - textColor: "mock text color", - logo: nil, - cardDescription: "mock description") - let mockConsentDisplay = ConsentDisplayDescriptor(title: nil, - instructions: "mock purpose") - let mockDisplayDescriptor = DisplayDescriptor(id: nil, - locale: nil, - contract: nil, - card: mockCardDisplay, - consent: mockConsentDisplay, - claims: [:]) - let mockContractInputDescriptor = ContractInputDescriptor(credentialIssuer: "mock credential issuer", - issuer: "mock issuer", - attestations: nil) - return Contract(id: "mockContract", - display: mockDisplayDescriptor, - input: mockContractInputDescriptor) - } } From 6ad07f8be642dd5f5f78d8c9f3e38d1628338992 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Mar 2023 16:44:08 -0700 Subject: [PATCH 346/373] Move Contract tests to different folder. --- .../Manifest/ContractIssuanceRequestTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename WalletLibrary/WalletLibraryTests/Requests/{RequestProtocols => }/Manifest/ContractIssuanceRequestTests.swift (98%) diff --git a/WalletLibrary/WalletLibraryTests/Requests/RequestProtocols/Manifest/ContractIssuanceRequestTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Manifest/ContractIssuanceRequestTests.swift similarity index 98% rename from WalletLibrary/WalletLibraryTests/Requests/RequestProtocols/Manifest/ContractIssuanceRequestTests.swift rename to WalletLibrary/WalletLibraryTests/Requests/Manifest/ContractIssuanceRequestTests.swift index 958059e8..c57586dd 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/RequestProtocols/Manifest/ContractIssuanceRequestTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/Manifest/ContractIssuanceRequestTests.swift @@ -13,7 +13,7 @@ class ContractIssuanceRequestTests: XCTestCase { case expectedToBeThrown } - func testIsSatisfied_WithInvalidRequirement_ThrowsError() async throws { + func testIsSatisfied_WithInvalidRequirement_ReturnsFalse() async throws { // Arrange let mockStyle = MockRequesterStyle(requester: "mock requester") let mockRootOfTrust = RootOfTrust(verified: true, source: "") @@ -41,7 +41,7 @@ class ContractIssuanceRequestTests: XCTestCase { XCTAssertFalse(actualResult) } - func testIsSatisfied_WithOneInvalidRequirementofMultipleValidRequirements_ThrowsError() async throws { + func testIsSatisfied_WithOneInvalidRequirementofMultipleValidRequirements_ReturnsFalse() async throws { // Arrange let mockStyle = MockRequesterStyle(requester: "mock requester") let mockRootOfTrust = RootOfTrust(verified: true, source: "") From bbfcb86610156efb1b21be27c34a64a9d777dd5a Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 13 Mar 2023 16:44:27 -0700 Subject: [PATCH 347/373] Complete test cases for open id presentation request. --- .../OpenIdPresentationRequestTests.swift | 256 ++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 WalletLibrary/WalletLibraryTests/Requests/OpenId/OpenIdPresentationRequestTests.swift diff --git a/WalletLibrary/WalletLibraryTests/Requests/OpenId/OpenIdPresentationRequestTests.swift b/WalletLibrary/WalletLibraryTests/Requests/OpenId/OpenIdPresentationRequestTests.swift new file mode 100644 index 00000000..8d61e96b --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Requests/OpenId/OpenIdPresentationRequestTests.swift @@ -0,0 +1,256 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import XCTest +import VCEntities +import VCToken +@testable import WalletLibrary + +struct MockOpenIdResponder: OpenIdResponder { + + let mockSend: ((WalletLibrary.PresentationResponse) async throws -> ())? + + init(mockSend: ((WalletLibrary.PresentationResponse) async throws -> ())? = nil) { + self.mockSend = mockSend + } + func send(response: WalletLibrary.PresentationResponse) async throws { + try await self.mockSend?(response) + } +} + +class OpenIdPresentationRequestTests: XCTestCase { + + enum ExpectedError: Error, Equatable { + case expectedToBeThrown + case expectedToBeThrownInResponder + } + + func testIsSatisfied_WithInvalidRequirement_ReturnsFalse() async throws { + // Arrange + let mockStyle = MockRequesterStyle(requester: "mock requester") + let mockRootOfTrust = RootOfTrust(verified: true, source: "") + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: Mapper()) + + let mockRequirement = MockRequirement(id: "mockRequirement324", + mockValidateCallback: { throw ExpectedError.expectedToBeThrown }) + + let content = PresentationRequestContent(style: mockStyle, + requirement: mockRequirement, + rootOfTrust: mockRootOfTrust) + + let mockResponder = MockOpenIdResponder() + + let request = OpenIdPresentationRequest(content: content, + rawRequest: MockOpenIdRawRequest(raw: nil), + openIdResponder: mockResponder, + configuration: configuration) + + // Act + let actualResult = request.isSatisfied() + + // Assert + XCTAssertFalse(actualResult) + } + + func testIsSatisfied_WithValidRequirement_ReturnsTrue() async throws { + // Arrange + let mockStyle = MockRequesterStyle(requester: "mock requester") + let mockRootOfTrust = RootOfTrust(verified: true, source: "") + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: Mapper()) + + let mockRequirement = MockRequirement(id: "mockRequirement324") + + let content = PresentationRequestContent(style: mockStyle, + requirement: mockRequirement, + rootOfTrust: mockRootOfTrust) + + let mockResponder = MockOpenIdResponder() + + let request = OpenIdPresentationRequest(content: content, + rawRequest: MockOpenIdRawRequest(raw: nil), + openIdResponder: mockResponder, + configuration: configuration) + + // Act + let actualResult = request.isSatisfied() + + // Assert + XCTAssert(actualResult) + } + + func testComplete_WithPresentationResponseContainerThrows_ReturnsFailureResult() async throws { + // Arrange + let mockStyle = MockRequesterStyle(requester: "mock requester") + let mockRootOfTrust = RootOfTrust(verified: true, source: "") + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: Mapper()) + + let mockRequirement = MockRequirement(id: "mockRequirement324") + + let content = PresentationRequestContent(style: mockStyle, + requirement: mockRequirement, + rootOfTrust: mockRootOfTrust) + + let mockResponder = MockOpenIdResponder() + + let request = OpenIdPresentationRequest(content: content, + rawRequest: MockOpenIdRawRequest(raw: nil), + openIdResponder: mockResponder, + configuration: configuration) + + // Act + let actualResult = await request.complete() + + // Assert + switch (actualResult) { + case .success(_): + XCTFail() + case .failure(let error): + XCTAssert(error is PresentationResponseError) + XCTAssertEqual((error as? PresentationResponseError), + .unableToCastVCSDKPresentationRequestFromRawRequestOfType("MockOpenIdRawRequest")) + } + } + + func testComplete_WithAddRequirementToPresentationResponseThrows_ReturnsFailureResult() async throws { + // Arrange + let mockStyle = MockRequesterStyle(requester: "mock requester") + let mockRootOfTrust = RootOfTrust(verified: true, source: "") + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: Mapper()) + + let mockRequirement = MockRequirement(id: "mockRequirement324") + + let content = PresentationRequestContent(style: mockStyle, + requirement: mockRequirement, + rootOfTrust: mockRootOfTrust) + + let mockResponder = MockOpenIdResponder() + + let mockRawRequest = createMockPresentationRequest() + + let request = OpenIdPresentationRequest(content: content, + rawRequest: mockRawRequest, + openIdResponder: mockResponder, + configuration: configuration) + + // Act + let actualResult = await request.complete() + + // Assert + switch (actualResult) { + case .success(_): + XCTFail() + case .failure(let error): + XCTAssert(error is PresentationResponseError) + XCTAssertEqual((error as? PresentationResponseError), + .unsupportedRequirementOfType("MockRequirement")) + } + } + + func testComplete_WithResponderThrows_ReturnsFailureResult() async throws { + // Arrange + let mockStyle = MockRequesterStyle(requester: "mock requester") + let mockRootOfTrust = RootOfTrust(verified: true, source: "") + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: Mapper()) + + let mockRequirement = VerifiedIdRequirement(encrypted: false, + required: true, + types: [], + purpose: nil, + issuanceOptions: [], + id: "mockId", + constraint: MockConstraint(doesMatchResult: true)) + mockRequirement.selectedVerifiedId = MockVerifiableCredentialHelper().createMockVerifiableCredential() + + let content = PresentationRequestContent(style: mockStyle, + requirement: mockRequirement, + rootOfTrust: mockRootOfTrust) + + func mockSend(response: WalletLibrary.PresentationResponse) async throws { + throw ExpectedError.expectedToBeThrownInResponder + } + + let mockResponder = MockOpenIdResponder(mockSend: mockSend) + + let mockRawRequest = createMockPresentationRequest() + + let request = OpenIdPresentationRequest(content: content, + rawRequest: mockRawRequest, + openIdResponder: mockResponder, + configuration: configuration) + + // Act + let actualResult = await request.complete() + + // Assert + switch (actualResult) { + case .success(_): + XCTFail() + case .failure(let error): + print(error) + XCTAssert(error is ExpectedError) + XCTAssertEqual((error as? ExpectedError), .expectedToBeThrownInResponder) + } + } + + func testComplete_WithValidInput_ReturnsSuccessResult() async throws { + // Arrange + let mockStyle = MockRequesterStyle(requester: "mock requester") + let mockRootOfTrust = RootOfTrust(verified: true, source: "") + let configuration = LibraryConfiguration(logger: WalletLibraryLogger(), mapper: Mapper()) + + let mockRequirement = VerifiedIdRequirement(encrypted: false, + required: true, + types: [], + purpose: nil, + issuanceOptions: [], + id: "mockId", + constraint: MockConstraint(doesMatchResult: true)) + mockRequirement.selectedVerifiedId = MockVerifiableCredentialHelper().createMockVerifiableCredential() + + let content = PresentationRequestContent(style: mockStyle, + requirement: mockRequirement, + rootOfTrust: mockRootOfTrust) + + let mockResponder = MockOpenIdResponder() + + let mockRawRequest = createMockPresentationRequest() + + let request = OpenIdPresentationRequest(content: content, + rawRequest: mockRawRequest, + openIdResponder: mockResponder, + configuration: configuration) + + // Act + let actualResult = await request.complete() + + // Assert + switch (actualResult) { + case .success(_): + XCTAssert(true) + case .failure(let error): + XCTFail(error.localizedDescription) + } + } + + private func createMockPresentationRequest() -> PresentationRequest { + let mockClaims = PresentationRequestClaims(jti: nil, + clientID: "mockAudienceDID", + redirectURI: "mockAudienceURL", + responseMode: nil, + responseType: nil, + claims: nil, + state: nil, + nonce: "mockNonce", + scope: nil, + prompt: nil, + registration: nil, + iat: nil, + exp: nil, + pin: nil) + let mockToken = PresentationRequestToken(headers: Header(), content: mockClaims)! + let mockRawRequest = PresentationRequest(from: mockToken, linkedDomainResult: .linkedDomainMissing) + return mockRawRequest + } +} From 20653d95f567c2a3887ca5b3543f158272fe3d03 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 14 Mar 2023 08:26:28 -0700 Subject: [PATCH 348/373] Complete test cases for VerifiedIdRequirement methods. --- .../WalletLibrary.xcodeproj/project.pbxproj | 4 + .../VerifiedIdRequirementTests.swift | 153 ++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 WalletLibrary/WalletLibraryTests/Requests/Requirements/VerifiedIdRequirementTests.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 155c3de6..16e6d37a 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -114,6 +114,7 @@ 55DECC3829BFE14E00D5C802 /* MockConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECC3729BFE14E00D5C802 /* MockConstraint.swift */; }; 55DECC3D29BFE22900D5C802 /* OpenIdPresentationRequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECC3C29BFE22900D5C802 /* OpenIdPresentationRequestTests.swift */; }; 55DECC3F29BFEACB00D5C802 /* MockVerifiableCredentialHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECC3E29BFEACB00D5C802 /* MockVerifiableCredentialHelper.swift */; }; + 55DECC4129BFEDE400D5C802 /* VerifiedIdRequirementTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECC4029BFEDE400D5C802 /* VerifiedIdRequirementTests.swift */; }; 55E2F06C299553300008010D /* PresentationRequest+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */; }; 55E2F06F299553B40008010D /* OpenIdRequestHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F06E299553B40008010D /* OpenIdRequestHandlerTests.swift */; }; 55E2F071299554110008010D /* LibraryConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F070299554110008010D /* LibraryConfiguration.swift */; }; @@ -418,6 +419,7 @@ 55DECC3729BFE14E00D5C802 /* MockConstraint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockConstraint.swift; sourceTree = ""; }; 55DECC3C29BFE22900D5C802 /* OpenIdPresentationRequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenIdPresentationRequestTests.swift; sourceTree = ""; }; 55DECC3E29BFEACB00D5C802 /* MockVerifiableCredentialHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockVerifiableCredentialHelper.swift; sourceTree = ""; }; + 55DECC4029BFEDE400D5C802 /* VerifiedIdRequirementTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdRequirementTests.swift; sourceTree = ""; }; 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PresentationRequest+Mappable.swift"; sourceTree = ""; }; 55E2F06E299553B40008010D /* OpenIdRequestHandlerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenIdRequestHandlerTests.swift; sourceTree = ""; }; 55E2F070299554110008010D /* LibraryConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryConfiguration.swift; sourceTree = ""; }; @@ -809,6 +811,7 @@ 559BD4EC299EEAD100BD61AC /* IdTokenRequirementTests.swift */, 559BD4F0299EEAEA00BD61AC /* PinRequirementTests.swift */, 559BD4F2299EEAFD00BD61AC /* SelfAttestedClaimRequirementTests.swift */, + 55DECC4029BFEDE400D5C802 /* VerifiedIdRequirementTests.swift */, ); path = Requirements; sourceTree = ""; @@ -1559,6 +1562,7 @@ 55DECBED29BA29FB00D5C802 /* PresentationResponseContainerExtensionTests.swift in Sources */, 559BD3AF299AF42C00BD61AC /* MockManifestResolver.swift in Sources */, 553CC09929A96C38005A5FD6 /* MockVerifiedId.swift in Sources */, + 55DECC4129BFEDE400D5C802 /* VerifiedIdRequirementTests.swift in Sources */, 559BD4F8299EED5700BD61AC /* MockRawManifest.swift in Sources */, 550A919829940DCE0014D030 /* MockOpenIdforVCResolver.swift in Sources */, 559BD459299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift in Sources */, diff --git a/WalletLibrary/WalletLibraryTests/Requests/Requirements/VerifiedIdRequirementTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Requirements/VerifiedIdRequirementTests.swift new file mode 100644 index 00000000..4d529aa9 --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Requests/Requirements/VerifiedIdRequirementTests.swift @@ -0,0 +1,153 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import XCTest +@testable import WalletLibrary + +class VerifiedIdRequirementTests: XCTestCase { + + func testValidate_WithNoSelectedVerifiedId_ThrowsError() async throws { + // Arrange + let mockConstraint = MockConstraint(doesMatchResult: false) + let requirement = VerifiedIdRequirement(encrypted: false, + required: false, + types: ["mockType"], + purpose: nil, + issuanceOptions: [], + id: nil, + constraint: mockConstraint) + + // Act + XCTAssertThrowsError(try requirement.validate()) { error in + // Assert + XCTAssert(error is VerifiedIdRequirementError) + XCTAssertEqual(error as? VerifiedIdRequirementError, .requirementHasNotBeenFulfilled) + } + } + + func testValidate_WhenConstraintsDoNotMatch_ThrowsError() async throws { + // Arrange + let mockVerifiedId = MockVerifiedId(id: "mock verified id", issuedOn: Date()) + let mockConstraint = MockConstraint(doesMatchResult: false) + + let requirement = VerifiedIdRequirement(encrypted: false, + required: false, + types: ["mockType"], + purpose: nil, + issuanceOptions: [], + id: nil, + constraint: mockConstraint) + requirement.selectedVerifiedId = mockVerifiedId + + // Act + XCTAssertThrowsError(try requirement.validate()) { error in + // Assert + XCTAssert(error is VerifiedIdRequirementError) + XCTAssertEqual(error as? VerifiedIdRequirementError, .verifiedIdDoesNotMeetConstraints) + } + } + + func testGetMatches_WhenConstraintsDoNotMatchAnyVerifiedIds_ReturnsEmptyList() async throws { + // Arrange + let mockVerifiedId = MockVerifiedId(id: "mock verified id", issuedOn: Date()) + let mockConstraint = MockConstraint(doesMatchResult: false) + + let requirement = VerifiedIdRequirement(encrypted: false, + required: false, + types: ["mockType"], + purpose: nil, + issuanceOptions: [], + id: nil, + constraint: mockConstraint) + + // Act + let result = requirement.getMatches(verifiedIds: [mockVerifiedId]) + + // Assert + XCTAssert(result.isEmpty) + } + + func testGetMatches_WhenConstraintsDoMatchOneVerifiedId_ReturnsVerifiedIdInList() async throws { + // Arrange + let mockVerifiedId = MockVerifiedId(id: "mock verified id", issuedOn: Date()) + let mockConstraint = MockConstraint(doesMatchResult: true) + + let requirement = VerifiedIdRequirement(encrypted: false, + required: false, + types: ["mockType"], + purpose: nil, + issuanceOptions: [], + id: nil, + constraint: mockConstraint) + + // Act + let result = requirement.getMatches(verifiedIds: [mockVerifiedId]) + + // Assert + XCTAssertEqual(result.first as? MockVerifiedId, mockVerifiedId) + } + + func testGetMatches_WhenConstraintsDoMatchTwoVerifiedIds_ReturnsVerifiedIdsInList() async throws { + // Arrange + let mockVerifiedId1 = MockVerifiedId(id: "mock verified id", issuedOn: Date()) + let mockVerifiedId2 = MockVerifiedId(id: "mock verified id", issuedOn: Date()) + let mockConstraint = MockConstraint(doesMatchResult: true) + + let requirement = VerifiedIdRequirement(encrypted: false, + required: false, + types: ["mockType"], + purpose: nil, + issuanceOptions: [], + id: nil, + constraint: mockConstraint) + + // Act + let result = requirement.getMatches(verifiedIds: [mockVerifiedId1, mockVerifiedId2]) + + // Assert + XCTAssertEqual(result.count, 2) + XCTAssertEqual(result.first as? MockVerifiedId, mockVerifiedId1) + XCTAssertEqual(result[1] as? MockVerifiedId, mockVerifiedId2) + } + + func testFulfill_WhenConstraintsDoNotMatchVerifiedId_ThrowsError() async throws { + // Arrange + let mockVerifiedId = MockVerifiedId(id: "mock verified id", issuedOn: Date()) + let mockConstraint = MockConstraint(doesMatchResult: false) + + let requirement = VerifiedIdRequirement(encrypted: false, + required: false, + types: ["mockType"], + purpose: nil, + issuanceOptions: [], + id: nil, + constraint: mockConstraint) + + // Act + XCTAssertThrowsError(try requirement.fulfill(with: mockVerifiedId)) { error in + // Assert + XCTAssert(error is VerifiedIdRequirementError) + XCTAssertEqual(error as? VerifiedIdRequirementError, .verifiedIdDoesNotMeetConstraints) + } + } + + func testFulfill_WhenConstraintsDoMatchVerifiedId_SelectedVerifiedIdFulfilled() async throws { + // Arrange + let mockVerifiedId = MockVerifiedId(id: "mock verified id", issuedOn: Date()) + let mockConstraint = MockConstraint(doesMatchResult: true) + + let requirement = VerifiedIdRequirement(encrypted: false, + required: false, + types: ["mockType"], + purpose: nil, + issuanceOptions: [], + id: nil, + constraint: mockConstraint) + + // Act / Assert + XCTAssertNoThrow(try requirement.fulfill(with: mockVerifiedId)) + XCTAssertEqual(requirement.selectedVerifiedId as? MockVerifiedId, mockVerifiedId) + } +} From 0f8e1f138b4379bc4dc1744cc852cafab7b89791 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 14 Mar 2023 08:26:43 -0700 Subject: [PATCH 349/373] Update presentation response container tests. --- ...ationResponseContainerExtensionTests.swift | 108 +++++++++++++++++- 1 file changed, 106 insertions(+), 2 deletions(-) diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Entities/PresentationResponseContainerExtensionTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Entities/PresentationResponseContainerExtensionTests.swift index 379ad6c5..0c87e526 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Entities/PresentationResponseContainerExtensionTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Entities/PresentationResponseContainerExtensionTests.swift @@ -16,6 +16,8 @@ class PresentationResponseContainerExtensionTests: XCTestCase { case thirdExpectedInvalidRequirement } + private let mockVerifiableCredentialHelper = MockVerifiableCredentialHelper() + func testInit_WithInvalidOpenIdRawRequest_ThrowsError() throws { // Arrange @@ -59,12 +61,114 @@ class PresentationResponseContainerExtensionTests: XCTestCase { } } + func testAddRequirement_WithNoIdInVerifiedIdRequirement_ThrowsError() async throws { + // Arrange + let mockPresentationRequest = createPresentationRequest() + var presentationResponse = try PresentationResponseContainer(rawRequest: mockPresentationRequest) + + let mockConstraint = MockConstraint(doesMatchResult: true) + let verifiedIdRequirement = VerifiedIdRequirement(encrypted: false, + required: false, + types: ["mockType"], + purpose: nil, + issuanceOptions: [], + id: nil, + constraint: mockConstraint) + + // Act + XCTAssertThrowsError(try presentationResponse.add(requirement: verifiedIdRequirement)) { error in + // Assert + XCTAssert(error is PresentationResponseError) + XCTAssertEqual(error as? PresentationResponseError, + PresentationResponseError.missingIdInVerifiedIdRequirement) + } + } + + func testAddRequirement_WithSelectedVerifiedIdTypeUnsupportedInVerifiedIdRequirement_AddsVCsToMap() async throws { + // Arrange + let mockPresentationRequest = createPresentationRequest() + var presentationResponse = try PresentationResponseContainer(rawRequest: mockPresentationRequest) + + let mockConstraint = MockConstraint(doesMatchResult: true) + let verifiedIdRequirement = VerifiedIdRequirement(encrypted: false, + required: false, + types: ["mockType"], + purpose: nil, + issuanceOptions: [], + id: "mockId", + constraint: mockConstraint) + verifiedIdRequirement.selectedVerifiedId = MockVerifiedId(id: "mockId", issuedOn: Date()) + + // Act + XCTAssertThrowsError(try presentationResponse.add(requirement: verifiedIdRequirement)) { error in + // Assert + XCTAssert(error is PresentationResponseError) + XCTAssertEqual(error as? PresentationResponseError, + PresentationResponseError.unableToCastVerifableCredentialFromVerifiedId) + } + } + func testAddRequirement_WithVerifiedIdRequirement_AddsVCToMap() async throws { - // TODO + // Arrange + let mockPresentationRequest = createPresentationRequest() + var presentationResponse = try PresentationResponseContainer(rawRequest: mockPresentationRequest) + + let mockConstraint = MockConstraint(doesMatchResult: true) + let verifiedIdRequirement = VerifiedIdRequirement(encrypted: false, + required: false, + types: ["mockType"], + purpose: nil, + issuanceOptions: [], + id: "mockId", + constraint: mockConstraint) + let vc = mockVerifiableCredentialHelper.createMockVerifiableCredential() + verifiedIdRequirement.selectedVerifiedId = vc + + // Act / Assert + XCTAssertNoThrow(try presentationResponse.add(requirement: verifiedIdRequirement)) + XCTAssertEqual(presentationResponse.requestVCMap.count, 1) + XCTAssertEqual(try presentationResponse.requestVCMap.first?.vc.serialize(), + try vc.raw.serialize()) + XCTAssertEqual(presentationResponse.requestVCMap.first?.inputDescriptorId, verifiedIdRequirement.id) } func testAddRequirement_WithMultipleVerifiedIdRequirements_AddsVCsToMap() async throws { - // TODO + // Arrange + let mockPresentationRequest = createPresentationRequest() + var presentationResponse = try PresentationResponseContainer(rawRequest: mockPresentationRequest) + + let mockConstraint = MockConstraint(doesMatchResult: true) + let verifiedIdRequirement1 = VerifiedIdRequirement(encrypted: false, + required: false, + types: ["mockType1"], + purpose: nil, + issuanceOptions: [], + id: "mockId1", + constraint: mockConstraint) + let verifiedIdRequirement2 = VerifiedIdRequirement(encrypted: false, + required: false, + types: ["mockType2"], + purpose: nil, + issuanceOptions: [], + id: "mockId2", + constraint: mockConstraint) + let vc1 = mockVerifiableCredentialHelper.createMockVerifiableCredential(expectedTypes: ["mockType1"]) + let vc2 = mockVerifiableCredentialHelper.createMockVerifiableCredential(expectedTypes: ["mockType2"]) + verifiedIdRequirement1.selectedVerifiedId = vc1 + verifiedIdRequirement2.selectedVerifiedId = vc2 + let groupRequirement = GroupRequirement(required: true, + requirements: [verifiedIdRequirement1, verifiedIdRequirement2], + requirementOperator: .ALL) + + // Act / Assert + XCTAssertNoThrow(try presentationResponse.add(requirement: groupRequirement)) + XCTAssertEqual(presentationResponse.requestVCMap.count, 2) + XCTAssertEqual(try presentationResponse.requestVCMap.first?.vc.serialize(), + try vc1.raw.serialize()) + XCTAssertEqual(presentationResponse.requestVCMap.first?.inputDescriptorId, verifiedIdRequirement1.id) + XCTAssertEqual(try presentationResponse.requestVCMap[1].vc.serialize(), + try vc2.raw.serialize()) + XCTAssertEqual(presentationResponse.requestVCMap[1].inputDescriptorId, verifiedIdRequirement2.id) } private func createPresentationRequest() -> PresentationRequest { From e59da1e9c47a69be34adf7a6f91cf6c480fb5e4e Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 14 Mar 2023 08:27:03 -0700 Subject: [PATCH 350/373] Update error in PresentatioResponseContainer extension. --- .../PresentationResponseContainer+WalletLibrary.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationResponseContainer+WalletLibrary.swift b/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationResponseContainer+WalletLibrary.swift index 0a5ea21a..4f119dde 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationResponseContainer+WalletLibrary.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Entities/PresentationResponseContainer+WalletLibrary.swift @@ -8,7 +8,7 @@ import VCEntities enum PresentationResponseError: Error, Equatable { case unableToCastVCSDKPresentationRequestFromRawRequestOfType(String) case unsupportedRequirementOfType(String) - case unableToCastVerifableCredentialFromVerifiedIdOfType(String) + case unableToCastVerifableCredentialFromVerifiedId case missingIdInVerifiedIdRequirement } @@ -54,8 +54,7 @@ extension VCEntities.PresentationResponseContainer: PresentationResponse { } guard let verifiableCredential = verifiedIdRequirement.selectedVerifiedId as? VerifiableCredential else { - let verifiedIdType = String(describing: type(of: verifiedIdRequirement.selectedVerifiedId)) - throw PresentationResponseError.unableToCastVerifableCredentialFromVerifiedIdOfType(verifiedIdType) + throw PresentationResponseError.unableToCastVerifableCredentialFromVerifiedId } let mapping = RequestedVerifiableCredentialMapping(id: requirementId, From aa169b9fb2b43af6f7653bc7e7a2546704ef215b Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 14 Mar 2023 08:53:12 -0700 Subject: [PATCH 351/373] Rename VerifiedIdGroupConstraint to GroupConstraint --- .../WalletLibrary.xcodeproj/project.pbxproj | 20 +++++++++++-------- ...Constraint.swift => GroupConstraint.swift} | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) rename WalletLibrary/WalletLibrary/Requests/Constraints/{VerifiedIdGroupConstraint.swift => GroupConstraint.swift} (95%) diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 16e6d37a..73d9daf2 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -107,14 +107,15 @@ 55DECBED29BA29FB00D5C802 /* PresentationResponseContainerExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECBEC29BA29FB00D5C802 /* PresentationResponseContainerExtensionTests.swift */; }; 55DECBEF29BA2BEE00D5C802 /* IssuanceRequestContentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECBEE29BA2BEE00D5C802 /* IssuanceRequestContentTests.swift */; }; 55DECC2B29BB8C8C00D5C802 /* VerifiedIdConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECC2A29BB8C8C00D5C802 /* VerifiedIdConstraint.swift */; }; - 55DECC2E29BB8DA800D5C802 /* VerifiedIdGroupConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECC2D29BB8DA800D5C802 /* VerifiedIdGroupConstraint.swift */; }; + 55DECC2E29BB8DA800D5C802 /* GroupConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECC2D29BB8DA800D5C802 /* GroupConstraint.swift */; }; 55DECC3029BB8E5C00D5C802 /* VCTypeConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECC2F29BB8E5C00D5C802 /* VCTypeConstraint.swift */; }; - 55DECC3329BFDB6200D5C802 /* VerifiedIdGroupConstraintTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECC3229BFDB6200D5C802 /* VerifiedIdGroupConstraintTests.swift */; }; + 55DECC3329BFDB6200D5C802 /* GroupConstraintTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECC3229BFDB6200D5C802 /* GroupConstraintTests.swift */; }; 55DECC3529BFDB7600D5C802 /* VCTypeConstraintTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECC3429BFDB7600D5C802 /* VCTypeConstraintTests.swift */; }; 55DECC3829BFE14E00D5C802 /* MockConstraint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECC3729BFE14E00D5C802 /* MockConstraint.swift */; }; 55DECC3D29BFE22900D5C802 /* OpenIdPresentationRequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECC3C29BFE22900D5C802 /* OpenIdPresentationRequestTests.swift */; }; 55DECC3F29BFEACB00D5C802 /* MockVerifiableCredentialHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECC3E29BFEACB00D5C802 /* MockVerifiableCredentialHelper.swift */; }; 55DECC4129BFEDE400D5C802 /* VerifiedIdRequirementTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECC4029BFEDE400D5C802 /* VerifiedIdRequirementTests.swift */; }; + 55DECC4329C0CF5F00D5C802 /* MockOpenIdResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55DECC4229C0CF5F00D5C802 /* MockOpenIdResponder.swift */; }; 55E2F06C299553300008010D /* PresentationRequest+Mappable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */; }; 55E2F06F299553B40008010D /* OpenIdRequestHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F06E299553B40008010D /* OpenIdRequestHandlerTests.swift */; }; 55E2F071299554110008010D /* LibraryConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55E2F070299554110008010D /* LibraryConfiguration.swift */; }; @@ -412,14 +413,15 @@ 55DECBEC29BA29FB00D5C802 /* PresentationResponseContainerExtensionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentationResponseContainerExtensionTests.swift; sourceTree = ""; }; 55DECBEE29BA2BEE00D5C802 /* IssuanceRequestContentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssuanceRequestContentTests.swift; sourceTree = ""; }; 55DECC2A29BB8C8C00D5C802 /* VerifiedIdConstraint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdConstraint.swift; sourceTree = ""; }; - 55DECC2D29BB8DA800D5C802 /* VerifiedIdGroupConstraint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdGroupConstraint.swift; sourceTree = ""; }; + 55DECC2D29BB8DA800D5C802 /* GroupConstraint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupConstraint.swift; sourceTree = ""; }; 55DECC2F29BB8E5C00D5C802 /* VCTypeConstraint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VCTypeConstraint.swift; sourceTree = ""; }; - 55DECC3229BFDB6200D5C802 /* VerifiedIdGroupConstraintTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdGroupConstraintTests.swift; sourceTree = ""; }; + 55DECC3229BFDB6200D5C802 /* GroupConstraintTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupConstraintTests.swift; sourceTree = ""; }; 55DECC3429BFDB7600D5C802 /* VCTypeConstraintTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VCTypeConstraintTests.swift; sourceTree = ""; }; 55DECC3729BFE14E00D5C802 /* MockConstraint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockConstraint.swift; sourceTree = ""; }; 55DECC3C29BFE22900D5C802 /* OpenIdPresentationRequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenIdPresentationRequestTests.swift; sourceTree = ""; }; 55DECC3E29BFEACB00D5C802 /* MockVerifiableCredentialHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockVerifiableCredentialHelper.swift; sourceTree = ""; }; 55DECC4029BFEDE400D5C802 /* VerifiedIdRequirementTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdRequirementTests.swift; sourceTree = ""; }; + 55DECC4229C0CF5F00D5C802 /* MockOpenIdResponder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockOpenIdResponder.swift; sourceTree = ""; }; 55E2F056299553300008010D /* PresentationRequest+Mappable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PresentationRequest+Mappable.swift"; sourceTree = ""; }; 55E2F06E299553B40008010D /* OpenIdRequestHandlerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenIdRequestHandlerTests.swift; sourceTree = ""; }; 55E2F070299554110008010D /* LibraryConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibraryConfiguration.swift; sourceTree = ""; }; @@ -513,6 +515,7 @@ children = ( 550A919729940DCE0014D030 /* MockOpenIdforVCResolver.swift */, 550A919F299412790014D030 /* MockOpenIdRawRequest.swift */, + 55DECC4229C0CF5F00D5C802 /* MockOpenIdResponder.swift */, ); path = OpenIdForVC; sourceTree = ""; @@ -899,7 +902,7 @@ 55DECC2C29BB8D9100D5C802 /* Constraints */ = { isa = PBXGroup; children = ( - 55DECC2D29BB8DA800D5C802 /* VerifiedIdGroupConstraint.swift */, + 55DECC2D29BB8DA800D5C802 /* GroupConstraint.swift */, 55DECC2F29BB8E5C00D5C802 /* VCTypeConstraint.swift */, ); path = Constraints; @@ -908,7 +911,7 @@ 55DECC3129BFDB4E00D5C802 /* Constraints */ = { isa = PBXGroup; children = ( - 55DECC3229BFDB6200D5C802 /* VerifiedIdGroupConstraintTests.swift */, + 55DECC3229BFDB6200D5C802 /* GroupConstraintTests.swift */, 55DECC3429BFDB7600D5C802 /* VCTypeConstraintTests.swift */, ); path = Constraints; @@ -1464,7 +1467,7 @@ 55E2F08C299576730008010D /* GroupRequirement.swift in Sources */, 550E3212298B1150004E7716 /* VerifiedIdRequestInput.swift in Sources */, 550A919B29940E9E0014D030 /* PresentationRequest+OpenIdRawRequest.swift in Sources */, - 55DECC2E29BB8DA800D5C802 /* VerifiedIdGroupConstraint.swift in Sources */, + 55DECC2E29BB8DA800D5C802 /* GroupConstraint.swift in Sources */, 5534E667294A0B6C005D0765 /* Mappable.swift in Sources */, 559BD3A8299ADE9C00BD61AC /* RawManifest.swift in Sources */, 55E2F08129955A960008010D /* RequestType.swift in Sources */, @@ -1542,6 +1545,7 @@ 5534E6AD294AAFA6005D0765 /* PresentationDescriptor+MappableTests.swift in Sources */, 559BD4ED299EEAD100BD61AC /* IdTokenRequirementTests.swift in Sources */, 55DECC3D29BFE22900D5C802 /* OpenIdPresentationRequestTests.swift in Sources */, + 55DECC4329C0CF5F00D5C802 /* MockOpenIdResponder.swift in Sources */, 559BD455299D3E4800BD61AC /* IssuanceRequest+MappableTests.swift in Sources */, 553CC0A029A98003005A5FD6 /* VerifiableCredentialTests.swift in Sources */, 55A81C132991C829002C259A /* MockHandler.swift in Sources */, @@ -1568,7 +1572,7 @@ 559BD459299D3E7F00BD61AC /* SelfIssuedClaimsDescriptor+MappableTests.swift in Sources */, 559BD3092995ABE800BD61AC /* PresentationRequest+MappableTests.swift in Sources */, 550A91A0299412790014D030 /* MockOpenIdRawRequest.swift in Sources */, - 55DECC3329BFDB6200D5C802 /* VerifiedIdGroupConstraintTests.swift in Sources */, + 55DECC3329BFDB6200D5C802 /* GroupConstraintTests.swift in Sources */, 559BD4F1299EEAEA00BD61AC /* PinRequirementTests.swift in Sources */, 559BD30F2996C1C500BD61AC /* MockLogConsumer.swift in Sources */, 55E2F076299555280008010D /* MockRequirement.swift in Sources */, diff --git a/WalletLibrary/WalletLibrary/Requests/Constraints/VerifiedIdGroupConstraint.swift b/WalletLibrary/WalletLibrary/Requests/Constraints/GroupConstraint.swift similarity index 95% rename from WalletLibrary/WalletLibrary/Requests/Constraints/VerifiedIdGroupConstraint.swift rename to WalletLibrary/WalletLibrary/Requests/Constraints/GroupConstraint.swift index 805ec7dd..10e7f344 100644 --- a/WalletLibrary/WalletLibrary/Requests/Constraints/VerifiedIdGroupConstraint.swift +++ b/WalletLibrary/WalletLibrary/Requests/Constraints/GroupConstraint.swift @@ -11,7 +11,7 @@ enum GroupConstraintOperator { /** * A group of constraints. */ -struct VerifiedIdGroupConstraint: VerifiedIdConstraint { +struct GroupConstraint: VerifiedIdConstraint { let constraints: [VerifiedIdConstraint] From a6d7facfd253c20cbe0717687853872fbf2227cb Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 14 Mar 2023 08:53:51 -0700 Subject: [PATCH 352/373] Fix test formats. --- ...ationResponseContainerExtensionTests.swift | 20 +++++----- ...AttestationsDescriptor+MappableTests.swift | 6 +-- ...PresentationDescriptor+MappableTests.swift | 2 +- ...PresentationDefinition+MappableTests.swift | 38 +++++++++---------- ...ntationInputDescriptor+MappableTests.swift | 28 +++++++++----- .../PresentationRequest+MappableTests.swift | 10 ++--- .../MockVerifiableCredentialHelper.swift | 1 - .../OpenIdForVC/MockOpenIdResponder.swift | 18 +++++++++ ...Tests.swift => GroupConstraintTests.swift} | 10 ++--- .../Constraints/VCTypeConstraintTests.swift | 8 ++-- .../Handlers/OpenIdRequestHandlerTests.swift | 12 +++--- .../OpenIdPresentationRequestTests.swift | 20 +++------- 12 files changed, 94 insertions(+), 79 deletions(-) create mode 100644 WalletLibrary/WalletLibraryTests/Mocks/Requests/OpenIdForVC/MockOpenIdResponder.swift rename WalletLibrary/WalletLibraryTests/Requests/Constraints/{VerifiedIdGroupConstraintTests.swift => GroupConstraintTests.swift} (89%) diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Entities/PresentationResponseContainerExtensionTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Entities/PresentationResponseContainerExtensionTests.swift index 0c87e526..911f6386 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Entities/PresentationResponseContainerExtensionTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Entities/PresentationResponseContainerExtensionTests.swift @@ -97,7 +97,7 @@ class PresentationResponseContainerExtensionTests: XCTestCase { issuanceOptions: [], id: "mockId", constraint: mockConstraint) - verifiedIdRequirement.selectedVerifiedId = MockVerifiedId(id: "mockId", issuedOn: Date()) + try verifiedIdRequirement.fulfill(with: MockVerifiedId(id: "mockId", issuedOn: Date())) // Act XCTAssertThrowsError(try presentationResponse.add(requirement: verifiedIdRequirement)) { error in @@ -121,14 +121,14 @@ class PresentationResponseContainerExtensionTests: XCTestCase { issuanceOptions: [], id: "mockId", constraint: mockConstraint) - let vc = mockVerifiableCredentialHelper.createMockVerifiableCredential() - verifiedIdRequirement.selectedVerifiedId = vc + let mockVC = mockVerifiableCredentialHelper.createMockVerifiableCredential() + try verifiedIdRequirement.fulfill(with: mockVC) // Act / Assert XCTAssertNoThrow(try presentationResponse.add(requirement: verifiedIdRequirement)) XCTAssertEqual(presentationResponse.requestVCMap.count, 1) XCTAssertEqual(try presentationResponse.requestVCMap.first?.vc.serialize(), - try vc.raw.serialize()) + try mockVC.raw.serialize()) XCTAssertEqual(presentationResponse.requestVCMap.first?.inputDescriptorId, verifiedIdRequirement.id) } @@ -152,10 +152,10 @@ class PresentationResponseContainerExtensionTests: XCTestCase { issuanceOptions: [], id: "mockId2", constraint: mockConstraint) - let vc1 = mockVerifiableCredentialHelper.createMockVerifiableCredential(expectedTypes: ["mockType1"]) - let vc2 = mockVerifiableCredentialHelper.createMockVerifiableCredential(expectedTypes: ["mockType2"]) - verifiedIdRequirement1.selectedVerifiedId = vc1 - verifiedIdRequirement2.selectedVerifiedId = vc2 + let mockVC1 = mockVerifiableCredentialHelper.createMockVerifiableCredential(expectedTypes: ["mockType1"]) + let mockVC2 = mockVerifiableCredentialHelper.createMockVerifiableCredential(expectedTypes: ["mockType2"]) + try verifiedIdRequirement1.fulfill(with: mockVC1) + try verifiedIdRequirement2.fulfill(with: mockVC2) let groupRequirement = GroupRequirement(required: true, requirements: [verifiedIdRequirement1, verifiedIdRequirement2], requirementOperator: .ALL) @@ -164,10 +164,10 @@ class PresentationResponseContainerExtensionTests: XCTestCase { XCTAssertNoThrow(try presentationResponse.add(requirement: groupRequirement)) XCTAssertEqual(presentationResponse.requestVCMap.count, 2) XCTAssertEqual(try presentationResponse.requestVCMap.first?.vc.serialize(), - try vc1.raw.serialize()) + try mockVC1.raw.serialize()) XCTAssertEqual(presentationResponse.requestVCMap.first?.inputDescriptorId, verifiedIdRequirement1.id) XCTAssertEqual(try presentationResponse.requestVCMap[1].vc.serialize(), - try vc2.raw.serialize()) + try mockVC2.raw.serialize()) XCTAssertEqual(presentationResponse.requestVCMap[1].inputDescriptorId, verifiedIdRequirement2.id) } diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+MappableTests.swift index 466c4f7e..cb915bc3 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/AttestationsDescriptor+MappableTests.swift @@ -128,7 +128,7 @@ class AttestationsDescriptorMappingTests: XCTestCase { purpose: nil, issuanceOptions: [], id: nil, - constraint: VerifiedIdGroupConstraint(constraints: [], + constraint: GroupConstraint(constraints: [], constraintOperator: .ALL)) let mockPresentationDescriptor = PresentationDescriptor(claims: [], credentialType: "mock type") @@ -236,7 +236,7 @@ class AttestationsDescriptorMappingTests: XCTestCase { purpose: nil, issuanceOptions: [], id: nil, - constraint: VerifiedIdGroupConstraint(constraints: [], + constraint: GroupConstraint(constraints: [], constraintOperator: .ALL)) let expectedIdTokenRequirement = IdTokenRequirement(encrypted: false, required: true, @@ -307,7 +307,7 @@ class AttestationsDescriptorMappingTests: XCTestCase { purpose: nil, issuanceOptions: [], id: nil, - constraint: VerifiedIdGroupConstraint(constraints: [], + constraint: GroupConstraint(constraints: [], constraintOperator: .ALL)) let expectedIdTokenRequirement = IdTokenRequirement(encrypted: false, required: true, diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+MappableTests.swift index d2d673f4..a188f4f2 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Issuance/PresentationDescriptor+MappableTests.swift @@ -117,7 +117,7 @@ class PresentationDescriptorMappingTests: XCTestCase { purpose: nil, issuanceOptions: expectedIssuanceOptions ?? [], id: nil, - constraint: VerifiedIdGroupConstraint(constraints: [], constraintOperator: .ALL)) + constraint: GroupConstraint(constraints: [], constraintOperator: .ALL)) return (input, expectedResult) } } diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+MappableTests.swift index cf4dc9b9..e4de9410 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationDefinition+MappableTests.swift @@ -50,8 +50,8 @@ class PresentationDefinitionMappingTests: XCTestCase { purpose: nil, issuanceOptions: [], id: nil, - constraint: VerifiedIdGroupConstraint(constraints: [], - constraintOperator: .ALL)) + constraint: GroupConstraint(constraints: [], + constraintOperator: .ALL)) let inputDescriptor = PresentationInputDescriptor(id: nil, schema: nil, issuanceMetadata: nil, @@ -89,26 +89,26 @@ class PresentationDefinitionMappingTests: XCTestCase { purpose: nil, issuanceOptions: [], id: nil, - constraint: VerifiedIdGroupConstraint(constraints: [], - constraintOperator: .ALL)) + constraint: GroupConstraint(constraints: [], + constraintOperator: .ALL)) let firstMockInputDescriptor = PresentationInputDescriptor(id: nil, - schema: nil, - issuanceMetadata: nil, - name: nil, - purpose: nil, - constraints: nil) + schema: nil, + issuanceMetadata: nil, + name: nil, + purpose: nil, + constraints: nil) let secondMockInputDescriptor = PresentationInputDescriptor(id: nil, - schema: nil, - issuanceMetadata: nil, - name: nil, - purpose: nil, - constraints: nil) + schema: nil, + issuanceMetadata: nil, + name: nil, + purpose: nil, + constraints: nil) let thirdMockInputDescriptor = PresentationInputDescriptor(id: nil, - schema: nil, - issuanceMetadata: nil, - name: nil, - purpose: nil, - constraints: nil) + schema: nil, + issuanceMetadata: nil, + name: nil, + purpose: nil, + constraints: nil) let inputDescriptors = [firstMockInputDescriptor, secondMockInputDescriptor, thirdMockInputDescriptor] let presentationDefinition = PresentationDefinition(id: nil, inputDescriptors: inputDescriptors, diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+MappableTests.swift index 5b28fa2d..d6c193ae 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+MappableTests.swift @@ -74,7 +74,8 @@ class PresentationInputDescriptorMappingTests: XCTestCase { purpose: nil, issuanceOptions: [], id: nil, - constraint: VerifiedIdGroupConstraint(constraints: [], constraintOperator: .ALL)) + constraint: GroupConstraint(constraints: [], + constraintOperator: .ALL)) let presentationInputDescriptor = PresentationInputDescriptor(id: nil, schema: [mockSchema], issuanceMetadata: nil, @@ -111,7 +112,8 @@ class PresentationInputDescriptorMappingTests: XCTestCase { purpose: nil, issuanceOptions: [], id: nil, - constraint: VerifiedIdGroupConstraint(constraints: [], constraintOperator: .ALL)) + constraint: GroupConstraint(constraints: [], + constraintOperator: .ALL)) let presentationInputDescriptor = PresentationInputDescriptor(id: nil, schema: [firstSchema, secondSchema, thirdSchema], issuanceMetadata: nil, @@ -128,8 +130,8 @@ class PresentationInputDescriptorMappingTests: XCTestCase { XCTAssertEqual(actualResult.types, expectedVerifiedIdRequest.types) XCTAssertEqual(actualResult.issuanceOptions as? [VerifiedIdRequestURL], []) XCTAssertEqual(actualResult.purpose, expectedVerifiedIdRequest.purpose) - XCTAssert(actualResult.constraint is VerifiedIdGroupConstraint) - let constraints = (actualResult.constraint as? VerifiedIdGroupConstraint)!.constraints + XCTAssert(actualResult.constraint is GroupConstraint) + let constraints = (actualResult.constraint as? GroupConstraint)!.constraints XCTAssertEqual(constraints.count, 3) XCTAssertEqual((constraints[0] as? VCTypeConstraint)?.type, firstType) XCTAssertEqual((constraints[1] as? VCTypeConstraint)?.type, secondType) @@ -147,7 +149,8 @@ class PresentationInputDescriptorMappingTests: XCTestCase { purpose: nil, issuanceOptions: [], id: nil, - constraint: VerifiedIdGroupConstraint(constraints: [], constraintOperator: .ALL)) + constraint: GroupConstraint(constraints: [], + constraintOperator: .ALL)) let presentationInputDescriptor = PresentationInputDescriptor(id: nil, schema: [mockSchema], issuanceMetadata: nil, @@ -182,7 +185,8 @@ class PresentationInputDescriptorMappingTests: XCTestCase { purpose: nil, issuanceOptions: [], id: mockId, - constraint: VerifiedIdGroupConstraint(constraints: [], constraintOperator: .ALL)) + constraint: GroupConstraint(constraints: [], + constraintOperator: .ALL)) let presentationInputDescriptor = PresentationInputDescriptor(id: mockId, schema: [mockSchema], issuanceMetadata: [invalidIssuanceMetadata], @@ -224,9 +228,11 @@ class PresentationInputDescriptorMappingTests: XCTestCase { required: true, types: ["mockType"], purpose: nil, - issuanceOptions: [firstVerifiedIdRequestURL, secondVerifiedIdRequestURL], + issuanceOptions: [firstVerifiedIdRequestURL, + secondVerifiedIdRequestURL], id: nil, - constraint: VerifiedIdGroupConstraint(constraints: [], constraintOperator: .ALL)) + constraint: GroupConstraint(constraints: [], + constraintOperator: .ALL)) let issuanceMetadatas = [firstValidIssuanceMetadata, invalidIssuanceMetadata, secondValidIssuanceMetadata] let presentationInputDescriptor = PresentationInputDescriptor(id: mockId, @@ -243,7 +249,8 @@ class PresentationInputDescriptorMappingTests: XCTestCase { XCTAssertEqual(actualResult.encrypted, expectedVerifiedIdRequest.encrypted) XCTAssertEqual(actualResult.required, expectedVerifiedIdRequest.required) XCTAssertEqual(actualResult.types, expectedVerifiedIdRequest.types) - XCTAssertEqual(actualResult.issuanceOptions as? [VerifiedIdRequestURL], [firstVerifiedIdRequestURL, secondVerifiedIdRequestURL]) + XCTAssertEqual(actualResult.issuanceOptions as? [VerifiedIdRequestURL], [firstVerifiedIdRequestURL, + secondVerifiedIdRequestURL]) XCTAssertEqual(actualResult.purpose, expectedVerifiedIdRequest.purpose) XCTAssert(actualResult.constraint is VCTypeConstraint) XCTAssertEqual((actualResult.constraint as? VCTypeConstraint)?.type, "mockType") @@ -261,7 +268,8 @@ class PresentationInputDescriptorMappingTests: XCTestCase { purpose: purpose, issuanceOptions: [], id: nil, - constraint: VerifiedIdGroupConstraint(constraints: [], constraintOperator: .ALL)) + constraint: GroupConstraint(constraints: [], + constraintOperator: .ALL)) let presentationInputDescriptor = PresentationInputDescriptor(id: nil, schema: [mockSchema], issuanceMetadata: nil, diff --git a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+MappableTests.swift b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+MappableTests.swift index f6cd45ed..308556fb 100644 --- a/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+MappableTests.swift +++ b/WalletLibrary/WalletLibraryTests/Extensions/Mappings/VCSDK/Presentation/PresentationRequest+MappableTests.swift @@ -69,7 +69,7 @@ class PresentationRequestMappingTests: XCTestCase { purpose: nil, issuanceOptions: [], id: nil, - constraint: VerifiedIdGroupConstraint(constraints: [], + constraint: GroupConstraint(constraints: [], constraintOperator: .ALL)) let mockPresentationDefinition = PresentationDefinition(id: nil, inputDescriptors: nil, issuance: nil) let mockRequestClaims = RequestedClaims(vpToken: RequestedVPToken(presentationDefinition: mockPresentationDefinition)) @@ -112,7 +112,7 @@ class PresentationRequestMappingTests: XCTestCase { purpose: nil, issuanceOptions: [], id: nil, - constraint: VerifiedIdGroupConstraint(constraints: [], + constraint: GroupConstraint(constraints: [], constraintOperator: .ALL)) let mockPresentationDefinition = PresentationDefinition(id: nil, inputDescriptors: nil, issuance: nil) let mockRequestClaims = RequestedClaims(vpToken: RequestedVPToken(presentationDefinition: mockPresentationDefinition)) @@ -163,7 +163,7 @@ class PresentationRequestMappingTests: XCTestCase { purpose: nil, issuanceOptions: [], id: nil, - constraint: VerifiedIdGroupConstraint(constraints: [], + constraint: GroupConstraint(constraints: [], constraintOperator: .ALL)) let mockPresentationDefinition = PresentationDefinition(id: nil, inputDescriptors: nil, issuance: nil) let mockRequestClaims = RequestedClaims(vpToken: RequestedVPToken(presentationDefinition: mockPresentationDefinition)) @@ -214,7 +214,7 @@ class PresentationRequestMappingTests: XCTestCase { purpose: nil, issuanceOptions: [], id: nil, - constraint: VerifiedIdGroupConstraint(constraints: [], + constraint: GroupConstraint(constraints: [], constraintOperator: .ALL)) let mockPresentationDefinition = PresentationDefinition(id: nil, inputDescriptors: nil, issuance: nil) let mockRequestClaims = RequestedClaims(vpToken: RequestedVPToken(presentationDefinition: mockPresentationDefinition)) @@ -272,7 +272,7 @@ class PresentationRequestMappingTests: XCTestCase { purpose: nil, issuanceOptions: [], id: nil, - constraint: VerifiedIdGroupConstraint(constraints: [], + constraint: GroupConstraint(constraints: [], constraintOperator: .ALL)) let mockPresentationDefinition = PresentationDefinition(id: nil, inputDescriptors: nil, issuance: nil) let mockRequestClaims = RequestedClaims(vpToken: RequestedVPToken(presentationDefinition: mockPresentationDefinition)) diff --git a/WalletLibrary/WalletLibraryTests/Mocks/MockVerifiableCredentialHelper.swift b/WalletLibrary/WalletLibraryTests/Mocks/MockVerifiableCredentialHelper.swift index d00d04e7..ea20cf9f 100644 --- a/WalletLibrary/WalletLibraryTests/Mocks/MockVerifiableCredentialHelper.swift +++ b/WalletLibrary/WalletLibraryTests/Mocks/MockVerifiableCredentialHelper.swift @@ -49,5 +49,4 @@ struct MockVerifiableCredentialHelper { display: mockDisplayDescriptor, input: mockContractInputDescriptor) } - } diff --git a/WalletLibrary/WalletLibraryTests/Mocks/Requests/OpenIdForVC/MockOpenIdResponder.swift b/WalletLibrary/WalletLibraryTests/Mocks/Requests/OpenIdForVC/MockOpenIdResponder.swift new file mode 100644 index 00000000..fbaf72aa --- /dev/null +++ b/WalletLibrary/WalletLibraryTests/Mocks/Requests/OpenIdForVC/MockOpenIdResponder.swift @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +@testable import WalletLibrary + +struct MockOpenIdResponder: OpenIdResponder { + + let mockSend: ((PresentationResponse) async throws -> ())? + + init(mockSend: ((PresentationResponse) async throws -> ())? = nil) { + self.mockSend = mockSend + } + func send(response: PresentationResponse) async throws { + try await self.mockSend?(response) + } +} diff --git a/WalletLibrary/WalletLibraryTests/Requests/Constraints/VerifiedIdGroupConstraintTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Constraints/GroupConstraintTests.swift similarity index 89% rename from WalletLibrary/WalletLibraryTests/Requests/Constraints/VerifiedIdGroupConstraintTests.swift rename to WalletLibrary/WalletLibraryTests/Requests/Constraints/GroupConstraintTests.swift index 0aad89b3..86401398 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/Constraints/VerifiedIdGroupConstraintTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/Constraints/GroupConstraintTests.swift @@ -8,7 +8,7 @@ import VCEntities import VCToken @testable import WalletLibrary -class VerifiedIdGroupConstraintTests: XCTestCase { +class GroupConstraintTests: XCTestCase { func testDoesMatch_WithALLOperatorAndAllMatch_ReturnsTrue() throws { // Arrange @@ -17,7 +17,7 @@ class VerifiedIdGroupConstraintTests: XCTestCase { let secondConstraint = MockConstraint(doesMatchResult: true) let thirdConstraint = MockConstraint(doesMatchResult: true) let constraints = [firstConstraint, secondConstraint, thirdConstraint] - let groupConstraint = VerifiedIdGroupConstraint(constraints: constraints, + let groupConstraint = GroupConstraint(constraints: constraints, constraintOperator: .ALL) // Act / Assert @@ -31,7 +31,7 @@ class VerifiedIdGroupConstraintTests: XCTestCase { let secondConstraint = MockConstraint(doesMatchResult: false) let thirdConstraint = MockConstraint(doesMatchResult: true) let constraints = [firstConstraint, secondConstraint, thirdConstraint] - let groupConstraint = VerifiedIdGroupConstraint(constraints: constraints, + let groupConstraint = GroupConstraint(constraints: constraints, constraintOperator: .ALL) // Act / Assert @@ -45,7 +45,7 @@ class VerifiedIdGroupConstraintTests: XCTestCase { let secondConstraint = MockConstraint(doesMatchResult: false) let thirdConstraint = MockConstraint(doesMatchResult: true) let constraints = [firstConstraint, secondConstraint, thirdConstraint] - let groupConstraint = VerifiedIdGroupConstraint(constraints: constraints, + let groupConstraint = GroupConstraint(constraints: constraints, constraintOperator: .ANY) // Act / Assert @@ -59,7 +59,7 @@ class VerifiedIdGroupConstraintTests: XCTestCase { let secondConstraint = MockConstraint(doesMatchResult: false) let thirdConstraint = MockConstraint(doesMatchResult: false) let constraints = [firstConstraint, secondConstraint, thirdConstraint] - let groupConstraint = VerifiedIdGroupConstraint(constraints: constraints, + let groupConstraint = GroupConstraint(constraints: constraints, constraintOperator: .ANY) // Act / Assert diff --git a/WalletLibrary/WalletLibraryTests/Requests/Constraints/VCTypeConstraintTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Constraints/VCTypeConstraintTests.swift index 8ab89922..3c571a88 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/Constraints/VCTypeConstraintTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/Constraints/VCTypeConstraintTests.swift @@ -10,7 +10,7 @@ import VCToken class VCTypeConstraintTests: XCTestCase { - let vcHelper = MockVerifiableCredentialHelper() + let mockVerifiableCredentialHelper = MockVerifiableCredentialHelper() func testDoesMatch_WithUnsupportedVerifiedIdType_ReturnFalse() throws { // Arrange @@ -27,7 +27,7 @@ class VCTypeConstraintTests: XCTestCase { func testDoesMatch_WhenVCContainsType_ReturnTrue() throws { // Arrange let constraint = VCTypeConstraint(type: "mockType") - let verifiableCredential = vcHelper.createMockVerifiableCredential(expectedTypes: ["mockType"]) + let verifiableCredential = mockVerifiableCredentialHelper.createMockVerifiableCredential(expectedTypes: ["mockType"]) // Act let result = constraint.doesMatch(verifiedId: verifiableCredential) @@ -39,7 +39,7 @@ class VCTypeConstraintTests: XCTestCase { func testDoesMatch_WhenVCContainsMultipleTypes_ReturnTrue() throws { // Arrange let constraint = VCTypeConstraint(type: "mockType") - let verifiableCredential = vcHelper.createMockVerifiableCredential(expectedTypes: ["mockType", "unmatchingType"]) + let verifiableCredential = mockVerifiableCredentialHelper.createMockVerifiableCredential(expectedTypes: ["mockType", "unmatchingType"]) // Act let result = constraint.doesMatch(verifiedId: verifiableCredential) @@ -51,7 +51,7 @@ class VCTypeConstraintTests: XCTestCase { func testDoesMatch_WhenVCDoesNotContainType_ReturnFalse() throws { // Arrange let constraint = VCTypeConstraint(type: "mockType") - let verifiableCredential = vcHelper.createMockVerifiableCredential(expectedTypes: ["unmatchingType"]) + let verifiableCredential = mockVerifiableCredentialHelper.createMockVerifiableCredential(expectedTypes: ["unmatchingType"]) // Act let result = constraint.doesMatch(verifiedId: verifiableCredential) diff --git a/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift index f8715e83..e4188920 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/Handlers/OpenIdRequestHandlerTests.swift @@ -130,7 +130,7 @@ class OpenIdRequestHandlerTests: XCTestCase { purpose: nil, issuanceOptions: [], id: nil, - constraint: VerifiedIdGroupConstraint(constraints: [], constraintOperator: .ALL)) + constraint: GroupConstraint(constraints: [], constraintOperator: .ALL)) let expectedRootOfTrust = RootOfTrust(verified: true, source: "mock source") let expectedContent = PresentationRequestContent(style: expectedStyle, requirement: expectedRequirement, @@ -173,7 +173,7 @@ class OpenIdRequestHandlerTests: XCTestCase { purpose: nil, issuanceOptions: [MockInput(mockData: "")], id: nil, - constraint: VerifiedIdGroupConstraint(constraints: [], constraintOperator: .ALL)) + constraint: GroupConstraint(constraints: [], constraintOperator: .ALL)) let expectedRootOfTrust = RootOfTrust(verified: true, source: "mock source") let expectedContent = PresentationRequestContent(style: expectedStyle, requirement: expectedRequirement, @@ -217,7 +217,7 @@ class OpenIdRequestHandlerTests: XCTestCase { purpose: nil, issuanceOptions: [VerifiedIdRequestURL(url: issuanceOptionURL)], id: nil, - constraint: VerifiedIdGroupConstraint(constraints: [], constraintOperator: .ALL)) + constraint: GroupConstraint(constraints: [], constraintOperator: .ALL)) let expectedRootOfTrust = RootOfTrust(verified: true, source: "mock source") let expectedContent = PresentationRequestContent(style: expectedStyle, requirement: expectedRequirement, @@ -265,7 +265,7 @@ class OpenIdRequestHandlerTests: XCTestCase { purpose: nil, issuanceOptions: [VerifiedIdRequestURL(url: issuanceOptionURL)], id: nil, - constraint: VerifiedIdGroupConstraint(constraints: [], constraintOperator: .ALL)) + constraint: GroupConstraint(constraints: [], constraintOperator: .ALL)) let expectedRootOfTrust = RootOfTrust(verified: true, source: "mock source") let expectedContent = PresentationRequestContent(style: expectedStyle, requirement: expectedRequirement, @@ -317,7 +317,7 @@ class OpenIdRequestHandlerTests: XCTestCase { purpose: nil, issuanceOptions: [VerifiedIdRequestURL(url: issuanceOptionURL)], id: nil, - constraint: VerifiedIdGroupConstraint(constraints: [], + constraint: GroupConstraint(constraints: [], constraintOperator: .ALL)) let expectedPresentationRootOfTrust = RootOfTrust(verified: true, source: "mock source") let expectedPresentationContent = PresentationRequestContent(style: expectedPresentationStyle, @@ -377,7 +377,7 @@ class OpenIdRequestHandlerTests: XCTestCase { purpose: nil, issuanceOptions: [VerifiedIdRequestURL(url: issuanceOptionURL)], id: nil, - constraint: VerifiedIdGroupConstraint(constraints: [], + constraint: GroupConstraint(constraints: [], constraintOperator: .ALL)) let expectedPresentationRootOfTrust = RootOfTrust(verified: true, source: "mock source") let expectedInjectedIdToken = InjectedIdToken(rawToken: "mock idToken hint", pin: nil) diff --git a/WalletLibrary/WalletLibraryTests/Requests/OpenId/OpenIdPresentationRequestTests.swift b/WalletLibrary/WalletLibraryTests/Requests/OpenId/OpenIdPresentationRequestTests.swift index 8d61e96b..6bc06300 100644 --- a/WalletLibrary/WalletLibraryTests/Requests/OpenId/OpenIdPresentationRequestTests.swift +++ b/WalletLibrary/WalletLibraryTests/Requests/OpenId/OpenIdPresentationRequestTests.swift @@ -8,18 +8,6 @@ import VCEntities import VCToken @testable import WalletLibrary -struct MockOpenIdResponder: OpenIdResponder { - - let mockSend: ((WalletLibrary.PresentationResponse) async throws -> ())? - - init(mockSend: ((WalletLibrary.PresentationResponse) async throws -> ())? = nil) { - self.mockSend = mockSend - } - func send(response: WalletLibrary.PresentationResponse) async throws { - try await self.mockSend?(response) - } -} - class OpenIdPresentationRequestTests: XCTestCase { enum ExpectedError: Error, Equatable { @@ -161,7 +149,8 @@ class OpenIdPresentationRequestTests: XCTestCase { issuanceOptions: [], id: "mockId", constraint: MockConstraint(doesMatchResult: true)) - mockRequirement.selectedVerifiedId = MockVerifiableCredentialHelper().createMockVerifiableCredential() + let mockVC = MockVerifiableCredentialHelper().createMockVerifiableCredential() + try mockRequirement.fulfill(with: mockVC) let content = PresentationRequestContent(style: mockStyle, requirement: mockRequirement, @@ -207,7 +196,8 @@ class OpenIdPresentationRequestTests: XCTestCase { issuanceOptions: [], id: "mockId", constraint: MockConstraint(doesMatchResult: true)) - mockRequirement.selectedVerifiedId = MockVerifiableCredentialHelper().createMockVerifiableCredential() + let mockVC = MockVerifiableCredentialHelper().createMockVerifiableCredential() + try mockRequirement.fulfill(with: mockVC) let content = PresentationRequestContent(style: mockStyle, requirement: mockRequirement, @@ -230,7 +220,7 @@ class OpenIdPresentationRequestTests: XCTestCase { case .success(_): XCTAssert(true) case .failure(let error): - XCTFail(error.localizedDescription) + XCTFail(String(describing: error)) } } From d37b91e549fb4efa55d32b0aea53970a9c0c108a Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 14 Mar 2023 08:54:03 -0700 Subject: [PATCH 353/373] Add comments to VerifiedIdRequirement. --- .../Requests/Requirements/VerifiedIdRequirement.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/WalletLibrary/WalletLibrary/Requests/Requirements/VerifiedIdRequirement.swift b/WalletLibrary/WalletLibrary/Requests/Requirements/VerifiedIdRequirement.swift index ec237900..81b8ad7f 100644 --- a/WalletLibrary/WalletLibrary/Requests/Requirements/VerifiedIdRequirement.swift +++ b/WalletLibrary/WalletLibrary/Requests/Requirements/VerifiedIdRequirement.swift @@ -53,7 +53,9 @@ public class VerifiedIdRequirement: Requirement { self.constraint = constraint } + /// Validate the requirement to determine if public func validate() throws { + guard let selectedVerifiedId = self.selectedVerifiedId else { throw VerifiedIdRequirementError.requirementHasNotBeenFulfilled } @@ -63,12 +65,16 @@ public class VerifiedIdRequirement: Requirement { } } + /// Given a list of Verified Ids, return a filtered list of Verified Ids that satisfy the requirement. public func getMatches(verifiedIds: [VerifiedId]) -> [VerifiedId] { return verifiedIds.filter { constraint.doesMatch(verifiedId: $0) } } + /// Fulfill the requirement with a VerifiedId. Throws if Verified Id does not satisfy the requirement. + /// TODO: it might be beneficial to use a doesMatch method on Constraint that throws a specific error as to why + /// the Verified Id does not satisfy the requirement. public func fulfill(with verifiedId: VerifiedId) throws { guard constraint.doesMatch(verifiedId: verifiedId) else { From 362aa4c2723abbcef4f5e5cd2912ef6293b03b15 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Tue, 14 Mar 2023 08:54:22 -0700 Subject: [PATCH 354/373] Add comments to constraints. --- .../Presentation/PresentationInputDescriptor+Mappable.swift | 2 +- .../Requests/Constraints/VerifiedIdConstraint.swift | 5 +++-- .../Requests/Constraints/VCTypeConstraint.swift | 3 +++ 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+Mappable.swift b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+Mappable.swift index 34a74037..beccc8cb 100644 --- a/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+Mappable.swift +++ b/WalletLibrary/WalletLibrary/Extensions/Mappings/VCSDK/Presentation/PresentationInputDescriptor+Mappable.swift @@ -59,6 +59,6 @@ extension VCEntities.PresentationInputDescriptor: Mappable { } /// TODO: Is this an ANY or ALL operation? - return VerifiedIdGroupConstraint(constraints: typeConstraints, constraintOperator: .ANY) + return GroupConstraint(constraints: typeConstraints, constraintOperator: .ANY) } } diff --git a/WalletLibrary/WalletLibrary/Protocols/Requests/Constraints/VerifiedIdConstraint.swift b/WalletLibrary/WalletLibrary/Protocols/Requests/Constraints/VerifiedIdConstraint.swift index 6bef50e6..3d2b8d79 100644 --- a/WalletLibrary/WalletLibrary/Protocols/Requests/Constraints/VerifiedIdConstraint.swift +++ b/WalletLibrary/WalletLibrary/Protocols/Requests/Constraints/VerifiedIdConstraint.swift @@ -4,8 +4,9 @@ *--------------------------------------------------------------------------------------------*/ /** - * A constraint on a requirement that defines a behavior of determining whether a VerifiedId matches - * matches that constraint or not. + * A constraint on a requirement that defines a behavior of determining whether a VerifiedId + * matches that constraint or not. + * TODO: add method that throws an error to get more information about why constraint was not met. */ protocol VerifiedIdConstraint { func doesMatch(verifiedId: VerifiedId) -> Bool diff --git a/WalletLibrary/WalletLibrary/Requests/Constraints/VCTypeConstraint.swift b/WalletLibrary/WalletLibrary/Requests/Constraints/VCTypeConstraint.swift index 2cde7ea0..e04c44d3 100644 --- a/WalletLibrary/WalletLibrary/Requests/Constraints/VCTypeConstraint.swift +++ b/WalletLibrary/WalletLibrary/Requests/Constraints/VCTypeConstraint.swift @@ -3,6 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/** + * A Verifiable Credential specific constraint that checks to see if the VC's type matches a value. + */ struct VCTypeConstraint: VerifiedIdConstraint { let type: String From 6085cdc15da6f897c6f1f3d78d95f60cd98712f0 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 17 Mar 2023 14:10:39 -0700 Subject: [PATCH 355/373] Create VerifiedIdRepository. --- .../WalletLibraryDemo/Persistence.swift | 18 ------- .../VerifiedIdRepository.swift | 50 +++++++++++++++++++ .../WalletLibraryDemo.xcdatamodel/contents | 10 ++-- 3 files changed, 54 insertions(+), 24 deletions(-) create mode 100644 Demo/WalletLibraryDemo/WalletLibraryDemo/VerifiedIdRepository.swift diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/Persistence.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/Persistence.swift index 026696fb..ae994655 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/Persistence.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/Persistence.swift @@ -8,24 +8,6 @@ import CoreData struct PersistenceController { static let shared = PersistenceController() - static var preview: PersistenceController = { - let result = PersistenceController(inMemory: true) - let viewContext = result.container.viewContext - for _ in 0..<10 { - let newItem = Item(context: viewContext) - newItem.timestamp = Date() - } - do { - try viewContext.save() - } catch { - // Replace this implementation with code to handle the error appropriately. - // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. - let nsError = error as NSError - fatalError("Unresolved error \(nsError), \(nsError.userInfo)") - } - return result - }() - let container: NSPersistentContainer init(inMemory: Bool = false) { diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/VerifiedIdRepository.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/VerifiedIdRepository.swift new file mode 100644 index 00000000..c57a506e --- /dev/null +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/VerifiedIdRepository.swift @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import CoreData +import WalletLibrary + +struct VerifiedIdRepository { + + let container = PersistenceController.shared.container + + func save(verifiedId: VerifiedId) throws { + let rawVerifiedId = try VerifiedIdEncoder().encode(verifiedId: verifiedId) + let viewContext = container.viewContext + let newVerifiedId = RawVerifiedId(context: viewContext) + newVerifiedId.raw = rawVerifiedId + try viewContext.save() + } + + func delete(verifiedId: VerifiedId) throws { + let viewContext = container.viewContext + let request = NSFetchRequest(entityName: "RawVerifiedId") + let results = try viewContext.fetch(request) + + for result in results as! [NSManagedObject] { + let id = result.value(forKey: "id") as! String + if verifiedId.id == id { + viewContext.delete(result) + } + } + + try viewContext.save() + } + + func getAllStoredVerifiedIds() throws -> [VerifiedId] { + let viewContext = container.viewContext + let request = NSFetchRequest(entityName: "RawVerifiedId") + let results = try viewContext.fetch(request) + + var verifiedIds: [VerifiedId] = [] + for result in results as! [NSManagedObject] { + let rawValue = result.value(forKey: "raw") as! Data + let verifiedId = try VerifiedIdDecoder().decode(from: rawValue) + verifiedIds.append(verifiedId) + } + + return verifiedIds + } +} diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/WalletLibraryDemo.xcdatamodeld/WalletLibraryDemo.xcdatamodel/contents b/Demo/WalletLibraryDemo/WalletLibraryDemo/WalletLibraryDemo.xcdatamodeld/WalletLibraryDemo.xcdatamodel/contents index 9ed2921a..7db0af42 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/WalletLibraryDemo.xcdatamodeld/WalletLibraryDemo.xcdatamodel/contents +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/WalletLibraryDemo.xcdatamodeld/WalletLibraryDemo.xcdatamodel/contents @@ -1,9 +1,7 @@ - - - + + + + - - - \ No newline at end of file From 7a262c2e53fd8d7032984e119d151316daa5a28d Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 17 Mar 2023 14:12:33 -0700 Subject: [PATCH 356/373] Create a VerifiedIdDecoder that is public. --- .../VerifiedId/VerifiedIdDecoder.swift | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdDecoder.swift diff --git a/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdDecoder.swift b/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdDecoder.swift new file mode 100644 index 00000000..c6b84e79 --- /dev/null +++ b/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdDecoder.swift @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +/** + * The Verified Id Decoder + */ +public struct VerifiedIdDecoder { + + let jsonDecoder = JSONDecoder() + + public init() {} + + public func decode(from data: Data) throws -> any VerifiedId { + let encodedVerifiedId = try jsonDecoder.decode(EncodedVerifiedId.self, from: data) + switch encodedVerifiedId.type { + case .VerifiableCredential: + return try jsonDecoder.decode(VerifiableCredential.self, from: encodedVerifiedId.raw) + } + } +} + +enum VerifiedIdEncoderError: Error { + case unsupportedVerifiedIdType(String) +} + +public struct VerifiedIdEncoder { + + let jsonEncoder = JSONEncoder() + + public init() {} + + public func encode(verifiedId: any VerifiedId) throws -> Data { + switch verifiedId { + case let vc as VerifiableCredential: + let rawVC = try jsonEncoder.encode(vc) + let encodedVerifiedId = EncodedVerifiedId(type: .VerifiableCredential, + raw: rawVC) + return try jsonEncoder.encode(encodedVerifiedId) + default: + let type = String(describing: type(of: verifiedId)) + throw VerifiedIdEncoderError.unsupportedVerifiedIdType(type) + } + } +} + +struct EncodedVerifiedId: Codable { + let type: SupportedVerifiedIdType + + let raw: Data +} + +enum SupportedVerifiedIdType: String, Codable { + case VerifiableCredential = "VerifiableCredential" +} From f9aee15506fd318493c5bd4988554b7db112f744 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 17 Mar 2023 14:12:44 -0700 Subject: [PATCH 357/373] Update project files. --- .../WalletLibraryDemo.xcodeproj/project.pbxproj | 12 ++++++++---- .../WalletLibrary.xcodeproj/project.pbxproj | 4 ++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo.xcodeproj/project.pbxproj b/Demo/WalletLibraryDemo/WalletLibraryDemo.xcodeproj/project.pbxproj index abc4c029..b3fa1e4a 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo.xcodeproj/project.pbxproj +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo.xcodeproj/project.pbxproj @@ -7,8 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 552A75BB29C50DBA005BFF3D /* VerifiedIdRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 552A75BA29C50DB9005BFF3D /* VerifiedIdRepository.swift */; }; 553CC08929A94686005A5FD6 /* ErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC08829A94686005A5FD6 /* ErrorView.swift */; }; - 553CC08B29A9469B005A5FD6 /* SuccessView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC08A29A9469B005A5FD6 /* SuccessView.swift */; }; + 553CC08B29A9469B005A5FD6 /* VerifiedIdView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC08A29A9469B005A5FD6 /* VerifiedIdView.swift */; }; 553CC08D29A94A13005A5FD6 /* RequirementListCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC08C29A94A13005A5FD6 /* RequirementListCellView.swift */; }; 5585BDE129A047370059710B /* SampleViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585BDE029A047370059710B /* SampleViewModel.swift */; }; 5585BDE529A692E10059710B /* RequirementState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5585BDE429A692E10059710B /* RequirementState.swift */; }; @@ -39,8 +40,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 552A75BA29C50DB9005BFF3D /* VerifiedIdRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdRepository.swift; sourceTree = ""; }; 553CC08829A94686005A5FD6 /* ErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorView.swift; sourceTree = ""; }; - 553CC08A29A9469B005A5FD6 /* SuccessView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuccessView.swift; sourceTree = ""; }; + 553CC08A29A9469B005A5FD6 /* VerifiedIdView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdView.swift; sourceTree = ""; }; 553CC08C29A94A13005A5FD6 /* RequirementListCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequirementListCellView.swift; sourceTree = ""; }; 5585BDE029A047370059710B /* SampleViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleViewModel.swift; sourceTree = ""; }; 5585BDE429A692E10059710B /* RequirementState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequirementState.swift; sourceTree = ""; }; @@ -76,7 +78,7 @@ 5585BDFD29A6C4CA0059710B /* RequestView.swift */, 553CC08C29A94A13005A5FD6 /* RequirementListCellView.swift */, 5585BDFF29A6C5070059710B /* RequirementView.swift */, - 553CC08A29A9469B005A5FD6 /* SuccessView.swift */, + 553CC08A29A9469B005A5FD6 /* VerifiedIdView.swift */, ); path = Views; sourceTree = ""; @@ -106,6 +108,7 @@ 55E33699293E8F7500CD2ED7 /* Persistence.swift */, 5585BDE429A692E10059710B /* RequirementState.swift */, 5585BDE029A047370059710B /* SampleViewModel.swift */, + 552A75BA29C50DB9005BFF3D /* VerifiedIdRepository.swift */, 55E33697293E8F7500CD2ED7 /* WalletLibraryDemoApp.swift */, 55E336A0293E8F7700CD2ED7 /* Assets.xcassets */, 55E336A2293E8F7700CD2ED7 /* WalletLibraryDemo.entitlements */, @@ -204,8 +207,9 @@ files = ( 5585BDFE29A6C4CA0059710B /* RequestView.swift in Sources */, 55E3369D293E8F7500CD2ED7 /* WalletLibraryDemo.xcdatamodeld in Sources */, - 553CC08B29A9469B005A5FD6 /* SuccessView.swift in Sources */, + 553CC08B29A9469B005A5FD6 /* VerifiedIdView.swift in Sources */, 55E33698293E8F7500CD2ED7 /* WalletLibraryDemoApp.swift in Sources */, + 552A75BB29C50DBA005BFF3D /* VerifiedIdRepository.swift in Sources */, 55E3369F293E8F7500CD2ED7 /* ContentView.swift in Sources */, 5585BE0029A6C5070059710B /* RequirementView.swift in Sources */, 5585BDE529A692E10059710B /* RequirementState.swift in Sources */, diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 73d9daf2..e22e7815 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -25,6 +25,7 @@ 550E3212298B1150004E7716 /* VerifiedIdRequestInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 550E3211298B1150004E7716 /* VerifiedIdRequestInput.swift */; }; 550E3214298B11A5004E7716 /* Requirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 550E3213298B11A5004E7716 /* Requirement.swift */; }; 550E3217298B1236004E7716 /* RequesterStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 550E3216298B1236004E7716 /* RequesterStyle.swift */; }; + 552A75A429C4F19B005BFF3D /* VerifiedIdDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 552A75A329C4F19B005BFF3D /* VerifiedIdDecoder.swift */; }; 552E509B293E6AB200868F47 /* WalletLibrary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 552E5092293E6AB200868F47 /* WalletLibrary.framework */; }; 552E50A0293E6AB200868F47 /* VerifiedIdClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 552E509F293E6AB200868F47 /* VerifiedIdClientTests.swift */; }; 552E50A1293E6AB200868F47 /* WalletLibrary.h in Headers */ = {isa = PBXBuildFile; fileRef = 552E5095293E6AB200868F47 /* WalletLibrary.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -337,6 +338,7 @@ 550E3211298B1150004E7716 /* VerifiedIdRequestInput.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VerifiedIdRequestInput.swift; sourceTree = ""; }; 550E3213298B11A5004E7716 /* Requirement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Requirement.swift; sourceTree = ""; }; 550E3216298B1236004E7716 /* RequesterStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequesterStyle.swift; sourceTree = ""; }; + 552A75A329C4F19B005BFF3D /* VerifiedIdDecoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdDecoder.swift; sourceTree = ""; }; 552E5092293E6AB200868F47 /* WalletLibrary.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = WalletLibrary.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 552E5095293E6AB200868F47 /* WalletLibrary.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WalletLibrary.h; sourceTree = ""; }; 552E509A293E6AB200868F47 /* WalletLibraryTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WalletLibraryTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1025,6 +1027,7 @@ 559BD4B1299EDF9E00BD61AC /* VerifiableCredential */, 55E336D3293FA75300CD2ED7 /* VerifiedId.swift */, 55E336D5293FA78C00CD2ED7 /* VerifiedIdClaim.swift */, + 552A75A329C4F19B005BFF3D /* VerifiedIdDecoder.swift */, ); path = VerifiedId; sourceTree = ""; @@ -1531,6 +1534,7 @@ 55E2F0882995743D0008010D /* LinkedDomainResult+Mappable.swift in Sources */, 55E33708293FD25900CD2ED7 /* AccessTokenRequirement.swift in Sources */, 55E2F07D2995561E0008010D /* OpenIdPresentationRequest.swift in Sources */, + 552A75A429C4F19B005BFF3D /* VerifiedIdDecoder.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; From c2a7b05ad25535177572c1a50eb6635b7aaaa947 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 17 Mar 2023 16:26:23 -0700 Subject: [PATCH 358/373] Create new files for VerifiedId. --- .../WalletLibrary.xcodeproj/project.pbxproj | 8 +++++ .../VerifiedId/EncodedVerifiedId.swift | 14 ++++++++ .../VerifiedId/VerifiedIdDecoder.swift | 36 +------------------ .../VerifiedId/VerifiedIdEncoder.swift | 28 +++++++++++++++ 4 files changed, 51 insertions(+), 35 deletions(-) create mode 100644 WalletLibrary/WalletLibrary/VerifiedId/EncodedVerifiedId.swift create mode 100644 WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdEncoder.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index e22e7815..57a8e79c 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -26,6 +26,8 @@ 550E3214298B11A5004E7716 /* Requirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 550E3213298B11A5004E7716 /* Requirement.swift */; }; 550E3217298B1236004E7716 /* RequesterStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 550E3216298B1236004E7716 /* RequesterStyle.swift */; }; 552A75A429C4F19B005BFF3D /* VerifiedIdDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 552A75A329C4F19B005BFF3D /* VerifiedIdDecoder.swift */; }; + 552A75BD29C52B0A005BFF3D /* VerifiedIdEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 552A75BC29C52B0A005BFF3D /* VerifiedIdEncoder.swift */; }; + 552A75BF29C52DBB005BFF3D /* EncodedVerifiedId.swift in Sources */ = {isa = PBXBuildFile; fileRef = 552A75BE29C52DBB005BFF3D /* EncodedVerifiedId.swift */; }; 552E509B293E6AB200868F47 /* WalletLibrary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 552E5092293E6AB200868F47 /* WalletLibrary.framework */; }; 552E50A0293E6AB200868F47 /* VerifiedIdClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 552E509F293E6AB200868F47 /* VerifiedIdClientTests.swift */; }; 552E50A1293E6AB200868F47 /* WalletLibrary.h in Headers */ = {isa = PBXBuildFile; fileRef = 552E5095293E6AB200868F47 /* WalletLibrary.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -339,6 +341,8 @@ 550E3213298B11A5004E7716 /* Requirement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Requirement.swift; sourceTree = ""; }; 550E3216298B1236004E7716 /* RequesterStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequesterStyle.swift; sourceTree = ""; }; 552A75A329C4F19B005BFF3D /* VerifiedIdDecoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdDecoder.swift; sourceTree = ""; }; + 552A75BC29C52B0A005BFF3D /* VerifiedIdEncoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdEncoder.swift; sourceTree = ""; }; + 552A75BE29C52DBB005BFF3D /* EncodedVerifiedId.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncodedVerifiedId.swift; sourceTree = ""; }; 552E5092293E6AB200868F47 /* WalletLibrary.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = WalletLibrary.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 552E5095293E6AB200868F47 /* WalletLibrary.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WalletLibrary.h; sourceTree = ""; }; 552E509A293E6AB200868F47 /* WalletLibraryTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WalletLibraryTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1025,9 +1029,11 @@ isa = PBXGroup; children = ( 559BD4B1299EDF9E00BD61AC /* VerifiableCredential */, + 552A75BE29C52DBB005BFF3D /* EncodedVerifiedId.swift */, 55E336D3293FA75300CD2ED7 /* VerifiedId.swift */, 55E336D5293FA78C00CD2ED7 /* VerifiedIdClaim.swift */, 552A75A329C4F19B005BFF3D /* VerifiedIdDecoder.swift */, + 552A75BC29C52B0A005BFF3D /* VerifiedIdEncoder.swift */, ); path = VerifiedId; sourceTree = ""; @@ -1488,6 +1494,7 @@ 55DECBE729B92E3900D5C802 /* PresentationResponse.swift in Sources */, 559BD4B3299EDFB400BD61AC /* VerifiableCredential.swift in Sources */, 55E336D6293FA78C00CD2ED7 /* VerifiedIdClaim.swift in Sources */, + 552A75BF29C52DBB005BFF3D /* EncodedVerifiedId.swift in Sources */, 553CC09029A955E6005A5FD6 /* PinDescriptor+Mappable.swift in Sources */, 55E337652943749600CD2ED7 /* AsyncWrapper.swift in Sources */, 550E3214298B11A5004E7716 /* Requirement.swift in Sources */, @@ -1518,6 +1525,7 @@ 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */, 55E2F071299554110008010D /* LibraryConfiguration.swift in Sources */, 550E3209298B0EA4004E7716 /* VerifiedIdClientBuilder.swift in Sources */, + 552A75BD29C52B0A005BFF3D /* VerifiedIdEncoder.swift in Sources */, 55DECC3029BB8E5C00D5C802 /* VCTypeConstraint.swift in Sources */, 550A9192299400820014D030 /* OpenIdResponder.swift in Sources */, 5534E672294A19D5005D0765 /* IdTokenDescriptor+Mappable.swift in Sources */, diff --git a/WalletLibrary/WalletLibrary/VerifiedId/EncodedVerifiedId.swift b/WalletLibrary/WalletLibrary/VerifiedId/EncodedVerifiedId.swift new file mode 100644 index 00000000..bf798073 --- /dev/null +++ b/WalletLibrary/WalletLibrary/VerifiedId/EncodedVerifiedId.swift @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +enum SupportedVerifiedIdType: String, Codable { + case VerifiableCredential = "VerifiableCredential" +} + +struct EncodedVerifiedId: Codable { + let type: SupportedVerifiedIdType + + let raw: Data +} diff --git a/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdDecoder.swift b/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdDecoder.swift index c6b84e79..d5b71a31 100644 --- a/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdDecoder.swift +++ b/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdDecoder.swift @@ -8,7 +8,7 @@ */ public struct VerifiedIdDecoder { - let jsonDecoder = JSONDecoder() + private let jsonDecoder = JSONDecoder() public init() {} @@ -20,37 +20,3 @@ public struct VerifiedIdDecoder { } } } - -enum VerifiedIdEncoderError: Error { - case unsupportedVerifiedIdType(String) -} - -public struct VerifiedIdEncoder { - - let jsonEncoder = JSONEncoder() - - public init() {} - - public func encode(verifiedId: any VerifiedId) throws -> Data { - switch verifiedId { - case let vc as VerifiableCredential: - let rawVC = try jsonEncoder.encode(vc) - let encodedVerifiedId = EncodedVerifiedId(type: .VerifiableCredential, - raw: rawVC) - return try jsonEncoder.encode(encodedVerifiedId) - default: - let type = String(describing: type(of: verifiedId)) - throw VerifiedIdEncoderError.unsupportedVerifiedIdType(type) - } - } -} - -struct EncodedVerifiedId: Codable { - let type: SupportedVerifiedIdType - - let raw: Data -} - -enum SupportedVerifiedIdType: String, Codable { - case VerifiableCredential = "VerifiableCredential" -} diff --git a/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdEncoder.swift b/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdEncoder.swift new file mode 100644 index 00000000..9b04f0f6 --- /dev/null +++ b/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdEncoder.swift @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +enum VerifiedIdEncoderError: Error { + case unsupportedVerifiedIdType(String) +} + +public struct VerifiedIdEncoder { + + private let jsonEncoder = JSONEncoder() + + public init() {} + + public func encode(verifiedId: any VerifiedId) throws -> Data { + switch verifiedId { + case let vc as VerifiableCredential: + let rawVC = try jsonEncoder.encode(vc) + let encodedVerifiedId = EncodedVerifiedId(type: .VerifiableCredential, + raw: rawVC) + return try jsonEncoder.encode(encodedVerifiedId) + default: + let type = String(describing: type(of: verifiedId)) + throw VerifiedIdEncoderError.unsupportedVerifiedIdType(type) + } + } +} From 43283de7fb551129698e2f61cca691ed985f9d1f Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 17 Mar 2023 16:26:46 -0700 Subject: [PATCH 359/373] Add fulfill for Verifid id to requirement state. --- .../WalletLibraryDemo/RequirementState.swift | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/RequirementState.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/RequirementState.swift index dc0f6274..872fc308 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/RequirementState.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/RequirementState.swift @@ -8,6 +8,7 @@ import WalletLibrary enum RequirementStateError: String, Error { case unsupportedRequirementType = "Unsupported Requirement Type" case invalidInputToFulfillRequirement = "Invalid Input to Fulfill Requirement" + case requirementIsNotOfTypeVerifiedIdRequirement = "Requirement cannot be fulfilled by Verified Id" } /// Requirement Status based on whether requirement is valid or missing. @@ -46,6 +47,8 @@ class RequirementState: Identifiable, ObservableObject { case let idTokenRequirement as IdTokenRequirement: self.label = "Id Token for: \(idTokenRequirement.configuration)" try? addNewLabelIfValid(newLabel: "Valid Id Token Present") + case let verifiedIdRequirement as VerifiedIdRequirement: + self.label = "Verified Id Requirement for types: \(verifiedIdRequirement.types)" default: throw RequirementStateError.unsupportedRequirementType } @@ -64,6 +67,16 @@ class RequirementState: Identifiable, ObservableObject { } } + func fulfill(with value: VerifiedId) throws { + + guard let verifiedIdRequirement = requirement as? VerifiedIdRequirement else { + throw RequirementStateError.requirementIsNotOfTypeVerifiedIdRequirement + } + + try verifiedIdRequirement.fulfill(with: value) + try addNewLabelIfValid(newLabel: "Verified Id Requirement Fulfilled.") + } + private func addNewLabelIfValid(newLabel: String) throws { do { try requirement.validate() From 7f2b9d4595b211b20b6a00b20c0d4a6148bd1f43 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 17 Mar 2023 16:27:38 -0700 Subject: [PATCH 360/373] Add ViewState. --- .../WalletLibraryDemo/SampleViewModel.swift | 127 ++++++++++-------- 1 file changed, 74 insertions(+), 53 deletions(-) diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift index a0994dfe..7c33b364 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift @@ -13,29 +13,30 @@ enum SampleViewModelError: String, Error { case unsupportedRequirementType = "One of the requirement types is not supported." } +enum ViewState { + case Initialized + case InProgress + case GatheringRequirements + case IssuanceSuccess(with: VerifiedId) + case PresentationSuccess(with: String) + case Error(withMessage: String) +} + @MainActor class SampleViewModel: ObservableObject { + @Published var viewState: ViewState + /// The requirements to be gathered by the user. @Published var requirements: [RequirementState] = [] - /// The input string to kick off the flow (e.g. openid request url). - @Published var input: String = "" - /// If all requirements are satisfied, complete button is enabled. @Published var isCompleteButtonEnabled: Bool = false /// Show a progress view when while doing internal logic. @Published var isProgressViewShowing: Bool = true - /// if not nil, error message to be displayed. - @Published var errorMessage: String? = nil - - /// If not nil, success message to be displayed. - @Published var successMessage: String? = nil - - /// If not nil, show issued verified id. - @Published var issuedVerifiedId: VerifiedId? = nil + @Published var issuedVerifiedIds: [VerifiedId] = [] /// The Verified Id Client is used to create requests with a configuration set by the Builder. private let verifiedIdClient: VerifiedIdClient? @@ -43,10 +44,14 @@ enum SampleViewModelError: String, Error { /// The current issuance or presentation request that is being processed. private var request: (any VerifiedIdRequest)? = nil + private let verifiedIdRepository = VerifiedIdRepository() + /// TODO: enable deeplinking and the rest of the requirements. init() { + viewState = .Initialized do { let builder = VerifiedIdClientBuilder() + issuedVerifiedIds = try verifiedIdRepository.getAllStoredVerifiedIds() verifiedIdClient = try builder.build() } catch { verifiedIdClient = nil @@ -54,30 +59,47 @@ enum SampleViewModelError: String, Error { } } - func createRequest() { + func createRequest(fromInput input: String) { + + guard let client = verifiedIdClient else { + showErrorMessage(from: SampleViewModelError.unableToCreateRequest) + return + } + Task { reset() - isProgressViewShowing = true + viewState = .InProgress do { - let input = try createInput() - self.request = try await verifiedIdClient?.createVerifiedIdRequest(from: input) - if let request = request { - try configureRequirements(requirement: request.requirement) - isCompleteButtonEnabled = request.isSatisfied() - } else { - showErrorMessage(from: SampleViewModelError.unableToCreateRequest) - } + let input: VerifiedIdRequestInput = try createInput(fromInput: input) + + // VerifiedIdClient is used to create a request from an input + // such as, in the case, a VerifiedIdRequestURL. + let request = try await client.createVerifiedIdRequest(from: input) + self.request = request + try configureRequirements(requirement: request.requirement) + isCompleteButtonEnabled = request.isSatisfied() + viewState = .GatheringRequirements } catch { showErrorMessage(from: error, additionalInfo: "Unable to create request.") } - isProgressViewShowing = false } } + private func createInput(fromInput input: String) throws -> VerifiedIdRequestInput { + + guard let openidUrl = URL(string: input) else { + throw SampleViewModelError.unableToCreateInput + } + + return VerifiedIdRequestURL(url: openidUrl) + } + + // Associate each requirement with a RequirementState for views. private func configureRequirements(requirement: Requirement) throws { + // If a request has more than one requirement, they will be contained in a GroupRequirement. if let groupRequirement = requirement as? GroupRequirement { for req in groupRequirement.requirements { try configureRequirements(requirement: req) @@ -85,21 +107,8 @@ enum SampleViewModelError: String, Error { return } - do { - let requirementState = try RequirementState(requirement: requirement) - requirements.append(requirementState) - } catch { - throw SampleViewModelError.unsupportedRequirementType - } - } - - private func createInput() throws -> VerifiedIdRequestInput { - - guard let openidUrl = URL(string: input) else { - throw SampleViewModelError.unableToCreateInput - } - - return VerifiedIdRequestURL(url: openidUrl) + let requirementState = try RequirementState(requirement: requirement) + requirements.append(requirementState) } func complete() { @@ -118,7 +127,9 @@ enum SampleViewModelError: String, Error { let result = await issuanceRequest.complete() switch (result) { case .success(let verifiedId): - showSuccessfulFlow(with: verifiedId) + issuedVerifiedIds.append(verifiedId) + try verifiedIdRepository.save(verifiedId: verifiedId) + viewState = .IssuanceSuccess(with: verifiedId) case .failure(let error): showErrorMessage(from: error) } @@ -130,7 +141,7 @@ enum SampleViewModelError: String, Error { let result = await presentationRequest.complete() switch (result) { case .success(_): - showSuccessfulFlow() + viewState = .PresentationSuccess(with: "Successful Presentation!") case .failure(let error): showErrorMessage(from: error) } @@ -140,40 +151,50 @@ enum SampleViewModelError: String, Error { func fulfill(requirementState: RequirementState, with value: String) throws { do { try requirementState.fulfill(with: value) - } catch RequirementStateError.invalidInputToFulfillRequirement { - showErrorMessage(from: RequirementStateError.invalidInputToFulfillRequirement, additionalInfo: "Value == \(value)") + } catch let error as RequirementStateError { + showErrorMessage(from: error, + additionalInfo: "Value == \(value)") + } + self.isCompleteButtonEnabled = request?.isSatisfied() ?? false + } + + func fulfill(requirementState: RequirementState, with value: VerifiedId) throws { + do { + try requirementState.fulfill(with: value) + } catch let error as RequirementStateError { + showErrorMessage(from: error, + additionalInfo: "Value == \(value)") } self.isCompleteButtonEnabled = request?.isSatisfied() ?? false } private func showErrorMessage(from error: Error, additionalInfo: String? = nil) { print(error) + var errorMessage: String = error.localizedDescription if let error = error as? SampleViewModelError { errorMessage = error.rawValue } else if let error = error as? RequirementStateError { errorMessage = error.rawValue - } else { - errorMessage = error.localizedDescription } if let additionalInfo = additionalInfo { - errorMessage?.append("\n\(additionalInfo)") + errorMessage.append("\n\(additionalInfo)") } + viewState = .Error(withMessage: errorMessage) } - private func showSuccessfulFlow(with verifiedId: VerifiedId? = nil) { - - if let verifiedId = verifiedId { - issuedVerifiedId = verifiedId - } else { - successMessage = "Presentation Successful!" + func deleteVerifiedId(indexSet: IndexSet) { + for index in indexSet { + if issuedVerifiedIds.count > index { + let verifiedId = issuedVerifiedIds[index] + issuedVerifiedIds.remove(at: index) + try? verifiedIdRepository.delete(verifiedId: verifiedId) + } } } func reset() { - errorMessage = nil - successMessage = nil - issuedVerifiedId = nil + viewState = .Initialized requirements = [] request = nil } From eaa1bef364961d31a632b54c08a654627063b585 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 17 Mar 2023 16:27:50 -0700 Subject: [PATCH 361/373] Add VerifiedId list to ContentView. --- .../WalletLibraryDemo/ContentView.swift | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/ContentView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/ContentView.swift index fc2912da..51270736 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/ContentView.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/ContentView.swift @@ -12,13 +12,15 @@ struct ContentView: View { @StateObject private var viewModel = SampleViewModel() + @State private var input: String = "" + var body: some View { NavigationView { VStack { Text("Sample Request URL:") TextField( "OpenId Request URL", - text: $viewModel.input, + text: $input, axis: .vertical ) .padding(EdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5)) @@ -29,8 +31,23 @@ struct ContentView: View { NavigationLink(destination: RequestView()) { Text("Create Request") }.navigationTitle("Verified Id Sample App") + Spacer() + Text("Issued Verified Ids") + .bold() + List { + ForEach(viewModel.issuedVerifiedIds, id: \.id) { verifiedId in + NavigationLink { + VerifiedIdView(verifiedId: verifiedId) + } label: { + Text(verifiedId.id) + } + }.onDelete { indexSet in + viewModel.deleteVerifiedId(indexSet: indexSet) + } + } + .listStyle(.inset) }.onDisappear { - viewModel.createRequest() + viewModel.createRequest(fromInput: input) } } .environmentObject(viewModel) From ebbed77dc31a477c18e7aefe431dda6f4d4d5d45 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 17 Mar 2023 16:28:05 -0700 Subject: [PATCH 362/373] Add ViewState to RequestView. --- .../WalletLibraryDemo/Views/RequestView.swift | 57 ++++++++++--------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequestView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequestView.swift index fb3b85ef..b0345e85 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequestView.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequestView.swift @@ -12,33 +12,36 @@ struct RequestView: View { @Environment(\.dismiss) var dismiss var body: some View { - VStack { - if viewModel.isProgressViewShowing { - ProgressView() - } - else if let errorMessage = viewModel.errorMessage { - ErrorView(errorMessage: errorMessage) - } - else if viewModel.successMessage != nil || viewModel.issuedVerifiedId != nil { - SuccessView() - } else { - VStack { - List(viewModel.requirements) { requirement in - NavigationLink(destination: RequirementView(requirement: requirement)) { - RequirementListViewCell(requirement: requirement) - } - }.listStyle(.inset) - Spacer() - Divider() - Button { - viewModel.complete() - } label: { - Text("Complete") - }.disabled(!viewModel.isCompleteButtonEnabled) - Spacer() - }.navigationTitle("Fulfill Requirements") - .navigationBarTitleDisplayMode(.inline) - } + switch (viewModel.viewState) { + case .InProgress: + ProgressView() + case .GatheringRequirements: + requirementsView + case .IssuanceSuccess(with: let verifiedId): + VerifiedIdView(verifiedId: verifiedId) + case .Error(withMessage: let message): + ErrorView(errorMessage: message) + default: + Spacer() } } + + var requirementsView: some View { + VStack { + List(viewModel.requirements) { requirement in + NavigationLink(destination: RequirementView(requirement: requirement)) { + RequirementListViewCell(requirement: requirement) + } + }.listStyle(.inset) + Spacer() + Divider() + Button { + viewModel.complete() + } label: { + Text("Complete") + }.disabled(!viewModel.isCompleteButtonEnabled) + Spacer() + }.navigationTitle("Fulfill Requirements") + .navigationBarTitleDisplayMode(.inline) + } } From 112749f96e7deb17c634c2f89f61328fbc869bc6 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 17 Mar 2023 16:28:38 -0700 Subject: [PATCH 363/373] Add verified id requirement support to RequirementView. --- .../Views/RequirementView.swift | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequirementView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequirementView.swift index 314da61c..fdfb3e0e 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequirementView.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequirementView.swift @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import SwiftUI +import WalletLibrary struct RequirementView: View { @@ -18,6 +19,26 @@ struct RequirementView: View { var requirement: RequirementState var body: some View { + switch (requirement.requirement) { + case is SelfAttestedClaimRequirement: + userInputView + case is PinRequirement: + userInputView + case let verifiedIdRequirement as VerifiedIdRequirement: + let matches = verifiedIdRequirement.getMatches(verifiedIds: viewModel.issuedVerifiedIds) + List(matches, id: \.id) { match in + Button { + fulfill(with: match) + } label: { + Text(match.id) + } + }.listStyle(.inset) + default: + Text("Do not support requirement.") + } + } + + var userInputView: some View { VStack { if isInvalidInput { Text("Invalid Input") @@ -43,6 +64,15 @@ struct RequirementView: View { } } + private func fulfill(with value: VerifiedId) { + do { + try viewModel.fulfill(requirementState: requirement, with: value) + dismiss() + } catch { + isInvalidInput = true + } + } + private func fulfill(with value: String) { do { try viewModel.fulfill(requirementState: requirement, with: value) From 849bd48feffad15b7bd8736b66cb54213acd4635 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Fri, 17 Mar 2023 16:29:02 -0700 Subject: [PATCH 364/373] Create VerifiedIdView. --- .../WalletLibraryDemo/Views/SuccessView.swift | 53 ------------------- .../Views/VerifiedIdView.swift | 40 ++++++++++++++ 2 files changed, 40 insertions(+), 53 deletions(-) delete mode 100644 Demo/WalletLibraryDemo/WalletLibraryDemo/Views/SuccessView.swift create mode 100644 Demo/WalletLibraryDemo/WalletLibraryDemo/Views/VerifiedIdView.swift diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/SuccessView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/SuccessView.swift deleted file mode 100644 index be3e7b7b..00000000 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/SuccessView.swift +++ /dev/null @@ -1,53 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Microsoft Corporation. All rights reserved. -* Licensed under the MIT License. See License.txt in the project root for license information. -*--------------------------------------------------------------------------------------------*/ - -import SwiftUI - -struct SuccessView: View { - - @EnvironmentObject var viewModel: SampleViewModel - - @Environment(\.dismiss) var dismiss - - var body: some View { - VStack { - if let successMessage = viewModel.successMessage { - Text(successMessage) - Button { - viewModel.reset() - dismiss() - } label: { - Text("Reset") - } - } - else if let issuedVerifiedId = viewModel.issuedVerifiedId { - VStack { - Text("\(issuedVerifiedId.id)") - .multilineTextAlignment(.center) - Spacer() - Text("Issued on: \(issuedVerifiedId.issuedOn.formatted())") - .multilineTextAlignment(.center) - Spacer() - if let expiresOn = issuedVerifiedId.expiresOn { - Text("Expires on: \(expiresOn.formatted())") - .multilineTextAlignment(.center) - Spacer() - } - Text("Claims:") - let claims = issuedVerifiedId.getClaims() - List(claims.indices, id: \.self) { index in - HStack { - let claim = claims[index] - Text("\(claim.id):") - Text(String(describing: claims[index].value)) - } - }.listStyle(.inset) - }.navigationTitle("Verified Id Successfully Issued!") - .navigationBarTitleDisplayMode(.inline) - } - } - } -} - diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/VerifiedIdView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/VerifiedIdView.swift new file mode 100644 index 00000000..34b314f6 --- /dev/null +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/VerifiedIdView.swift @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import SwiftUI +import WalletLibrary + +struct VerifiedIdView: View { + + let verifiedId: VerifiedId + + @Environment(\.dismiss) var dismiss + + var body: some View { + VStack { + Text("\(verifiedId.id)") + .multilineTextAlignment(.center) + Spacer() + Text("Issued on: \(verifiedId.issuedOn.formatted())") + .multilineTextAlignment(.center) + Spacer() + if let expiresOn = verifiedId.expiresOn { + Text("Expires on: \(expiresOn.formatted())") + .multilineTextAlignment(.center) + Spacer() + } + Text("Claims:") + let claims = verifiedId.getClaims() + List(claims.indices, id: \.self) { index in + HStack { + let claim = claims[index] + Text("\(claim.id):") + Text(String(describing: claims[index].value)) + } + }.listStyle(.inset) + }.navigationTitle("Verified Id") + .navigationBarTitleDisplayMode(.inline) + } +} From de03973878e2b280f7010ec1c35fbbb909870970 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 20 Mar 2023 10:06:21 -0700 Subject: [PATCH 365/373] Change name of in progress to update. --- .../WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift | 4 ++-- .../WalletLibraryDemo/Views/RequestView.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift index 7c33b364..a2489b95 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift @@ -15,7 +15,7 @@ enum SampleViewModelError: String, Error { enum ViewState { case Initialized - case InProgress + case CreatingRequest case GatheringRequirements case IssuanceSuccess(with: VerifiedId) case PresentationSuccess(with: String) @@ -68,7 +68,7 @@ enum ViewState { Task { reset() - viewState = .InProgress + viewState = .CreatingRequest do { let input: VerifiedIdRequestInput = try createInput(fromInput: input) diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequestView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequestView.swift index b0345e85..381571d2 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequestView.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequestView.swift @@ -13,7 +13,7 @@ struct RequestView: View { var body: some View { switch (viewModel.viewState) { - case .InProgress: + case .CreatingRequest: ProgressView() case .GatheringRequirements: requirementsView From 72d24f20be8bd2c5e78257c04e8882b7934e818a Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 20 Mar 2023 11:46:44 -0700 Subject: [PATCH 366/373] Removed VerifiedId Decoder for next PR. --- .../WalletLibraryDemo/SampleViewModel.swift | 10 +++-- .../VerifiedIdRepository.swift | 39 ++++--------------- .../WalletLibrary.xcodeproj/project.pbxproj | 8 ---- .../VerifiedId/VerifiedIdDecoder.swift | 22 ----------- .../VerifiedId/VerifiedIdEncoder.swift | 28 ------------- 5 files changed, 14 insertions(+), 93 deletions(-) delete mode 100644 WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdDecoder.swift delete mode 100644 WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdEncoder.swift diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift index a2489b95..93748674 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift @@ -36,6 +36,7 @@ enum ViewState { /// Show a progress view when while doing internal logic. @Published var isProgressViewShowing: Bool = true + /// List of issued Verified Ids. @Published var issuedVerifiedIds: [VerifiedId] = [] /// The Verified Id Client is used to create requests with a configuration set by the Builder. @@ -44,7 +45,8 @@ enum ViewState { /// The current issuance or presentation request that is being processed. private var request: (any VerifiedIdRequest)? = nil - private let verifiedIdRepository = VerifiedIdRepository() + /// Repository in charge of storing/retrieving verified ids. + private var verifiedIdRepository = VerifiedIdRepository() /// TODO: enable deeplinking and the rest of the requirements. init() { @@ -74,7 +76,7 @@ enum ViewState { let input: VerifiedIdRequestInput = try createInput(fromInput: input) // VerifiedIdClient is used to create a request from an input - // such as, in the case, a VerifiedIdRequestURL. + // such as, in this case, a VerifiedIdRequestURL. let request = try await client.createVerifiedIdRequest(from: input) self.request = request @@ -122,6 +124,7 @@ enum ViewState { } } + /// Once the request is satisfied, complete the request. private func complete(issuanceRequest: any VerifiedIdIssuanceRequest) { Task { let result = await issuanceRequest.complete() @@ -151,11 +154,11 @@ enum ViewState { func fulfill(requirementState: RequirementState, with value: String) throws { do { try requirementState.fulfill(with: value) + isCompleteButtonEnabled = request?.isSatisfied() ?? false } catch let error as RequirementStateError { showErrorMessage(from: error, additionalInfo: "Value == \(value)") } - self.isCompleteButtonEnabled = request?.isSatisfied() ?? false } func fulfill(requirementState: RequirementState, with value: VerifiedId) throws { @@ -169,7 +172,6 @@ enum ViewState { } private func showErrorMessage(from error: Error, additionalInfo: String? = nil) { - print(error) var errorMessage: String = error.localizedDescription if let error = error as? SampleViewModelError { errorMessage = error.rawValue diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/VerifiedIdRepository.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/VerifiedIdRepository.swift index c57a506e..5e25dcf8 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/VerifiedIdRepository.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/VerifiedIdRepository.swift @@ -8,43 +8,20 @@ import WalletLibrary struct VerifiedIdRepository { - let container = PersistenceController.shared.container + /// TODO: add persistent storage. + var storedVerifiedIds: [VerifiedId] = [] - func save(verifiedId: VerifiedId) throws { - let rawVerifiedId = try VerifiedIdEncoder().encode(verifiedId: verifiedId) - let viewContext = container.viewContext - let newVerifiedId = RawVerifiedId(context: viewContext) - newVerifiedId.raw = rawVerifiedId - try viewContext.save() + mutating func save(verifiedId: VerifiedId) throws { + storedVerifiedIds.append(verifiedId) } - func delete(verifiedId: VerifiedId) throws { - let viewContext = container.viewContext - let request = NSFetchRequest(entityName: "RawVerifiedId") - let results = try viewContext.fetch(request) - - for result in results as! [NSManagedObject] { - let id = result.value(forKey: "id") as! String - if verifiedId.id == id { - viewContext.delete(result) - } + mutating func delete(verifiedId: VerifiedId) throws { + storedVerifiedIds.removeAll { + $0.id == verifiedId.id } - - try viewContext.save() } func getAllStoredVerifiedIds() throws -> [VerifiedId] { - let viewContext = container.viewContext - let request = NSFetchRequest(entityName: "RawVerifiedId") - let results = try viewContext.fetch(request) - - var verifiedIds: [VerifiedId] = [] - for result in results as! [NSManagedObject] { - let rawValue = result.value(forKey: "raw") as! Data - let verifiedId = try VerifiedIdDecoder().decode(from: rawValue) - verifiedIds.append(verifiedId) - } - - return verifiedIds + return storedVerifiedIds } } diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 57a8e79c..093cf5cb 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -25,8 +25,6 @@ 550E3212298B1150004E7716 /* VerifiedIdRequestInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 550E3211298B1150004E7716 /* VerifiedIdRequestInput.swift */; }; 550E3214298B11A5004E7716 /* Requirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 550E3213298B11A5004E7716 /* Requirement.swift */; }; 550E3217298B1236004E7716 /* RequesterStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 550E3216298B1236004E7716 /* RequesterStyle.swift */; }; - 552A75A429C4F19B005BFF3D /* VerifiedIdDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 552A75A329C4F19B005BFF3D /* VerifiedIdDecoder.swift */; }; - 552A75BD29C52B0A005BFF3D /* VerifiedIdEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 552A75BC29C52B0A005BFF3D /* VerifiedIdEncoder.swift */; }; 552A75BF29C52DBB005BFF3D /* EncodedVerifiedId.swift in Sources */ = {isa = PBXBuildFile; fileRef = 552A75BE29C52DBB005BFF3D /* EncodedVerifiedId.swift */; }; 552E509B293E6AB200868F47 /* WalletLibrary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 552E5092293E6AB200868F47 /* WalletLibrary.framework */; }; 552E50A0293E6AB200868F47 /* VerifiedIdClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 552E509F293E6AB200868F47 /* VerifiedIdClientTests.swift */; }; @@ -340,8 +338,6 @@ 550E3211298B1150004E7716 /* VerifiedIdRequestInput.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VerifiedIdRequestInput.swift; sourceTree = ""; }; 550E3213298B11A5004E7716 /* Requirement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Requirement.swift; sourceTree = ""; }; 550E3216298B1236004E7716 /* RequesterStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequesterStyle.swift; sourceTree = ""; }; - 552A75A329C4F19B005BFF3D /* VerifiedIdDecoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdDecoder.swift; sourceTree = ""; }; - 552A75BC29C52B0A005BFF3D /* VerifiedIdEncoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdEncoder.swift; sourceTree = ""; }; 552A75BE29C52DBB005BFF3D /* EncodedVerifiedId.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncodedVerifiedId.swift; sourceTree = ""; }; 552E5092293E6AB200868F47 /* WalletLibrary.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = WalletLibrary.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 552E5095293E6AB200868F47 /* WalletLibrary.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WalletLibrary.h; sourceTree = ""; }; @@ -1032,8 +1028,6 @@ 552A75BE29C52DBB005BFF3D /* EncodedVerifiedId.swift */, 55E336D3293FA75300CD2ED7 /* VerifiedId.swift */, 55E336D5293FA78C00CD2ED7 /* VerifiedIdClaim.swift */, - 552A75A329C4F19B005BFF3D /* VerifiedIdDecoder.swift */, - 552A75BC29C52B0A005BFF3D /* VerifiedIdEncoder.swift */, ); path = VerifiedId; sourceTree = ""; @@ -1525,7 +1519,6 @@ 55A81BE12991AB96002C259A /* RequestHandling.swift in Sources */, 55E2F071299554110008010D /* LibraryConfiguration.swift in Sources */, 550E3209298B0EA4004E7716 /* VerifiedIdClientBuilder.swift in Sources */, - 552A75BD29C52B0A005BFF3D /* VerifiedIdEncoder.swift in Sources */, 55DECC3029BB8E5C00D5C802 /* VCTypeConstraint.swift in Sources */, 550A9192299400820014D030 /* OpenIdResponder.swift in Sources */, 5534E672294A19D5005D0765 /* IdTokenDescriptor+Mappable.swift in Sources */, @@ -1542,7 +1535,6 @@ 55E2F0882995743D0008010D /* LinkedDomainResult+Mappable.swift in Sources */, 55E33708293FD25900CD2ED7 /* AccessTokenRequirement.swift in Sources */, 55E2F07D2995561E0008010D /* OpenIdPresentationRequest.swift in Sources */, - 552A75A429C4F19B005BFF3D /* VerifiedIdDecoder.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdDecoder.swift b/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdDecoder.swift deleted file mode 100644 index d5b71a31..00000000 --- a/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdDecoder.swift +++ /dev/null @@ -1,22 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Microsoft Corporation. All rights reserved. -* Licensed under the MIT License. See License.txt in the project root for license information. -*--------------------------------------------------------------------------------------------*/ - -/** - * The Verified Id Decoder - */ -public struct VerifiedIdDecoder { - - private let jsonDecoder = JSONDecoder() - - public init() {} - - public func decode(from data: Data) throws -> any VerifiedId { - let encodedVerifiedId = try jsonDecoder.decode(EncodedVerifiedId.self, from: data) - switch encodedVerifiedId.type { - case .VerifiableCredential: - return try jsonDecoder.decode(VerifiableCredential.self, from: encodedVerifiedId.raw) - } - } -} diff --git a/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdEncoder.swift b/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdEncoder.swift deleted file mode 100644 index 9b04f0f6..00000000 --- a/WalletLibrary/WalletLibrary/VerifiedId/VerifiedIdEncoder.swift +++ /dev/null @@ -1,28 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Microsoft Corporation. All rights reserved. -* Licensed under the MIT License. See License.txt in the project root for license information. -*--------------------------------------------------------------------------------------------*/ - -enum VerifiedIdEncoderError: Error { - case unsupportedVerifiedIdType(String) -} - -public struct VerifiedIdEncoder { - - private let jsonEncoder = JSONEncoder() - - public init() {} - - public func encode(verifiedId: any VerifiedId) throws -> Data { - switch verifiedId { - case let vc as VerifiableCredential: - let rawVC = try jsonEncoder.encode(vc) - let encodedVerifiedId = EncodedVerifiedId(type: .VerifiableCredential, - raw: rawVC) - return try jsonEncoder.encode(encodedVerifiedId) - default: - let type = String(describing: type(of: verifiedId)) - throw VerifiedIdEncoderError.unsupportedVerifiedIdType(type) - } - } -} From edfe2f7c377bb5a9d8411d2aeab6b3befcac1362 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 20 Mar 2023 13:12:22 -0700 Subject: [PATCH 367/373] Split View up into separate views. --- .../project.pbxproj | 12 ++++ .../WalletLibraryDemo/Views/RequestView.swift | 28 ++-------- .../Views/RequirementListView.swift | 30 ++++++++++ .../Views/RequirementView.swift | 39 +------------ .../Views/UserInputView.swift | 55 +++++++++++++++++++ .../Views/VerifiedIdRequirementView.swift | 41 ++++++++++++++ 6 files changed, 145 insertions(+), 60 deletions(-) create mode 100644 Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequirementListView.swift create mode 100644 Demo/WalletLibraryDemo/WalletLibraryDemo/Views/UserInputView.swift create mode 100644 Demo/WalletLibraryDemo/WalletLibraryDemo/Views/VerifiedIdRequirementView.swift diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo.xcodeproj/project.pbxproj b/Demo/WalletLibraryDemo/WalletLibraryDemo.xcodeproj/project.pbxproj index b3fa1e4a..d86d82fc 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo.xcodeproj/project.pbxproj +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo.xcodeproj/project.pbxproj @@ -8,6 +8,9 @@ /* Begin PBXBuildFile section */ 552A75BB29C50DBA005BFF3D /* VerifiedIdRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 552A75BA29C50DB9005BFF3D /* VerifiedIdRepository.swift */; }; + 552A760629C8E32C005BFF3D /* RequirementListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 552A760529C8E32C005BFF3D /* RequirementListView.swift */; }; + 552A760829C8E3B5005BFF3D /* UserInputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 552A760729C8E3B5005BFF3D /* UserInputView.swift */; }; + 552A760A29C8EC9F005BFF3D /* VerifiedIdRequirementView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 552A760929C8EC9F005BFF3D /* VerifiedIdRequirementView.swift */; }; 553CC08929A94686005A5FD6 /* ErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC08829A94686005A5FD6 /* ErrorView.swift */; }; 553CC08B29A9469B005A5FD6 /* VerifiedIdView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC08A29A9469B005A5FD6 /* VerifiedIdView.swift */; }; 553CC08D29A94A13005A5FD6 /* RequirementListCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC08C29A94A13005A5FD6 /* RequirementListCellView.swift */; }; @@ -41,6 +44,9 @@ /* Begin PBXFileReference section */ 552A75BA29C50DB9005BFF3D /* VerifiedIdRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdRepository.swift; sourceTree = ""; }; + 552A760529C8E32C005BFF3D /* RequirementListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequirementListView.swift; sourceTree = ""; }; + 552A760729C8E3B5005BFF3D /* UserInputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInputView.swift; sourceTree = ""; }; + 552A760929C8EC9F005BFF3D /* VerifiedIdRequirementView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdRequirementView.swift; sourceTree = ""; }; 553CC08829A94686005A5FD6 /* ErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorView.swift; sourceTree = ""; }; 553CC08A29A9469B005A5FD6 /* VerifiedIdView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdView.swift; sourceTree = ""; }; 553CC08C29A94A13005A5FD6 /* RequirementListCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequirementListCellView.swift; sourceTree = ""; }; @@ -77,8 +83,11 @@ 553CC08829A94686005A5FD6 /* ErrorView.swift */, 5585BDFD29A6C4CA0059710B /* RequestView.swift */, 553CC08C29A94A13005A5FD6 /* RequirementListCellView.swift */, + 552A760529C8E32C005BFF3D /* RequirementListView.swift */, 5585BDFF29A6C5070059710B /* RequirementView.swift */, 553CC08A29A9469B005A5FD6 /* VerifiedIdView.swift */, + 552A760729C8E3B5005BFF3D /* UserInputView.swift */, + 552A760929C8EC9F005BFF3D /* VerifiedIdRequirementView.swift */, ); path = Views; sourceTree = ""; @@ -205,15 +214,18 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 552A760A29C8EC9F005BFF3D /* VerifiedIdRequirementView.swift in Sources */, 5585BDFE29A6C4CA0059710B /* RequestView.swift in Sources */, 55E3369D293E8F7500CD2ED7 /* WalletLibraryDemo.xcdatamodeld in Sources */, 553CC08B29A9469B005A5FD6 /* VerifiedIdView.swift in Sources */, 55E33698293E8F7500CD2ED7 /* WalletLibraryDemoApp.swift in Sources */, 552A75BB29C50DBA005BFF3D /* VerifiedIdRepository.swift in Sources */, + 552A760629C8E32C005BFF3D /* RequirementListView.swift in Sources */, 55E3369F293E8F7500CD2ED7 /* ContentView.swift in Sources */, 5585BE0029A6C5070059710B /* RequirementView.swift in Sources */, 5585BDE529A692E10059710B /* RequirementState.swift in Sources */, 55E3369A293E8F7500CD2ED7 /* Persistence.swift in Sources */, + 552A760829C8E3B5005BFF3D /* UserInputView.swift in Sources */, 553CC08929A94686005A5FD6 /* ErrorView.swift in Sources */, 553CC08D29A94A13005A5FD6 /* RequirementListCellView.swift in Sources */, 5585BDE129A047370059710B /* SampleViewModel.swift in Sources */, diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequestView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequestView.swift index 381571d2..d2dcf414 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequestView.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequestView.swift @@ -9,39 +9,21 @@ struct RequestView: View { @EnvironmentObject var viewModel: SampleViewModel - @Environment(\.dismiss) var dismiss - var body: some View { - switch (viewModel.viewState) { + switch (viewModel.requestState) { case .CreatingRequest: ProgressView() case .GatheringRequirements: - requirementsView + RequirementListView() case .IssuanceSuccess(with: let verifiedId): VerifiedIdView(verifiedId: verifiedId) + case .PresentationSuccess(with: let message): + Text(message) + .bold() case .Error(withMessage: let message): ErrorView(errorMessage: message) default: Spacer() } } - - var requirementsView: some View { - VStack { - List(viewModel.requirements) { requirement in - NavigationLink(destination: RequirementView(requirement: requirement)) { - RequirementListViewCell(requirement: requirement) - } - }.listStyle(.inset) - Spacer() - Divider() - Button { - viewModel.complete() - } label: { - Text("Complete") - }.disabled(!viewModel.isCompleteButtonEnabled) - Spacer() - }.navigationTitle("Fulfill Requirements") - .navigationBarTitleDisplayMode(.inline) - } } diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequirementListView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequirementListView.swift new file mode 100644 index 00000000..8c7da3d7 --- /dev/null +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequirementListView.swift @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import SwiftUI + +struct RequirementListView: View { + + @EnvironmentObject var viewModel: SampleViewModel + + var body: some View { + VStack { + List(viewModel.requirements) { requirement in + NavigationLink(destination: RequirementView(requirement: requirement)) { + RequirementListViewCell(requirement: requirement) + } + }.listStyle(.inset) + Spacer() + Divider() + Button { + viewModel.complete() + } label: { + Text("Complete") + }.disabled(!viewModel.isCompleteButtonEnabled) + Spacer() + }.navigationTitle("Fulfill Requirements") + .navigationBarTitleDisplayMode(.inline) + } +} diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequirementView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequirementView.swift index fdfb3e0e..8446f352 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequirementView.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequirementView.swift @@ -21,9 +21,9 @@ struct RequirementView: View { var body: some View { switch (requirement.requirement) { case is SelfAttestedClaimRequirement: - userInputView + UserInputView(requirement: requirement) case is PinRequirement: - userInputView + UserInputView(requirement: requirement) case let verifiedIdRequirement as VerifiedIdRequirement: let matches = verifiedIdRequirement.getMatches(verifiedIds: viewModel.issuedVerifiedIds) List(matches, id: \.id) { match in @@ -38,32 +38,6 @@ struct RequirementView: View { } } - var userInputView: some View { - VStack { - if isInvalidInput { - Text("Invalid Input") - .foregroundColor(.red) - } - Text(requirement.label) - TextField( - "", - text: $userInput - ) - .padding(EdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5)) - .textInputAutocapitalization(.never) - .disableAutocorrection(true) - .textFieldStyle(.roundedBorder) - .frame(width: UIScreen.main.bounds.width - 20) - Button { - fulfill(with: userInput) - } label: { - Text("Add") - } - }.onDisappear { - isInvalidInput = false - } - } - private func fulfill(with value: VerifiedId) { do { try viewModel.fulfill(requirementState: requirement, with: value) @@ -72,13 +46,4 @@ struct RequirementView: View { isInvalidInput = true } } - - private func fulfill(with value: String) { - do { - try viewModel.fulfill(requirementState: requirement, with: value) - dismiss() - } catch { - isInvalidInput = true - } - } } diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/UserInputView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/UserInputView.swift new file mode 100644 index 00000000..30d069cb --- /dev/null +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/UserInputView.swift @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import SwiftUI +import WalletLibrary + +struct UserInputView: View { + + @EnvironmentObject var viewModel: SampleViewModel + + @State private var userInput: String = "" + + @State private var isInvalidInput: Bool = false + + @Environment(\.dismiss) var dismiss + + var requirement: RequirementState + + var body: some View { + VStack { + if isInvalidInput { + Text("Invalid Input") + .foregroundColor(.red) + } + Text(requirement.label) + TextField( + "", + text: $userInput + ) + .padding(EdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5)) + .textInputAutocapitalization(.never) + .disableAutocorrection(true) + .textFieldStyle(.roundedBorder) + .frame(width: UIScreen.main.bounds.width - 20) + Button { + fulfill(with: userInput) + } label: { + Text("Add") + } + }.onDisappear { + isInvalidInput = false + } + } + + private func fulfill(with value: String) { + do { + try viewModel.fulfill(requirementState: requirement, with: value) + dismiss() + } catch { + isInvalidInput = true + } + } +} diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/VerifiedIdRequirementView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/VerifiedIdRequirementView.swift new file mode 100644 index 00000000..64b9088a --- /dev/null +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/VerifiedIdRequirementView.swift @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. See License.txt in the project root for license information. +*--------------------------------------------------------------------------------------------*/ + +import SwiftUI +import WalletLibrary + +struct VerifiedIdRequirementView: View { + + @EnvironmentObject var viewModel: SampleViewModel + + @State private var userInput: String = "" + + @State private var isInvalidInput: Bool = false + + @Environment(\.dismiss) var dismiss + + var requirement: RequirementState + + var requirementMatches: [VerifiedId] + + var body: some View { + List(requirementMatches, id: \.id) { match in + Button { + fulfill(with: match) + } label: { + Text(match.id) + } + }.listStyle(.inset) + } + + private func fulfill(with value: VerifiedId) { + do { + try viewModel.fulfill(requirementState: requirement, with: value) + } catch { + viewModel.showErrorMessage(from: error) + } + dismiss() + } +} From e60f4981a80a1ffe738150b796469c87bb460d27 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 20 Mar 2023 13:12:40 -0700 Subject: [PATCH 368/373] Only show issued verified id list if there is one. --- .../WalletLibraryDemo/ContentView.swift | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/ContentView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/ContentView.swift index 51270736..6d548f42 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/ContentView.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/ContentView.swift @@ -32,20 +32,22 @@ struct ContentView: View { Text("Create Request") }.navigationTitle("Verified Id Sample App") Spacer() - Text("Issued Verified Ids") - .bold() - List { - ForEach(viewModel.issuedVerifiedIds, id: \.id) { verifiedId in - NavigationLink { - VerifiedIdView(verifiedId: verifiedId) - } label: { - Text(verifiedId.id) + if !viewModel.issuedVerifiedIds.isEmpty { + Text("Issued Verified Ids") + .bold() + List { + ForEach(viewModel.issuedVerifiedIds, id: \.id) { verifiedId in + NavigationLink { + VerifiedIdView(verifiedId: verifiedId) + } label: { + Text(verifiedId.id) + } + }.onDelete { indexSet in + viewModel.deleteVerifiedId(indexSet: indexSet) } - }.onDelete { indexSet in - viewModel.deleteVerifiedId(indexSet: indexSet) } + .listStyle(.inset) } - .listStyle(.inset) }.onDisappear { viewModel.createRequest(fromInput: input) } From 5d72c07acf0287acc67f08833d0c6759efc6092d Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 20 Mar 2023 13:12:45 -0700 Subject: [PATCH 369/373] Update SampleViewModel.swift --- .../WalletLibraryDemo/SampleViewModel.swift | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift index 93748674..0a475b07 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift @@ -11,9 +11,11 @@ enum SampleViewModelError: String, Error { case unableToCreateRequest = "Unable to create request." case requestIsUndefined = "Verified Id Request is Undefined." case unsupportedRequirementType = "One of the requirement types is not supported." + case verifiedIdClientHasNotBeenInitialized = "Verified Id Client has not been initialized." } -enum ViewState { +/// State to keep track of what views should be shown. +enum RequestState { case Initialized case CreatingRequest case GatheringRequirements @@ -25,7 +27,8 @@ enum ViewState { @MainActor class SampleViewModel: ObservableObject { - @Published var viewState: ViewState + /// The state of the request. + @Published var requestState: RequestState /// The requirements to be gathered by the user. @Published var requirements: [RequirementState] = [] @@ -50,7 +53,7 @@ enum ViewState { /// TODO: enable deeplinking and the rest of the requirements. init() { - viewState = .Initialized + requestState = .Initialized do { let builder = VerifiedIdClientBuilder() issuedVerifiedIds = try verifiedIdRepository.getAllStoredVerifiedIds() @@ -64,13 +67,13 @@ enum ViewState { func createRequest(fromInput input: String) { guard let client = verifiedIdClient else { - showErrorMessage(from: SampleViewModelError.unableToCreateRequest) + showErrorMessage(from: SampleViewModelError.verifiedIdClientHasNotBeenInitialized) return } Task { reset() - viewState = .CreatingRequest + requestState = .CreatingRequest do { let input: VerifiedIdRequestInput = try createInput(fromInput: input) @@ -82,7 +85,7 @@ enum ViewState { try configureRequirements(requirement: request.requirement) isCompleteButtonEnabled = request.isSatisfied() - viewState = .GatheringRequirements + requestState = .GatheringRequirements } catch { showErrorMessage(from: error, additionalInfo: "Unable to create request.") } @@ -132,7 +135,7 @@ enum ViewState { case .success(let verifiedId): issuedVerifiedIds.append(verifiedId) try verifiedIdRepository.save(verifiedId: verifiedId) - viewState = .IssuanceSuccess(with: verifiedId) + requestState = .IssuanceSuccess(with: verifiedId) case .failure(let error): showErrorMessage(from: error) } @@ -144,7 +147,7 @@ enum ViewState { let result = await presentationRequest.complete() switch (result) { case .success(_): - viewState = .PresentationSuccess(with: "Successful Presentation!") + requestState = .PresentationSuccess(with: "Successful Presentation!") case .failure(let error): showErrorMessage(from: error) } @@ -171,7 +174,7 @@ enum ViewState { self.isCompleteButtonEnabled = request?.isSatisfied() ?? false } - private func showErrorMessage(from error: Error, additionalInfo: String? = nil) { + func showErrorMessage(from error: Error, additionalInfo: String? = nil) { var errorMessage: String = error.localizedDescription if let error = error as? SampleViewModelError { errorMessage = error.rawValue @@ -182,7 +185,7 @@ enum ViewState { if let additionalInfo = additionalInfo { errorMessage.append("\n\(additionalInfo)") } - viewState = .Error(withMessage: errorMessage) + requestState = .Error(withMessage: errorMessage) } func deleteVerifiedId(indexSet: IndexSet) { @@ -196,7 +199,7 @@ enum ViewState { } func reset() { - viewState = .Initialized + requestState = .Initialized requirements = [] request = nil } From 2d57fd3d70291000c4a4bd90d67acd21e4843d4e Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 20 Mar 2023 13:28:52 -0700 Subject: [PATCH 370/373] Create VerifiedIdRequirementPicker --- .../WalletLibraryDemo.xcodeproj/project.pbxproj | 8 ++++---- ...equirementView.swift => VerifiedIdPickerView.swift} | 10 +++------- 2 files changed, 7 insertions(+), 11 deletions(-) rename Demo/WalletLibraryDemo/WalletLibraryDemo/Views/{VerifiedIdRequirementView.swift => VerifiedIdPickerView.swift} (81%) diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo.xcodeproj/project.pbxproj b/Demo/WalletLibraryDemo/WalletLibraryDemo.xcodeproj/project.pbxproj index d86d82fc..dddbedaf 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo.xcodeproj/project.pbxproj +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo.xcodeproj/project.pbxproj @@ -10,7 +10,7 @@ 552A75BB29C50DBA005BFF3D /* VerifiedIdRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 552A75BA29C50DB9005BFF3D /* VerifiedIdRepository.swift */; }; 552A760629C8E32C005BFF3D /* RequirementListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 552A760529C8E32C005BFF3D /* RequirementListView.swift */; }; 552A760829C8E3B5005BFF3D /* UserInputView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 552A760729C8E3B5005BFF3D /* UserInputView.swift */; }; - 552A760A29C8EC9F005BFF3D /* VerifiedIdRequirementView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 552A760929C8EC9F005BFF3D /* VerifiedIdRequirementView.swift */; }; + 552A760C29C8F70E005BFF3D /* VerifiedIdPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 552A760B29C8F70E005BFF3D /* VerifiedIdPickerView.swift */; }; 553CC08929A94686005A5FD6 /* ErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC08829A94686005A5FD6 /* ErrorView.swift */; }; 553CC08B29A9469B005A5FD6 /* VerifiedIdView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC08A29A9469B005A5FD6 /* VerifiedIdView.swift */; }; 553CC08D29A94A13005A5FD6 /* RequirementListCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553CC08C29A94A13005A5FD6 /* RequirementListCellView.swift */; }; @@ -46,7 +46,7 @@ 552A75BA29C50DB9005BFF3D /* VerifiedIdRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdRepository.swift; sourceTree = ""; }; 552A760529C8E32C005BFF3D /* RequirementListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequirementListView.swift; sourceTree = ""; }; 552A760729C8E3B5005BFF3D /* UserInputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserInputView.swift; sourceTree = ""; }; - 552A760929C8EC9F005BFF3D /* VerifiedIdRequirementView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdRequirementView.swift; sourceTree = ""; }; + 552A760B29C8F70E005BFF3D /* VerifiedIdPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdPickerView.swift; sourceTree = ""; }; 553CC08829A94686005A5FD6 /* ErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorView.swift; sourceTree = ""; }; 553CC08A29A9469B005A5FD6 /* VerifiedIdView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifiedIdView.swift; sourceTree = ""; }; 553CC08C29A94A13005A5FD6 /* RequirementListCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequirementListCellView.swift; sourceTree = ""; }; @@ -87,7 +87,7 @@ 5585BDFF29A6C5070059710B /* RequirementView.swift */, 553CC08A29A9469B005A5FD6 /* VerifiedIdView.swift */, 552A760729C8E3B5005BFF3D /* UserInputView.swift */, - 552A760929C8EC9F005BFF3D /* VerifiedIdRequirementView.swift */, + 552A760B29C8F70E005BFF3D /* VerifiedIdPickerView.swift */, ); path = Views; sourceTree = ""; @@ -214,7 +214,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 552A760A29C8EC9F005BFF3D /* VerifiedIdRequirementView.swift in Sources */, 5585BDFE29A6C4CA0059710B /* RequestView.swift in Sources */, 55E3369D293E8F7500CD2ED7 /* WalletLibraryDemo.xcdatamodeld in Sources */, 553CC08B29A9469B005A5FD6 /* VerifiedIdView.swift in Sources */, @@ -224,6 +223,7 @@ 55E3369F293E8F7500CD2ED7 /* ContentView.swift in Sources */, 5585BE0029A6C5070059710B /* RequirementView.swift in Sources */, 5585BDE529A692E10059710B /* RequirementState.swift in Sources */, + 552A760C29C8F70E005BFF3D /* VerifiedIdPickerView.swift in Sources */, 55E3369A293E8F7500CD2ED7 /* Persistence.swift in Sources */, 552A760829C8E3B5005BFF3D /* UserInputView.swift in Sources */, 553CC08929A94686005A5FD6 /* ErrorView.swift in Sources */, diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/VerifiedIdRequirementView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/VerifiedIdPickerView.swift similarity index 81% rename from Demo/WalletLibraryDemo/WalletLibraryDemo/Views/VerifiedIdRequirementView.swift rename to Demo/WalletLibraryDemo/WalletLibraryDemo/Views/VerifiedIdPickerView.swift index 64b9088a..165826be 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/VerifiedIdRequirementView.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/VerifiedIdPickerView.swift @@ -6,19 +6,15 @@ import SwiftUI import WalletLibrary -struct VerifiedIdRequirementView: View { +struct VerifiedIdPickerView: View { @EnvironmentObject var viewModel: SampleViewModel - @State private var userInput: String = "" - - @State private var isInvalidInput: Bool = false - @Environment(\.dismiss) var dismiss - var requirement: RequirementState + let requirement: RequirementState - var requirementMatches: [VerifiedId] + let requirementMatches: [VerifiedId] var body: some View { List(requirementMatches, id: \.id) { match in From 8935a29a33a874d827e855bfef9b3d0a03e4eb5c Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 20 Mar 2023 13:29:06 -0700 Subject: [PATCH 371/373] Add comments to SampleViewModel. --- .../WalletLibraryDemo/SampleViewModel.swift | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift index 0a475b07..eaa6a3a7 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/SampleViewModel.swift @@ -14,7 +14,7 @@ enum SampleViewModelError: String, Error { case verifiedIdClientHasNotBeenInitialized = "Verified Id Client has not been initialized." } -/// State to keep track of what views should be shown. +/// State to keep track of what state the request is in. enum RequestState { case Initialized case CreatingRequest @@ -64,6 +64,7 @@ enum RequestState { } } + /// Create request using Wallet Library and configure requirements to be shown. func createRequest(fromInput input: String) { guard let client = verifiedIdClient else { @@ -116,6 +117,12 @@ enum RequestState { requirements.append(requirementState) } + /// The helper method `getMatches` can be used to filter a list of Verified Ids and returns only the ones that satisfy the requirement. + func getVerifiedIdMatches(from requirement: VerifiedIdRequirement) -> [VerifiedId] { + return requirement.getMatches(verifiedIds: issuedVerifiedIds) + } + + /// Once the request is satisfied, complete the request. func complete() { switch (request) { case let issuanceRequest as any VerifiedIdIssuanceRequest: @@ -127,7 +134,6 @@ enum RequestState { } } - /// Once the request is satisfied, complete the request. private func complete(issuanceRequest: any VerifiedIdIssuanceRequest) { Task { let result = await issuanceRequest.complete() @@ -154,6 +160,7 @@ enum RequestState { } } + /// Fulfill a requirement with a string value. func fulfill(requirementState: RequirementState, with value: String) throws { do { try requirementState.fulfill(with: value) @@ -164,6 +171,7 @@ enum RequestState { } } + /// Fulfill a requirement with a Verified id value. func fulfill(requirementState: RequirementState, with value: VerifiedId) throws { do { try requirementState.fulfill(with: value) @@ -174,8 +182,9 @@ enum RequestState { self.isCompleteButtonEnabled = request?.isSatisfied() ?? false } + /// If an error occurs during the flow, show the error message. func showErrorMessage(from error: Error, additionalInfo: String? = nil) { - var errorMessage: String = error.localizedDescription + var errorMessage = String(describing: error) if let error = error as? SampleViewModelError { errorMessage = error.rawValue } else if let error = error as? RequirementStateError { @@ -188,6 +197,7 @@ enum RequestState { requestState = .Error(withMessage: errorMessage) } + /// Delete a verified id from list. func deleteVerifiedId(indexSet: IndexSet) { for index in indexSet { if issuedVerifiedIds.count > index { @@ -198,6 +208,7 @@ enum RequestState { } } + /// Reset to start a new request. func reset() { requestState = .Initialized requirements = [] From 84f6b2bfa2a9cd4d58fbd5bd1b0c89f6c192d1b0 Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 20 Mar 2023 13:29:15 -0700 Subject: [PATCH 372/373] Update RequirementView. --- .../Views/RequirementView.swift | 19 ++----------------- .../Views/UserInputView.swift | 2 +- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequirementView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequirementView.swift index 8446f352..06345756 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequirementView.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/RequirementView.swift @@ -25,25 +25,10 @@ struct RequirementView: View { case is PinRequirement: UserInputView(requirement: requirement) case let verifiedIdRequirement as VerifiedIdRequirement: - let matches = verifiedIdRequirement.getMatches(verifiedIds: viewModel.issuedVerifiedIds) - List(matches, id: \.id) { match in - Button { - fulfill(with: match) - } label: { - Text(match.id) - } - }.listStyle(.inset) + VerifiedIdPickerView(requirement: requirement, + requirementMatches: viewModel.getVerifiedIdMatches(from: verifiedIdRequirement)) default: Text("Do not support requirement.") } } - - private func fulfill(with value: VerifiedId) { - do { - try viewModel.fulfill(requirementState: requirement, with: value) - dismiss() - } catch { - isInvalidInput = true - } - } } diff --git a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/UserInputView.swift b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/UserInputView.swift index 30d069cb..eb7cf04e 100644 --- a/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/UserInputView.swift +++ b/Demo/WalletLibraryDemo/WalletLibraryDemo/Views/UserInputView.swift @@ -16,7 +16,7 @@ struct UserInputView: View { @Environment(\.dismiss) var dismiss - var requirement: RequirementState + let requirement: RequirementState var body: some View { VStack { From bcd0edc5582f7c65e1a7eb18fb94540faa9c15eb Mon Sep 17 00:00:00 2001 From: Sydney Morton Date: Mon, 20 Mar 2023 13:31:09 -0700 Subject: [PATCH 373/373] Remove EncodedVerifiedId for next PR. --- .../WalletLibrary.xcodeproj/project.pbxproj | 4 ---- .../VerifiedId/EncodedVerifiedId.swift | 14 -------------- 2 files changed, 18 deletions(-) delete mode 100644 WalletLibrary/WalletLibrary/VerifiedId/EncodedVerifiedId.swift diff --git a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj index 093cf5cb..73d9daf2 100644 --- a/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj +++ b/WalletLibrary/WalletLibrary.xcodeproj/project.pbxproj @@ -25,7 +25,6 @@ 550E3212298B1150004E7716 /* VerifiedIdRequestInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 550E3211298B1150004E7716 /* VerifiedIdRequestInput.swift */; }; 550E3214298B11A5004E7716 /* Requirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 550E3213298B11A5004E7716 /* Requirement.swift */; }; 550E3217298B1236004E7716 /* RequesterStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 550E3216298B1236004E7716 /* RequesterStyle.swift */; }; - 552A75BF29C52DBB005BFF3D /* EncodedVerifiedId.swift in Sources */ = {isa = PBXBuildFile; fileRef = 552A75BE29C52DBB005BFF3D /* EncodedVerifiedId.swift */; }; 552E509B293E6AB200868F47 /* WalletLibrary.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 552E5092293E6AB200868F47 /* WalletLibrary.framework */; }; 552E50A0293E6AB200868F47 /* VerifiedIdClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 552E509F293E6AB200868F47 /* VerifiedIdClientTests.swift */; }; 552E50A1293E6AB200868F47 /* WalletLibrary.h in Headers */ = {isa = PBXBuildFile; fileRef = 552E5095293E6AB200868F47 /* WalletLibrary.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -338,7 +337,6 @@ 550E3211298B1150004E7716 /* VerifiedIdRequestInput.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VerifiedIdRequestInput.swift; sourceTree = ""; }; 550E3213298B11A5004E7716 /* Requirement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Requirement.swift; sourceTree = ""; }; 550E3216298B1236004E7716 /* RequesterStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequesterStyle.swift; sourceTree = ""; }; - 552A75BE29C52DBB005BFF3D /* EncodedVerifiedId.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncodedVerifiedId.swift; sourceTree = ""; }; 552E5092293E6AB200868F47 /* WalletLibrary.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = WalletLibrary.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 552E5095293E6AB200868F47 /* WalletLibrary.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WalletLibrary.h; sourceTree = ""; }; 552E509A293E6AB200868F47 /* WalletLibraryTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WalletLibraryTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1025,7 +1023,6 @@ isa = PBXGroup; children = ( 559BD4B1299EDF9E00BD61AC /* VerifiableCredential */, - 552A75BE29C52DBB005BFF3D /* EncodedVerifiedId.swift */, 55E336D3293FA75300CD2ED7 /* VerifiedId.swift */, 55E336D5293FA78C00CD2ED7 /* VerifiedIdClaim.swift */, ); @@ -1488,7 +1485,6 @@ 55DECBE729B92E3900D5C802 /* PresentationResponse.swift in Sources */, 559BD4B3299EDFB400BD61AC /* VerifiableCredential.swift in Sources */, 55E336D6293FA78C00CD2ED7 /* VerifiedIdClaim.swift in Sources */, - 552A75BF29C52DBB005BFF3D /* EncodedVerifiedId.swift in Sources */, 553CC09029A955E6005A5FD6 /* PinDescriptor+Mappable.swift in Sources */, 55E337652943749600CD2ED7 /* AsyncWrapper.swift in Sources */, 550E3214298B11A5004E7716 /* Requirement.swift in Sources */, diff --git a/WalletLibrary/WalletLibrary/VerifiedId/EncodedVerifiedId.swift b/WalletLibrary/WalletLibrary/VerifiedId/EncodedVerifiedId.swift deleted file mode 100644 index bf798073..00000000 --- a/WalletLibrary/WalletLibrary/VerifiedId/EncodedVerifiedId.swift +++ /dev/null @@ -1,14 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Copyright (c) Microsoft Corporation. All rights reserved. -* Licensed under the MIT License. See License.txt in the project root for license information. -*--------------------------------------------------------------------------------------------*/ - -enum SupportedVerifiedIdType: String, Codable { - case VerifiableCredential = "VerifiableCredential" -} - -struct EncodedVerifiedId: Codable { - let type: SupportedVerifiedIdType - - let raw: Data -}