async def handle(self, context: RequestContext, responder: BaseResponder):
     """Handle create invitation request."""
     connection_mgr = ConnectionManager(context)
     connection, invitation = await connection_mgr.create_invitation(
         my_label=context.message.label,
         their_role=context.message.role,
         auto_accept=context.message.auto_accept,
         multi_use=bool(context.message.multi_use),
         public=False,
         alias=context.message.alias,
     )
     invite_response = Invitation(
         id=connection.connection_id,
         label=invitation.label,
         alias=connection.alias,
         role=connection.their_role,
         auto_accept=connection.accept == ConnectionRecord.ACCEPT_AUTO,
         multi_use=(
             connection.invitation_mode ==
             ConnectionRecord.INVITATION_MODE_MULTI
         ),
         invitation_url=invitation.to_url(),
         created_date=connection.created_at,
         raw_repr={
             'connection': connection.serialize(),
             'invitation': invitation.serialize(),
         }
     )
     invite_response.assign_thread_from(context.message)
     await responder.send_reply(invite_response)
 async def handle(self, context: RequestContext, responder: BaseResponder):
     """Handle recieve invitation request."""
     connection_mgr = ConnectionManager(context)
     invitation = ConnectionInvitation.from_url(context.message.invitation)
     connection = await connection_mgr.receive_invitation(
         invitation, auto_accept=context.message.auto_accept)
     connection_resp = Connection(**conn_record_to_message_repr(connection))
     await responder.send_reply(connection_resp)
    async def handle(self, context: RequestContext, responder: BaseResponder):
        """Handle static connection get list request."""
        session = await context.session()
        connection_mgr = ConnectionManager(session)
        wallet: BaseWallet = session.inject(BaseWallet)
        try:
            tag_filter = dict(
                filter(
                    lambda item: item[1] is not None, {
                        'my_did': context.message.my_did,
                        'their_did': context.message.their_did,
                    }.items()))
            post_filter_positive = dict(
                filter(
                    lambda item: item[1] is not None, {
                        'initiator': context.message.initiator,
                        'invitation_key': context.message.invitation_key,
                        'invitation_mode': ConnRecord.INVITATION_MODE_STATIC,
                        'their_role': context.message.their_role
                    }.items()))
            records = await ConnRecord.query(
                session, tag_filter, post_filter_positive=post_filter_positive)
        except StorageNotFoundError:
            report = ProblemReport(explain_ltxt='Connection not found.',
                                   who_retries='none')
            report.assign_thread_from(context.message)
            await responder.send_reply(report)
            return

        def flatten_target(connection, target, my_info):
            """Map for flattening results."""
            return {
                'connection_id': connection.connection_id,
                'their_info': {
                    'label': target.label,
                    'did': target.did,
                    'vk': target.recipient_keys[0],
                    'endpoint': target.endpoint
                },
                'my_info': {
                    'did': my_info.did,
                    'vk': my_info.verkey,
                    'endpoint': context.settings.get("default_endpoint")
                }
            }

        targets = []
        my_info = []
        for record in records:
            targets.extend(
                await connection_mgr.get_connection_targets(connection=record))
            my_info.append(await wallet.get_local_did(record.my_did))

        results = list(map(flatten_target, records, targets, my_info))

        static_connections = StaticConnectionList(results=results)
        static_connections.assign_thread_from(context.message)
        await responder.send_reply(static_connections)
    async def handle(self, context: RequestContext, responder: BaseResponder):
        """Handle received basic message."""
        session = await context.session()
        msg = BasicMessageRecord(
            connection_id=context.connection_record.connection_id,
            message_id=context.message._id,
            sent_time=context.message.sent_time,
            content=context.message.content,
            state=BasicMessageRecord.STATE_RECV)
        await msg.save(session, reason='New message received.')

        await responder.send_webhook(
            "basicmessages",
            {
                "connection_id": context.connection_record.connection_id,
                "message_id": context.message._id,
                "content": context.message.content,
                "state": "received",
            },
        )

        session = await context.session()
        connection_mgr = ConnectionManager(session)
        storage = session.inject(BaseStorage)
        admin_ids = map(
            lambda record: record.tags['connection_id'],
            filter(
                lambda record: json.loads(record.value) == 'admin', await
                storage.find_all_records(ConnRecord.RECORD_TYPE_METADATA,
                                         {'key': 'group'})))
        admins = [
            await ConnRecord.retrieve_by_id(session, id) for id in admin_ids
        ]

        if not admins:
            return

        admins = filter(lambda admin: admin.state == 'active', admins)
        admin_verkeys = [
            target.recipient_keys[0] for admin in admins
            for target in await connection_mgr.get_connection_targets(
                connection=admin)
        ]

        notification = New(
            connection_id=context.connection_record.connection_id, message=msg)

        for verkey in admin_verkeys:
            await responder.send(notification,
                                 reply_to_verkey=verkey,
                                 to_session_only=True)
    async def handle(self, context: RequestContext, responder: BaseResponder):
        """Handle static connection creation request."""

        session = await context.session()
        connection_mgr = ConnectionManager(session)
        wallet: BaseWallet = session.inject(BaseWallet)

        # Make our info for the connection
        my_info = await wallet.create_local_did()

        # Create connection record
        connection = ConnRecord(
            initiator=ConnRecord.INITIATOR_SELF,
            my_did=my_info.did,
            their_did=context.message.static_did,
            their_label=context.message.label,
            their_role=context.message.role if context.message.role else None,
            state=ConnRecord.STATE_ACTIVE,
            invitation_mode=ConnRecord.INVITATION_MODE_STATIC)

        # Construct their did doc from the basic components in message
        diddoc = DIDDoc(context.message.static_did)
        public_key = PublicKey(did=context.message.static_did,
                               ident="1",
                               value=context.message.static_key,
                               pk_type=PublicKeyType.ED25519_SIG_2018,
                               controller=context.message.static_did)
        service = Service(did=context.message.static_did,
                          ident="indy",
                          typ="IndyAgent",
                          recip_keys=[public_key],
                          routing_keys=[],
                          endpoint=context.message.static_endpoint)
        diddoc.set(public_key)
        diddoc.set(service)

        # Save
        await connection_mgr.store_did_document(diddoc)
        await connection.save(session, reason='Created new static connection')

        # Prepare response
        info = StaticConnectionInfo(
            did=my_info.did,
            key=my_info.verkey,
            endpoint=context.settings.get("default_endpoint"))
        info.assign_thread_from(context.message)
        await responder.send_reply(info)
        return
    async def handle(self, context: RequestContext, responder: BaseResponder):
        """Message handler implementation."""
        self._logger.debug("InvitationRequestHandler called with context %s",
                           context)
        assert isinstance(context.message, InvitationRequest)

        if not context.connection_ready:
            raise HandlerException(
                "No connection established for invitation request message")

        # Need a way to prompt the user for acceptance?

        if context.settings.get("accept_requests"):
            # Create a new connection invitation and send it back in an Invitation
            connection_mgr = ConnectionManager(context)
            _connection, invite = await connection_mgr.create_invitation()
            response = Invitation(invitation=invite)
            response.assign_thread_from(context.message)
            response.assign_trace_from(context.message)
            await responder.send_reply(response)
    async def handle(self, context: RequestContext, responder: BaseResponder):
        """Message handler implementation."""
        self._logger.debug("ForwardInvitationHandler called with context %s",
                           context)
        assert isinstance(context.message, ForwardInvitation)

        if not context.connection_ready:
            raise HandlerException(
                "No connection established for forward invitation message")

        # Store invitation
        connection_mgr = ConnectionManager(context)
        connection = await connection_mgr.receive_invitation(
            context.message.invitation, their_role=None)

        # Auto-accept
        if context.settings.get("accept_invites"):
            request = await connection_mgr.create_request(connection)
            await responder.send(request,
                                 connection_id=connection.connection_id)
Exemple #8
0
    async def handle(self, context: RequestContext, responder: BaseResponder):
        """Handle received basic message."""
        msg = BasicMessageRecord(
            connection_id=context.connection_record.connection_id,
            message_id=context.message._id,
            sent_time=context.message.sent_time,
            content=context.message.content,
            state=BasicMessageRecord.STATE_RECV)
        await msg.save(context, reason='New message received.')

        await responder.send_webhook(
            "basicmessages",
            {
                "connection_id": context.connection_record.connection_id,
                "message_id": context.message._id,
                "content": context.message.content,
                "state": "received",
            },
        )

        connection_mgr = ConnectionManager(context)
        admins = await ConnectionRecord.query(
            context, post_filter_positive={'their_role': 'admin'})

        if not admins:
            return

        admins = filter(lambda admin: admin.state == 'active', admins)
        admin_verkeys = [
            target.recipient_keys[0] for admin in admins
            for target in await connection_mgr.get_connection_targets(
                connection=admin)
        ]

        notification = New(
            connection_id=context.connection_record.connection_id, message=msg)

        for verkey in admin_verkeys:
            await responder.send(notification,
                                 reply_to_verkey=verkey,
                                 to_session_only=True)
Exemple #9
0
    async def handle(self, context: RequestContext, responder: BaseResponder):
        """Message handler implementation."""
        self._logger.debug(
            "%s called with context %s", self.__class__.__name__, context
        )
        assert isinstance(context.message, RouteUpdateResponse)

        if not context.connection_ready:
            raise HandlerException("Cannot handle updated routes: no active connection")

        conn_mgr = ConnectionManager(context)
        router_id = context.connection_record.connection_id

        for update in context.message.updated:
            if update.action == RouteUpdate.ACTION_CREATE:
                if update.result in (
                    RouteUpdated.RESULT_NO_CHANGE,
                    RouteUpdated.RESULT_SUCCESS,
                ):
                    routing_state = ConnectionRecord.ROUTING_STATE_ACTIVE
                else:
                    routing_state = ConnectionRecord.ROUTING_STATE_ERROR
                    self._logger.warning(
                        f"Unexpected result from inbound route update ({update.action})"
                    )
                await conn_mgr.update_inbound(
                    router_id, update.recipient_key, routing_state
                )
            elif update.action == RouteUpdate.ACTION_DELETE:
                self._logger.info(
                    "Inbound route deletion status: {}, {}".format(
                        update.recipient_key, update.result
                    )
                )
            else:
                self._logger.error(f"Unsupported inbound route action: {update.action}")
    async def create_invitation(
        self,
        my_label: str = None,
        my_endpoint: str = None,
        use_public_did: bool = False,
        include_handshake: bool = False,
        multi_use: bool = False,
        attachments: list = None,
    ) -> InvitationModel:
        """
        Generate new out of band invitation.

        This interaction represents an out-of-band communication channel. The resulting
        message is expected to be used to bootstrap the secure peer-to-peer communication
        channel.

        Args:
            my_label: label for this connection
            my_endpoint: endpoint where other party can reach me
            public: set to create an invitation from the public DID
            multi_use: set to True to create an invitation for multiple use
            attachments: list of dicts in the form of
                {"id": "jh5k23j5gh2123", "type": "credential-offer"}

        Returns:
            A tuple of the new `InvitationModel` and out of band `InvitationMessage`
            instances

        """

        connection_mgr = ConnectionManager(self.context)
        connection, connection_invitation = await connection_mgr.create_invitation(
            auto_accept=True, public=use_public_did, multi_use=multi_use)

        # wallet: BaseWallet = await self.context.inject(BaseWallet)

        if not my_label:
            my_label = self.context.settings.get("default_label")
        # if not my_endpoint:
        #     my_endpoint = self.context.settings.get("default_endpoint")

        message_attachments = []
        if attachments:
            for attachment in attachments:
                if attachment["type"] == "credential-offer":
                    instance_id = attachment["id"]
                    model = await V10CredentialExchange.retrieve_by_id(
                        self.context, instance_id)
                    # Wrap as attachment decorators
                    message_attachments.append(
                        InvitationMessage.wrap_message(
                            model.credential_offer_dict))
                elif attachment["type"] == "present-proof":
                    instance_id = attachment["id"]
                    model = await V10PresentationExchange.retrieve_by_id(
                        self.context, instance_id)
                    # Wrap as attachment decorators
                    message_attachments.append(
                        InvitationMessage.wrap_message(
                            model.presentation_request_dict))
                else:
                    raise OutOfBandManagerError(
                        f"Unknown attachment type: {attachment['type']}")

        # We plug into existing connection structure during migration phase
        if use_public_did:
            # service = (await wallet.get_public_did()).did
            service = connection_invitation.did
        else:
            # connection_key = await wallet.create_signing_key()
            # service = ServiceMessage(
            #     id="#inline",
            #     type="did-communication",
            #     recipient_keys=[connection_key.verkey],
            #     routing_keys=[],
            #     service_endpoint=my_endpoint,
            # )
            service = ServiceMessage(
                _id="#inline",
                _type="did-communication",
                recipient_keys=connection_invitation.recipient_keys,
                routing_keys=connection_invitation.routing_keys,
                service_endpoint=connection_invitation.endpoint,
            ).validate()

        handshake_protocols = []
        if include_handshake:
            # handshake_protocols.append("https://didcomm.org/connections/1.0")
            # handshake_protocols.append("https://didcomm.org/didexchange/1.0")
            handshake_protocols.append(
                "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation"
            )

        invitation_message = InvitationMessage(
            label=my_label,
            service=[service],
            request_attach=message_attachments,
            handshake_protocols=handshake_protocols,
        ).validate()

        # Create record
        invitation_model = InvitationModel(
            state=InvitationModel.STATE_INITIAL,
            invitation=invitation_message.serialize(),
        )

        await invitation_model.save(self.context,
                                    reason="Created new invitation")

        return invitation_model
    async def receive_invitation(
            self, invitation: InvitationMessage) -> ConnectionRecord:
        """Receive an out of band invitation message."""

        ledger: BaseLedger = await self.context.inject(BaseLedger)

        # New message format
        invitation_message = InvitationMessage.deserialize(invitation)

        # Convert to old format and pass to relevant manager
        # The following logic adheres to Aries RFC 0496

        # There must be exactly 1 service entry
        if (len(invitation_message.service_blocks) +
                len(invitation_message.service_dids) != 1):
            raise OutOfBandManagerError(
                "service array must have exactly one element")

        # Get the single service item
        if invitation_message.service_blocks:
            service = invitation_message.service_blocks[0]
        else:
            # If it's in the did format, we need to convert to a full service block
            service_did = invitation_message.service_dids[0]
            async with ledger:
                verkey = await ledger.get_key_for_did(service_did)
                endpoint = await ledger.get_endpoint_for_did(service_did)
            service = ServiceMessage.deserialize({
                "id": "#inline",
                "type": "did-communication",
                "recipientKeys": [verkey],
                "routingKeys": [],
                "serviceEndpoint": endpoint,
            })

        # If we are dealing with an invitation
        if (len(invitation_message.handshake_protocols) == 1
                and invitation_message.handshake_protocols[0] ==
                "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec/connections/1.0/invitation"
            ):

            if len(invitation_message.request_attach) != 0:
                raise OutOfBandManagerError(
                    "request block must be empty for invitation message type.")

            # Convert to the old message format
            connection_invitation = ConnectionInvitation.deserialize({
                "@id":
                invitation_message._id,
                "@type":
                invitation_message.handshake_protocols[0],
                "label":
                invitation_message.label,
                "recipientKeys":
                service.recipient_keys,
                "serviceEndpoint":
                service.service_endpoint,
                "routingKeys":
                service.routing_keys,
            })

            connection_mgr = ConnectionManager(self.context)
            connection = await connection_mgr.receive_invitation(
                connection_invitation, auto_accept=True)

        elif (len(invitation_message.request_attach) == 1
              and invitation_message.request_attach[0].data.json["@type"]
              == "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec" +
              "/present-proof/1.0/request-presentation"):
            raise OutOfBandManagerNotImplementedError(
                "did:sov:BzCbsNYhMrjHiqZDTUASHg;spec" +
                "/present-proof/1.0/request-presentation " +
                "request type not implemented.")
        else:
            raise OutOfBandManagerError("Invalid request type")

        return connection