Exemple #1
0
    async def create_response(self,
                              connection: ConnectionRecord,
                              my_endpoint: str = None) -> ConnectionResponse:
        """
        Create a connection response for a received connection request.

        Args:
            connection: The `ConnectionRecord` with a pending connection request
            my_endpoint: The endpoint I can be reached at

        Returns:
            A tuple of the updated `ConnectionRecord` new `ConnectionResponse` message

        """
        ConnectionRecord.log_state(
            self.context,
            "Creating connection response",
            {"connection_id": connection.connection_id},
        )

        if connection.state not in (
                ConnectionRecord.STATE_REQUEST,
                ConnectionRecord.STATE_RESPONSE,
        ):
            raise ConnectionManagerError(
                "Connection is not in the request or response state")

        request = await connection.retrieve_request(self.context)
        wallet: BaseWallet = await self.context.inject(BaseWallet)
        if connection.my_did:
            my_info = await wallet.get_local_did(connection.my_did)
        else:
            my_info = await wallet.create_local_did()
            connection.my_did = my_info.did

        # Create connection response message
        if not my_endpoint:
            my_endpoints = []
            default_endpoint = self.context.settings.get("default_endpoint")
            if default_endpoint:
                my_endpoints.append(default_endpoint)
            my_endpoints.extend(
                self.context.settings.get("additional_endpoints", []))
        did_doc = await self.create_did_document(
            my_info, connection.inbound_connection_id, my_endpoints)
        response = ConnectionResponse(
            connection=ConnectionDetail(did=my_info.did, did_doc=did_doc))
        # Assign thread information
        response.assign_thread_from(request)
        response.assign_trace_from(request)
        # Sign connection field using the invitation key
        wallet: BaseWallet = await self.context.inject(BaseWallet)
        await response.sign_field("connection", connection.invitation_key,
                                  wallet)

        # Update connection state
        connection.state = ConnectionRecord.STATE_RESPONSE

        await connection.save(
            self.context,
            reason="Created connection response",
            log_params={"response": response},
        )
        return response
Exemple #2
0
    async def receive_request(self, request: ConnectionRequest,
                              receipt: MessageReceipt) -> ConnectionRecord:
        """
        Receive and store a connection request.

        Args:
            request: The `ConnectionRequest` to accept
            receipt: The message receipt

        Returns:
            The new or updated `ConnectionRecord` instance

        """
        ConnectionRecord.log_state(self.context,
                                   "Receiving connection request",
                                   {"request": request})

        connection = None
        connection_key = None

        # Determine what key will need to sign the response
        if receipt.recipient_did_public:
            wallet: BaseWallet = await self.context.inject(BaseWallet)
            my_info = await wallet.get_local_did(receipt.recipient_did)
            connection_key = my_info.verkey
        else:
            connection_key = receipt.recipient_verkey
            try:
                connection = await ConnectionRecord.retrieve_by_invitation_key(
                    self.context, connection_key,
                    ConnectionRecord.INITIATOR_SELF)
            except StorageNotFoundError:
                raise ConnectionManagerError(
                    "No invitation found for pairwise connection")

        invitation = None
        if connection:
            invitation = await connection.retrieve_invitation(self.context)
            connection_key = connection.invitation_key
            ConnectionRecord.log_state(self.context, "Found invitation",
                                       {"invitation": invitation})

            if connection.is_multiuse_invitation:
                wallet: BaseWallet = await self.context.inject(BaseWallet)
                my_info = await wallet.create_local_did()
                new_connection = ConnectionRecord(
                    initiator=ConnectionRecord.INITIATOR_MULTIUSE,
                    invitation_key=connection_key,
                    my_did=my_info.did,
                    state=ConnectionRecord.STATE_INVITATION,
                    accept=connection.accept,
                    their_role=connection.their_role,
                )

                await new_connection.save(
                    self.context,
                    reason=
                    "Received connection request from multi-use invitation DID",
                )
                connection = new_connection

        conn_did_doc = request.connection.did_doc
        if not conn_did_doc:
            raise ConnectionManagerError(
                "No DIDDoc provided; cannot connect to public DID")
        if request.connection.did != conn_did_doc.did:
            raise ConnectionManagerError(
                "Connection DID does not match DIDDoc id",
                error_code=ProblemReportReason.REQUEST_NOT_ACCEPTED,
            )
        await self.store_did_document(conn_did_doc)

        if connection:
            connection.their_label = request.label
            connection.their_did = request.connection.did
            connection.state = ConnectionRecord.STATE_REQUEST
            await connection.save(
                self.context,
                reason="Received connection request from invitation")
        elif not self.context.settings.get("public_invites"):
            raise ConnectionManagerError("Public invitations are not enabled")
        else:
            my_info = await wallet.create_local_did()
            connection = ConnectionRecord(
                initiator=ConnectionRecord.INITIATOR_EXTERNAL,
                invitation_key=connection_key,
                my_did=my_info.did,
                their_did=request.connection.did,
                their_label=request.label,
                state=ConnectionRecord.STATE_REQUEST,
            )
            if self.context.settings.get("debug.auto_accept_requests"):
                connection.accept = ConnectionRecord.ACCEPT_AUTO

            await connection.save(
                self.context,
                reason="Received connection request from public DID")

        # Attach the connection request so it can be found and responded to
        await connection.attach_request(self.context, request)

        if connection.accept == ConnectionRecord.ACCEPT_AUTO:
            response = await self.create_response(connection)
            responder: BaseResponder = await self._context.inject(
                BaseResponder, required=False)
            if responder:
                await responder.send_reply(
                    response, connection_id=connection.connection_id)
                # refetch connection for accurate state
                connection = await ConnectionRecord.retrieve_by_id(
                    self._context, connection.connection_id)
        else:
            self._logger.debug("Connection request will await acceptance")

        return connection