def build_drone_mode_subscription_update(request_id, subscription_id,
                                         update_id, subscription_update):
    """
    Build Subscription Update proto
    :param request_id:
    :param subscription_id:
    :param update_id:
    :param subscription_update:
    :return:
    """
    server_application_message = envelope_pb2.ServerApplicationMessage()

    server_application_message.serverSubscriptionUpdate.requestId = request_id
    server_application_message.serverSubscriptionUpdate.subscriptionId = subscription_id
    server_application_message.serverSubscriptionUpdate.updateId = update_id

    # if/else case statement for drone mode subscription_update types
    # TV events
    if isinstance(subscription_update, DroneModeTVEvent):
        subscription_update.set_protobuf(
            server_application_message.serverSubscriptionUpdate.
            droneModeTVEvent)

    # iPad Events
    elif isinstance(subscription_update, DroneModeiPadEvent):
        subscription_update.set_protobuf(
            server_application_message.serverSubscriptionUpdate.
            droneModeiPadEvent)

    else:
        server_application_message.serverSubscriptionUpdate.error.code = common_pb2.Error.ERROR_UNKNOWN
        server_application_message.serverSubscriptionUpdate.error.message = 'Unexpected Error!'

    return server_application_message
Exemple #2
0
def build_splapp_subscription_update(request_id, subscription_id, update_id, subscription_update):
    """
    Build Subscription Update proto
    :param request_id:
    :param subscription_id:
    :param update_id:
    :param subscription_update:
    :return:
    """
    server_application_message = envelope_pb2.ServerApplicationMessage()

    server_application_message.serverSubscriptionUpdate.requestId = request_id
    server_application_message.serverSubscriptionUpdate.subscriptionId = subscription_id
    server_application_message.serverSubscriptionUpdate.updateId = update_id

    # if/else case statement for subscription_update types
    if isinstance(subscription_update, ServerDashboardVisualizationEvent):
        subscription_update.set_protobuf(server_application_message.serverSubscriptionUpdate.dashboardVisualizationEvent)
    elif isinstance(subscription_update, ServerUdfDatasourceEvent):
        subscription_update.set_protobuf(server_application_message.serverSubscriptionUpdate.udfDataSourceEvent)
    elif isinstance(subscription_update, ServerDashboardInputSearchEvent):
        subscription_update.set_protobuf(server_application_message.serverSubscriptionUpdate.dashboardInputSearchEvent)
    elif isinstance(subscription_update, ServerSavedSearchEvent):
        subscription_update.set_protobuf(server_application_message.serverSubscriptionUpdate.serverSavedSearchResultEvent)
    elif isinstance(subscription_update, SpacebridgeError):
        subscription_update.set_proto(server_application_message.serverSubscriptionUpdate)
    else:
        server_application_message.serverSubscriptionUpdate.error.code = common_pb2.Error.ERROR_UNKNOWN
        server_application_message.serverSubscriptionUpdate.error.message = 'Unexpected Error!'

    return server_application_message
Exemple #3
0
    async def handle_application_message(self, msg, sender, request_id):
        """
        Business logic for how to handle an application level message from a client device
        :param msg: decrypted payload of message
        :param sender: id of the sender sending the message
        :return: ServerResponse object containing payload to be sent back to client
        """
        self.logger.debug("Incoming message size=%s, request_id=%s", len(msg), request_id)

        try:
            # Parse message to proto
            server_application_message = envelope_pb2.ServerApplicationMessage()
            client_application_message = envelope_pb2.ClientApplicationMessage()
            client_application_message.ParseFromString(msg)
            server_response_id = self.guid_generator()


            # Transform legacy requests into a generic message requests
            # TODO: Remove when client devices migrate to generic message api
            transform_legacy_client_message(client_application_message)

            # process message
            request_context = await process_message(sender,
                                                    client_application_message,
                                                    server_application_message,
                                                    self.async_client_factory,
                                                    self.encryption_context,
                                                    server_response_id,
                                                    self.system_auth_header,
                                                    self.shard_id)

            # Transform generic message to legacy protobuf format
            # TODO: Remove when client devices migrate to generic message api
            transform_generic_response_to_legacy(server_application_message)
            payload = server_application_message.SerializeToString()

            # Construct response to send back to client
            server_response = ServerResponse(payload, request_context.request_id)
            responses = [server_response] if payload else []


            # Do post processing for managing subscriptions
            subscription_update = await post_process_message(request_context,
                                                             server_application_message,
                                                             self.async_client_factory,
                                                             get_guid
                                                             )

            if subscription_update:
                subscription_response = ServerResponse(subscription_update)
                responses.append(subscription_response)

            return responses

        except Exception as e:
            self.logger.exception("Exception handling application message={0}".format(e))
def post_process_message(request_context,
                         input_server_application_message,
                         async_client_factory,
                         guid_generator):
    # Post processing on serverSubscriptionResponse
    if input_server_application_message.HasField(SERVER_SUBSCRIPTION_RESPONSE):
        try:
            # Create Server Application Message to return for post_processing
            server_application_message = envelope_pb2.ServerApplicationMessage()

            # Populate a server_subscription_update which
            server_subscription_update = server_application_message.serverSubscriptionUpdate
            server_subscription_update.requestId = guid_generator()

            # Get subscription_id
            server_subscription_response = input_server_application_message.serverSubscriptionResponse
            subscription_id = server_subscription_response.subscriptionId
            server_subscription_update.subscriptionId = subscription_id

            if server_subscription_response.HasField(SERVER_SUBSCRIBE_RESPONSE):
                if (server_subscription_response.serverSubscribeResponse.HasField(DRONE_MODE_TV_SUBSCRIBE) or
                   server_subscription_response.serverSubscribeResponse.HasField(DRONE_MODE_IPAD_SUBSCRIBE)):
                    yield process_subscriptions(request_context,
                                                async_client_factory)

                else:
                    map_post_search = server_subscription_response.serverSubscribeResponse.postSearch or None
                    yield subscription_processor.process_subscription(request_context,
                                                                      subscription_id,
                                                                      server_subscription_update,
                                                                      async_client_factory,
                                                                      guid_generator,
                                                                      map_post_search=map_post_search)
        except SpacebridgeError as e:
            LOGGER.exception("SpacebridgeError during post_process_message")
            e.set_proto(server_subscription_update)

        except Exception as e:
            LOGGER.exception("Unhandled exception during post_process_message")
            server_subscription_update.error.code = common_pb2.Error.ERROR_UNKNOWN
            server_subscription_update.error.message = str(e)

        # Send out response if any errors or an update is encountered
        if not server_subscription_update.WhichOneof('subscription_update'):
            LOGGER.info("No Post Processing Response required for subscription_id={}".format(subscription_id))
            defer.returnValue(None)

        # Send out response if update is specified, returns errors and single saved search responses
        defer.returnValue(server_application_message.SerializeToString())
    def handle_application_message(self, msg, sender, request_id):
        """
        Business logic for how to handle an application level message from a client device
        :param msg: decrypted payload of message
        :param sender: id of the sender sending the message
        :return: ServerResponse object containing payload to be sent back to client
        """
        try:
            # Parse message to proto
            server_application_message = envelope_pb2.ServerApplicationMessage()
            client_application_message = envelope_pb2.ClientApplicationMessage()
            client_application_message.ParseFromString(msg)
            server_response_id = self.guid_generator()


            # process message
            request_context = yield process_message(sender,
                                                    client_application_message,
                                                    server_application_message,
                                                    self.async_client_factory,
                                                    self.encryption_context,
                                                    server_response_id,
                                                    self.system_auth_header,
                                                    self.shard_id)

            payload = server_application_message.SerializeToString()

            # Construct response to send back to client
            server_response = ServerResponse(payload, request_context.request_id)
            responses = [server_response]


            # Do post processing for managing subscriptions
            subscription_update = yield post_process_message(request_context,
                                                             server_application_message,
                                                             self.async_client_factory,
                                                             get_guid
                                                             )

            if subscription_update:
                subscription_response = ServerResponse(subscription_update)
                responses.append(subscription_response)

            defer.returnValue(responses)

        except Exception as e:
            self.logger.exception("Exception handling application message={0}".format(e))
def build_connectivity_test_response(server_single_response, recipient, sender_id, request_id, encrypt, signer):
    """
    Build connectivity test response to send to spacebridge send_message api
    :param server_single_response:
    :param recipient:
    :param sender_id:
    :param request_id:
    :param encrypt:
    :param signer:
    :return:
    """
    send_connectivity_message = http_pb2.SendMessageRequest()
    server_single_response.connectivityTestResponse.requestId = request_id

    # Package in ServerApplicationMessage
    server_application_message = envelope_pb2.ServerApplicationMessage()
    server_application_message.serverSingleResponse.CopyFrom(server_single_response)

    encrypted_payload = encrypt(server_application_message.SerializeToString())
    build_signed_envelope(send_connectivity_message.signedEnvelope, recipient,
                          sender_id, request_id, encrypted_payload, signer)
    return send_connectivity_message
def process(system_auth_header, message, websocket_protocol, async_client_factory,
            guid_generator=get_guid, shard_id=None):
    """Accepts a message which is assumed to be serialized using protobuf,
    deserializes the message, unencrypts the payload and routes to the appropriate
    processor depending on the type of message.

    For example, we check whether the message is a client single request, if it is,
    we call the process_request method to process the contents. Analagously, we have
    methods for other message types as well such as process_subscription.

    Arguments:
        message {serialized proto} -- serialized protobuf object

    Returns:
        Serialized Application Message representing response from the server
    """

    # Deserialize Signed Envelope
    signed_envelope = parse_signed_envelope(message)

    # Deserialize application message
    if signed_envelope.messageType == sb_common_pb2.SignedEnvelope.MESSAGE_TYPE_APPLICATION_MESSAGE:
        LOGGER.info("message=RECEIVED_ENVELOPE type=application_message")
        application_message = parse_application_message(signed_envelope.serialized)
    elif signed_envelope.messageType == sb_common_pb2.SignedEnvelope.MESSAGE_TYPE_SPACEBRIDGE_MESSAGE:
        # TODO: error handling
        LOGGER.info("message=RECEIVED_ENVELOPE type=spacebridge_message")
        spacebridge_message = parse_spacebridge_message(signed_envelope.serialized)
        handle_spacebridge_message(system_auth_header, spacebridge_message,
                                   async_client_factory.splunk_client(), async_client_factory.kvstore_client())
        defer.returnValue(True)
    else:
        LOGGER.info("message=RECEIVED_ENVELOPE type={}".format(signed_envelope.messageType))
        defer.returnValue("Unknown message type")

    message_sender = application_message.sender
    keys = yield public_keys_for_device(message_sender, system_auth_header, async_client_factory.kvstore_client())

    sender_sign_public_key, sender_encrypt_public_key = keys

    encryption_context = websocket_protocol.encryption_context
    sodium_client = encryption_context.sodium_client

    decryptor = partial(decrypt_for_receive,
                        sodium_client,
                        encryption_context.encrypt_public_key(),
                        encryption_context.encrypt_private_key())
    encryptor = partial(encrypt_for_send,
                        sodium_client,
                        sender_encrypt_public_key)

    signer = partial(sign_detached, sodium_client, encryption_context.sign_private_key())

    generichash = encryption_context.generichash_raw

    if not sign_verify(sodium_client, sender_sign_public_key, signed_envelope.serialized, signed_envelope.signature):
        defer.returnValue("Signature validation failed")

    server_response_id = guid_generator()
    client_application_message = parse_client_application_message(application_message, decryptor)

    if application_message.ByteSize() == 0 or client_application_message.ByteSize() == 0:
        defer.returnValue("Unknown message type")

    server_application_message = envelope_pb2.ServerApplicationMessage()

    # Check if message is a request or subscription and then populate the server_application_response
    request_context = yield process_message(message_sender,
                                            client_application_message,
                                            server_application_message,
                                            async_client_factory,
                                            websocket_protocol.encryption_context,
                                            server_response_id,
                                            system_auth_header,
                                            shard_id)

    response = yield send_response(request_context,
                                   message_sender,
                                   server_application_message,
                                   websocket_protocol,
                                   encryptor,
                                   signer,
                                   generichash)

    # Post processing function
    yield post_process_message(request_context,
                               server_application_message,
                               async_client_factory,
                               guid_generator)
    defer.returnValue(response)