/// <inheritdoc /> public virtual async Task <string> ProcessResponseAsync(IAgentContext agentContext, ConnectionResponseMessage response, ConnectionRecord connection) { Logger.LogTrace(LoggingEvents.AcceptConnectionResponse, "To {1}", connection.MyDid); await connection.TriggerAsync(ConnectionTrigger.Response); //TODO throw exception or a problem report if the connection request features a did doc that has no indy agent did doc convention featured //i.e there is no way for this agent to respond to messages. And or no keys specified var connectionObj = await SignatureUtils.UnpackAndVerifyAsync <Common.Connection>(response.ConnectionSig); await Did.StoreTheirDidAsync(agentContext.Wallet, new { did = connectionObj.Did, verkey = connectionObj.DidDoc.Keys[0].PublicKeyBase58 }.ToJson()); connection.TheirDid = connectionObj.Did; connection.TheirVk = connectionObj.DidDoc.Keys[0].PublicKeyBase58; connection.SetTag(TagConstants.LastThreadId, response.GetThreadId()); if (connectionObj.DidDoc.Services[0] is IndyAgentDidDocService service) { connection.Endpoint = new AgentEndpoint(service.ServiceEndpoint, null, service.RoutingKeys != null && service.RoutingKeys.Count > 0 ? service.RoutingKeys.ToArray() : null); } await RecordService.UpdateAsync(agentContext.Wallet, connection); EventAggregator.Publish(new ServiceMessageProcessingEvent { RecordId = connection.Id, MessageType = response.Type, ThreadId = response.GetThreadId() }); return(connection.Id); }
/// <inheritdoc/> public async Task <(DidExchangeResponseMessage, ConnectionRecord)> CreateResponseAsync(IAgentContext agentContext, ConnectionRecord connectionRecord) { await connectionRecord.TriggerAsync(ConnectionTrigger.Response); var myDid = await Did.CreateAndStoreMyDidAsync(agentContext.Wallet, "{}"); connectionRecord.MyDid = DidUtils.ConvertVerkeyToDidKey(myDid.VerKey); connectionRecord.MyVk = myDid.VerKey; var provisioningRecord = await _provisioningService.GetProvisioningAsync(agentContext.Wallet); var didDoc = new AttachmentContent { Base64 = connectionRecord.MyDidDoc(provisioningRecord).ToJson().ToBase64Url() }; await didDoc.SignWithJsonWebSignature(agentContext.Wallet, myDid.VerKey); var attachment = new Attachment { Id = Guid.NewGuid().ToString(), MimeType = "application/json", Data = didDoc }; var response = new DidExchangeResponseMessage { Id = Guid.NewGuid().ToString(), Did = connectionRecord.MyDid, DidDoc = attachment }; await _recordService.UpdateAsync(agentContext.Wallet, connectionRecord); return(response, connectionRecord); }
/// <inheritdoc /> public virtual async Task <string> AcceptInvitationAsync(Wallet wallet, ConnectionInvitationMessage invitation) { Logger.LogInformation(LoggingEvents.AcceptInvitation, "Key {0}, Endpoint {1}", invitation.ConnectionKey, invitation.Endpoint.Uri); var my = await Did.CreateAndStoreMyDidAsync(wallet, "{}"); var connection = new ConnectionRecord { Endpoint = invitation.Endpoint, MyDid = my.Did, MyVk = my.VerKey, ConnectionId = Guid.NewGuid().ToString().ToLowerInvariant() }; connection.Tags.Add(TagConstants.MyDid, my.Did); if (!string.IsNullOrEmpty(invitation.Name) || !string.IsNullOrEmpty(invitation.ImageUrl)) { connection.Alias = new ConnectionAlias { Name = invitation.Name, ImageUrl = invitation.ImageUrl }; if (string.IsNullOrEmpty(invitation.Name)) { connection.Tags.Add(TagConstants.Alias, invitation.Name); } } await connection.TriggerAsync(ConnectionTrigger.InvitationAccept); await RecordService.AddAsync(wallet, connection); var provisioning = await ProvisioningService.GetProvisioningAsync(wallet); var connectionDetails = new ConnectionDetails { Did = my.Did, Verkey = my.VerKey, Endpoint = provisioning.Endpoint }; var request = await MessageSerializer.PackSealedAsync <ConnectionRequestMessage>(connectionDetails, wallet, my.VerKey, invitation.ConnectionKey); request.Key = invitation.ConnectionKey; request.Type = MessageUtils.FormatKeyMessageType(invitation.ConnectionKey, MessageTypes.ConnectionRequest); var forwardMessage = new ForwardToKeyEnvelopeMessage { Type = MessageUtils.FormatKeyMessageType(invitation.ConnectionKey, MessageTypes.ForwardToKey), Content = request.ToJson() }; await RouterService.ForwardAsync(forwardMessage, invitation.Endpoint); return(connection.GetId()); }
/// <inheritdoc /> public virtual async Task <(ConnectionRequestMessage, ConnectionRecord)> CreateRequestAsync(IAgentContext agentContext, ConnectionInvitationMessage invitation) { Logger.LogInformation(LoggingEvents.AcceptInvitation, "Key {0}, Endpoint {1}", invitation.RecipientKeys[0], invitation.ServiceEndpoint); var my = await Did.CreateAndStoreMyDidAsync(agentContext.Wallet, "{}"); var connection = new ConnectionRecord { Endpoint = new AgentEndpoint(invitation.ServiceEndpoint, null, invitation.RoutingKeys != null && invitation.RoutingKeys.Count != 0 ? invitation.RoutingKeys[0] : null), MyDid = my.Did, MyVk = my.VerKey, Id = Guid.NewGuid().ToString().ToLowerInvariant() }; if (!string.IsNullOrEmpty(invitation.Label) || !string.IsNullOrEmpty(invitation.ImageUrl)) { connection.Alias = new ConnectionAlias { Name = invitation.Label, ImageUrl = invitation.ImageUrl }; if (string.IsNullOrEmpty(invitation.Label)) { connection.SetTag(TagConstants.Alias, invitation.Label); } } await connection.TriggerAsync(ConnectionTrigger.InvitationAccept); var provisioning = await ProvisioningService.GetProvisioningAsync(agentContext.Wallet); var request = new ConnectionRequestMessage { Connection = new Connection { Did = connection.MyDid, DidDoc = connection.MyDidDoc(provisioning) }, Label = provisioning.Owner?.Name, ImageUrl = provisioning.Owner?.ImageUrl }; // also set image as attachment if (provisioning.Owner?.ImageUrl != null) { request.AddAttachment(new Attachment { Nickname = "profile-image", Content = new AttachmentContent { Links = new[] { provisioning.Owner.ImageUrl } } }); } await RecordService.AddAsync(agentContext.Wallet, connection); return(request, connection); }
public async Task OnlyRequestAllowsTransitionFromInvitedToNegotiating(ConnectionTrigger trigger) { var record = new ConnectionRecord(); Assert.Equal(ConnectionState.Invited, record.State); await Assert.ThrowsAsync <InvalidOperationException>(async() => await record.TriggerAsync(trigger)); Assert.Equal(ConnectionState.Invited, record.State); }
public async Task CannotTransitionFromAbandonedToAnotherState(ConnectionTrigger trigger) { var record = new ConnectionRecord { State = ConnectionState.Abandoned }; await Assert.ThrowsAsync <InvalidOperationException>(async() => await record.TriggerAsync(trigger)); Assert.Equal(ConnectionState.Abandoned, record.State); }
public async Task CanTransitionFromDisconnetedToNegotiatingWithInvitationAccept() { var record = new ConnectionRecord(); Assert.True(ConnectionState.Invited == record.State); await record.TriggerAsync(ConnectionTrigger.InvitationAccept); Assert.True(ConnectionState.Negotiating == record.State); }
public async Task CanTransitionToAbandonedFromAllStates(ConnectionState state) { var record = new ConnectionRecord { State = state }; Assert.Equal(state, record.State); await record.TriggerAsync(ConnectionTrigger.Abandon); Assert.Equal(ConnectionState.Abandoned, record.State); }
public async Task CannotTransitionFromNegotiatingToConnectedWithRequest() { var record = new ConnectionRecord { State = ConnectionState.Negotiating }; Assert.True(ConnectionState.Negotiating == record.State); await Assert.ThrowsAsync <InvalidOperationException>(async() => await record.TriggerAsync(ConnectionTrigger.Request)); Assert.True(ConnectionState.Negotiating == record.State); }
public async Task CanTransitionFromNegotiatingToConnectedWithRequest() { var record = new ConnectionRecord { State = ConnectionState.Negotiating }; Assert.True(ConnectionState.Negotiating == record.State); await record.TriggerAsync(ConnectionTrigger.Request); Assert.True(ConnectionState.Connected == record.State); }
/// <inheritdoc/> public async Task <(DidExchangeCompleteMessage, ConnectionRecord)> CreateComplete(IAgentContext agentContext, ConnectionRecord connectionRecord) { await connectionRecord.TriggerAsync(ConnectionTrigger.Complete); await _recordService.UpdateAsync(agentContext.Wallet, connectionRecord); var completeMessage = new DidExchangeCompleteMessage { Id = Guid.NewGuid().ToString() }; return(completeMessage, connectionRecord); }
public async Task OnlyRespondAllowsTransitionFromNegotiatingToConnected(ConnectionTrigger trigger) { var record = new ConnectionRecord() { State = ConnectionState.Negotiating }; Assert.Equal(ConnectionState.Negotiating, record.State); await Assert.ThrowsAsync <InvalidOperationException>(async() => await record.TriggerAsync(trigger)); Assert.Equal(ConnectionState.Negotiating, record.State); }
/// <inheritdoc/> public async Task <ConnectionRecord> ProcessResponseAsync(IAgentContext agentContext, DidExchangeResponseMessage responseMessage, ConnectionRecord connectionRecord) { await connectionRecord.TriggerAsync(ConnectionTrigger.Response); DidDoc didDoc = null; if (responseMessage.DidDoc.Data.Base64 is { } data) { var isValidSignature = await responseMessage.DidDoc.Data.VerifyJsonWebSignature(); if (isValidSignature == false) { throw new AriesFrameworkException(ErrorCode.InvalidSignatureEncoding, "The given JSON web signature is invalid"); } var json = data.FromBase64Url(); didDoc = json.ToObject <DidDoc>(); } if (didDoc == null) { throw new NotImplementedException("Response message must provide an attached did document"); } if (didDoc.Keys.All(key => key.Type == DidDocExtensions.DefaultKeyType) == false) { throw new NotImplementedException($"Only {DidDocExtensions.DefaultKeyType} is supported"); } var indyService = (IndyAgentDidDocService)didDoc.Services.First(service => service is IndyAgentDidDocService); var agentEndpoint = new AgentEndpoint(indyService.ServiceEndpoint, null, indyService.RoutingKeys.ToArray()); connectionRecord.TheirDid = responseMessage.Did; connectionRecord.TheirVk = didDoc.Keys.FirstOrDefault(key => key.Controller == responseMessage.Did)?.PublicKeyBase58 ?? throw new NullReferenceException("Missing public key for controller"); connectionRecord.Endpoint = agentEndpoint; await _recordService.UpdateAsync(agentContext.Wallet, connectionRecord); _eventAggregator.Publish(new ServiceMessageProcessingEvent() { MessageType = responseMessage.Type, RecordId = connectionRecord.Id, ThreadId = responseMessage.GetThreadId() }); return(connectionRecord); }
public async Task CannotTransitionFromInvitedWithAccept() { var record = new ConnectionRecord { State = ConnectionState.Negotiating }; Assert.True(ConnectionState.Negotiating == record.State); var exception = await Assert.ThrowsAsync <InvalidOperationException>( () => record.TriggerAsync(ConnectionTrigger.InvitationAccept)); Assert.Equal("Stateless", exception.Source); }
/// <inheritdoc/> public async Task <ConnectionRecord> ProcessProblemReportMessage(IAgentContext agentContext, DidExchangeProblemReportMessage problemReportMessage, ConnectionRecord connectionRecord) { await connectionRecord.TriggerAsync(ConnectionTrigger.Abandon); await _recordService.UpdateAsync(agentContext.Wallet, connectionRecord); _eventAggregator.Publish(new ServiceMessageProcessingEvent { MessageType = problemReportMessage.Type, RecordId = connectionRecord.Id, ThreadId = problemReportMessage.GetThreadId() }); return(connectionRecord); }
/// <inheritdoc /> public virtual async Task <AcceptInvitationResult> AcceptInvitationAsync(IAgentContext agentContext, ConnectionInvitationMessage invitation) { Logger.LogInformation(LoggingEvents.AcceptInvitation, "Key {0}, Endpoint {1}", invitation.ConnectionKey, invitation.Endpoint.Uri); var my = await Did.CreateAndStoreMyDidAsync(agentContext.Wallet, "{}"); var connection = new ConnectionRecord { Endpoint = invitation.Endpoint, MyDid = my.Did, MyVk = my.VerKey, Id = Guid.NewGuid().ToString().ToLowerInvariant() }; if (!string.IsNullOrEmpty(invitation.Name) || !string.IsNullOrEmpty(invitation.ImageUrl)) { connection.Alias = new ConnectionAlias { Name = invitation.Name, ImageUrl = invitation.ImageUrl }; if (string.IsNullOrEmpty(invitation.Name)) { connection.SetTag(TagConstants.Alias, invitation.Name); } } await connection.TriggerAsync(ConnectionTrigger.InvitationAccept); await RecordService.AddAsync(agentContext.Wallet, connection); var provisioning = await ProvisioningService.GetProvisioningAsync(agentContext.Wallet); return(new AcceptInvitationResult { Request = new ConnectionRequestMessage { Did = my.Did, Verkey = my.VerKey, Endpoint = provisioning.Endpoint, Name = provisioning.Owner?.Name, ImageUrl = provisioning.Owner?.ImageUrl }, Connection = connection }); }
/// <inheritdoc/> public async Task <(DidExchangeProblemReportMessage, ConnectionRecord)> AbandonDidExchange(IAgentContext agentContext, ConnectionRecord connectionRecord) { await connectionRecord.TriggerAsync(ConnectionTrigger.Abandon); await _recordService.UpdateAsync(agentContext.Wallet, connectionRecord); var myRole = connectionRecord.Role; var problemCode = myRole == ConnectionRole.Invitee ? DidExchangeProblemReportMessage.Error.ResponseNotAccepted : DidExchangeProblemReportMessage.Error.RequestNotAccepted; var problemReport = new DidExchangeProblemReportMessage { ProblemCode = problemCode }; return(problemReport, connectionRecord); }
/// <inheritdoc /> public async Task <(ConnectionRequestMessage, ConnectionRecord)> CreateRequestAsync(IAgentContext agentContext, ConnectionRecord connection) { Logger.LogInformation(LoggingEvents.AcceptInvitation, "Key {0}, Endpoint {1}", connection.Endpoint.Verkey, connection.Endpoint.Uri); await connection.TriggerAsync(ConnectionTrigger.Request); var provisioning = await ProvisioningService.GetProvisioningAsync(agentContext.Wallet); var request = new ConnectionRequestMessage(agentContext.UseMessageTypesHttps) { Connection = new Common.Connection { Did = connection.MyDid, DidDoc = connection.MyDidDoc(provisioning) }, Label = provisioning.Owner?.Name, ImageUrl = provisioning.Owner?.ImageUrl }; // also set image as attachment if (provisioning.Owner?.ImageUrl != null) { request.AddAttachment(new Attachment { Nickname = "profile-image", Data = new AttachmentContent { Links = new[] { provisioning.Owner.ImageUrl } } }); } await RecordService.UpdateAsync(agentContext.Wallet, connection); return(request, connection); }
/// <inheritdoc /> public virtual async Task <string> ProcessRequestAsync(IAgentContext agentContext, ConnectionRequestMessage request, ConnectionRecord connection) { Logger.LogInformation(LoggingEvents.ProcessConnectionRequest, "Did {0}", request.Connection.Did); var my = await Did.CreateAndStoreMyDidAsync(agentContext.Wallet, "{}"); //TODO throw exception or a problem report if the connection request features a did doc that has no indy agent did doc convention featured //i.e there is no way for this agent to respond to messages. And or no keys specified await Did.StoreTheirDidAsync(agentContext.Wallet, new { did = request.Connection.Did, verkey = request.Connection.DidDoc.Keys[0].PublicKeyBase58 }.ToJson()); if (request.Connection.DidDoc.Services != null && request.Connection.DidDoc.Services.Count > 0 && request.Connection.DidDoc.Services[0] is IndyAgentDidDocService service) { connection.Endpoint = new AgentEndpoint(service.ServiceEndpoint, null, service.RoutingKeys != null && service.RoutingKeys.Count > 0 ? service.RoutingKeys.ToArray() : null); } connection.TheirDid = request.Connection.Did; connection.TheirVk = request.Connection.DidDoc.Keys[0].PublicKeyBase58; connection.MyDid = my.Did; connection.MyVk = my.VerKey; connection.SetTag(TagConstants.LastThreadId, request.Id); if (connection.Alias == null) { connection.Alias = new ConnectionAlias(); } if (!string.IsNullOrEmpty(request.Label) && string.IsNullOrEmpty(connection.Alias.Name)) { connection.Alias.Name = request.Label; } if (!string.IsNullOrEmpty(request.ImageUrl) && string.IsNullOrEmpty(connection.Alias.ImageUrl)) { connection.Alias.ImageUrl = request.ImageUrl; } if (!connection.MultiPartyInvitation) { await connection.TriggerAsync(ConnectionTrigger.Request); await RecordService.UpdateAsync(agentContext.Wallet, connection); EventAggregator.Publish(new ServiceMessageProcessingEvent { RecordId = connection.Id, MessageType = request.Type, ThreadId = request.GetThreadId() }); return(connection.Id); } else { var newConnection = connection.DeepCopy(); newConnection.Id = Guid.NewGuid().ToString(); newConnection.MultiPartyInvitation = false; await newConnection.TriggerAsync(ConnectionTrigger.Request); await RecordService.AddAsync(agentContext.Wallet, newConnection); EventAggregator.Publish(new ServiceMessageProcessingEvent { RecordId = newConnection.Id, MessageType = request.Type, ThreadId = request.GetThreadId() }); return(newConnection.Id); } }