diff --git a/E2E/e2eTests/Source/Abilities/OpenEnterpriseAPI.swift b/E2E/e2eTests/Source/Abilities/OpenEnterpriseAPI.swift index edfc2131..917ddc52 100644 --- a/E2E/e2eTests/Source/Abilities/OpenEnterpriseAPI.swift +++ b/E2E/e2eTests/Source/Abilities/OpenEnterpriseAPI.swift @@ -4,468 +4,474 @@ import OpenAPIURLSession import HTTPTypes class OpenEnterpriseAPI: Ability { - typealias AbilityInstanceType = API - private var api: AbilityInstanceType? = nil - - let actor: Actor + lazy var actor: Actor = { + return actor + }() let abilityName: String = "OEA API" + var isInitialized: Bool = true - required init(_ actor: Actor) { - self.actor = actor - } + private lazy var transport: URLSessionTransport = { return transport }() + private lazy var client: Client = { return client }() - func instance() -> AbilityInstanceType { - return api! - } + required init() {} func setUp(_ actor: Actor) async throws { - api = API(StepReporterMiddleware(actor.name)) + self.actor = actor + createClient(StepReporterMiddleware(actor.name)) + } + + func createClient(_ middlewares: ClientMiddleware...) { + transport = URLSessionTransport() + client = Client( + serverURL: URL(string: Config.agentUrl)!, + configuration: .init(dateTranscoder: MyDateTranscoder()), + transport: transport, + middlewares: [APITokenMiddleware(apikey: Config.apiKey)] + middlewares + ) + + isInitialized = true } func tearDown() async throws { } - class API { - private var transport: URLSessionTransport? = nil - private var client: Client? = nil - - init(_ middlewares: ClientMiddleware...) { - transport = URLSessionTransport() - client = Client( - serverURL: URL(string: Config.agentUrl)!, - configuration: .init(dateTranscoder: MyDateTranscoder()), - transport: transport!, - middlewares: [APITokenMiddleware(apikey: Config.apiKey)] + middlewares - ) + func isDidPresent(_ did: String) async throws -> Bool { + if (did.isEmpty) { + return false } - func isDidPresent(_ did: String) async throws -> Bool { - if (did.isEmpty) { - return false - } - - do { - _ = try await getDid(did) - return true - } catch { - return false - } + do { + _ = try await getDid(did) + return true + } catch { + return false } - - func getDid(_ did: String) async throws -> Components.Schemas.ManagedDID { - let response = try await client!.getDid_hyphen_registrarDidsDidref(.init(path: .init(didRef: did))) - switch(response) { - case .ok(let okResponse): - switch(okResponse.body) { - case .json(let body): - return body - } - default: - throw Error.WrongResponse(response) + } + + func getDid(_ did: String) async throws -> Components.Schemas.ManagedDID { + let response = try await client.getDid_hyphen_registrarDidsDidref(.init(path: .init(didRef: did))) + switch(response) { + case .ok(let okResponse): + switch(okResponse.body) { + case .json(let body): + return body } + default: + throw Error.WrongResponse(response) } - - func createUnpublishedDid() async throws -> Components.Schemas.CreateManagedDIDResponse { - let createManagedDidRequest = Components.Schemas.CreateManagedDidRequest( - documentTemplate: .init( - publicKeys: .init( - arrayLiteral: .init( - id: "key-1", - purpose: .assertionMethod - ) - ), - services: [] - ) + } + + func createUnpublishedDid() async throws -> Components.Schemas.CreateManagedDIDResponse { + let createManagedDidRequest = Components.Schemas.CreateManagedDidRequest( + documentTemplate: .init( + publicKeys: .init( + arrayLiteral: .init( + id: "key-1", + purpose: .assertionMethod + ) + ), + services: [] ) - - let response = try await client!.postDid_hyphen_registrarDids(body: .json(createManagedDidRequest)) - - switch(response) { - case .created(let createdResponse): - switch(createdResponse.body) { - case .json(let body): - return body - } - default: - throw Error.WrongResponse(response) + ) + + let response = try await client.postDid_hyphen_registrarDids(body: .json(createManagedDidRequest)) + + switch(response) { + case .created(let createdResponse): + switch(createdResponse.body) { + case .json(let body): + return body } + default: + throw Error.WrongResponse(response) } - - func publishDid(_ longFormDid: String) async throws -> Components.Schemas.DIDOperationResponse { - let response = try await client!.postDid_hyphen_registrarDidsDidrefPublications(path: .init(didRef: longFormDid)) - switch (response) { - case .accepted(let acceptedResponse): - switch(acceptedResponse.body) { - case .json(let body): - return body - } - default: - throw Error.WrongResponse(response) + } + + func publishDid(_ longFormDid: String) async throws -> Components.Schemas.DIDOperationResponse { + let response = try await client.postDid_hyphen_registrarDidsDidrefPublications(path: .init(didRef: longFormDid)) + switch (response) { + case .accepted(let acceptedResponse): + switch(acceptedResponse.body) { + case .json(let body): + return body } + default: + throw Error.WrongResponse(response) + } + } + + func isJwtSchemaGuidPresent(_ guid: String) async throws -> Bool { + if (guid.isEmpty) { + return false } - func isJwtSchemaGuidPresent(_ guid: String) async throws -> Bool { - if (guid.isEmpty) { - return false - } - - do { - _ = try await getJwtSchema(guid) - return true - } catch { - return false - } + do { + _ = try await getJwtSchema(guid) + return true + } catch { + return false } + } + + func getJwtSchema(_ guid: String) async throws -> Components.Schemas.CredentialSchemaResponse { + let response = try await client.getSchemaById(path: .init(guid: guid)) - func getJwtSchema(_ guid: String) async throws -> Components.Schemas.CredentialSchemaResponse { - let response = try await client!.getSchemaById(path: .init(guid: guid)) - - switch(response) { - case .ok(let okResponse): - switch(okResponse.body) { - case .json(let body): - return body - } - default: - throw Error.WrongResponse(response) + switch(response) { + case .ok(let okResponse): + switch(okResponse.body) { + case .json(let body): + return body } + default: + throw Error.WrongResponse(response) } - - func createJwtSchema(author: String) async throws -> Components.Schemas.CredentialSchemaResponse { - let schemaName = "automation-jwt-schema-" + UUID().uuidString - var schema = try OpenAPIValueContainer() - schema.value = [ - "$id": "https://example.com/\(schemaName)", - "$schema": "https://json-schema.org/draft/2020-12/schema", - "description": "Automation schema description", - "type": "object", - "properties": [ - "automation-required": [ - "type": "string" - ], - "automation-optional": [ - "type": "string" - ] + } + + func createJwtSchema(author: String) async throws -> Components.Schemas.CredentialSchemaResponse { + let schemaName = "automation-jwt-schema-" + UUID().uuidString + var schema = try OpenAPIValueContainer() + schema.value = [ + "$id": "https://example.com/\(schemaName)", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "Automation schema description", + "type": "object", + "properties": [ + "automation-required": [ + "type": "string" + ], + "automation-optional": [ + "type": "string" ] ] - - let credentialSchemaInput = Components.Schemas.CredentialSchemaInput( - name: schemaName, - version: "0.0.1", - description: "Some description to automation generated schema", - _type: "https://w3c-ccg.github.io/vc-json-schemas/schema/2.0/schema.json", - schema: schema, - tags: ["automation"], - author: author - ) - - let response = try await client!.createSchema(body: .json(credentialSchemaInput)) - switch (response) { - case .created(let createdResponse): - switch (createdResponse.body) { - case .json(let body): - return body - } - default: - throw Error.WrongResponse(response) + ] + + let credentialSchemaInput = Components.Schemas.CredentialSchemaInput( + name: schemaName, + version: "0.0.1", + description: "Some description to automation generated schema", + _type: "https://w3c-ccg.github.io/vc-json-schemas/schema/2.0/schema.json", + schema: schema, + tags: ["automation"], + author: author + ) + + let response = try await client.createSchema(body: .json(credentialSchemaInput)) + switch (response) { + case .created(let createdResponse): + switch (createdResponse.body) { + case .json(let body): + return body } + default: + throw Error.WrongResponse(response) + } + } + + func isAnoncredDefinitionPresent(_ anoncredDefinitionGuid: String) async throws -> Bool { + if (anoncredDefinitionGuid.isEmpty) { + return false } - func isAnoncredDefinitionPresent(_ anoncredDefinitionGuid: String) async throws -> Bool { - if (anoncredDefinitionGuid.isEmpty) { - return false - } - - do { - _ = try await getAnoncredDefinition(anoncredDefinitionGuid) - return true - } catch { - return false - } + do { + _ = try await getAnoncredDefinition(anoncredDefinitionGuid) + return true + } catch { + return false } + } + + func getAnoncredDefinition(_ anoncredDefinitionGuid: String) async throws -> Components.Schemas.CredentialDefinitionResponse { + let response = try await client.getCredentialDefinitionById(path: .init(guid: anoncredDefinitionGuid)) - func getAnoncredDefinition(_ anoncredDefinitionGuid: String) async throws -> Components.Schemas.CredentialDefinitionResponse { - let response = try await client!.getCredentialDefinitionById(path: .init(guid: anoncredDefinitionGuid)) - - switch(response) { - case .ok(let okResponse): - switch(okResponse.body){ - case .json(let body): - return body - } - default: - throw Error.WrongResponse(response) + switch(response) { + case .ok(let okResponse): + switch(okResponse.body){ + case .json(let body): + return body } + default: + throw Error.WrongResponse(response) } + } + + func createAnoncredSchema(_ issuerId: String) async throws -> Components.Schemas.CredentialSchemaResponse { + let schemaName = "automation-anoncred-schema-" + UUID().uuidString - func createAnoncredSchema(_ issuerId: String) async throws -> Components.Schemas.CredentialSchemaResponse { - let schemaName = "automation-anoncred-schema-" + UUID().uuidString - - var schema = try OpenAPIValueContainer() - schema.value = [ - "name": "Automation Anoncred", - "version": "1.0", - "issuerId": issuerId, - "attrNames": [ - "name", - "age", - "gender" - ] + var schema = try OpenAPIValueContainer() + schema.value = [ + "name": "Automation Anoncred", + "version": "1.0", + "issuerId": issuerId, + "attrNames": [ + "name", + "age", + "gender" ] - - let credentialSchemaInput = Components.Schemas.CredentialSchemaInput( - name: schemaName, - version: "2.0.0", - description: "Some description to automation generated schema", - _type: "AnoncredSchemaV1", - schema: schema, - tags: ["automation"], - author: issuerId - ) - - let response = try await client!.createSchema(body: .json(credentialSchemaInput)) - switch (response) { - case .created(let createdResponse): - switch (createdResponse.body) { - case .json(let body): - return body - } - default: - throw Error.WrongResponse(response) + ] + + let credentialSchemaInput = Components.Schemas.CredentialSchemaInput( + name: schemaName, + version: "2.0.0", + description: "Some description to automation generated schema", + _type: "AnoncredSchemaV1", + schema: schema, + tags: ["automation"], + author: issuerId + ) + + let response = try await client.createSchema(body: .json(credentialSchemaInput)) + switch (response) { + case .created(let createdResponse): + switch (createdResponse.body) { + case .json(let body): + return body } + default: + throw Error.WrongResponse(response) } + } + + func createAnoncredDefinition(_ issuerId: String, _ anoncredSchemaGuid: String) async throws -> Components.Schemas.CredentialDefinitionResponse { + let definitionName = "automation-anoncred-definition-" + UUID().uuidString + let anoncredDefinition = Components.Schemas.CredentialDefinitionInput.init( + name: definitionName, + description: "Test Automation Auto-Generated", + version: "1.0.0", + tag: "automation-test", + author: issuerId, + schemaId: "\(Config.agentUrl)/schema-registry/schemas/\(anoncredSchemaGuid)/schema", + signatureType: "CL", + supportRevocation: false + ) - func createAnoncredDefinition(_ issuerId: String, _ anoncredSchemaGuid: String) async throws -> Components.Schemas.CredentialDefinitionResponse { - let definitionName = "automation-anoncred-definition-" + UUID().uuidString - let anoncredDefinition = Components.Schemas.CredentialDefinitionInput.init( - name: definitionName, - description: "Test Automation Auto-Generated", - version: "1.0.0", - tag: "automation-test", - author: issuerId, - schemaId: "\(Config.agentUrl)/schema-registry/schemas/\(anoncredSchemaGuid)/schema", - signatureType: "CL", - supportRevocation: false - ) - - let response = try await client!.createCredentialDefinition(body: .json(anoncredDefinition)) - switch(response) { - case .created(let createdResponse): - switch(createdResponse.body) { - case .json(let body): - return body - } - default: - throw Error.WrongResponse(response) + let response = try await client.createCredentialDefinition(body: .json(anoncredDefinition)) + switch(response) { + case .created(let createdResponse): + switch(createdResponse.body) { + case .json(let body): + return body } + default: + throw Error.WrongResponse(response) } + } + + func getConnections() async throws -> Components.Schemas.ConnectionsPage { + let response = try await client.getConnections(.init()) - func getConnections() async throws -> Components.Schemas.ConnectionsPage { - let response = try await client!.getConnections(.init()) - - switch(response) { - case .ok(let okResponse): - switch(okResponse.body) { - case .json(let response): - return response - } - default: - throw Error.WrongResponse(response) + switch(response) { + case .ok(let okResponse): + switch(okResponse.body) { + case .json(let response): + return response } + default: + throw Error.WrongResponse(response) } + } + + func getConnection(_ connectionId: String) async throws -> Components.Schemas.Connection { + let response = try await client.getConnection(path: .init(connectionId: connectionId)) - func getConnection(_ connectionId: String) async throws -> Components.Schemas.Connection { - let response = try await client!.getConnection(path: .init(connectionId: connectionId)) - - switch(response) { - case .ok(let okResponse): - switch(okResponse.body) { - case .json(let response): - return response - } - default: - throw Error.WrongResponse(response) + switch(response) { + case .ok(let okResponse): + switch(okResponse.body) { + case .json(let response): + return response } + default: + throw Error.WrongResponse(response) } + } + + func createConnection() async throws -> Components.Schemas.Connection { + let body = Components.Schemas.CreateConnectionRequest(label: "Alice") + let response = try await client.createConnection(.init(body: .json(body))) - func createConnection() async throws -> Components.Schemas.Connection { - let body = Components.Schemas.CreateConnectionRequest(label: "Alice") - let response = try await client!.createConnection(.init(body: .json(body))) - - switch(response) { - case .created(let okResponse): - switch(okResponse.body) { - case .json(let body): - return body - } - default: - throw Error.WrongResponse(response) + switch(response) { + case .created(let okResponse): + switch(okResponse.body) { + case .json(let body): + return body } + default: + throw Error.WrongResponse(response) } + } + + func offerCredential(_ connectionId: String) async throws -> Components.Schemas.IssueCredentialRecord { + var claims: OpenAPIValueContainer = try OpenAPIValueContainer() + claims.value = [ + "automation-required" : UUID().uuidString + ] - func offerCredential(_ connectionId: String) async throws -> Components.Schemas.IssueCredentialRecord { - var claims: OpenAPIValueContainer = try OpenAPIValueContainer() - claims.value = [ - "automation-required" : UUID().uuidString - ] - - let body = Components.Schemas.CreateIssueCredentialRecordRequest( - schemaId: "\(Config.agentUrl)/schema-registry/schemas/\(Config.jwtSchemaGuid)", - claims: claims, - issuingDID: Config.publishedDid, - connectionId: connectionId - ) - - let response = try await client!.createCredentialOffer(body: .json(body)) - switch(response) { - case .created(let createdResponse): - switch(createdResponse.body){ - case .json(let body): - return body - } - default: - throw Error.WrongResponse(response) + let body = Components.Schemas.CreateIssueCredentialRecordRequest( + schemaId: "\(Config.agentUrl)/schema-registry/schemas/\(Config.jwtSchemaGuid)", + claims: claims, + issuingDID: Config.publishedDid, + connectionId: connectionId + ) + + let response = try await client.createCredentialOffer(body: .json(body)) + switch(response) { + case .created(let createdResponse): + switch(createdResponse.body){ + case .json(let body): + return body } + default: + throw Error.WrongResponse(response) } + } + + func offerAnonymousCredential(_ connectionId: String) async throws -> Components.Schemas.IssueCredentialRecord { + var claims: OpenAPIValueContainer = try OpenAPIValueContainer() + claims.value = [ + "name" : "automation", + "age" : "99", + "gender": "M" + ] - func offerAnonymousCredential(_ connectionId: String) async throws -> Components.Schemas.IssueCredentialRecord { - var claims: OpenAPIValueContainer = try OpenAPIValueContainer() - claims.value = [ - "name" : "automation", - "age" : "99", - "gender": "M" - ] - - let body = Components.Schemas.CreateIssueCredentialRecordRequest( - credentialDefinitionId: Config.anoncredDefinitionGuid, - credentialFormat: "AnonCreds", - claims: claims, - automaticIssuance: true, - issuingDID: Config.publishedDid, - connectionId: connectionId - ) - - let response = try await client!.createCredentialOffer(body: .json(body)) - switch(response) { - case .created(let createdResponse): - switch(createdResponse.body){ - case .json(let body): - return body - } - default: - throw Error.WrongResponse(response) + let body = Components.Schemas.CreateIssueCredentialRecordRequest( + credentialDefinitionId: Config.anoncredDefinitionGuid, + credentialFormat: "AnonCreds", + claims: claims, + automaticIssuance: true, + issuingDID: Config.publishedDid, + connectionId: connectionId + ) + + let response = try await client.createCredentialOffer(body: .json(body)) + switch(response) { + case .created(let createdResponse): + switch(createdResponse.body){ + case .json(let body): + return body } + default: + throw Error.WrongResponse(response) } - - func getCredentialRecord(_ recordId: String) async throws -> Components.Schemas.IssueCredentialRecord { - let response = try await client!.getCredentialRecord(path: .init(recordId: recordId)) - switch(response){ - case .ok(let okResponse): - switch(okResponse.body){ - case .json(let body): - return body - } - default: - throw Error.WrongResponse(response) + } + + func getCredentialRecord(_ recordId: String) async throws -> Components.Schemas.IssueCredentialRecord { + let response = try await client.getCredentialRecord(path: .init(recordId: recordId)) + switch(response){ + case .ok(let okResponse): + switch(okResponse.body){ + case .json(let body): + return body } + default: + throw Error.WrongResponse(response) } + } + + func requestPresentProof(_ connectionId: String) async throws -> Components.Schemas.PresentationStatus { + let options = Components.Schemas.Options( + challenge: UUID().uuidString, + domain: Config.agentUrl + ) - func requestPresentProof(_ connectionId: String) async throws -> Components.Schemas.PresentationStatus { - let options = Components.Schemas.Options( - challenge: UUID().uuidString, - domain: Config.agentUrl - ) - - let proof = Components.Schemas.ProofRequestAux( - schemaId: Config.jwtSchemaGuid, - trustIssuers: [] - ) - - let body = Components.Schemas.RequestPresentationInput( - connectionId: connectionId, - options: options, - proofs: [proof], - credentialFormat: "JWT" - ) - - let response = try await client!.requestPresentation(body: .json(body)) - - switch(response){ - case .created(let createdResponse): - switch(createdResponse.body){ - case .json(let body): - return body - } - default: - throw Error.WrongResponse(response) + let proof = Components.Schemas.ProofRequestAux( + schemaId: Config.jwtSchemaGuid, + trustIssuers: [] + ) + + let body = Components.Schemas.RequestPresentationInput( + connectionId: connectionId, + options: options, + proofs: [proof], + credentialFormat: "JWT" + ) + + let response = try await client.requestPresentation(body: .json(body)) + + switch(response){ + case .created(let createdResponse): + switch(createdResponse.body){ + case .json(let body): + return body } + default: + throw Error.WrongResponse(response) } + } + + func requestAnonymousPresentProof(_ connectionId: String) async throws -> Components.Schemas.PresentationStatus { + let credentialDefinitionUrl = Config.agentUrl + "/credential-definition-registry/definitions/" + Config.anoncredDefinitionGuid + "/definition" + let anoncredPresentationRequest = Components.Schemas.AnoncredPresentationRequestV1( + requested_attributes: .init(additionalProperties: [ + "gender": .init( + name: "gender", + restrictions: [ + .init(additionalProperties: [ + "attr::gender::value": "M", + "cred_def_id": credentialDefinitionUrl + ]) + ] + ) + ]), + requested_predicates: .init(additionalProperties: [ + "age": .init( + name: "age", + p_type: ">", + p_value: 18, + restrictions: [] + ) + ]), + name: "proof_req_1", + nonce: Utils.generateNonce(length: 25), + version: "1.0" + ) - func requestAnonymousPresentProof(_ connectionId: String) async throws -> Components.Schemas.PresentationStatus { - let credentialDefinitionUrl = Config.agentUrl + "/credential-definition-registry/definitions/" + Config.anoncredDefinitionGuid + "/definition" - let anoncredPresentationRequest = Components.Schemas.AnoncredPresentationRequestV1( - requested_attributes: .init(additionalProperties: [ - "gender": .init( - name: "gender", - restrictions: [ - .init(additionalProperties: [ - "attr::gender::value": "M", - "cred_def_id": credentialDefinitionUrl - ]) - ] - ) - ]), - requested_predicates: .init(additionalProperties: [ - "age": .init( - name: "age", - p_type: ">", - p_value: 18, - restrictions: [] - ) - ]), - name: "proof_req_1", - nonce: Utils.generateNonce(length: 25), - version: "1.0" - ) - - let body = Components.Schemas.RequestPresentationInput( - connectionId: connectionId, - options: nil, - proofs: [], - anoncredPresentationRequest: anoncredPresentationRequest, - credentialFormat: "AnonCreds" - ) - - let response = try await client!.requestPresentation(body: .json(body)) - - switch(response){ - case .created(let createdResponse): - switch(createdResponse.body){ - case .json(let body): - return body - } - default: - throw Error.WrongResponse(response) + let body = Components.Schemas.RequestPresentationInput( + connectionId: connectionId, + options: nil, + proofs: [], + anoncredPresentationRequest: anoncredPresentationRequest, + credentialFormat: "AnonCreds" + ) + + let response = try await client.requestPresentation(body: .json(body)) + + switch(response){ + case .created(let createdResponse): + switch(createdResponse.body){ + case .json(let body): + return body } + default: + throw Error.WrongResponse(response) } - - func getPresentation(_ presentationId: String) async throws -> Components.Schemas.PresentationStatus { - let response = try await client!.getPresentation(path: .init(presentationId: presentationId)) - switch(response){ - case .ok(let okResponse): - switch(okResponse.body){ - case .json(let body): - return body - } - default: - throw Error.WrongResponse(response) + } + + func getPresentation(_ presentationId: String) async throws -> Components.Schemas.PresentationStatus { + let response = try await client.getPresentation(path: .init(presentationId: presentationId)) + switch(response){ + case .ok(let okResponse): + switch(okResponse.body){ + case .json(let body): + return body } + default: + throw Error.WrongResponse(response) } - - enum Error: Swift.Error { - case WrongResponse(_ response: Any) + } + + func revokeCredential(_ recordId: String) async throws -> Int { + let response = try await client.patchCredential_hyphen_statusRevoke_hyphen_credentialId(path: .init(id: recordId)) + switch(response) { + case .ok(let okResponse): + return 200 + default: + throw Error.WrongResponse(response) } } + + enum Error: Swift.Error { + case WrongResponse(_ response: Any) + } + } // https://github.com/apple/swift-openapi-generator/issues/84 diff --git a/E2E/e2eTests/Source/Abilities/Sdk.swift b/E2E/e2eTests/Source/Abilities/Sdk.swift deleted file mode 100644 index 599c40ff..00000000 --- a/E2E/e2eTests/Source/Abilities/Sdk.swift +++ /dev/null @@ -1,209 +0,0 @@ -import Foundation -import EdgeAgent -import Builders -import Combine -import Domain -import Pluto -import Core - -class Sdk: Ability { - typealias AbilityInstanceType = Client - let actor: Actor - let abilityName: String = "Swift SDK" - private var client: Client? = nil - - required init(_ actor: Actor) { - self.actor = actor - } - - func instance() -> AbilityInstanceType { - return client! - } - - func setUp(_ actor: Actor) async throws { - client = try await Client() - try await client!.initialize() - } - - func tearDown() async throws { - try await client?.tearDown() - } - - class Client { - var credentialOfferStack: [Message] = [] - var issueCredentialStack: [Message] = [] - var proofOfRequestStack: [Message] = [] - var receivedMessages: [String] = [] - var cancellables = Set() - - let edgeAgent: EdgeAgent - - init() async throws { - let mediatorDID = try await Client.getPrismMediatorDid() -// let mediatorDID = try await Client.getRootsMediatorDid() - - let apollo = ApolloBuilder().build() - let castor = CastorBuilder(apollo: apollo).build() - let pluto = PlutoBuilder(setup: .init( - coreDataSetup: .init( - modelPath: .storeName("PrismPluto"), - storeType: .memory - ), - keychain: KeychainMock() - )).build() - let pollux = PolluxBuilder(pluto: pluto, castor: castor).build() - let mercury = MercuryBuilder( - castor: castor, - secretsStream: Client.createSecretsStream( - keyRestoration: apollo, - pluto: pluto, - castor: castor - ) - ).build() - - EdgeAgent.setupLogging(logLevels: [ - .edgeAgent: .info - ]) - - edgeAgent = EdgeAgent( - apollo: apollo, - castor: castor, - pluto: pluto, - pollux: pollux, - mercury: mercury, - mediationHandler: BasicMediatorHandler( - mediatorDID: mediatorDID, - mercury: mercury, - store: BasicMediatorHandler.PlutoMediatorStoreImpl(pluto: pluto) - ) - ) - } - - func initialize() async throws { - try await edgeAgent.start() - - edgeAgent.handleReceivedMessagesEvents() - .sink( - receiveCompletion: { completion in - switch completion { - case .finished: - print("Publisher completed successfully.") - case .failure(let error): - print("Publisher failed with error: \(error)") - } - }, - receiveValue: { message in - if (self.receivedMessages.contains(message.id)) { - return - } - self.receivedMessages.append(message.id) - switch(message.piuri) { - case ProtocolTypes.didcommOfferCredential3_0.rawValue: - self.credentialOfferStack.append(message) - case ProtocolTypes.didcommIssueCredential3_0.rawValue: - self.issueCredentialStack.append(message) - case ProtocolTypes.didcommRequestPresentation.rawValue: - self.proofOfRequestStack.append(message) - default: - break - } - } - ) - .store(in: &cancellables) - - edgeAgent.startFetchingMessages() - } - - func tearDown() async throws { - edgeAgent.stopFetchingMessages() - try await edgeAgent.stop() - } - - static private func getPrismMediatorDid() async throws -> DID { - let url = URL(string: Config.mediatorOobUrl)! - let jsonData: [String: Any] = try await Api.get(from: url) - let did = (jsonData["from"] as? String)! - return try DID(string: did) - } - - static private func getRootsMediatorDid() async throws -> DID { - let url = URL(string: Config.mediatorOobUrl)! - let invitationUrl: String = try await Api.get(from: url) - let base64data: String = String(invitationUrl.split(separator: "?_oob=").last!) - let decodedData = Data(base64Encoded: base64data)! - let json = try (JSONSerialization.jsonObject(with: decodedData, options: []) as? [String: Any])! - let from = (json["from"] as? String)! - return try DID(string: from) - } - - private static func fromBase64(_ encoded: String) -> Data { - var encoded = encoded; - let remainder = encoded.count % 4 - if remainder > 0 { - encoded = encoded.padding( - toLength: encoded.count + 4 - remainder, - withPad: "=", startingAt: 0); - } - return Data(base64Encoded: encoded)! - // return String(data: data, encoding: .utf8)! - } - - static private func createSecretsStream( - keyRestoration: KeyRestoration, - pluto: Pluto, - castor: Castor - ) -> AnyPublisher<[Secret], Error> { - pluto.getAllKeys() - .first() - .flatMap { keys in - Future { - let privateKeys = await keys.asyncMap { - try? await keyRestoration.restorePrivateKey($0) - }.compactMap { $0 } - return try parsePrivateKeys( - privateKeys: privateKeys, - castor: castor - ) - } - } - .eraseToAnyPublisher() - } - - static private func parsePrivateKeys( - privateKeys: [PrivateKey], - castor: Castor - ) throws -> [Domain.Secret] { - return try privateKeys - .map { $0 as? (PrivateKey & ExportableKey & StorableKey) } - .compactMap { $0 } - .map { privateKey in - return privateKey - } - .map { privateKey in - try parseToSecret( - privateKey: privateKey, - identifier: privateKey.identifier - ) - } - } - - static private func parseToSecret( - privateKey: PrivateKey & ExportableKey, - identifier: String - ) throws -> Domain.Secret { - let jwk = privateKey.jwk - guard - let dataJson = try? JSONEncoder().encode(jwk), - let stringJson = String(data: dataJson, encoding: .utf8) - else { - throw CommonError.invalidCoding(message: "Could not encode privateKey.jwk") - } - return .init( - id: identifier, - type: .jsonWebKey2020, - secretMaterial: .jwk(value: stringJson) - ) - } - } - -} diff --git a/E2E/e2eTests/Source/Abilities/UseWalletSdk.swift b/E2E/e2eTests/Source/Abilities/UseWalletSdk.swift new file mode 100644 index 00000000..cd705372 --- /dev/null +++ b/E2E/e2eTests/Source/Abilities/UseWalletSdk.swift @@ -0,0 +1,216 @@ +import Foundation +import EdgeAgent +import Builders +import Combine +import Domain +import Pluto +import Core + +class UseWalletSdk: Ability { + static let defaultSeed: Seed = { + let byteArray: [UInt8] = [69, 191, 35, 232, 213, 102, 3, 93, 180, 106, 224, 144, 79, 171, 79, 223, 154, 217, 235, 232, 96, 30, 248, 92, 100, 38, 38, 42, 101, 53, 2, 247, 56, 111, 148, 220, 237, 122, 15, 120, 55, 82, 89, 150, 35, 45, 123, 135, 159, 140, 52, 127, 239, 148, 150, 109, 86, 145, 77, 109, 47, 60, 20, 16] + return Seed(value: Data(byteArray)) + }() + + static let wrongSeed: Seed = { + let byteArray: [UInt8] = [69, 191, 35, 232, 213, 102, 3, 93, 180, 106, 224, 144, 79, 171, 79, 223, 154, 217, 235, 232, 96, 30, 248, 92, 100, 38, 38, 42, 101, 53, 2, 247, 57, 111, 148, 220, 237, 122, 15, 120, 55, 82, 89, 150, 35, 45, 123, 135, 159, 140, 52, 127, 239, 148, 150, 109, 86, 145, 77, 109, 47, 60, 20, 16] + return Seed(value: Data(byteArray)) + }() + + lazy var actor: Actor = { + return actor + }() + let abilityName: String = "Swift SDK" + var isInitialized: Bool = false + + var credentialOfferStack: [Message] = [] + var issueCredentialStack: [Message] = [] + var proofOfRequestStack: [Message] = [] + var revocationStack: [Message] = [] + var receivedMessages: [String] = [] + var cancellables = Set() + + lazy var sdk: EdgeAgent = { + return sdk + }() + + required init() {} + + func setUp(_ actor: Actor) async throws { + self.actor = actor + if (!isInitialized) { + _ = try await createSdk() + try await startSdk() + } + } + + func createSdk(seed: Seed = defaultSeed) async throws { + let mediatorDID = try await UseWalletSdk.getPrismMediatorDid() + + let apollo = ApolloBuilder().build() + let castor = CastorBuilder(apollo: apollo).build() + let pluto = PlutoBuilder(setup: .init( + coreDataSetup: .init( + modelPath: .storeName("PrismPluto"), + storeType: .memory + ), + keychain: KeychainMock() + )).build() + let pollux = PolluxBuilder(pluto: pluto, castor: castor).build() + let mercury = MercuryBuilder( + castor: castor, + secretsStream: UseWalletSdk.createSecretsStream( + keyRestoration: apollo, + pluto: pluto, + castor: castor + ) + ).build() + + EdgeAgent.setupLogging(logLevels: [ + .edgeAgent: .info + ]) + + sdk = EdgeAgent( + apollo: apollo, + castor: castor, + pluto: pluto, + pollux: pollux, + mercury: mercury, + mediationHandler: BasicMediatorHandler( + mediatorDID: mediatorDID, + mercury: mercury, + store: BasicMediatorHandler.PlutoMediatorStoreImpl(pluto: pluto) + ), + seed: seed + ) + } + + func startSdk() async throws { + try await sdk.start() + sdk.handleReceivedMessagesEvents() + .sink( + receiveCompletion: { completion in + switch completion { + case .finished: + print("Publisher completed successfully.") + case .failure(let error): + print("Publisher failed with error: \(error)") + } + }, + receiveValue: { message in + if (self.receivedMessages.contains(message.id)) { + return + } + self.receivedMessages.append(message.id) + switch(message.piuri) { + case ProtocolTypes.didcommOfferCredential3_0.rawValue: + self.credentialOfferStack.append(message) + case ProtocolTypes.didcommIssueCredential3_0.rawValue: + self.issueCredentialStack.append(message) + case ProtocolTypes.didcommRequestPresentation.rawValue: + self.proofOfRequestStack.append(message) + case ProtocolTypes.didcommRevocationNotification.rawValue: + self.revocationStack.append(message) + default: + print("Message", message.piuri, "not part of event handler.") + break + } + } + ) + .store(in: &cancellables) + + sdk.startFetchingMessages() + self.isInitialized = true + } + + func tearDown() async throws { + sdk.stopFetchingMessages() + try await sdk.stop() + } + + static private func getPrismMediatorDid() async throws -> DID { + let url = URL(string: Config.mediatorOobUrl)! + let jsonData: [String: Any] = try await Api.get(from: url) + let did = (jsonData["from"] as? String)! + return try DID(string: did) + } + + static private func getRootsMediatorDid() async throws -> DID { + let url = URL(string: Config.mediatorOobUrl)! + let invitationUrl: String = try await Api.get(from: url) + let base64data: String = String(invitationUrl.split(separator: "?_oob=").last!) + let decodedData = Data(base64Encoded: base64data)! + let json = try (JSONSerialization.jsonObject(with: decodedData, options: []) as? [String: Any])! + let from = (json["from"] as? String)! + return try DID(string: from) + } + + private static func fromBase64(_ encoded: String) -> Data { + var encoded = encoded; + let remainder = encoded.count % 4 + if remainder > 0 { + encoded = encoded.padding( + toLength: encoded.count + 4 - remainder, + withPad: "=", startingAt: 0); + } + return Data(base64Encoded: encoded)! + // return String(data: data, encoding: .utf8)! + } + + static private func createSecretsStream( + keyRestoration: KeyRestoration, + pluto: Pluto, + castor: Castor + ) -> AnyPublisher<[Secret], Error> { + pluto.getAllKeys() + .first() + .flatMap { keys in + Future { + let privateKeys = await keys.asyncMap { + try? await keyRestoration.restorePrivateKey($0) + }.compactMap { $0 } + return try parsePrivateKeys( + privateKeys: privateKeys, + castor: castor + ) + } + } + .eraseToAnyPublisher() + } + + static private func parsePrivateKeys( + privateKeys: [PrivateKey], + castor: Castor + ) throws -> [Domain.Secret] { + return try privateKeys + .map { $0 as? (PrivateKey & ExportableKey & StorableKey) } + .compactMap { $0 } + .map { privateKey in + return privateKey + } + .map { privateKey in + try parseToSecret( + privateKey: privateKey, + identifier: privateKey.identifier + ) + } + } + + static private func parseToSecret( + privateKey: PrivateKey & ExportableKey, + identifier: String + ) throws -> Domain.Secret { + let jwk = privateKey.jwk + guard + let dataJson = try? JSONEncoder().encode(jwk), + let stringJson = String(data: dataJson, encoding: .utf8) + else { + throw CommonError.invalidCoding(message: "Could not encode privateKey.jwk") + } + return .init( + id: identifier, + type: .jsonWebKey2020, + secretMaterial: .jwk(value: stringJson) + ) + } +} diff --git a/E2E/e2eTests/Source/Config.swift b/E2E/e2eTests/Source/Config.swift index 3a99131c..17501212 100644 --- a/E2E/e2eTests/Source/Config.swift +++ b/E2E/e2eTests/Source/Config.swift @@ -9,6 +9,10 @@ class Config: TestConfiguration { static var anoncredDefinitionGuid: String = "" static var apiKey: String = "" + lazy var api: OpenEnterpriseAPI = { + return api + }() + override class func createInstance() -> ITestConfiguration { return Config() } @@ -25,11 +29,11 @@ class Config: TestConfiguration { } override func createActors() async throws -> [Actor] { - let cloudAgent = Actor("Cloud Agent").whoCanUse(OpenEnterpriseAPI.self) - let edgeAgent = Actor("Edge Agent").whoCanUse(Sdk.self ) + let cloudAgent = Actor("Cloud Agent").whoCanUse(OpenEnterpriseAPI()) + let edgeAgent = Actor("Edge Agent").whoCanUse(UseWalletSdk()) return [cloudAgent, edgeAgent] } - + override func setUp() async throws { Config.mediatorOobUrl = environment["MEDIATOR_OOB_URL"]! Config.agentUrl = environment["PRISM_AGENT_URL"]! @@ -38,6 +42,11 @@ class Config: TestConfiguration { Config.anoncredDefinitionGuid = environment["ANONCRED_DEFINITION_GUID"] ?? "" Config.apiKey = environment["APIKEY"] ?? "" + // should be initialized after the configuration variables + let openEnterpriseApi = OpenEnterpriseAPI() + openEnterpriseApi.createClient() + self.api = openEnterpriseApi + try await checkPublishedDid() try await checkJwtSchema() try await checkAnoncredDefinition() @@ -54,7 +63,6 @@ class Config: TestConfiguration { } private func checkPublishedDid() async throws { - let api = OpenEnterpriseAPI.API() let isPresent = try await api.isDidPresent(Config.publishedDid) if (isPresent) { return @@ -74,7 +82,6 @@ class Config: TestConfiguration { } private func checkJwtSchema() async throws { - let api = OpenEnterpriseAPI.API() let isPresent = try await api.isJwtSchemaGuidPresent(Config.jwtSchemaGuid) if (isPresent) { return @@ -86,7 +93,6 @@ class Config: TestConfiguration { } private func checkAnoncredDefinition() async throws { - let api = OpenEnterpriseAPI.API() let isPresent = try await api.isAnoncredDefinitionPresent(Config.anoncredDefinitionGuid) if (isPresent) { return diff --git a/E2E/e2eTests/Source/Features/AnoncredFeature.swift b/E2E/e2eTests/Source/Features/anoncred/AnoncredCredentialFeature.swift similarity index 86% rename from E2E/e2eTests/Source/Features/AnoncredFeature.swift rename to E2E/e2eTests/Source/Features/anoncred/AnoncredCredentialFeature.swift index 33aedb58..d6e7d5b6 100644 --- a/E2E/e2eTests/Source/Features/AnoncredFeature.swift +++ b/E2E/e2eTests/Source/Features/anoncred/AnoncredCredentialFeature.swift @@ -1,7 +1,7 @@ import Foundation import XCTest -class AnoncredFeature: Feature { +class AnoncredCredentialFeature: Feature { override func title() -> String { "Receive anonymous credential" } @@ -18,6 +18,6 @@ class AnoncredFeature: Feature { .when("Edge Agent accepts the credential") .when("Cloud Agent should see the credential was accepted") .then("Edge Agent wait to receive 1 issued credentials") - .then("Edge Agent process 1 issued credentials") + .then("Edge Agent process issued credentials from Cloud Agent") } } diff --git a/E2E/e2eTests/Source/Features/AnoncredProofOfRequestFeature.swift b/E2E/e2eTests/Source/Features/anoncred/AnoncredProofOfRequestFeature.swift similarity index 100% rename from E2E/e2eTests/Source/Features/AnoncredProofOfRequestFeature.swift rename to E2E/e2eTests/Source/Features/anoncred/AnoncredProofOfRequestFeature.swift diff --git a/E2E/e2eTests/Source/Features/backup/BackupFeature.swift b/E2E/e2eTests/Source/Features/backup/BackupFeature.swift new file mode 100644 index 00000000..357eb1d3 --- /dev/null +++ b/E2E/e2eTests/Source/Features/backup/BackupFeature.swift @@ -0,0 +1,39 @@ +import Foundation + +final class BackupFeature: Feature { + override func title() -> String { + "Backup" + } + + override func description() -> String { + "The Edge Agent should be able to create and restore a backup" + } + + func testCreateAndRestoreABackup() async throws { + currentScenario = Scenario("Create and restore a backup") + .given("Edge Agent has created a backup") + .then("a new SDK can be restored from Edge Agent") + } + + func testAgentWithoutProperSeedShouldNotBeAbleToRestoreTheBackup() async throws { + currentScenario = Scenario("Agent without a seed should not be able to restore the backup") + .given("Edge Agent has created a backup") + .then("a new SDK cannot be restored from Edge Agent with wrong seed") + } + + func testRestoredBackupShouldBeFunctional() async throws { + currentScenario = Scenario("Restored backup should be functional") + .given("Cloud Agent is connected to Edge Agent") + .and("Edge Agent has '1' jwt credentials issued by Cloud Agent") + .and("Edge Agent creates '5' peer DIDs") + .and("Edge Agent creates '3' prism DIDs") + .and("Edge Agent has created a backup") + .then("a new Restored Agent is restored from Edge Agent") + .and("Restored Agent should have the expected values from Edge Agent") + .and("Edge Agent is dismissed") + .given("Cloud Agent is connected to Restored Agent") + .and("Cloud Agent asks for present-proof") + .when("Restored Agent sends the present-proof") + .then("Cloud Agent should see the present-proof is verified") + } +} diff --git a/E2E/e2eTests/Source/Features/ConnectionFeature.swift b/E2E/e2eTests/Source/Features/connection/ConnectionFeature.swift similarity index 100% rename from E2E/e2eTests/Source/Features/ConnectionFeature.swift rename to E2E/e2eTests/Source/Features/connection/ConnectionFeature.swift diff --git a/E2E/e2eTests/Source/Features/CredentialFeature.swift b/E2E/e2eTests/Source/Features/jwt/JwtCredentialFeature.swift similarity index 70% rename from E2E/e2eTests/Source/Features/CredentialFeature.swift rename to E2E/e2eTests/Source/Features/jwt/JwtCredentialFeature.swift index 55556cf9..3b0a103e 100644 --- a/E2E/e2eTests/Source/Features/CredentialFeature.swift +++ b/E2E/e2eTests/Source/Features/jwt/JwtCredentialFeature.swift @@ -1,6 +1,4 @@ -import XCTest - -final class CredentialTests: Feature { +final class JwtCredentialTests: Feature { override func title() -> String { "Receive verifiable credential" } @@ -12,29 +10,29 @@ final class CredentialTests: Feature { func testReceiveOneCredential() async throws { currentScenario = Scenario("Receive one verifiable credential") .given("Cloud Agent is connected to Edge Agent") - .when("Cloud Agent offers a credential") + .when("Cloud Agent offers '1' jwt credentials") .then("Edge Agent should receive the credential") .when("Edge Agent accepts the credential") .when("Cloud Agent should see the credential was accepted") .then("Edge Agent wait to receive 1 issued credentials") - .then("Edge Agent process 1 issued credentials") + .then("Edge Agent process issued credentials from Cloud Agent") } func testReceiveMultipleCredentialsSequentially() async throws { currentScenario = Scenario("Receive multiple verifiable credentials sequentially") - .given("Cloud Agent is connected to Edge Agent") - .when("Edge Agent accepts 3 credential offer sequentially from Cloud Agent") + . given("Cloud Agent is connected to Edge Agent") + .when("Edge Agent accepts 3 jwt credential offer sequentially from Cloud Agent") .then("Cloud Agent should see all credentials were accepted") .and("Edge Agent wait to receive 3 issued credentials") - .and("Edge Agent process 3 issued credentials") + .and("Edge Agent process issued credentials from Cloud Agent") } func testReceiveMultipleCredentialsAtOnce() async throws { currentScenario = Scenario("Receive multiple verifiable credentials at once") .given("Cloud Agent is connected to Edge Agent") - .when("Edge Agent accepts 3 credentials offer at once from Cloud Agent") + .when("Edge Agent accepts 3 jwt credentials offer at once from Cloud Agent") .then("Cloud Agent should see all credentials were accepted") .and("Edge Agent wait to receive 3 issued credentials") - .and("Edge Agent process 3 issued credentials") + .and("Edge Agent process issued credentials from Cloud Agent") } } diff --git a/E2E/e2eTests/Source/Features/ProofOfRequestFeature.swift b/E2E/e2eTests/Source/Features/jwt/JwtProofOfRequestFeature.swift similarity index 81% rename from E2E/e2eTests/Source/Features/ProofOfRequestFeature.swift rename to E2E/e2eTests/Source/Features/jwt/JwtProofOfRequestFeature.swift index 319079dc..afcfbe68 100644 --- a/E2E/e2eTests/Source/Features/ProofOfRequestFeature.swift +++ b/E2E/e2eTests/Source/Features/jwt/JwtProofOfRequestFeature.swift @@ -1,6 +1,4 @@ -import XCTest - -final class ProofOfRequestFeature: Feature { +final class JwtProofOfRequestFeature: Feature { override func title() -> String { "Provide proof of request" } @@ -12,7 +10,7 @@ final class ProofOfRequestFeature: Feature { func testRespondToProofOfRequest() async throws { currentScenario = Scenario("Respond to request proof") .given("Cloud Agent is connected to Edge Agent") - .and("Edge Agent has '1' credentials issued by Cloud Agent") + .and("Edge Agent has '1' jwt credentials issued by Cloud Agent") .when("Cloud Agent asks for present-proof") .and("Edge Agent sends the present-proof") .then("Cloud Agent should see the present-proof is verified") diff --git a/E2E/e2eTests/Source/Features/jwt/JwtRevocationFeature.swift b/E2E/e2eTests/Source/Features/jwt/JwtRevocationFeature.swift new file mode 100644 index 00000000..3b31d1c8 --- /dev/null +++ b/E2E/e2eTests/Source/Features/jwt/JwtRevocationFeature.swift @@ -0,0 +1,18 @@ +final class JwtRevocationFeature: Feature { + override func title() -> String { + "Revoke JWT Credential" + } + + override func description() -> String { + "Edge Agent should be notified when Cloud Agent revokes a credential" + } + + func testReceiveOneCredential() async throws { + currentScenario = Scenario("Revoke one verifiable credential") + .given("Cloud Agent is connected to Edge Agent") + .and("Edge Agent has '1' jwt credentials issued by Cloud Agent") + .when("Cloud Agent revokes '1' credentials") + .then("Edge Agent waits to receive the revocation notifications from Cloud Agent") + .and("Edge Agent should see the credentials were revoked by Cloud Agent") + } +} diff --git a/E2E/e2eTests/Source/Steps/CloudAgentSteps.swift b/E2E/e2eTests/Source/Steps/CloudAgentSteps.swift index ac6aa6d4..714afdec 100644 --- a/E2E/e2eTests/Source/Steps/CloudAgentSteps.swift +++ b/E2E/e2eTests/Source/Steps/CloudAgentSteps.swift @@ -1,6 +1,6 @@ import Foundation -class CloudAgentSteps: Steps { +class CloudAgentSteps: Steps { @Step("{actor} offers an anonymous credential") var cloudAgentOffersAnAnonymousCredential = { (cloudAgent: Actor) in try await CloudAgentWorkflow.offersAnonymousCredential(cloudAgent: cloudAgent) @@ -21,20 +21,26 @@ class CloudAgentSteps: Steps { try await CloudAgentWorkflow.verifyPresentProof(cloudAgent: cloudAgent, expectedState: .PresentationVerified) } - @Step("{actor} offers a credential") - var cloudAgentOffersACredential = { (cloudAgent: Actor) in - try await CloudAgentWorkflow.offersACredential(cloudAgent: cloudAgent) + @Step("{actor} offers '{int}' jwt credentials") + var cloudAgentOffersACredential = { (cloudAgent: Actor, numberOfCredentials: Int) in + var recordIdList: [String] = [] + for _ in 0.. String { fatalError("Set feature title") } - + func description() -> String { return "" } - + /// our lifecycle starts after xctest is ending override func tearDown() async throws { try await run() try await super.tearDown() + if (Feature.scenarioEnd) { + try await TestConfiguration.shared().endCurrentFeature() + } } override class func tearDown() { // signal end of feature - TestConfiguration.shared().endCurrentFeature() + scenarioEnd = true } func run() async throws { diff --git a/E2E/e2eTests/TestFramework/BDD/Step.swift b/E2E/e2eTests/TestFramework/BDD/Step.swift index 0640ff59..7216753b 100644 --- a/E2E/e2eTests/TestFramework/BDD/Step.swift +++ b/E2E/e2eTests/TestFramework/BDD/Step.swift @@ -6,17 +6,12 @@ class Step { let file: StaticString let line: UInt var definition: String - var callback: (T) async throws -> () - var wrappedValue: () async throws -> () { - get { - return {} - } - } + var wrappedValue: (T) async throws -> () init(wrappedValue: @escaping (T) async throws -> (), _ definition: String, file: StaticString = #file, line: UInt = #line) { self.file = file self.line = line - self.callback = wrappedValue + self.wrappedValue = wrappedValue self.definition = definition StepRegistry.addStep(self) } diff --git a/E2E/e2eTests/TestFramework/Configuration/ITestConfiguration.swift b/E2E/e2eTests/TestFramework/Configuration/ITestConfiguration.swift index e6a24554..6825b2be 100644 --- a/E2E/e2eTests/TestFramework/Configuration/ITestConfiguration.swift +++ b/E2E/e2eTests/TestFramework/Configuration/ITestConfiguration.swift @@ -15,19 +15,19 @@ protocol ITestConfiguration { /// phases func beforeFeature(_ feature: Feature) async throws func beforeScenario(_ scenario: Scenario) async throws - func beforeStep(_ step: ConcreteStep) - func afterStep(_ stepOutcome: StepOutcome) + func beforeStep(_ step: ConcreteStep) async throws + func afterStep(_ stepOutcome: StepOutcome) async throws func afterScenario(_ scenarioOutcome: ScenarioOutcome) async throws func afterFeature(_ featureOutcome: FeatureOutcome) async throws func afterFeatures(_ featuresOutcome: [FeatureOutcome]) async throws - func endCurrentFeature() + func endCurrentFeature() async throws func end() /// methods func createActors() async throws -> [Actor] func createReporters() async throws -> [Reporter] - func report(_ phase: Phase, _ object: Any) + func report(_ phase: Phase, _ object: Any) async throws func targetDirectory() -> URL } diff --git a/E2E/e2eTests/TestFramework/Configuration/TestConfiguration.swift b/E2E/e2eTests/TestFramework/Configuration/TestConfiguration.swift index a2200f67..6fe35825 100644 --- a/E2E/e2eTests/TestFramework/Configuration/TestConfiguration.swift +++ b/E2E/e2eTests/TestFramework/Configuration/TestConfiguration.swift @@ -4,9 +4,9 @@ import SwiftHamcrest class TestConfiguration: ITestConfiguration { static var shared = { instance! } - - let environment: [String: String] = { readEnvironmentVariables() }() + let environment: [String: String] = { readEnvironmentVariables() }() + private var testSuiteFinished: Bool = false private static var instance: ITestConfiguration? = nil private static var actors: [String: Actor] = [:] @@ -71,6 +71,7 @@ class TestConfiguration: ITestConfiguration { func beforeFeature(_ feature: Feature) async throws { let type: Feature.Type = type(of: feature) + try await self.setUpActors() if (features.contains(where: { $0 == type })) { return @@ -80,15 +81,15 @@ class TestConfiguration: ITestConfiguration { currentFeatureOutcome = FeatureOutcome(feature) result.featuresOutcome.append(currentFeatureOutcome!) - report(.BEFORE_FEATURE, feature) + try await report(.BEFORE_FEATURE, feature) } func beforeScenario(_ scenario: Scenario) async throws { - report(.BEFORE_SCENARIO, scenario) + try await report(.BEFORE_SCENARIO, scenario) } - func beforeStep(_ step: ConcreteStep) { - report(.BEFORE_STEP, step.action) + func beforeStep(_ step: ConcreteStep) async throws { + try await report(.BEFORE_STEP, step.action) } func runSteps(_ scenario: Scenario) async throws -> ScenarioOutcome { @@ -96,7 +97,7 @@ class TestConfiguration: ITestConfiguration { for step in scenario.steps { let stepOutcome: StepOutcome - report(.BEFORE_STEP, step) + try await report(.BEFORE_STEP, step) do { try await StepRegistry.run(step) @@ -118,9 +119,9 @@ class TestConfiguration: ITestConfiguration { } scenarioOutcome.steps.append(stepOutcome) - report(.AFTER_STEP, stepOutcome) + try await report(.AFTER_STEP, stepOutcome) assertionFailure = nil - + if (stepOutcome.error != nil) { scenarioOutcome.failedStep = stepOutcome break @@ -129,8 +130,8 @@ class TestConfiguration: ITestConfiguration { return scenarioOutcome } - func afterStep(_ stepOutcome: StepOutcome) { - report(.AFTER_STEP, stepOutcome.step.action) + func afterStep(_ stepOutcome: StepOutcome) async throws { + try await report(.AFTER_STEP, stepOutcome.step.action) } func afterScenario(_ scenarioOutcome: ScenarioOutcome) async throws { @@ -138,61 +139,52 @@ class TestConfiguration: ITestConfiguration { if (scenarioOutcome.failedStep != nil) { currentFeatureOutcome!.failedScenarios.append(scenarioOutcome) } - report(.AFTER_SCENARIO, scenarioOutcome) + try await report(.AFTER_SCENARIO, scenarioOutcome) } func afterFeature(_ featureOutcome: FeatureOutcome) async throws { - report(.AFTER_FEATURE, featureOutcome) + try await tearDownActors() + try await report(.AFTER_FEATURE, featureOutcome) } func afterFeatures(_ featuresOutcome: [FeatureOutcome]) async throws { - report(.AFTER_FEATURES, featuresOutcome) + try await report(.AFTER_FEATURES, featuresOutcome) } - private func unsafeSync(_ f: @escaping () async -> ()) { - let semaphore = DispatchSemaphore(value: 0) - Task.init { - await f() - semaphore.signal() - } - semaphore.wait() - } - - func endCurrentFeature() { - unsafeSync { - try! await self.afterFeature(self.currentFeatureOutcome!) + func endCurrentFeature() async throws { + try await self.afterFeature(self.currentFeatureOutcome!) + + if (testSuiteFinished) { + try await self.afterFeatures(self.result.featuresOutcome) + try await self.tearDownInstance() + // TODO: throw exception if it fails } } + /// signals the suite has ended func end() { - unsafeSync { - try! await self.afterFeatures(self.result.featuresOutcome) - try! await self.tearDownInstance() - } - // TODO: throw exception if it fails - } - - func report(_ phase: Phase, _ object: Any) { - self.reporters.forEach { reporter in - unsafeSync { - switch(phase) { - case .BEFORE_FEATURE: - try! await reporter.beforeFeature(object as! Feature) - case .AFTER_FEATURE: - try! await reporter.afterFeature(object as! FeatureOutcome) - case .BEFORE_SCENARIO: - try! await reporter.beforeScenario(object as! Scenario) - case .AFTER_SCENARIO: - try! await reporter.afterScenario(object as! ScenarioOutcome) - case .BEFORE_STEP: - try! await reporter.beforeStep(object as! ConcreteStep) - case .AFTER_STEP: - try! await reporter.afterStep(object as! StepOutcome) - case .ACTION: - try! await reporter.action(object as! ActionOutcome) - case .AFTER_FEATURES: - try! await reporter.afterFeatures(object as! [FeatureOutcome]) - } + testSuiteFinished = true + } + + func report(_ phase: Phase, _ object: Any) async throws { + for reporter in reporters { + switch(phase) { + case .BEFORE_FEATURE: + try! await reporter.beforeFeature(object as! Feature) + case .AFTER_FEATURE: + try! await reporter.afterFeature(object as! FeatureOutcome) + case .BEFORE_SCENARIO: + try! await reporter.beforeScenario(object as! Scenario) + case .AFTER_SCENARIO: + try! await reporter.afterScenario(object as! ScenarioOutcome) + case .BEFORE_STEP: + try! await reporter.beforeStep(object as! ConcreteStep) + case .AFTER_STEP: + try! await reporter.afterStep(object as! StepOutcome) + case .ACTION: + try! await reporter.action(object as! ActionOutcome) + case .AFTER_FEATURES: + try! await reporter.afterFeatures(object as! [FeatureOutcome]) } } } @@ -247,7 +239,6 @@ class TestConfiguration: ITestConfiguration { try await instance.setUp() try await instance.setUpReporters() try await instance.setUpSteps() - try await instance.setUpActors() /// setup hamcrest to update variable if failed HamcrestReportFunction = { message, file, line in @@ -299,6 +290,9 @@ class TestConfiguration: ITestConfiguration { /// Default parsers @ParameterParser var actorParser = { (actor: String) in + if (!actors.contains(where: { $0.key == actor })) { + actors[actor] = Actor(actor) + } return actors[actor]! } diff --git a/E2E/e2eTests/TestFramework/Runner/StepRunner.swift b/E2E/e2eTests/TestFramework/Runner/StepRunner.swift index eb0dc2b4..83ea5d47 100644 --- a/E2E/e2eTests/TestFramework/Runner/StepRunner.swift +++ b/E2E/e2eTests/TestFramework/Runner/StepRunner.swift @@ -31,7 +31,7 @@ class StepRunner { actual: String(describing: actualType) ) } - return try await step.callback(typedInput) + return try await step.wrappedValue(typedInput) } self.stepMatcher = StepRunner.createMatcher(stepDefinition) self.parsers = StepRunner.createParsers(stepDefinition) diff --git a/E2E/e2eTests/TestFramework/Screenplay/Ability.swift b/E2E/e2eTests/TestFramework/Screenplay/Ability.swift index a187dd3f..4dbd217d 100644 --- a/E2E/e2eTests/TestFramework/Screenplay/Ability.swift +++ b/E2E/e2eTests/TestFramework/Screenplay/Ability.swift @@ -1,17 +1,12 @@ import Foundation protocol Ability { - /// return interface for the ability - associatedtype AbilityInstanceType - var abilityName: String {get} var actor: Actor {get} + var isInitialized: Bool {get} - init(_ actor: Actor) + init() - /// object instance returned by the ability - func instance() -> AbilityInstanceType - /// initialization hook, used to create the object instance for ability func setUp(_ actor: Actor) async throws diff --git a/E2E/e2eTests/TestFramework/Screenplay/Actor.swift b/E2E/e2eTests/TestFramework/Screenplay/Actor.swift index 60aa38d5..f3753b35 100644 --- a/E2E/e2eTests/TestFramework/Screenplay/Actor.swift +++ b/E2E/e2eTests/TestFramework/Screenplay/Actor.swift @@ -5,25 +5,32 @@ class Actor { var name: String private var context: [String: Any] = [:] private var abilities: [String : any Ability] = [:] + private var isInitialized: Bool = false init(_ name: String) { self.name = name } func initialize() async throws { + if (isInitialized) { + return + } + for ability in abilities.values { try await ability.setUp(self) } + self.isInitialized = true } func tearDown() async throws { for ability in abilities.values { try await ability.tearDown() } + context.removeAll() + isInitialized = false } - func whoCanUse(_ abilityType: T.Type) -> Actor { - let ability = abilityType.init(self) + func whoCanUse(_ ability: T) -> Actor { abilities[String(describing: T.self)] = ability return self } @@ -34,39 +41,39 @@ class Actor { func using(ability: T.Type, action: String // = "executes an action" - ) throws -> T.AbilityInstanceType { - let dummy = ability.init(self) - return try execute("\(name) \(action) using \(dummy.abilityName)") { + ) async throws -> T { + let dummy = T.init() + return try await execute("\(name) \(action) using \(dummy.abilityName)") { if !abilities.contains(where: { $0.key == String(describing: ability.self) }) { throw ActorError.CantUseAbility("Actor [\(name)] don't have the ability to use [\(ability.self)]") } let ability = getAbility(ability) - return ability.instance() + return ability } } func waitUsingAbility(ability: T.Type, action: String, // = "an expectation is met", - callback: (_ ability: T.AbilityInstanceType) async throws -> Bool + callback: (_ ability: T) async throws -> Bool ) async throws { - let dummy = ability.init(self) + let dummy = ability.init() return try await execute("\(name) waits until \(action) using \(dummy.abilityName)") { let ability = getAbility(ability) return try await Wait.until { - try await callback(ability.instance()) + try await callback(ability) } } } - func remember(key: String, value: Any) throws { - return execute("\(name) remembers [\(key)]") { + func remember(key: String, value: Any) async throws { + return try await execute("\(name) remembers [\(key)]") { context[key] = value } } - func recall(key: String) throws -> T { - return try execute("\(name) recalls [\(key)]") { + func recall(key: String) async throws -> T { + return try await execute("\(name) recalls [\(key)]") { if (context[key] == nil) { throw ActorError.CantFindNote("\(name) don't have any note named [\(key)]") } @@ -74,32 +81,32 @@ class Actor { } } - private func execute(_ message: String, _ closure: () async throws -> T) async rethrows -> T { + private func execute(_ message: String, _ closure: () async throws -> T) async throws -> T { let actionOutcome = ActionOutcome() actionOutcome.action = message do { let result = try await closure() actionOutcome.executed = true - TestConfiguration.shared().report(.ACTION, actionOutcome) + try await TestConfiguration.shared().report(.ACTION, actionOutcome) return result } catch { actionOutcome.error = error - TestConfiguration.shared().report(.ACTION, actionOutcome) + try await TestConfiguration.shared().report(.ACTION, actionOutcome) throw error } } - private func execute(_ message: String, _ closure: () throws -> T) rethrows -> T { + private func execute(_ message: String, _ closure: () throws -> T) async throws -> T { let actionOutcome = ActionOutcome() actionOutcome.action = message do { let result = try closure() actionOutcome.executed = true - TestConfiguration.shared().report(.ACTION, actionOutcome) + try await TestConfiguration.shared().report(.ACTION, actionOutcome) return result } catch { actionOutcome.error = error - TestConfiguration.shared().report(.ACTION, actionOutcome) + try await TestConfiguration.shared().report(.ACTION, actionOutcome) throw error } } diff --git a/E2E/e2eTests/openapi.yaml b/E2E/e2eTests/openapi.yaml index e21bba00..fd0c0735 100644 --- a/E2E/e2eTests/openapi.yaml +++ b/E2E/e2eTests/openapi.yaml @@ -1,18 +1,14 @@ openapi: 3.0.3 info: - title: Open Enterprise Agent API Reference - version: 1.31.0 + title: Identus Cloud Agent API Reference + version: 1.38.0 description: |2 - The Open Enterprise Agent API facilitates the integration and management of self-sovereign identity capabilities within applications. + The Identus Cloud Agent API facilitates the integration and management of self-sovereign identity capabilities within applications. It supports DID (Decentralized Identifiers) management, verifiable credential exchange, and secure messaging based on DIDComm standards. The API is designed to be interoperable with various blockchain and DLT (Distributed Ledger Technology) platforms, ensuring wide compatibility and flexibility. Key features include connection management, credential issuance and verification, and secure, privacy-preserving communication between entities. Additional information and the full list of capabilities can be found in the [Open Enterprise Agent documentation](https://docs.atalaprism.io/docs/category/prism-cloud-agent) - termsOfService: |2 - - Users of the Open Enterprise Agent API must adhere to the terms and conditions outlined in [Link to Terms of Service](/). - This includes compliance with relevant data protection regulations, responsible usage policies, and adherence to the principles of decentralized identity management. license: name: Apache 2.0 url: https://www.apache.org/licenses/LICENSE-2.0 @@ -131,13 +127,38 @@ tags: The __metrics__ endpoint returns the runtime metrics of the running service scraped from the internal prometheus registry. This information is collected by the prometheus server and can be used to monitor the running service. +- name: Events + description: | + The __Events__ endpoints enable users to manage event-related resources, such as webhook notifications. + These notifications are specifically designed to inform about events occurring within the wallet, including but not limited to: + + - DID publication notifications + - DIDComm connection notifications + - Issuance protocol notifications + - Presentation protocol notifications + + For more detailed information regarding event notifications, please refer to this [documentation](https://docs.atalaprism.io/tutorials/webhooks/webhook). +- name: Identity and Access Management + description: |2 + + The __Identity and Access Management__ endpoints allow [agent administrators](https://docs.atalaprism.io/docs/concepts/glossary#administrator) + to manage identity and access management for the agent's tenants. + It provides basic built-in IAM capabilities as an alternative to more feature rich external IAM solutions. + + Entities are resources that represent individual tenants and + wallets act as containers for Self-Sovereign Identity (SSI) resources within the agent. + The administrator can grant tenant access to specific wallets by associating the wallet ID with the Entity. + Additionally, the administrator can create API keys for entities and provide them to the tenants out-of-band. + These API keys can then be used for authorization to access specific wallets. + + For more detailed information related to the agent IAM and its usage, please refer to this [documentation](https://docs.atalaprism.io/docs/atala-prism/prism-cloud-agent/authentication). servers: - url: http://localhost:8085 - description: Local Prism Agent -- url: http://localhost/prism-agent - description: Local Prism Agent with APISIX proxy -- url: https://k8s-dev.atalaprism.io/prism-agent - description: Prism Agent on the Staging Environment + description: The local instance of the Cloud Agent +- url: http://localhost/cloud-agent + description: The local instance of the Cloud Agent behind the APISIX proxy +- url: https://k8s-dev.atalaprism.io/cloud-agent + description: The Cloud Agent in the Staging Environment paths: /credential-definition-registry/definitions: get: @@ -206,6 +227,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -255,6 +282,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -298,6 +331,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -338,6 +377,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -417,6 +462,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -466,6 +517,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -533,6 +590,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -576,6 +639,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -614,6 +683,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -672,6 +747,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -713,6 +794,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -762,6 +849,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -823,6 +916,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -867,6 +966,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -1230,6 +1335,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -1335,6 +1446,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -1390,6 +1507,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -1854,6 +1977,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -1890,6 +2019,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -1904,7 +2039,8 @@ paths: tags: - Present Proof summary: Gets the list of proof presentation records. - description: list of presentation statuses + description: Get the list of proof presentation records and its status that + the Agent have at moment operationId: getAllPresentation parameters: - name: offset @@ -1925,6 +2061,7 @@ paths: format: int32 - name: thid in: query + description: Filter by the DID Comm message's 'thid' of presentProof required: false schema: type: string @@ -1947,6 +2084,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -1997,6 +2140,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -2047,6 +2196,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -2103,6 +2258,64 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + /verification/credential: + post: + tags: + - Verifiable Credentials Verification + summary: Verify a set of credentials as a Verifier + description: Endpoint to verify a set of verifiable credentials as a Verifier. + operationId: verify + requestBody: + description: List of verifiable credentials to verify + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/VcVerificationRequest' + required: false + responses: + '200': + description: List of verifiable credentials verification outcomes + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/VcVerificationResponse' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -2132,6 +2345,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -2159,6 +2378,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -2208,6 +2433,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -2256,6 +2487,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -2311,6 +2548,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -2366,6 +2609,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -2415,6 +2664,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -2459,6 +2714,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -2503,6 +2764,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -2546,6 +2813,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -2599,6 +2872,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -2645,6 +2924,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -2695,6 +2980,483 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - adminApiKeyAuth: [] + - apiKeyAuth: [] + - jwtAuth: [] + /wallets/{walletId}/uma-permissions: + post: + tags: + - Wallet Management + summary: Create a UMA resource permission on an authorization server for the + wallet. + description: "Create a UMA resource permission on an authorization server for\ + \ the wallet.\nThis grants the wallet permission to the specified `subject`,\ + \ where the `subject` denotes the identity of the tenant on an authorization\ + \ server.\n " + operationId: createWalletUmaPermission + parameters: + - name: walletId + in: path + required: true + schema: + type: string + format: uuid + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateWalletUmaPermissionRequest' + required: true + responses: + '200': + description: UMA resource permission is created on an authorization server + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - adminApiKeyAuth: [] + - apiKeyAuth: [] + - jwtAuth: [] + delete: + tags: + - Wallet Management + summary: Delete a UMA resource permission on an authorization server for the + wallet. + description: "Remove a UMA resource permission on an authorization server for\ + \ the wallet.\nThis remove the wallet permission to the specified `subject`,\ + \ where the `subject` denotes the identity of the tenant on an authorization\ + \ server.\n " + operationId: deleteWalletUmaPermission + parameters: + - name: walletId + in: path + required: true + schema: + type: string + format: uuid + - name: subject + in: query + required: true + schema: + type: string + format: uuid + responses: + '200': + description: UMA resource permission is removed from an authorization server. + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - adminApiKeyAuth: [] + - apiKeyAuth: [] + - jwtAuth: [] + /events/webhooks: + get: + tags: + - Events + summary: List wallet webhook notifications + description: "List all registered webhook notifications.\nEach webhook notification\ + \ contains a unique identifier, the URL to which the events are sent,\nand\ + \ the custom headers to be included in the dispatched webhook request.\n \ + \ " + operationId: getEventsWebhooks + responses: + '200': + description: List wallet webhook notifications + content: + application/json: + schema: + $ref: '#/components/schemas/WebhookNotificationPage' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + post: + tags: + - Events + summary: Create wallet webhook notifications + description: "Create a new wallet webhook notification and subscribe to events.\n\ + A dispatched webhook request may contain static custom headers for authentication\ + \ or custom metadata.\n " + operationId: postEventsWebhooks + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CreateWebhookNotification' + required: true + responses: + '200': + description: Webhook notification has been created successfully + content: + application/json: + schema: + $ref: '#/components/schemas/WebhookNotification' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '409': + description: Cannot process due to conflict with current state of the resource + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + /events/webhooks/{id}: + delete: + tags: + - Events + summary: Delete the wallet webhook notification by `id` + operationId: deleteEventsWebhooksId + parameters: + - name: id + in: path + description: ID of the webhook notification to delete. + required: true + schema: + type: string + format: uuid + responses: + '200': + description: Webhook notification has been deleted. + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + /oid4vci/issuers/{issuerId}/credentials: + post: + tags: + - OpenID for Verifiable Credential Issuance + summary: Credential Endpoint + description: OID for VCI [Credential Endpoint](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-credential-endpoint) + operationId: oid4vciIssueCredential + parameters: + - name: issuerId + in: path + description: An issuer identifier in the oid4vci protocol + required: true + schema: + type: string + format: uuid + example: f47ac10b-58cc-4372-a567-0e02b2c3d479 + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CredentialRequest' + required: true + responses: + '200': + description: Credential issued successfully + content: + application/json: + schema: + $ref: '#/components/schemas/CredentialResponse' + '400': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ExtendedErrorResponse' + '401': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ExtendedErrorResponse' + '403': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ExtendedErrorResponse' + '500': + description: '' + content: + application/json: + schema: + $ref: '#/components/schemas/ExtendedErrorResponse' + security: + - {} + - jwtAuth: [] + /oid4vci/issuers/{issuerId}/credential-offers: + post: + tags: + - OpenID for Verifiable Credential Issuance + summary: Create a new credential offer + description: |- + Create a new credential offer and return a compliant `CredentialOffer` for the holder's + [Credential Offer Endpoint](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-credential-offer-endpoint). + operationId: oid4vciCreateCredentialOffer + parameters: + - name: issuerId + in: path + description: An issuer identifier in the oid4vci protocol + required: true + schema: + type: string + format: uuid + example: f47ac10b-58cc-4372-a567-0e02b2c3d479 + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CredentialOfferRequest' + required: true + responses: + '201': + description: CredentialOffer created successfully + content: + application/json: + schema: + $ref: '#/components/schemas/CredentialOfferResponse' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + /oid4vci/nonces: + post: + tags: + - OpenID for Verifiable Credential Issuance + summary: Nonce Endpoint + description: The endpoint that returns a `nonce` value for the [Token Endpoint](https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-nonce-endpoint) + operationId: getNonce + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/NonceRequest' + required: true + responses: + '200': + description: Nonce issued successfully + content: + application/json: + schema: + $ref: '#/components/schemas/NonceResponse' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - {} + - jwtAuth: [] + /oid4vci/issuers: + get: + tags: + - OpenID for Verifiable Credential Issuance + summary: List all credential issuers + operationId: getCredentialIssuers + responses: + '200': + description: List the credential issuers + content: + application/json: + schema: + $ref: '#/components/schemas/CredentialIssuerPage' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -2702,36 +3464,26 @@ paths: schema: $ref: '#/components/schemas/ErrorResponse' security: - - adminApiKeyAuth: [] - apiKeyAuth: [] - jwtAuth: [] - /wallets/{walletId}/uma-permissions: post: tags: - - Wallet Management - summary: Create a UMA resource permission on an authorization server for the - wallet. - description: "Create a UMA resource permission on an authorization server for\ - \ the wallet.\nThis grants the wallet permission to the specified `subject`,\ - \ where the `subject` denotes the identity of the tenant on an authorization\ - \ server.\n " - operationId: createWalletUmaPermission - parameters: - - name: walletId - in: path - required: true - schema: - type: string - format: uuid + - OpenID for Verifiable Credential Issuance + summary: Create a new credential issuer + operationId: createCredentialIssuer requestBody: content: application/json: schema: - $ref: '#/components/schemas/CreateWalletUmaPermissionRequest' + $ref: '#/components/schemas/CreateCredentialIssuerRequest' required: true responses: - '200': - description: UMA resource permission is created on an authorization server + '201': + description: Credential issuer created successfully + content: + application/json: + schema: + $ref: '#/components/schemas/CredentialIssuer' '400': description: Invalid request parameters content: @@ -2744,6 +3496,18 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -2751,35 +3515,26 @@ paths: schema: $ref: '#/components/schemas/ErrorResponse' security: - - adminApiKeyAuth: [] - apiKeyAuth: [] - jwtAuth: [] + /oid4vci/issuers/{issuerId}: delete: tags: - - Wallet Management - summary: Delete a UMA resource permission on an authorization server for the - wallet. - description: "Remove a UMA resource permission on an authorization server for\ - \ the wallet.\nThis remove the wallet permission to the specified `subject`,\ - \ where the `subject` denotes the identity of the tenant on an authorization\ - \ server.\n " - operationId: deleteWalletUmaPermission + - OpenID for Verifiable Credential Issuance + summary: Delete the credential issuer + operationId: deleteCredentialIssuer parameters: - - name: walletId + - name: issuerId in: path + description: An issuer identifier in the oid4vci protocol required: true schema: type: string format: uuid - - name: subject - in: query - required: true - schema: - type: string - format: uuid + example: f47ac10b-58cc-4372-a567-0e02b2c3d479 responses: '200': - description: UMA resource permission is removed from an authorization server. + description: Credential issuer deleted successfully '400': description: Invalid request parameters content: @@ -2792,6 +3547,18 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -2799,22 +3566,35 @@ paths: schema: $ref: '#/components/schemas/ErrorResponse' security: - - adminApiKeyAuth: [] - apiKeyAuth: [] - jwtAuth: [] - /events/webhooks: - get: + patch: tags: - - Events - summary: List wallet webhook notifications - operationId: getEventsWebhooks + - OpenID for Verifiable Credential Issuance + summary: Update the credential issuer + operationId: updateCredentialIssuer + parameters: + - name: issuerId + in: path + description: An issuer identifier in the oid4vci protocol + required: true + schema: + type: string + format: uuid + example: f47ac10b-58cc-4372-a567-0e02b2c3d479 + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PatchCredentialIssuerRequest' + required: true responses: '200': - description: List wallet webhook notifications + description: Credential issuer updated successfully content: application/json: schema: - $ref: '#/components/schemas/WebhookNotificationPage' + $ref: '#/components/schemas/CredentialIssuer' '400': description: Invalid request parameters content: @@ -2827,6 +3607,18 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -2836,24 +3628,100 @@ paths: security: - apiKeyAuth: [] - jwtAuth: [] + /oid4vci/issuers/{issuerId}/credential-configurations: post: tags: - - Events - summary: Create wallet webhook notifications - operationId: postEventsWebhooks + - OpenID for Verifiable Credential Issuance + summary: Create a new credential configuration + description: |- + Create a new credential configuration for the issuer. + It represents the configuration of the credential that can be issued by the issuer. + This credential configuration object will be displayed in the credential issuer metadata. + operationId: createCredentialConfiguration + parameters: + - name: issuerId + in: path + description: An issuer identifier in the oid4vci protocol + required: true + schema: + type: string + format: uuid + example: f47ac10b-58cc-4372-a567-0e02b2c3d479 requestBody: content: application/json: schema: - $ref: '#/components/schemas/CreateWebhookNotification' + $ref: '#/components/schemas/CreateCredentialConfigurationRequest' + required: true + responses: + '201': + description: Credential configuration created successfully + content: + application/json: + schema: + $ref: '#/components/schemas/CredentialConfiguration' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '403': + description: Forbidden + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + security: + - apiKeyAuth: [] + - jwtAuth: [] + /oid4vci/issuers/{issuerId}/credential-configurations/{credentialConfigId}: + get: + tags: + - OpenID for Verifiable Credential Issuance + summary: Get the credential configuration + operationId: getCredentialConfiguration + parameters: + - name: issuerId + in: path + description: An issuer identifier in the oid4vci protocol + required: true + schema: + type: string + format: uuid + example: f47ac10b-58cc-4372-a567-0e02b2c3d479 + - name: credentialConfigId + in: path + description: An identifier for the credential configuration required: true + schema: + type: string + example: UniversityDegree responses: '200': - description: Webhook notification has been created successfully + description: Get credential configuration successfully content: application/json: schema: - $ref: '#/components/schemas/WebhookNotification' + $ref: '#/components/schemas/CredentialConfiguration' '400': description: Invalid request parameters content: @@ -2866,8 +3734,14 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' - '409': - description: Cannot process due to conflict with current state of the resource + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request content: application/json: schema: @@ -2881,23 +3755,30 @@ paths: security: - apiKeyAuth: [] - jwtAuth: [] - /events/webhooks/{id}: delete: tags: - - Events - summary: Delete the wallet webhook notification by `id` - operationId: deleteEventsWebhooksId + - OpenID for Verifiable Credential Issuance + summary: Delete the credential configuration + operationId: deleteCredentialConfiguration parameters: - - name: id + - name: issuerId in: path - description: ID of the webhook notification to delete. + description: An issuer identifier in the oid4vci protocol required: true schema: type: string format: uuid + example: f47ac10b-58cc-4372-a567-0e02b2c3d479 + - name: credentialConfigId + in: path + description: An identifier for the credential configuration + required: true + schema: + type: string + example: UniversityDegree responses: '200': - description: Webhook notification has been deleted. + description: Credential configuration deleted successfully '400': description: Invalid request parameters content: @@ -2916,6 +3797,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' '500': description: Internal server error content: @@ -2925,6 +3812,52 @@ paths: security: - apiKeyAuth: [] - jwtAuth: [] + /oid4vci/issuers/{issuerId}/.well-known/openid-credential-issuer: + get: + tags: + - OpenID for Verifiable Credential Issuance + summary: Get the credential issuer metadata + operationId: getIssuerMetadata + parameters: + - name: issuerId + in: path + description: An issuer identifier in the oid4vci protocol + required: true + schema: + type: string + format: uuid + example: f47ac10b-58cc-4372-a567-0e02b2c3d479 + responses: + '200': + description: Issuer Metadata successfully retrieved + content: + application/json: + schema: + $ref: '#/components/schemas/IssuerMetadata' + '400': + description: Invalid request parameters + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: Resource could not be found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '422': + description: Unable to process the request + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' components: schemas: AcceptConnectionInvitationRequest: @@ -2946,6 +3879,13 @@ components: The short-form subject Prism DID to which the JWT verifiable credential will be issued. This parameter only applies if the offer is of type 'JWT'. example: did:prism:3bb0505d13fcb04d28a48234edb27b0d4e6d7e18a81e2c1abab58f3bbc21ce6f + keyId: + type: string + description: |2 + + The short-form subject Prism DID to which the JWT verifiable credential will be issued. + This parameter only applies if the offer is of type 'JWT'. + example: did:prism:3bb0505d13fcb04d28a48234edb27b0d4e6d7e18a81e2c1abab58f3bbc21ce6f ActionType: type: string enum: @@ -3047,6 +3987,24 @@ components: $ref: '#/components/schemas/Map_String' non_revoked: $ref: '#/components/schemas/AnoncredNonRevokedIntervalV1' + AnoncredsCredentialRequest: + required: + - format + - anoncreds + type: object + properties: + format: + $ref: '#/components/schemas/CredentialFormat' + proof: + $ref: '#/components/schemas/Proof2' + credential_identifier: + type: string + credential_response_encryption: + $ref: '#/components/schemas/CredentialResponseEncryption' + credential_definition: + $ref: '#/components/schemas/CredentialDefinition' + anoncreds: + type: string ApiKeyAuthenticationRequest: required: - entityId @@ -3070,6 +4028,19 @@ components: elements: type: array items: {} + AuthorizationServer: + required: + - url + - clientId + - clientSecret + type: object + properties: + url: + type: string + clientId: + type: string + clientSecret: + type: string Bool: required: - value @@ -3077,6 +4048,17 @@ components: properties: value: type: boolean + ClaimDescriptor: + type: object + properties: + mandatory: + type: boolean + value_type: + type: string + display: + type: array + items: + $ref: '#/components/schemas/Localization' Connection: required: - connectionId @@ -3230,7 +4212,7 @@ components: type: string description: The URL that uniquely identifies the resource being returned in the response. - example: /prism-agent/connections?offset=10&limit=10 + example: /cloud-agent/connections?offset=10&limit=10 pageOf: type: string description: A string field indicating the type of resource that the contents @@ -3241,13 +4223,13 @@ components: description: An optional string field containing the URL of the next page of results. If the API response does not contain any more pages, this field should be set to None. - example: /prism-agent/connections?offset=20&limit=10 + example: /cloud-agent/connections?offset=20&limit=10 previous: type: string description: An optional string field containing the URL of the previous page of results. If the API response is the first page of results, this field should be set to None. - example: /prism-agent/connections?offset=0&limit=10 + example: /cloud-agent/connections?offset=0&limit=10 CreateConnectionRequest: type: object properties: @@ -3266,6 +4248,29 @@ components: description: A self-attested string that the receiver may want to display to the user about the context-specific goal of the out-of-band message. example: To issue a Faber College Graduate credential + CreateCredentialConfigurationRequest: + required: + - configurationId + - format + - schemaId + type: object + properties: + configurationId: + type: string + format: + $ref: '#/components/schemas/CredentialFormat' + schemaId: + type: string + CreateCredentialIssuerRequest: + required: + - authorizationServer + type: object + properties: + id: + type: string + format: uuid + authorizationServer: + $ref: '#/components/schemas/AuthorizationServer' CreateEntityRequest: required: - name @@ -3309,7 +4314,7 @@ components: The URL pointing to the JSON schema that will be used for this offer (should be 'http' or 'https'). When dereferenced, the returned content should be a JSON schema compliant with the '[Draft 2020-12](https://json-schema.org/draft/2020-12/release-notes)' version of the specification. Note that this parameter only applies when the offer is of type 'JWT'. - example: https://agent-host.com/prism-agent/schema-registry/schemas/d9569cec-c81e-4779-aa86-0d5994d82676/schema + example: https://agent-host.com/cloud-agent/schema-registry/schemas/d9569cec-c81e-4779-aa86-0d5994d82676/schema credentialDefinitionId: type: string description: |2 @@ -3426,8 +4431,43 @@ components: properties: url: type: string + description: A URL of webhook for event notification + example: http://example.com customHeaders: $ref: '#/components/schemas/Map_String' + CredentialConfiguration: + required: + - configurationId + - format + - scope + - schemaId + - createdAt + type: object + properties: + configurationId: + type: string + format: + $ref: '#/components/schemas/CredentialFormat' + scope: + type: string + schemaId: + type: string + createdAt: + type: string + format: date-time + CredentialDefinition: + type: object + properties: + '@context': + type: array + items: + type: string + type: + type: array + items: + type: string + credentialSubject: + $ref: '#/components/schemas/Map_ClaimDescriptor' CredentialDefinitionInput: required: - name @@ -3473,7 +4513,7 @@ components: type: string description: The unique identifier of the schema used for this credential definition. - example: https://agent-host.com/prism-agent/schema-registry/schemas/d9569cec-c81e-4779-aa86-0d5994d82676 + example: https://agent-host.com/cloud-agent/schema-registry/schemas/d9569cec-c81e-4779-aa86-0d5994d82676 signatureType: type: string description: Signature type used in the CredentialDefinition. @@ -3555,7 +4595,7 @@ components: type: string description: The unique identifier of the schema used for this credential definition. - example: https://agent-host.com/prism-agent/schema-registry/schemas/d9569cec-c81e-4779-aa86-0d5994d82676 + example: https://agent-host.com/cloud-agent/schema-registry/schemas/d9569cec-c81e-4779-aa86-0d5994d82676 definition: description: Definition object that represents the actual definition of the credential. @@ -3590,7 +4630,7 @@ components: type: string description: The URL that uniquely identifies the resource being returned in the response. - example: /prism-agent/credential-definition-registry/schemas/0527aea1-d131-3948-a34d-03af39aba8b4 + example: /cloud-agent/credential-definition-registry/schemas/0527aea1-d131-3948-a34d-03af39aba8b4 CredentialDefinitionResponsePage: required: - kind @@ -3613,24 +4653,132 @@ components: self: type: string description: A string field containing the URL of the current API endpoint - example: /prism-agent/schema-registry/schemas?skip=10&limit=10 + example: /cloud-agent/schema-registry/schemas?skip=10&limit=10 pageOf: type: string description: A string field indicating the type of resource that the contents field contains - example: /prism-agent/schema-registry/schemas + example: /cloud-agent/schema-registry/schemas next: type: string description: An optional string field containing the URL of the next page of results. If the API response does not contain any more pages, this field should be set to None. - example: /prism-agent/schema-registry/schemas?skip=20&limit=10 + example: /cloud-agent/schema-registry/schemas?skip=20&limit=10 previous: type: string description: An optional string field containing the URL of the previous page of results. If the API response is the first page of results, this field should be set to None. - example: /prism-agent/schema-registry/schemas?skip=0&limit=10 + example: /cloud-agent/schema-registry/schemas?skip=0&limit=10 + CredentialErrorCode: + type: string + enum: + - insufficient_scope + - invalid_credential_request + - invalid_encryption_parameters + - invalid_proof + - invalid_request + - invalid_token + - unsupported_credential_format + - unsupported_credential_type + CredentialErrorResponse: + required: + - error + type: object + properties: + error: + $ref: '#/components/schemas/CredentialErrorCode' + error_description: + type: string + c_nonce: + type: string + c_nonce_expires_in: + type: integer + format: int64 + CredentialFormat: + type: string + enum: + - anoncreds + - jwt_vc_json + - vc+sd-jwt + CredentialIssuer: + required: + - id + - authorizationServerUrl + type: object + properties: + id: + type: string + format: uuid + authorizationServerUrl: + type: string + CredentialIssuerPage: + required: + - self + - kind + - pageOf + type: object + properties: + self: + type: string + kind: + type: string + pageOf: + type: string + next: + type: string + previous: + type: string + contents: + type: array + items: + $ref: '#/components/schemas/CredentialIssuer' + CredentialOfferRequest: + required: + - credentialConfigurationId + - issuingDID + - claims + type: object + properties: + credentialConfigurationId: + type: string + issuingDID: + type: string + claims: {} + CredentialOfferResponse: + required: + - credentialOffer + type: object + properties: + credentialOffer: + type: string + CredentialRequest: + oneOf: + - $ref: '#/components/schemas/AnoncredsCredentialRequest' + - $ref: '#/components/schemas/JwtCredentialRequest' + discriminator: + propertyName: format + mapping: + anoncreds: '#/components/schemas/AnoncredsCredentialRequest' + jwt_vc_json: '#/components/schemas/JwtCredentialRequest' + CredentialResponse: + oneOf: + - $ref: '#/components/schemas/DeferredCredentialResponse' + - $ref: '#/components/schemas/ImmediateCredentialResponse' + CredentialResponseEncryption: + required: + - jwk + - alg + - enc + type: object + properties: + jwk: + type: string + alg: + type: string + enc: + type: string CredentialSchemaInput: required: - name @@ -3823,7 +4971,7 @@ components: type: string description: The URL that uniquely identifies the resource being returned in the response. - example: /prism-agent/schema-registry/schemas/0527aea1-d131-3948-a34d-03af39aba8b4 + example: /cloud-agent/schema-registry/schemas/0527aea1-d131-3948-a34d-03af39aba8b4 CredentialSchemaResponsePage: required: - kind @@ -3846,36 +4994,31 @@ components: self: type: string description: A string field containing the URL of the current API endpoint - example: /prism-agent/schema-registry/schemas?skip=10&limit=10 + example: /cloud-agent/schema-registry/schemas?skip=10&limit=10 pageOf: type: string description: A string field indicating the type of resource that the contents field contains - example: /prism-agent/schema-registry/schemas + example: /cloud-agent/schema-registry/schemas next: type: string description: An optional string field containing the URL of the next page of results. If the API response does not contain any more pages, this field should be set to None. - example: /prism-agent/schema-registry/schemas?skip=20&limit=10 + example: /cloud-agent/schema-registry/schemas?skip=20&limit=10 previous: type: string description: An optional string field containing the URL of the previous page of results. If the API response is the first page of results, this field should be set to None. - example: /prism-agent/schema-registry/schemas?skip=0&limit=10 + example: /cloud-agent/schema-registry/schemas?skip=0&limit=10 CredentialSubject: required: - - id - type - statusPurpose - encodedList type: object properties: - id: - type: string - description: Url to resolve this particular status list credential - example: http://issuer-agent.com/credential-status/060a2bec-6d6f-4c1f-9414-d3c9dbd3ccc9 type: type: string description: Always equals to constnat value - StatusList2021 @@ -3887,6 +5030,25 @@ components: description: base64 url encoded bitstring of credential statuses example: H4sIAAAAAAAA_-3BMQEAAADCoPVPbQwfoAAAAAAAAAAAAAAAAAAAAIC3AYbSVKsAQAAA description: Object containing claims specific to status list credential + Curve: + type: string + description: The curve name of the verification material in the DID Document. + Defaults to `secp256k1` if not specified. + example: Ed25519 + enum: + - Ed25519 + - X25519 + - secp256k1 + CwtProof: + required: + - proof_type + - cwt + type: object + properties: + proof_type: + $ref: '#/components/schemas/ProofType' + cwt: + type: string DIDDocument: required: - id @@ -4013,6 +5175,31 @@ components: $ref: '#/components/schemas/DIDDocumentMetadata' didResolutionMetadata: $ref: '#/components/schemas/DIDResolutionMetadata' + DateTimeParameter: + required: + - dateTime + - parameterType + type: object + properties: + dateTime: + type: string + description: The date and time to use for verification. + format: date-time + example: '2022-03-10T12:00:00Z' + parameterType: + type: string + DeferredCredentialResponse: + required: + - transaction_id + type: object + properties: + transaction_id: + type: string + c_nonce: + type: string + c_nonce_expires_in: + type: integer + format: int32 DidOperationSubmission: required: - id @@ -4027,6 +5214,18 @@ components: type: string description: A DID affected by the scheduled operation example: did:prism:4a5b5cf0a513e83b598bbea25cd6196746747f361a73ef77068268bc9bd732ff + DidParameter: + required: + - did + - parameterType + type: object + properties: + did: + type: string + description: The DID (Decentralized Identifier) to use for verification. + example: did:prism:issuer + parameterType: + type: string EntityResponse: required: - kind @@ -4045,7 +5244,7 @@ components: self: type: string description: The `self` link of the entity. - example: http://localhost:8080/prism-agent/iam/entities/00000000-0000-0000-0000-000000000000 + example: http://localhost:8080/cloud-agent/iam/entities/00000000-0000-0000-0000-000000000000 id: type: string description: The unique `id` of the entity @@ -4084,8 +5283,13 @@ components: description: A sequence of CredentialSchemaResponse objects representing the list of credential schemas that the API response contains example: - - EntityResponse(Entity,/prism-agent/iam/entities/00000000-0000-0000-0000-000000000000,00000000-0000-0000-0000-000000000000,John - Doe,00000000-0000-0000-0000-000000000000,2023-01-01T00:00:00Z,2023-01-01T00:00:00Z) + - kind: Entity + self: /cloud-agent/iam/entities/00000000-0000-0000-0000-000000000000 + id: 00000000-0000-0000-0000-000000000000 + name: John Doe + walletId: 00000000-0000-0000-0000-000000000000 + createdAt: '2023-01-01T00:00:00Z' + updatedAt: '2023-01-01T00:00:00Z' kind: type: string description: A string field indicating the type of the API response. In @@ -4094,24 +5298,24 @@ components: self: type: string description: A string field containing the URL of the current API endpoint - example: /prism-agent/schema-registry/schemas?skip=10&limit=10 + example: /cloud-agent/schema-registry/schemas?skip=10&limit=10 pageOf: type: string description: A string field indicating the type of resource that the contents field contains - example: /prism-agent/schema-registry/schemas + example: /cloud-agent/schema-registry/schemas next: type: string description: An optional string field containing the URL of the next page of results. If the API response does not contain any more pages, this field should be set to None. - example: /prism-agent/schema-registry/schemas?skip=20&limit=10 + example: /cloud-agent/schema-registry/schemas?skip=20&limit=10 previous: type: string description: An optional string field containing the URL of the previous page of results. If the API response is the first page of results, this field should be set to None. - example: /prism-agent/schema-registry/schemas?skip=0&limit=10 + example: /cloud-agent/schema-registry/schemas?skip=0&limit=10 ErrorResponse: required: - status @@ -4144,6 +5348,10 @@ components: description: A URI reference that identifies the specific occurrence of the problem. It may or may not yield further information if dereferenced. example: The received '{}à!è@!.b}' email does not conform to the email format + ExtendedErrorResponse: + oneOf: + - $ref: '#/components/schemas/CredentialErrorResponse' + - $ref: '#/components/schemas/ErrorResponse' HealthInfo: required: - version @@ -4153,6 +5361,18 @@ components: type: string description: The semantic version number of the running service example: 1.1.0 + ImmediateCredentialResponse: + required: + - credential + type: object + properties: + credential: + type: string + c_nonce: + type: string + c_nonce_expires_in: + type: integer + format: int32 IssueCredentialRecord: required: - recordId @@ -4221,7 +5441,7 @@ components: type: string description: The date and time when the issue credential record was created. format: date-time - example: '2024-03-20T18:35:17.903237532Z' + example: '2024-07-15T12:05:35.865441004Z' updatedAt: type: string description: The date and time when the issue credential record was last @@ -4295,24 +5515,41 @@ components: type: string description: The URL that uniquely identifies the resource being returned in the response. - example: /prism-agent/issue-credentials/records?offset=10&limit=10 + example: /cloud-agent/issue-credentials/records?offset=10&limit=10 pageOf: type: string description: A string field indicating the type of resource that the contents field contains. - example: /prism-agent/issue-credentials/records + example: /cloud-agent/issue-credentials/records next: type: string description: An optional string field containing the URL of the next page of results. If the API response does not contain any more pages, this field should be set to None. - example: /prism-agent/issue-credentials/records?offset=20&limit=10 + example: /cloud-agent/issue-credentials/records?offset=20&limit=10 previous: type: string description: An optional string field containing the URL of the previous page of results. If the API response is the first page of results, this field should be set to None. - example: /prism-agent/issue-credentials/records?offset=0&limit=10 + example: /cloud-agent/issue-credentials/records?offset=0&limit=10 + IssuerMetadata: + required: + - credential_issuer + - credential_endpoint + - credential_configurations_supported + type: object + properties: + credential_issuer: + type: string + authorization_servers: + type: array + items: + type: string + credential_endpoint: + type: string + credential_configurations_supported: + $ref: '#/components/schemas/Map_SupportedCredentialConfiguration' Json: description: The service endpoint. Can contain multiple possible values as described in the [Create DID operation](https://github.com/input-output-hk/prism-did-method-spec/blob/main/w3c-spec/PRISM-method.md#create-did) @@ -4324,6 +5561,51 @@ components: - $ref: '#/components/schemas/Num' - $ref: '#/components/schemas/Obj' - $ref: '#/components/schemas/Str' + JwtCredentialRequest: + required: + - format + type: object + properties: + format: + $ref: '#/components/schemas/CredentialFormat' + proof: + $ref: '#/components/schemas/Proof2' + credential_identifier: + type: string + credential_response_encryption: + $ref: '#/components/schemas/CredentialResponseEncryption' + credential_definition: + $ref: '#/components/schemas/CredentialDefinition' + JwtProof: + required: + - proof_type + - jwt + type: object + properties: + proof_type: + $ref: '#/components/schemas/ProofType' + jwt: + type: string + LdpProof: + required: + - proof_type + - vp + type: object + properties: + proof_type: + $ref: '#/components/schemas/ProofType' + vp: + type: string + Localization: + required: + - name + - locale + type: object + properties: + name: + type: string + locale: + type: string ManagedDID: required: - did @@ -4357,6 +5639,8 @@ components: example: key-1 purpose: $ref: '#/components/schemas/Purpose' + curve: + $ref: '#/components/schemas/Curve' description: A key-pair template to add to DID document. ManagedDIDPage: required: @@ -4387,10 +5671,36 @@ components: type: object additionalProperties: $ref: '#/components/schemas/AnoncredRequestedPredicateV1' + Map_ClaimDescriptor: + type: object + additionalProperties: + $ref: '#/components/schemas/ClaimDescriptor' Map_String: type: object additionalProperties: type: string + Map_SupportedCredentialConfiguration: + type: object + additionalProperties: + $ref: '#/components/schemas/SupportedCredentialConfiguration' + NonceRequest: + required: + - issuerState + type: object + properties: + issuerState: + type: string + NonceResponse: + required: + - nonce + - nonceExpiresIn + type: object + properties: + nonce: + type: string + nonceExpiresIn: + type: integer + format: int64 'Null': type: object Num: @@ -4418,6 +5728,24 @@ components: example: https://example-verifier.com description: The options to use when creating the proof presentation request (e.g., domain, challenge). + ParameterizableVcVerification: + required: + - verification + type: object + properties: + verification: + $ref: '#/components/schemas/VcVerification' + parameter: + $ref: '#/components/schemas/VcVerificationParameter' + PatchAuthorizationServer: + type: object + properties: + url: + type: string + clientId: + type: string + clientSecret: + type: string PatchContextAction: type: object properties: @@ -4427,6 +5755,11 @@ components: type: string description: The JSON-LD context describing the JSON document example: https://didcomm.org/messaging/contexts/v2 + PatchCredentialIssuerRequest: + type: object + properties: + authorizationServer: + $ref: '#/components/schemas/PatchAuthorizationServer' PresentationStatus: required: - presentationId @@ -4468,6 +5801,7 @@ components: - PresentationSent - PresentationReceived - PresentationVerified + - PresentationVerificationFailed - PresentationAccepted - PresentationRejected - ProblemReportPending @@ -4509,7 +5843,38 @@ components: items: $ref: '#/components/schemas/PresentationStatus' description: A sequence of Presentation objects. - example: [] + example: + - presentationId: 938bfc23-f78d-4734-9bf3-6dccf300856f + thid: 04112f4d-e894-4bff-a706-85b3e7190a2c + role: Verifier + status: RequestSent + proofs: [] + data: [] + connectionId: e0d81be9-47ca-4e0b-b8a7-325e8c3abc2f + metaRetries: 5 + - presentationId: d22158b0-c650-48ea-be85-2920a845ef26 + thid: 04112f4d-e894-4bff-a706-85b3e7190a2c + role: Prover + status: RequestReceived + proofs: [] + data: [] + metaRetries: 5 + - presentationId: fd3f5e54-fae9-4f72-9413-ec66aab83a57 + thid: 6b42fd91-4c98-40ae-a371-a1fd1a39e05e + role: Prover + status: PresentationPending + proofs: [] + data: [] + metaRetries: 5 + - presentationId: e56dd3e0-79d0-45f4-ba6c-ff857211b07b + thid: 6b42fd91-4c98-40ae-a371-a1fd1a39e05e + role: Verifier + status: PresentationVerified + proofs: [] + data: + - '{"claimsToDisclose":{"emailAddress":{},"givenName":{}},"presentation":"{\"protected\":\"eyJhbGciOiJFZERTQSJ9\",\"payload\":\"eyJfc2QiOlsiMGl4d0tIV0dzbzFvZThFR0hQd2tGYW9EZE1TRFQ3SmgyNkZGSm1ZbGRnRSIsIjQ4VlFXZS1tcjBibHMyOWpicHFKeDNxX2dYY0k5N3dHcEpsZnRoNXQwMGciLCI0Wk9xanFNZVNUVHRKQTNJRExsc3ZXN0dTNzRIemNxY3N2NVFoZk1valE4IiwiUjhGRE0ydXB1V09mNmVJMVA5ckNPdG12c3puVWFFYXpncVNuN0JfeTE0MCIsIlU5MmpfUHlpcHN2TERNQTlDaVRWbnl3bUFzYTM4S2lDWm5TeVhyUE5mNG8iLCJldFB1Mmc5ajdRd01rZ3g5VnpEX1RnNTNUV3UydVpadk1KeHRnNEJ1WGJBIiwidGV3RG1LWklNcS10bUNrMkpqZU0wajNYbU1aUUFLN01heENVNlF4dm9OMCJdLCJfc2RfYWxnIjoic2hhLTI1NiIsImlzcyI6ImRpZDpwcmlzbToxMmEzOWI1YWEwZTcxODI3ZmMxYzYwMjg1ZDVlZWJjMTk0Yjg2NzFhYTJmY2QxZDM2NDBkMGYwMTBlMzliZmVlIiwiaWF0IjoxNzE3NDEwMzgzLCJleHAiOjE3MjAwMDIzODN9\",\"signature\":\"953FfSRU_0Y2q0ERrFPzbXJ_hkF0YQe5efwESaZwtXDCn8aanD3MUstp3lzqGZkhvcWRdtCCpIxzhy0zgKwLBg\",\"disclosures\":[\"WyI0SHF6MDZCeG5fRlJMb2hWX2lWNXp3IiwgImdpdmVuTmFtZSIsICJBbGljZSJd\",\"WyJLUnNYYU01c3NXZTl4UEhqQnNjT213IiwgImVtYWlsQWRkcmVzcyIsICJhbGljZUB3b25kZXJsYW5kLmNvbSJd\"],\"kb_jwt\":null}"}' + connectionId: e0d81be9-47ca-4e0b-b8a7-325e8c3abc2f + metaRetries: 5 self: type: string description: The reference to the presentation collection itself. @@ -4524,12 +5889,16 @@ components: example: 1 next: type: string - description: URL of the next page (if available) - example: '' + description: An optional string field containing the URL of the next page + of results. If the API response does not contain any more pages, this + field should be set to None. + example: /present-proof/presentations?offset=20&limit=10 previous: type: string - description: URL of the previous page (if available) - example: '' + description: An optional string field containing the URL of the previous + page of results. If the API response is the first page of results, this + field should be set to None. + example: /present-proof/presentations?offset=0&limit=10 Proof: required: - type @@ -4648,6 +6017,17 @@ components: proofValue: FiPfjknHikKmZ... jws: eyJhbGciOiJFZERTQSIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il0sImt0eSI6Ik... domain: prims.atala.com + Proof2: + oneOf: + - $ref: '#/components/schemas/CwtProof' + - $ref: '#/components/schemas/JwtProof' + - $ref: '#/components/schemas/LdpProof' + discriminator: + propertyName: proof_type + mapping: + cwt: '#/components/schemas/CwtProof' + jwt: '#/components/schemas/JwtProof' + ldp_vp: '#/components/schemas/LdpProof' ProofRequestAux: required: - schemaId @@ -4667,6 +6047,19 @@ components: - did:web:atalaprism.io/users/testUser - did.prism:123 - did:prism:... + ProofType: + type: string + enum: + - cwt + - jwt + - ldp_vp + ProofTypeConfiguration: + type: object + properties: + proof_signing_alg_values_supported: + type: array + items: + type: string PublicKeyJwk: required: - kty @@ -4720,6 +6113,12 @@ components: applicable on the prover side when the action is `request-accept`. anoncredPresentationRequest: $ref: '#/components/schemas/AnoncredCredentialProofsV1' + claims: + $ref: '#/components/schemas/Obj' + credentialFormat: + type: string + description: The credential format (default to 'JWT') + example: JWT RequestPresentationInput: required: - connectionId @@ -4742,6 +6141,8 @@ components: example: [] anoncredPresentationRequest: $ref: '#/components/schemas/AnoncredPresentationRequestV1' + claims: + $ref: '#/components/schemas/Obj' credentialFormat: type: string description: The credential format (default to 'JWT') @@ -4812,7 +6213,7 @@ components: type: string description: Issuance timestamp of status list credential format: date-time - example: '2024-03-20T18:35:17.949576647Z' + example: '2024-07-15T12:05:35.908034143Z' credentialSubject: $ref: '#/components/schemas/CredentialSubject' proof: @@ -4839,6 +6240,37 @@ components: properties: value: type: string + SupportProofType: + required: + - jwt + type: object + properties: + jwt: + $ref: '#/components/schemas/ProofTypeConfiguration' + SupportedCredentialConfiguration: + required: + - format + - scope + - credential_definition + - proof_types_supported + type: object + properties: + format: + $ref: '#/components/schemas/CredentialFormat' + scope: + type: string + credential_definition: + $ref: '#/components/schemas/CredentialDefinition' + cryptographic_binding_methods_supported: + type: array + items: + type: string + credential_signing_alg_values_supported: + type: array + items: + type: string + proof_types_supported: + $ref: '#/components/schemas/SupportProofType' Suspension: type: object UpdateEntityNameRequest: @@ -4913,6 +6345,119 @@ components: $ref: '#/components/schemas/Json' description: A patch to existing Service. 'type' and 'serviceEndpoint' cannot both be empty. + VcVerification: + type: string + description: The type of verification to perform. + enum: + - AlgorithmVerification + - AudienceCheck + - ComplianceWithStandards + - ExpirationCheck + - IntegrityOfClaims + - IssuerIdentification + - NotBeforeCheck + - RevocationCheck + - SchemaCheck + - SemanticCheckOfClaims + - SignatureVerification + - SubjectVerification + VcVerificationParameter: + description: Optional parameter for the verification. + oneOf: + - $ref: '#/components/schemas/DateTimeParameter' + - $ref: '#/components/schemas/DidParameter' + discriminator: + propertyName: parameterType + mapping: + DateTimeParameter: '#/components/schemas/DateTimeParameter' + DidParameter: '#/components/schemas/DidParameter' + VcVerificationRequest: + required: + - credential + type: object + properties: + credential: + type: string + description: Encoded Verifiable Credential to verify + example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c + verifications: + type: array + items: + $ref: '#/components/schemas/ParameterizableVcVerification' + description: The list of verifications to perform on the credential. If + the list is empty, all available verifications will be performed. + example: + - verification: SignatureVerification + - verification: IssuerIdentification + parameter: + did: did:prism:issuer + - verification: ExpirationCheck + parameter: + dateTime: '2022-03-10T12:00:00Z' + - verification: NotBeforeCheck + parameter: + dateTime: '2022-03-10T12:00:00Z' + - verification: AudienceCheck + parameter: + did: did:prism:holder + - verification: SubjectVerification + - verification: IntegrityOfClaims + - verification: ComplianceWithStandards + - verification: RevocationCheck + - verification: AlgorithmVerification + - verification: SchemaCheck + - verification: SemanticCheckOfClaims + VcVerificationResponse: + required: + - credential + type: object + properties: + credential: + type: string + description: Encoded Verifiable Credential that was verified. + example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c + result: + type: array + items: + $ref: '#/components/schemas/VcVerificationResult' + description: The list of verification results for each verification performed + on the credential. + example: + - verification: SignatureVerification + success: true + - verification: IssuerIdentification + success: true + - verification: ExpirationCheck + success: true + - verification: NotBeforeCheck + success: true + - verification: AudienceCheck + success: true + - verification: SubjectVerification + success: true + - verification: IntegrityOfClaims + success: true + - verification: ComplianceWithStandards + success: true + - verification: RevocationCheck + success: true + - verification: AlgorithmVerification + success: true + - verification: SchemaCheck + success: true + - verification: SemanticCheckOfClaims + success: true + VcVerificationResult: + required: + - verification + - success + type: object + properties: + verification: + $ref: '#/components/schemas/VcVerification' + success: + type: boolean + description: Indicates whether the verification was successful. VerificationMethod: required: - id @@ -5003,7 +6548,7 @@ components: type: string description: The URL that uniquely identifies the resource being returned in the response. - example: /prism-agent/verification/policies/0527aea1-d131-3948-a34d-03af39aba8b4 + example: /cloud-agent/verification/policies/0527aea1-d131-3948-a34d-03af39aba8b4 kind: type: string description: A string that identifies the type of resource being returned @@ -5066,7 +6611,7 @@ components: type: string description: The URL that uniquely identifies the resource being returned in the response. - example: /prism-agent/verification/policies?name=Trusted&offset=0&limit=10 + example: /cloud-agent/verification/policies?name=Trusted&offset=0&limit=10 kind: type: string description: A string that identifies the type of resource being returned @@ -5076,19 +6621,19 @@ components: type: string description: A string field indicating the type of resource that the contents field contains - example: /prism-agent/verification/policies + example: /cloud-agent/verification/policies next: type: string description: An optional string field containing the URL of the next page of results. If the API response does not contain any more pages, this field should be set to None. - example: /prism-agent/verification/policies?skip=20&limit=10 + example: /cloud-agent/verification/policies?skip=20&limit=10 previous: type: string description: An optional string field containing the URL of the previous page of results. If the API response is the first page of results, this field should be set to None. - example: /prism-agent/verification/policies?skip=0&limit=10 + example: /cloud-agent/verification/policies?skip=0&limit=10 contents: type: array items: @@ -5096,7 +6641,7 @@ components: description: A sequence of VerificationPolicyResponse objects representing the list of verification policies that the paginated response contains example: - - self: /prism-agent/verification/policies + - self: /cloud-agent/verification/policies kind: VerificationPolicy id: 0527aea1-d131-3948-a34d-03af39aba8b4 nonce: 0 diff --git a/EdgeAgentSDK/Pollux/Sources/Models/JWT/JWTRevocationCheck.swift b/EdgeAgentSDK/Pollux/Sources/Models/JWT/JWTRevocationCheck.swift index b89c1c6c..9b8038c9 100644 --- a/EdgeAgentSDK/Pollux/Sources/Models/JWT/JWTRevocationCheck.swift +++ b/EdgeAgentSDK/Pollux/Sources/Models/JWT/JWTRevocationCheck.swift @@ -24,7 +24,7 @@ struct JWTRevocationCheck { let statusList = try JSONDecoder.didComm().decode(JWTRevocationStatusListCredential.self, from: listData) let encodedList = statusList.credentialSubject.encodedList let index = status.statusListIndex - return try verifyRevocationOnEncodedList(encodedList.tryToData(), index: index) + return try verifyRevocationOnEncodedList(Data(fromBase64URL: encodedList)!, index: index) } func verifyRevocationOnEncodedList(_ list: Data, index: Int) throws -> Bool { @@ -41,7 +41,7 @@ extension UInt8 { func toBits() -> [Bool] { var bits = [Bool](repeating: false, count: 8) for i in 0..<8 { - bits[7 - i] = (self & (1 << i)) != 0 + bits[i] = (self & (1 << i)) != 0 } return bits } diff --git a/EdgeAgentSDK/Pollux/Sources/Models/JWT/JWTRevocationStatus.swift b/EdgeAgentSDK/Pollux/Sources/Models/JWT/JWTRevocationStatus.swift index 36499d14..85568b65 100644 --- a/EdgeAgentSDK/Pollux/Sources/Models/JWT/JWTRevocationStatus.swift +++ b/EdgeAgentSDK/Pollux/Sources/Models/JWT/JWTRevocationStatus.swift @@ -4,12 +4,12 @@ struct JWTRevocationStatus: Codable { enum CredentialStatusListType: String, Codable { case statusList2021Entry = "StatusList2021Entry" } - + enum CredentialStatusPurpose: String, Codable { - case revocation - case suspension + case revocation = "Revocation" + case suspension = "Suspension" } - + let id: String let type: String let statusPurpose: CredentialStatusPurpose @@ -18,15 +18,77 @@ struct JWTRevocationStatus: Codable { } struct JWTRevocationStatusListCredential: Codable { + enum CodingKeys: String, CodingKey { + case context = "@context" + case type + case issuer + case id + case issuanceDate + case credentialSubject + case proof + } + struct StatusListCredentialSubject: Codable { let type: String let statusPurpose: String let encodedList: String } - let context: [String] - let type: [String] + + let context: Set + let type: Set let id: String let issuer: String - let issuanceDate: String + let issuanceDate: Int let credentialSubject: StatusListCredentialSubject + + func encode(to encoder: any Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + try container.encode(self.context, forKey: .context) + try container.encode(self.type, forKey: .type) + try container.encode(self.id, forKey: .id) + try container.encode(self.issuer, forKey: .issuer) + try container.encode(self.issuanceDate, forKey: .issuanceDate) + try container.encode(self.credentialSubject, forKey: .credentialSubject) + } + + init( + context: Set, + type: Set, + id: String, + issuer: String, + issuanceDate: Int, + credentialSubject: StatusListCredentialSubject + ) { + self.context = context + self.type = type + self.id = id + self.issuer = issuer + self.issuanceDate = issuanceDate + self.credentialSubject = credentialSubject + } + + init(from decoder: any Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let context = (try? container.decode(Set.self, forKey: .context)) ?? Set() + let type: Set + if let value = try? container.decode(String.self, forKey: .type) { + type = Set([value]) + } else { + type = (try? container.decode(Set.self, forKey: .type)) ?? Set() + } + let id = try container.decode(String.self, forKey: .id) + let issuer = try container.decode(String.self, forKey: .issuer) + let issuanceDate = try container.decode(Int.self, forKey: .issuanceDate) + let credentialSubject = try container.decode(StatusListCredentialSubject.self, forKey: .credentialSubject) + + self.init( + context: context, + type: type, + id: id, + issuer: issuer, + issuanceDate: issuanceDate, + credentialSubject: credentialSubject + ) + } } diff --git a/EdgeAgentSDK/Pollux/Sources/PolluxImpl+CredentialVerification.swift b/EdgeAgentSDK/Pollux/Sources/PolluxImpl+CredentialVerification.swift index 933a9377..5574cd41 100644 --- a/EdgeAgentSDK/Pollux/Sources/PolluxImpl+CredentialVerification.swift +++ b/EdgeAgentSDK/Pollux/Sources/PolluxImpl+CredentialVerification.swift @@ -102,16 +102,13 @@ extension PolluxImpl { await credentials .asyncForEach { do { - guard try await verifyJWT(jwtString: $0) else { - errors.append(PolluxError.invalidSignature()) - return - } + try await verifyJWT(jwtString: $0) } catch { errors.append(error) } } guard errors.isEmpty else { - throw PolluxError.invalidSignature(internalErrors: errors) + throw PolluxError.cannotVerifyPresentationInputs(errors: errors) } } @@ -142,10 +139,10 @@ extension PolluxImpl { } let isRevoked = try await credential.isRevoked let isSuspended = try await credential.isSuspended - guard isRevoked else { + guard !isRevoked else { throw PolluxError.credentialIsRevoked(jwtString: jwtString) } - guard isSuspended else { + guard !isSuspended else { throw PolluxError.credentialIsSuspended(jwtString: jwtString) } }