github.com/hyperledger/aries-framework-go@v0.3.2/pkg/didcomm/protocol/mediator/service.go (about) 1 /* 2 Copyright SecureKey Technologies Inc. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package mediator 8 9 import ( 10 "encoding/json" 11 "errors" 12 "fmt" 13 "sync" 14 "time" 15 16 "github.com/cenkalti/backoff/v4" 17 "github.com/google/uuid" 18 19 "github.com/hyperledger/aries-framework-go/pkg/common/log" 20 "github.com/hyperledger/aries-framework-go/pkg/didcomm/common/model" 21 "github.com/hyperledger/aries-framework-go/pkg/didcomm/common/service" 22 "github.com/hyperledger/aries-framework-go/pkg/didcomm/dispatcher" 23 "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/decorator" 24 "github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/messagepickup" 25 "github.com/hyperledger/aries-framework-go/pkg/didcomm/transport" 26 "github.com/hyperledger/aries-framework-go/pkg/doc/util/kmsdidkey" 27 "github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdr" 28 "github.com/hyperledger/aries-framework-go/pkg/internal/logutil" 29 "github.com/hyperledger/aries-framework-go/pkg/kms" 30 "github.com/hyperledger/aries-framework-go/pkg/store/connection" 31 "github.com/hyperledger/aries-framework-go/pkg/vdr/fingerprint" 32 "github.com/hyperledger/aries-framework-go/spi/storage" 33 ) 34 35 var logger = log.New("aries-framework/route/service") 36 37 // constants for route coordination spec types. 38 const ( 39 // Coordination route coordination protocol. 40 Coordination = "coordinatemediation" 41 42 // RouteCoordinationSpec defines the route coordination spec. 43 CoordinationSpec = "https://didcomm.org/coordinatemediation/1.0/" 44 45 // RouteRequestMsgType defines the route coordination request message type. 46 RequestMsgType = CoordinationSpec + "mediate-request" 47 48 // RouteGrantMsgType defines the route coordination request grant message type. 49 GrantMsgType = CoordinationSpec + "mediate-grant" 50 51 // KeyListUpdateMsgType defines the route coordination key list update message type. 52 KeylistUpdateMsgType = CoordinationSpec + "keylist_update" 53 54 // KeyListUpdateResponseMsgType defines the route coordination key list update message response type. 55 KeylistUpdateResponseMsgType = CoordinationSpec + "keylist_update_response" 56 ) 57 58 // constants for key list update processing 59 // https://github.com/hyperledger/aries-rfcs/tree/master/features/0211-route-coordination#keylist-update 60 const ( 61 // add key to the store. 62 add = "add" 63 64 // remove key from the store. 65 remove = "remove" 66 67 // server error while storing the key. 68 serverError = "server_error" 69 70 // key save success. 71 success = "success" 72 ) 73 74 const ( 75 // data key to store router connection ID. 76 routeConnIDDataKey = "route_connID_%s" 77 78 // data key to store router config. 79 routeConfigDataKey = "route_config_%s" 80 81 routeGrantKey = "grant_%s" 82 ) 83 84 const ( 85 updateTimeout = 10 * time.Second 86 ) 87 88 // ErrConnectionNotFound connection not found error. 89 var ErrConnectionNotFound = errors.New("connection not found") 90 91 // ErrRouterNotRegistered router not registered error. 92 var ErrRouterNotRegistered = errors.New("router not registered") 93 94 // provider contains dependencies for the Routing protocol and is typically created by using aries.Context(). 95 type provider interface { 96 OutboundDispatcher() dispatcher.Outbound 97 StorageProvider() storage.Provider 98 ProtocolStateStorageProvider() storage.Provider 99 RouterEndpoint() string 100 KMS() kms.KeyManager 101 VDRegistry() vdr.Registry 102 Service(id string) (interface{}, error) 103 KeyAgreementType() kms.KeyType 104 MediaTypeProfiles() []string 105 } 106 107 // ClientOption configures the route client. 108 type ClientOption func(opts *ClientOptions) 109 110 // ClientOptions holds options for the router client. 111 type ClientOptions struct { 112 Timeout time.Duration 113 } 114 115 // Options is a container for route protocol options. 116 type Options struct { 117 ServiceEndpoint string 118 RoutingKeys []string 119 } 120 121 type callback struct { 122 msg service.DIDCommMsg 123 myDID string 124 theirDID string 125 options *Options 126 err error 127 } 128 129 type routerConnectionEntry struct { 130 ConnectionID string `json:"connectionID"` 131 DIDCommVersion service.Version `json:"didcomm_version,omitempty"` 132 } 133 134 type connections interface { 135 GetConnectionIDByDIDs(string, string) (string, error) 136 GetConnectionRecord(string) (*connection.Record, error) 137 GetConnectionRecordByDIDs(myDID string, theirDID string) (*connection.Record, error) 138 } 139 140 // Service for Route Coordination protocol. 141 // https://github.com/hyperledger/aries-rfcs/tree/master/features/0211-route-coordination 142 type Service struct { 143 service.Action 144 service.Message 145 routeStore storage.Store 146 connectionLookup connections 147 outbound dispatcher.Outbound 148 endpoint string 149 kms kms.KeyManager 150 vdRegistry vdr.Registry 151 keylistUpdateMap map[string]chan *KeylistUpdateResponse 152 keylistUpdateMapLock sync.RWMutex 153 callbacks chan *callback 154 messagePickupSvc messagepickup.ProtocolService 155 keyAgreementType kms.KeyType 156 mediaTypeProfiles []string 157 initialized bool 158 } 159 160 // New return route coordination service. 161 func New(prov provider) (*Service, error) { 162 svc := Service{} 163 164 err := svc.Initialize(prov) 165 if err != nil { 166 return nil, err 167 } 168 169 return &svc, nil 170 } 171 172 // Initialize initializes the Service. If Initialize succeeds, any further call is a no-op. 173 func (s *Service) Initialize(p interface{}) error { 174 if s.initialized { 175 return nil 176 } 177 178 prov, ok := p.(provider) 179 if !ok { 180 return fmt.Errorf("expected provider of type `%T`, got type `%T`", provider(nil), p) 181 } 182 183 store, err := prov.StorageProvider().OpenStore(Coordination) 184 if err != nil { 185 return fmt.Errorf("open route coordination store : %w", err) 186 } 187 188 err = prov.StorageProvider().SetStoreConfig(Coordination, 189 storage.StoreConfiguration{TagNames: []string{routeConnIDDataKey}}) 190 if err != nil { 191 return fmt.Errorf("failed to set store configuration: %w", err) 192 } 193 194 connectionLookup, err := connection.NewLookup(prov) 195 if err != nil { 196 return err 197 } 198 199 mp, err := prov.Service(messagepickup.MessagePickup) 200 if err != nil { 201 return err 202 } 203 204 messagePickupSvc, ok := mp.(messagepickup.ProtocolService) 205 if !ok { 206 return errors.New("cast service to message pickup service failed") 207 } 208 209 s.routeStore = store 210 s.outbound = prov.OutboundDispatcher() 211 s.endpoint = prov.RouterEndpoint() 212 s.kms = prov.KMS() 213 s.vdRegistry = prov.VDRegistry() 214 s.connectionLookup = connectionLookup 215 s.keylistUpdateMap = make(map[string]chan *KeylistUpdateResponse) 216 s.callbacks = make(chan *callback) 217 s.messagePickupSvc = messagePickupSvc 218 s.keyAgreementType = prov.KeyAgreementType() 219 s.mediaTypeProfiles = prov.MediaTypeProfiles() 220 221 logger.Debugf("default endpoint: %s", s.endpoint) 222 223 go s.listenForCallbacks() 224 225 s.initialized = true 226 227 return nil 228 } 229 230 func (s *Service) listenForCallbacks() { 231 for c := range s.callbacks { 232 logger.Debugf("handling user callback %+v with options %+v", c, c.options) 233 234 if c.err != nil { 235 go s.handleUserRejection(c) 236 237 continue 238 } 239 240 switch c.msg.Type() { 241 case RequestMsgType: 242 err := s.handleInboundRequest(c) 243 if err != nil { 244 logger.Errorf("failed to handle inbound request: %+v : %w", c.msg, err) 245 } 246 default: 247 logger.Warnf("ignoring unsupported message type %s", c.msg.Type()) 248 } 249 } 250 } 251 252 func (s *Service) handleUserRejection(c *callback) { 253 logger.Infof("user aborted response action for msgID=%s", c.msg.ID()) 254 } 255 256 func triggersActionEvent(msgType string) bool { 257 return msgType == RequestMsgType 258 } 259 260 func (s *Service) sendActionEvent(msg service.DIDCommMsg, myDID, theirDID string) error { 261 events := s.ActionEvent() 262 if events == nil { 263 return fmt.Errorf("no clients registered to handle action events for %s protocol", Coordination) 264 } 265 266 logger.Debugf("dispatching action event for msg=%+v myDID=%s theirDID=%s", msg, myDID, theirDID) 267 268 go func() { 269 c := &callback{ 270 msg: msg, 271 myDID: myDID, 272 theirDID: theirDID, 273 } 274 275 events <- service.DIDCommAction{ 276 ProtocolName: Coordination, 277 Message: msg, 278 Continue: func(args interface{}) { 279 switch o := args.(type) { 280 case Options: 281 c.options = &o 282 case *Options: 283 c.options = o 284 default: 285 c.options = &Options{} 286 } 287 288 s.callbacks <- c 289 }, 290 Stop: func(err error) { 291 c.err = err 292 293 s.callbacks <- c 294 }, 295 } 296 }() 297 298 return nil 299 } 300 301 // HandleInbound handles inbound route coordination messages. 302 func (s *Service) HandleInbound(msg service.DIDCommMsg, ctx service.DIDCommContext) (string, error) { 303 logger.Debugf("service.HandleInbound() input: msg=%+v myDID=%s theirDID=%s", msg, ctx.MyDID(), ctx.TheirDID()) 304 305 if triggersActionEvent(msg.Type()) { 306 return msg.ID(), s.sendActionEvent(msg, ctx.MyDID(), ctx.TheirDID()) 307 } 308 309 // perform action on inbound message asynchronously 310 go func(msg service.DIDCommMsg) { 311 var err error 312 313 switch msg.Type() { 314 case GrantMsgType: 315 err = s.saveGrant(msg) 316 case KeylistUpdateMsgType: 317 err = s.handleKeylistUpdate(msg, ctx.MyDID(), ctx.TheirDID()) 318 case KeylistUpdateResponseMsgType: 319 err = s.handleKeylistUpdateResponse(msg) 320 case service.ForwardMsgType, service.ForwardMsgTypeV2: 321 err = s.handleForward(msg) 322 } 323 324 connectionIDLog := "" 325 326 // mediator forward messages don't have connection established with the sender; hence skip the lookup 327 if msg.Type() != service.ForwardMsgType && msg.Type() != service.ForwardMsgTypeV2 { 328 connectionID, connErr := s.connectionLookup.GetConnectionIDByDIDs(ctx.MyDID(), ctx.TheirDID()) 329 if connErr != nil { 330 logutil.LogError(logger, Coordination, "connectionID lookup using DIDs", connErr.Error()) 331 } 332 333 connectionIDLog = logutil.CreateKeyValueString("connectionID", connectionID) 334 } 335 336 if err != nil { 337 logutil.LogError(logger, Coordination, "processMessage", err.Error(), 338 logutil.CreateKeyValueString("msgType", msg.Type()), 339 logutil.CreateKeyValueString("msgID", msg.ID()), 340 connectionIDLog) 341 } else { 342 logutil.LogDebug(logger, Coordination, "processMessage", "success", 343 logutil.CreateKeyValueString("msgType", msg.Type()), 344 logutil.CreateKeyValueString("msgID", msg.ID()), 345 connectionIDLog) 346 } 347 }(msg.Clone()) 348 349 return msg.ID(), nil 350 } 351 352 // HandleOutbound handles outbound route coordination messages. 353 func (s *Service) HandleOutbound(msg service.DIDCommMsg, myDID, theirDID string) (string, error) { 354 logger.Debugf("service.HandleOutbound input: msg=%+v myDID=%s theirDID=%s", msg, myDID, theirDID) 355 356 if !s.Accept(msg.Type()) { 357 return "", fmt.Errorf("unsupported message type %s", msg.Type()) 358 } 359 360 switch msg.Type() { 361 case RequestMsgType: 362 return "", s.handleOutboundRequest(msg, myDID, theirDID) 363 default: 364 return "", fmt.Errorf("invalid or unsupported outbound message type %s", msg.Type()) 365 } 366 } 367 368 // Accept checks whether the service can handle the message type. 369 func (s *Service) Accept(msgType string) bool { 370 switch msgType { 371 case RequestMsgType, GrantMsgType, KeylistUpdateMsgType, KeylistUpdateResponseMsgType, service.ForwardMsgType, 372 service.ForwardMsgTypeV2: 373 return true 374 } 375 376 return false 377 } 378 379 // Name of the service. 380 func (s *Service) Name() string { 381 return Coordination 382 } 383 384 func (s *Service) handleInboundRequest(c *callback) error { 385 logger.Debugf("handling callback: %+v", c) 386 logger.Debugf("options: %+v", c.options) 387 388 // unmarshal the payload 389 request := &Request{} 390 391 err := c.msg.Decode(request) 392 if err != nil { 393 return fmt.Errorf("handleInboundRequest: route request message unmarshal : %w", err) 394 } 395 396 err = validateRequestVersion(s.mediaTypeProfiles, request.DIDCommV2) 397 if err != nil { 398 return err 399 } 400 401 grant, err := outboundGrant( 402 c.msg.ID(), 403 c.options, 404 s.endpoint, 405 func() (string, error) { 406 if request.DIDCommV2 { 407 _, pubKeyBytes, e := s.kms.CreateAndExportPubKeyBytes(s.keyAgreementType) 408 if e != nil { 409 return "", fmt.Errorf("outboundGrant from handleInboundRequest: kms failed to create "+ 410 "and export %v key: %w", s.keyAgreementType, e) 411 } 412 413 return kmsdidkey.BuildDIDKeyByKeyType(pubKeyBytes, s.keyAgreementType) 414 } 415 416 _, pubKeyBytes, er := s.kms.CreateAndExportPubKeyBytes(kms.ED25519Type) 417 if er != nil { 418 return "", fmt.Errorf("outboundGrant from handleInboundRequest: kms failed to create and "+ 419 "export ED25519 key: %w", er) 420 } 421 422 didKey, _ := fingerprint.CreateDIDKey(pubKeyBytes) 423 424 return didKey, er 425 }, 426 ) 427 if err != nil { 428 return fmt.Errorf("handleInboundRequest: failed to handle inbound request : %w", err) 429 } 430 431 return s.outbound.SendToDID(service.NewDIDCommMsgMap(grant), c.myDID, c.theirDID) 432 } 433 434 func validateRequestVersion(mtps []string, requestedV2 bool) error { 435 if requestedV2 { 436 for _, mtp := range mtps { 437 if transport.IsDIDCommV2(mtp) { 438 return nil 439 } 440 } 441 442 return fmt.Errorf("client requested didcomm v2 mediation from mediator " + 443 "that does not support didcomm v2") 444 } 445 446 for _, mtp := range mtps { 447 if !transport.IsDIDCommV2(mtp) { 448 return nil 449 } 450 } 451 452 return fmt.Errorf("client requested didcomm v1 mediation from mediator " + 453 "that does not support didcomm v1") 454 } 455 456 func outboundGrant( 457 msgID string, opts *Options, 458 defaultEndpoint string, defaultKey func() (string, error)) (*Grant, error) { 459 grant := &Grant{ 460 ID: msgID, 461 Type: GrantMsgType, 462 Endpoint: opts.ServiceEndpoint, 463 RoutingKeys: opts.RoutingKeys, 464 } 465 466 if grant.Endpoint == "" { 467 grant.Endpoint = defaultEndpoint 468 } 469 470 if len(grant.RoutingKeys) == 0 { 471 keys, err := defaultKey() 472 if err != nil { 473 return nil, fmt.Errorf("outboundGrant: failed to create keys : %w", err) 474 } 475 476 grant.RoutingKeys = []string{keys} 477 } 478 479 logger.Debugf("outbound grant: %+v", grant) 480 481 return grant, nil 482 } 483 484 func (s *Service) handleKeylistUpdate(msg service.DIDCommMsg, myDID, theirDID string) error { 485 // unmarshal the payload 486 keyUpdate := &KeylistUpdate{} 487 488 err := msg.Decode(keyUpdate) 489 if err != nil { 490 return fmt.Errorf("route key list update message unmarshal : %w", err) 491 } 492 493 var updates []UpdateResponse 494 495 // update the db 496 for _, v := range keyUpdate.Updates { 497 if v.Action == add { 498 val := theirDID 499 result := success 500 501 toKey := dataKey(v.RecipientKey) 502 503 err = s.routeStore.Put(toKey, []byte(val)) 504 if err != nil { 505 logger.Errorf("failed to add the route key to store : %s", err) 506 507 result = serverError 508 } 509 510 // construct the response doc 511 updates = append(updates, UpdateResponse{ 512 RecipientKey: v.RecipientKey, 513 Action: v.Action, 514 Result: result, 515 }) 516 } else if v.Action == remove { 517 // TODO remove from the store 518 519 // construct the response doc 520 updates = append(updates, UpdateResponse{ 521 RecipientKey: v.RecipientKey, 522 Action: v.Action, 523 Result: serverError, 524 }) 525 } 526 } 527 528 // send the key update response 529 updateResponse := &KeylistUpdateResponse{ 530 Type: KeylistUpdateResponseMsgType, 531 ID: msg.ID(), 532 Updated: updates, 533 } 534 535 return s.outbound.SendToDID(service.NewDIDCommMsgMap(updateResponse), myDID, theirDID) 536 } 537 538 func (s *Service) handleKeylistUpdateResponse(msg service.DIDCommMsg) error { 539 // unmarshal the payload 540 respMsg := &KeylistUpdateResponse{} 541 542 err := msg.Decode(respMsg) 543 if err != nil { 544 return fmt.Errorf("route keylist update response message unmarshal : %w", err) 545 } 546 547 // check if there are any channels registered for the message ID 548 keylistUpdateCh := s.getKeyUpdateResponseCh(respMsg.ID) 549 550 if keylistUpdateCh != nil { 551 // invoke the channel for the incoming message 552 keylistUpdateCh <- respMsg 553 } 554 555 return nil 556 } 557 558 func (s *Service) handleForward(msg service.DIDCommMsg) error { 559 // unmarshal the payload 560 forward := &model.Forward{} 561 562 err := msg.Decode(forward) 563 if err != nil { 564 return fmt.Errorf("forward message unmarshal : %w", err) 565 } 566 567 // TODO Open question - https://github.com/hyperledger/aries-framework-go/issues/965 Mismatch between Route 568 // Coordination and Forward RFC. For now assume, the TO field contains the recipient key (DIDComm V2 uses 569 // keyAgreement.ID, double check if this to do comment is still needed). 570 toKey := dataKey(forward.To) 571 572 theirDID, err := s.routeStore.Get(toKey) 573 if err != nil { 574 return fmt.Errorf("route key fetch : %w", err) 575 } 576 577 dest, err := service.GetDestination(string(theirDID), s.vdRegistry) 578 if err != nil { 579 return fmt.Errorf("get destination : %w", err) 580 } 581 582 err = s.outbound.Forward(forward.Msg, dest) 583 if err != nil && s.messagePickupSvc != nil { 584 return s.messagePickupSvc.AddMessage(forward.Msg, string(theirDID)) 585 } 586 587 return err 588 } 589 590 // Register registers the agent with the router on the other end of the connection identified by 591 // connectionID. This method blocks until a response is received from the router or it times out. 592 // The agent is registered with the router and retrieves the router endpoint and routing keys. 593 // This function throws an error if the agent is already registered against a router. 594 func (s *Service) Register(connectionID string, options ...ClientOption) error { 595 record, err := s.getConnection(connectionID) 596 if err != nil { 597 return fmt.Errorf("get connection: %w", err) 598 } 599 600 opts := parseClientOpts(options...) 601 602 return s.doRegistration( 603 record, 604 &Request{ 605 Type: RequestMsgType, 606 ID: uuid.New().String(), 607 Timing: decorator.Timing{}, 608 }, 609 opts.Timeout, 610 ) 611 } 612 613 func (s *Service) doRegistration(record *connection.Record, req *Request, timeout time.Duration) error { 614 // check if router is already registered 615 err := s.ensureConnectionExists(record.ConnectionID) 616 if err == nil { 617 return errors.New("router is already registered") 618 } 619 620 if !errors.Is(err, ErrRouterNotRegistered) { 621 return fmt.Errorf("ensure connection exists: %w", err) 622 } 623 624 // TODO: would this be better served as time.Now().Add(timeout).Unix() as pkg/doc/verifiable/credential.go 625 // demonstrates? additionally `ExpiresTime` would need to be migrated to int64 626 req.ExpiresTime = time.Now().UTC().Add(timeout) 627 628 if record.DIDCommVersion == service.V2 { 629 req.DIDCommV2 = true 630 } 631 632 // send message to the router 633 if err = s.outbound.SendToDID(service.NewDIDCommMsgMap(req), record.MyDID, record.TheirDID); err != nil { 634 return fmt.Errorf("send route request: %w", err) 635 } 636 637 // waits until the mediate-grant message is received or timeout was exceeded 638 grant, err := s.getGrant(req.ID, timeout) 639 if err != nil { 640 return fmt.Errorf("get grant for request ID '%s': %w", req.ID, err) 641 } 642 643 err = s.saveRouterConfig(record.ConnectionID, &config{ 644 RouterEndpoint: grant.Endpoint, 645 RoutingKeys: grant.RoutingKeys, 646 }) 647 if err != nil { 648 return fmt.Errorf("save route config : %w", err) 649 } 650 651 logger.Debugf("saved router config from inbound grant: %+v", grant) 652 653 // save the connectionID of the router 654 return s.saveRouterConnectionID(record.ConnectionID, record.DIDCommVersion) 655 } 656 657 func (s *Service) getGrant(id string, timeout time.Duration) (*Grant, error) { 658 var ( 659 src []byte 660 err error 661 ) 662 663 err = backoff.Retry(func() error { 664 src, err = s.routeStore.Get(fmt.Sprintf(routeGrantKey, id)) 665 666 return err 667 }, backoff.WithMaxRetries(backoff.NewConstantBackOff(time.Second), uint64(timeout/time.Second))) 668 669 if err != nil { 670 return nil, fmt.Errorf("store: %w", err) 671 } 672 673 var grant *Grant 674 675 err = json.Unmarshal(src, &grant) 676 if err != nil { 677 return nil, fmt.Errorf("unmarshal grant: %w", err) 678 } 679 680 return grant, nil 681 } 682 683 func (s *Service) saveGrant(grant service.DIDCommMsg) error { 684 src, err := json.Marshal(grant) 685 if err != nil { 686 return fmt.Errorf("marshal grant: %w", err) 687 } 688 689 return s.routeStore.Put(fmt.Sprintf(routeGrantKey, grant.ID()), src) 690 } 691 692 // Unregister unregisters the agent with the router. 693 func (s *Service) Unregister(connID string) error { 694 // check if router is already registered 695 err := s.ensureConnectionExists(connID) 696 if err != nil { 697 return fmt.Errorf("ensure connection exists: %w", err) 698 } 699 700 // TODO Remove all the recKeys from the router 701 // https://github.com/hyperledger/aries-rfcs/tree/master/features/0211-route-coordination#keylist-update-response 702 703 // deletes the connectionID 704 return s.deleteRouterConnectionID(connID) 705 } 706 707 // GetConnections returns the connections of the router. 708 func (s *Service) GetConnections(options ...ConnectionOption) ([]string, error) { 709 opts := &getConnectionOpts{} 710 711 for _, option := range options { 712 option(opts) 713 } 714 715 records, err := s.routeStore.Query(routeConnIDDataKey) 716 if err != nil { 717 return nil, fmt.Errorf("failed to query route store: %w", err) 718 } 719 720 defer storage.Close(records, logger) 721 722 var conns []string 723 724 more, err := records.Next() 725 if err != nil { 726 return nil, fmt.Errorf("failed to get next record: %w", err) 727 } 728 729 for more { 730 value, err := records.Value() 731 if err != nil { 732 return nil, fmt.Errorf("failed to get value from records: %w", err) 733 } 734 735 data := &routerConnectionEntry{} 736 737 err = json.Unmarshal(value, data) 738 if err != nil { 739 return nil, fmt.Errorf("failed to unmarshal router connection entry: %w", err) 740 } 741 742 if opts.version == "" || opts.version == data.DIDCommVersion { 743 conns = append(conns, data.ConnectionID) 744 } 745 746 more, err = records.Next() 747 if err != nil { 748 return nil, fmt.Errorf("failed to get next record: %w", err) 749 } 750 } 751 752 return conns, nil 753 } 754 755 // AddKey adds a recKey of the agent to the registered router. This method blocks until a response is 756 // received from the router or it times out. 757 // TODO https://github.com/hyperledger/aries-framework-go/issues/1076 Support for multiple routers 758 // TODO https://github.com/hyperledger/aries-framework-go/issues/1105 Support to Add multiple 759 // recKeys to the Router 760 func (s *Service) AddKey(connID, recKey string) error { 761 // check if router is already registered 762 err := s.ensureConnectionExists(connID) 763 if err != nil { 764 return fmt.Errorf("ensure connection exists: %w", err) 765 } 766 767 // get the connection record for the ID to fetch DID information 768 conn, err := s.getConnection(connID) 769 if err != nil { 770 return fmt.Errorf("get connection: %w", err) 771 } 772 773 // generate message ID 774 msgID := uuid.New().String() 775 776 // register chan for callback processing 777 keyUpdateCh := make(chan *KeylistUpdateResponse) 778 s.setKeyUpdateResponseCh(msgID, keyUpdateCh) 779 780 keyUpdate := &KeylistUpdate{ 781 ID: msgID, 782 Type: KeylistUpdateMsgType, 783 Updates: []Update{ 784 { 785 RecipientKey: recKey, 786 Action: add, 787 }, 788 }, 789 } 790 791 if err := s.outbound.SendToDID(service.NewDIDCommMsgMap(keyUpdate), conn.MyDID, conn.TheirDID); err != nil { 792 return fmt.Errorf("send route request: %w", err) 793 } 794 795 select { 796 case keyUpdateResp := <-keyUpdateCh: 797 if err := processKeylistUpdateResp(recKey, keyUpdateResp); err != nil { 798 return err 799 } 800 case <-time.After(updateTimeout): 801 return errors.New("timeout waiting for keylist update response from the router") 802 } 803 804 // remove the channel once its been processed 805 s.setKeyUpdateResponseCh(msgID, nil) 806 807 return nil 808 } 809 810 // Config fetches the router config - endpoint and routingKeys. 811 func (s *Service) Config(connID string) (*Config, error) { 812 // check if router is already registered 813 if err := s.ensureConnectionExists(connID); err != nil { 814 return nil, fmt.Errorf("ensure connection exists: %w", err) 815 } 816 817 return s.getRouterConfig(connID) 818 } 819 820 func processKeylistUpdateResp(recKey string, keyUpdateResp *KeylistUpdateResponse) error { 821 for _, result := range keyUpdateResp.Updated { 822 if result.RecipientKey == recKey && result.Action == add && result.Result != success { 823 return errors.New("failed to update the recipient key with the router") 824 } 825 } 826 827 return nil 828 } 829 830 func (s *Service) getKeyUpdateResponseCh(msgID string) chan *KeylistUpdateResponse { 831 s.keylistUpdateMapLock.RLock() 832 defer s.keylistUpdateMapLock.RUnlock() 833 834 return s.keylistUpdateMap[msgID] 835 } 836 837 func (s *Service) setKeyUpdateResponseCh(msgID string, keyUpdateCh chan *KeylistUpdateResponse) { 838 s.keylistUpdateMapLock.Lock() 839 defer s.keylistUpdateMapLock.Unlock() 840 841 if keyUpdateCh == nil { 842 delete(s.keylistUpdateMap, msgID) 843 } else { 844 s.keylistUpdateMap[msgID] = keyUpdateCh 845 } 846 } 847 848 func (s *Service) ensureConnectionExists(connID string) error { 849 _, err := s.routeStore.Get(fmt.Sprintf(routeConnIDDataKey, connID)) 850 if errors.Is(err, storage.ErrDataNotFound) { 851 return ErrRouterNotRegistered 852 } 853 854 return err 855 } 856 857 func (s *Service) deleteRouterConnectionID(connID string) error { 858 return s.routeStore.Delete(fmt.Sprintf(routeConnIDDataKey, connID)) 859 } 860 861 func (s *Service) saveRouterConnectionID(connID string, didcommVersion service.Version) error { 862 data := &routerConnectionEntry{ 863 ConnectionID: connID, 864 DIDCommVersion: didcommVersion, 865 } 866 867 dataBytes, err := json.Marshal(data) 868 if err != nil { 869 return fmt.Errorf("marshalling router connection ID data: %w", err) 870 } 871 872 return s.routeStore.Put(fmt.Sprintf(routeConnIDDataKey, connID), dataBytes, storage.Tag{Name: routeConnIDDataKey}) 873 } 874 875 type config struct { 876 RouterEndpoint string 877 RoutingKeys []string 878 } 879 880 func (s *Service) getRouterConfig(connID string) (*Config, error) { 881 val, err := s.routeStore.Get(fmt.Sprintf(routeConfigDataKey, connID)) 882 if err != nil { 883 return nil, fmt.Errorf("get router config data : %w", err) 884 } 885 886 conf := &config{} 887 888 err = json.Unmarshal(val, conf) 889 if err != nil { 890 return nil, fmt.Errorf("unmarshal router config data : %w", err) 891 } 892 893 return NewConfig(conf.RouterEndpoint, conf.RoutingKeys), nil 894 } 895 896 func (s *Service) saveRouterConfig(connID string, conf *config) error { 897 bytes, err := json.Marshal(conf) 898 if err != nil { 899 return fmt.Errorf("marshal config data: %w", err) 900 } 901 902 return s.routeStore.Put(fmt.Sprintf(routeConfigDataKey, connID), bytes) 903 } 904 905 func (s *Service) getConnection(routerConnID string) (*connection.Record, error) { 906 conn, err := s.connectionLookup.GetConnectionRecord(routerConnID) 907 if err != nil { 908 if errors.Is(err, storage.ErrDataNotFound) { 909 return nil, ErrConnectionNotFound 910 } 911 912 return nil, fmt.Errorf("fetch connection record from store : %w", err) 913 } 914 915 return conn, nil 916 } 917 918 func (s *Service) handleOutboundRequest(msg service.DIDCommMsg, myDID, theirDID string) error { 919 req := &Request{} 920 921 err := msg.Decode(req) 922 if err != nil { 923 return fmt.Errorf("failed to decode request : %w", err) 924 } 925 926 record, err := s.connectionLookup.GetConnectionRecordByDIDs(myDID, theirDID) 927 if err != nil { 928 return fmt.Errorf("failed to lookup connection record with myDID=%s theirDID=%s : %w", 929 myDID, theirDID, err) 930 } 931 932 return s.doRegistration(record, req, updateTimeout) 933 } 934 935 func dataKey(id string) string { 936 return "route-" + id 937 } 938 939 func parseClientOpts(options ...ClientOption) *ClientOptions { 940 opts := &ClientOptions{ 941 Timeout: updateTimeout, 942 } 943 944 // generate router config from options 945 for _, option := range options { 946 option(opts) 947 } 948 949 return opts 950 } 951 952 type getConnectionOpts struct { 953 version service.Version 954 } 955 956 // ConnectionOption option for Service.GetConnections. 957 type ConnectionOption func(opts *getConnectionOpts) 958 959 // ConnectionByVersion filter for mediator connections of the given DIDComm version. 960 func ConnectionByVersion(v service.Version) ConnectionOption { 961 return func(opts *getConnectionOpts) { 962 opts.version = v 963 } 964 }