Add Root Of Trust Resolver Injection Support (#146)
This commit is contained in:
Родитель
6475c1be46
Коммит
659ca18865
|
@ -45,6 +45,7 @@
|
|||
55379AD725A8DF7A0048600A /* PresentationRequestValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55379AD625A8DF7A0048600A /* PresentationRequestValidator.swift */; };
|
||||
553D4C472821D89D00FD39A6 /* IssuanceRequestValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553D4C462821D89D00FD39A6 /* IssuanceRequestValidator.swift */; };
|
||||
5540226428C9587600C85C6D /* VCSDKConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5540226328C9587600C85C6D /* VCSDKConfiguration.swift */; };
|
||||
555089702A3289A100186885 /* RootOfTrustResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5550896F2A3289A100186885 /* RootOfTrustResolver.swift */; };
|
||||
55575738251BC575009979AB /* VCEntities.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5557572E251BC575009979AB /* VCEntities.framework */; };
|
||||
5557573F251BC575009979AB /* VCEntities.h in Headers */ = {isa = PBXBuildFile; fileRef = 55575731251BC575009979AB /* VCEntities.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
55575763251BC6CF009979AB /* AttestationsDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5557574B251BC6CE009979AB /* AttestationsDescriptor.swift */; };
|
||||
|
@ -196,6 +197,7 @@
|
|||
55379AD625A8DF7A0048600A /* PresentationRequestValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentationRequestValidator.swift; sourceTree = "<group>"; };
|
||||
553D4C462821D89D00FD39A6 /* IssuanceRequestValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssuanceRequestValidator.swift; sourceTree = "<group>"; };
|
||||
5540226328C9587600C85C6D /* VCSDKConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VCSDKConfiguration.swift; sourceTree = "<group>"; };
|
||||
5550896F2A3289A100186885 /* RootOfTrustResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootOfTrustResolver.swift; sourceTree = "<group>"; };
|
||||
5557572E251BC575009979AB /* VCEntities.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = VCEntities.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
55575731251BC575009979AB /* VCEntities.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VCEntities.h; sourceTree = "<group>"; };
|
||||
55575732251BC575009979AB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
|
@ -462,6 +464,14 @@
|
|||
path = validators;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5550896E2A32895800186885 /* Resolvers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5550896F2A3289A100186885 /* RootOfTrustResolver.swift */,
|
||||
);
|
||||
path = Resolvers;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
55575724251BC575009979AB = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -485,6 +495,7 @@
|
|||
55575730251BC575009979AB /* VCEntities */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5550896E2A32895800186885 /* Resolvers */,
|
||||
2F848B6F282D5D25005D3176 /* backup */,
|
||||
55B4D2D42787BB600086A9F1 /* oidc */,
|
||||
5517D28A25B90D0A00FBD239 /* exchange */,
|
||||
|
@ -912,6 +923,7 @@
|
|||
55575766251BC6CF009979AB /* LogoDisplayDescriptor.swift in Sources */,
|
||||
55B4D35F27A330460086A9F1 /* RequestedVerifiableCredentialMapping.swift in Sources */,
|
||||
5557576F251BC6CF009979AB /* IdTokenDescriptor.swift in Sources */,
|
||||
555089702A3289A100186885 /* RootOfTrustResolver.swift in Sources */,
|
||||
55B4D2E4278F48370086A9F1 /* RequestedVPToken.swift in Sources */,
|
||||
551F3057252D02AB0081D5E7 /* TokenTimeConstraints.swift in Sources */,
|
||||
55B4D2DC2787BE040086A9F1 /* PresentationExchangeField.swift in Sources */,
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/// Allows a different way of resolving the root of trust (aka Linked Domain Result)
|
||||
/// that can be injected into Issuance and Presentation Service.
|
||||
public protocol RootOfTrustResolver {
|
||||
func resolve(did: String) async throws -> LinkedDomainResult
|
||||
}
|
|
@ -23,6 +23,7 @@
|
|||
5531D3AE255F1F360002CC0E /* IdentifierService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5531D3AD255F1F360002CC0E /* IdentifierService.swift */; };
|
||||
5531D3B0255F68280002CC0E /* PairwiseService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5531D3AF255F68280002CC0E /* PairwiseService.swift */; };
|
||||
553D4B0F281075C200FD39A6 /* MockVCSDKConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553D4B0E281075C200FD39A6 /* MockVCSDKConfiguration.swift */; };
|
||||
555089A82A3794D500186885 /* MockRootOfTrustResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 555089A72A3794D500186885 /* MockRootOfTrustResolver.swift */; };
|
||||
555BDAEC2530AEC7001E7A18 /* VerifiableCredentialDataModel.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 555BDAEA2530AEC7001E7A18 /* VerifiableCredentialDataModel.xcdatamodeld */; };
|
||||
555BDAEE2530CF27001E7A18 /* CoreDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 555BDAED2530CF27001E7A18 /* CoreDataManager.swift */; };
|
||||
555BDAF02530E4B9001E7A18 /* CoreDataManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 555BDAEF2530E4B9001E7A18 /* CoreDataManagerTests.swift */; };
|
||||
|
@ -96,6 +97,7 @@
|
|||
5531D3AD255F1F360002CC0E /* IdentifierService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentifierService.swift; sourceTree = "<group>"; };
|
||||
5531D3AF255F68280002CC0E /* PairwiseService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PairwiseService.swift; sourceTree = "<group>"; };
|
||||
553D4B0E281075C200FD39A6 /* MockVCSDKConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockVCSDKConfiguration.swift; sourceTree = "<group>"; };
|
||||
555089A72A3794D500186885 /* MockRootOfTrustResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRootOfTrustResolver.swift; sourceTree = "<group>"; };
|
||||
555BDAEB2530AEC7001E7A18 /* Identifier.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Identifier.xcdatamodel; sourceTree = "<group>"; };
|
||||
555BDAED2530CF27001E7A18 /* CoreDataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataManager.swift; sourceTree = "<group>"; };
|
||||
555BDAEF2530E4B9001E7A18 /* CoreDataManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataManagerTests.swift; sourceTree = "<group>"; };
|
||||
|
@ -253,6 +255,7 @@
|
|||
55AF748F252FBF5B006A8B25 /* MockVCCryptoSecret.swift */,
|
||||
553D4B0E281075C200FD39A6 /* MockVCSDKConfiguration.swift */,
|
||||
5525397D252FCC7D003202D5 /* SecretStoreMock.swift */,
|
||||
555089A72A3794D500186885 /* MockRootOfTrustResolver.swift */,
|
||||
);
|
||||
path = mocks;
|
||||
sourceTree = "<group>";
|
||||
|
@ -483,6 +486,7 @@
|
|||
5525397E252FCC7D003202D5 /* SecretStoreMock.swift in Sources */,
|
||||
55DA770A25BE469F009C32E0 /* MockPresentationRequestValidator.swift in Sources */,
|
||||
550F1E6825116E86009AF467 /* MockTokenSigner.swift in Sources */,
|
||||
555089A82A3794D500186885 /* MockRootOfTrustResolver.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
@ -26,6 +26,7 @@ public class IssuanceService {
|
|||
let sdkLog: VCSDKLog
|
||||
|
||||
public convenience init(correlationVector: CorrelationHeader? = nil,
|
||||
rootOfTrustResolver: RootOfTrustResolver? = nil,
|
||||
urlSession: URLSession = URLSession.shared) {
|
||||
self.init(formatter: IssuanceResponseFormatter(),
|
||||
apiCalls: IssuanceNetworkCalls(correlationVector: correlationVector,
|
||||
|
@ -35,6 +36,7 @@ public class IssuanceService {
|
|||
requestValidator: IssuanceRequestValidator(),
|
||||
identifierService: IdentifierService(),
|
||||
linkedDomainService: LinkedDomainService(correlationVector: correlationVector,
|
||||
rootOfTrustResolver: rootOfTrustResolver,
|
||||
urlSession: urlSession),
|
||||
pairwiseService: PairwiseService(correlationVector: correlationVector,
|
||||
urlSession: urlSession),
|
||||
|
@ -48,6 +50,7 @@ public class IssuanceService {
|
|||
identifierService: IdentifierService,
|
||||
linkedDomainService: LinkedDomainService,
|
||||
pairwiseService: PairwiseService,
|
||||
didVerificationResolver: RootOfTrustResolver? = nil,
|
||||
sdkLog: VCSDKLog = VCSDKLog.sharedInstance) {
|
||||
self.formatter = formatter
|
||||
self.apiCalls = apiCalls
|
||||
|
|
|
@ -7,32 +7,71 @@ import PromiseKit
|
|||
import VCNetworking
|
||||
import VCEntities
|
||||
|
||||
enum LinkedDomainServiceError: String, Error {
|
||||
case UndefinedResolver = "Root of Trust Resolver is undefined."
|
||||
}
|
||||
|
||||
class LinkedDomainService {
|
||||
|
||||
private let didDocumentDiscoveryApiCalls: DiscoveryNetworking
|
||||
private let wellKnownDocumentApiCalls: WellKnownConfigDocumentNetworking
|
||||
private let validator: DomainLinkageCredentialValidating
|
||||
|
||||
private let rootOfTrustResolver: RootOfTrustResolver?
|
||||
|
||||
public convenience init(correlationVector: CorrelationHeader? = nil,
|
||||
rootOfTrustResolver: RootOfTrustResolver? = nil,
|
||||
urlSession: URLSession = URLSession.shared) {
|
||||
self.init(didDocumentDiscoveryApiCalls: DIDDocumentNetworkCalls(correlationVector: correlationVector,
|
||||
urlSession: urlSession),
|
||||
wellKnownDocumentApiCalls: WellKnownConfigDocumentNetworkCalls(correlationVector: correlationVector,
|
||||
urlSession: urlSession),
|
||||
domainLinkageValidator: DomainLinkageCredentialValidator())
|
||||
domainLinkageValidator: DomainLinkageCredentialValidator(),
|
||||
rootOfTrustResolver: rootOfTrustResolver)
|
||||
}
|
||||
|
||||
init(didDocumentDiscoveryApiCalls: DiscoveryNetworking,
|
||||
wellKnownDocumentApiCalls: WellKnownConfigDocumentNetworking,
|
||||
domainLinkageValidator: DomainLinkageCredentialValidating) {
|
||||
domainLinkageValidator: DomainLinkageCredentialValidating,
|
||||
rootOfTrustResolver: RootOfTrustResolver? = nil) {
|
||||
self.didDocumentDiscoveryApiCalls = didDocumentDiscoveryApiCalls
|
||||
self.wellKnownDocumentApiCalls = wellKnownDocumentApiCalls
|
||||
self.validator = domainLinkageValidator
|
||||
self.rootOfTrustResolver = rootOfTrustResolver
|
||||
}
|
||||
|
||||
/// Validate Linked Domain using injected Resolver. If error is thrown, validate using well-known configuration path.
|
||||
func validateLinkedDomain(from relyingPartyDid: String) -> Promise<LinkedDomainResult> {
|
||||
return validateLinkedDomainUsingResolver(did: relyingPartyDid)
|
||||
.recover { error in
|
||||
return self.validateLinkedDomainUsingWellknownDocument(did: relyingPartyDid)
|
||||
}
|
||||
}
|
||||
|
||||
private func validateLinkedDomainUsingResolver(did: String) -> Promise<LinkedDomainResult> {
|
||||
|
||||
guard let rootOfTrustResolver = rootOfTrustResolver else {
|
||||
VCSDKLog.sharedInstance.logInfo(message: "DID Verification Resolver is undefined, fetching Linked Domain Status from well-known DID configuration.")
|
||||
return Promise<LinkedDomainResult>(error: LinkedDomainServiceError.UndefinedResolver)
|
||||
}
|
||||
|
||||
/// Return a Promise instead of using built-in async functionality to fix async compatibility.
|
||||
return Promise { seal in
|
||||
Task {
|
||||
do {
|
||||
let result = try await rootOfTrustResolver.resolve(did: did)
|
||||
seal.fulfill(result)
|
||||
} catch {
|
||||
VCSDKLog.sharedInstance.logInfo(message: "DID Verification Resolver failed, fetching Linked Domain Status from well-known DID configuration.")
|
||||
seal.reject(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func validateLinkedDomainUsingWellknownDocument(did: String) -> Promise<LinkedDomainResult> {
|
||||
return firstly {
|
||||
getDidDocument(from: relyingPartyDid)
|
||||
getDidDocument(from: did)
|
||||
}.then { identifierDocument in
|
||||
self.validateDomain(from: identifierDocument)
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ public class PresentationService {
|
|||
let sdkLog: VCSDKLog
|
||||
|
||||
public convenience init(correlationVector: CorrelationHeader? = nil,
|
||||
rootOfTrustResolver: RootOfTrustResolver? = nil,
|
||||
urlSession: URLSession = URLSession.shared) {
|
||||
self.init(formatter: PresentationResponseFormatter(),
|
||||
presentationApiCalls: PresentationNetworkCalls(correlationVector: correlationVector,
|
||||
|
@ -40,6 +41,7 @@ public class PresentationService {
|
|||
urlSession: urlSession),
|
||||
requestValidator: PresentationRequestValidator(),
|
||||
linkedDomainService: LinkedDomainService(correlationVector: correlationVector,
|
||||
rootOfTrustResolver: rootOfTrustResolver,
|
||||
urlSession: urlSession),
|
||||
identifierService: IdentifierService(),
|
||||
pairwiseService: PairwiseService(correlationVector: correlationVector,
|
||||
|
|
|
@ -22,6 +22,33 @@ class LinkedDomainServiceTests: XCTestCase {
|
|||
MockDomainLinkageCredentialValidator.wasValidateCalled = false
|
||||
}
|
||||
|
||||
func testValidate_WithResolver_ReturnsResult() {
|
||||
|
||||
// Arrange
|
||||
let expectedDomainUrl = "expected domain"
|
||||
func mockResolve(did: String) throws -> LinkedDomainResult {
|
||||
return .linkedDomainVerified(domainUrl: expectedDomainUrl)
|
||||
}
|
||||
|
||||
let mockRootOfTrustResolver = MockRootOfTrustResolver(mockResolve: mockResolve)
|
||||
service = setUpService(rootOfTrustResolver: mockRootOfTrustResolver)
|
||||
|
||||
let expec = self.expectation(description: "Fire")
|
||||
|
||||
// Act
|
||||
service.validateLinkedDomain(from: validDid).done {
|
||||
result in
|
||||
// Assert
|
||||
XCTAssertEqual(result, .linkedDomainVerified(domainUrl: expectedDomainUrl))
|
||||
expec.fulfill()
|
||||
}.catch { error in
|
||||
XCTFail()
|
||||
expec.fulfill()
|
||||
}
|
||||
|
||||
wait(for: [expec], timeout: 5)
|
||||
}
|
||||
|
||||
func testLinkedDomainVerifiedResult() {
|
||||
|
||||
let expec = self.expectation(description: "Fire")
|
||||
|
@ -78,7 +105,8 @@ class LinkedDomainServiceTests: XCTestCase {
|
|||
}
|
||||
|
||||
private func setUpService(serviceEndpointType: String = Constants.LINKED_DOMAINS_SERVICE_ENDPOINT_TYPE,
|
||||
isValid: Bool = true) -> LinkedDomainService {
|
||||
isValid: Bool = true,
|
||||
rootOfTrustResolver: RootOfTrustResolver? = nil) -> LinkedDomainService {
|
||||
|
||||
let endpoint = IdentifierDocumentServiceEndpoint(origins: [mockDomainUrl])
|
||||
|
||||
|
@ -97,8 +125,9 @@ class LinkedDomainServiceTests: XCTestCase {
|
|||
let validator = MockDomainLinkageCredentialValidator(isValid: isValid)
|
||||
|
||||
return LinkedDomainService(didDocumentDiscoveryApiCalls: discoveryApiCalls,
|
||||
wellKnownDocumentApiCalls: wellKnownApiCalls,
|
||||
domainLinkageValidator: validator)
|
||||
wellKnownDocumentApiCalls: wellKnownApiCalls,
|
||||
domainLinkageValidator: validator,
|
||||
rootOfTrustResolver: rootOfTrustResolver)
|
||||
}
|
||||
|
||||
let validDid = "did:ion:EiD89iut8PMhRz5zSlPl_zkHgEfRBiA9zdXYwAdrk1_JYg:eyJkZWx0YSI6eyJwYXRjaGVzIjpbeyJhY3Rpb24iOiJyZXBsYWNlIiwiZG9jdW1lbnQiOnsicHVibGljX2tleXMiOlt7ImlkIjoic2lnX2FkODRkZjIzIiwiandrIjp7ImNydiI6InNlY3AyNTZrMSIsImt0eSI6IkVDIiwieCI6IllMM2xlNC12YlNsa2ZINnJoQWJlbEJXTDByVlBieVJYTGtIUmx3REFZOTQiLCJ5IjoiYWFDZG1idjF0R1htZU1QWUdUY0ZqLUFnc2d2c2RNLXlGR2tnVU1QX04tVSJ9LCJwdXJwb3NlIjpbImF1dGgiLCJnZW5lcmFsIl0sInR5cGUiOiJFY2RzYVNlY3AyNTZrMVZlcmlmaWNhdGlvbktleTIwMTkifV0sInNlcnZpY2VfZW5kcG9pbnRzIjpbeyJlbmRwb2ludCI6eyJvcmlnaW5zIjpbImh0dHBzOi8vY29udG9zb3VuaXZlcnNpdHktZGlkZGVtby5henVyZXdlYnNpdGVzLm5ldC8iXX0sImlkIjoibGlua2VkZG9tYWlucyIsInR5cGUiOiJMaW5rZWREb21haW5zIn1dfX1dLCJ1cGRhdGVfY29tbWl0bWVudCI6IkVpQS1HSFJRVkcxV2F0bGprSVQxOGxvbWZXbTY0N2QwYmtlRmpCR2tMUjZMRXcifSwic3VmZml4X2RhdGEiOnsiZGVsdGFfaGFzaCI6IkVpQXBta0dpa2RiX3pHb1JrVU5vbmd0TXJlZS1xTjhPeEtsVmJvYUk5ZG9DdXciLCJyZWNvdmVyeV9jb21taXRtZW50IjoiRWlCb19vVkJYSEJmZGhfRFpkZjlqU3ZwQ1lnRmNkcXc3cDZFa1pPanU3bUREQSJ9fQ"
|
||||
|
|
|
@ -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 XCTest
|
||||
import VCEntities
|
||||
|
||||
struct MockRootOfTrustResolver: RootOfTrustResolver {
|
||||
|
||||
let mockResolve: (String) throws -> LinkedDomainResult
|
||||
|
||||
init(mockResolve: @escaping (String) throws -> LinkedDomainResult) {
|
||||
self.mockResolve = mockResolve
|
||||
}
|
||||
|
||||
func resolve(did: String) async throws -> LinkedDomainResult {
|
||||
return try mockResolve(did)
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче