def get(self, request):
        auth_token = request['system_authtoken']
        friendly_name = get_deployment_friendly_name(auth_token)
        encryption_context = SplunkEncryptionContext(
            auth_token, constants.SPACEBRIDGE_APP_NAME, SodiumClient(LOGGER))
        mdm_sign_public_key = get_mdm_public_signing_key(auth_token)
        mdm_keypair_generation_time = get_mdm_update_timestamp(
            request, auth_token)

        return {
            'payload': {
                constants.DEPLOYMENT_FRIENDLY_NAME:
                friendly_name,
                constants.SIGN_PUBLIC_KEY:
                py23.b64encode_to_str(encryption_context.sign_public_key()),
                constants.DEPLOYMENT_ID:
                encryption_context.sign_public_key(
                    transform=encryption_context.generichash_hex),
                constants.ENCRYPT_PUBLIC_KEY:
                py23.b64encode_to_str(encryption_context.encrypt_public_key()),
                constants.SERVER_VERSION:
                str(app_version()),
                constants.MDM_SIGN_PUBLIC_KEY:
                mdm_sign_public_key,
                constants.MDM_KEYPAIR_GENERATION_TIME:
                mdm_keypair_generation_time
            },
            'status': 200
        }
Beispiel #2
0
def delete_device_from_spacebridge(device_id,
                                   system_authtoken,
                                   key_bundle=None):
    """
    Deletes device from spacebridge
    :param device_id:
    :param system_authtoken:
    :return: response from spacebridge
    """
    sodium_client = SodiumClient(LOGGER.getChild("sodium_client"))
    encryption_context = SplunkEncryptionContext(
        system_authtoken, constants.SPACEBRIDGE_APP_NAME, sodium_client)
    public_key_hash = encryption_context.sign_public_key(
        transform=encryption_context.generichash_hex)

    unregister_proto = http_pb2.DeviceUnregistrationRequest()
    unregister_proto.deviceId = b64decode(device_id)
    unregister_proto.deploymentId = encryption_context.sign_public_key(
        transform=encryption_context.generichash_raw)

    headers = {
        'Authorization': public_key_hash,
        'Content-Type': 'application/x-protobuf'
    }

    with requests_ssl_context(key_bundle):
        try:
            response = requests.delete(
                "%s/api/session" % config.get_spacebridge_domain(),
                headers=headers,
                proxies=config.get_proxies(),
                data=unregister_proto.SerializeToString())
        except Exception:
            LOGGER.exception(
                "Exception attempting sending delete device request to Spacebridge"
            )
            raise Errors.SpacebridgeServerError('Unable to reach Spacebridge',
                                                503)

    LOGGER.info(
        "Received response=%s on delete device from Spacebridge request" %
        response.status_code)

    spacebridge_response = http_pb2.DeviceUnregistrationResponse()
    spacebridge_response.ParseFromString(response.content)

    LOGGER.info('Spacebridge response: %s' % str(spacebridge_response))

    if spacebridge_response.HasField(
            'error'
    ) and spacebridge_response.error.code != http_pb2.HttpError.Code.Value(
            'ERROR_ROUTING_UNDELIVERABLE'):
        raise Errors.SpacebridgeServerError(
            "Spacebridge error on delete device request=%s" %
            spacebridge_response.error.message)

    return response
Beispiel #3
0
def generate_keys():
    """
    Generates the public and private keys necessary for encryption and signing.
    """
    sodium_client = SodiumClient()

    [sign_pk, sign_sk] = sodium_client.sign_generate_keypair()
    [box_pk, box_sk] = sodium_client.box_generate_keypair()

    return EncryptionKeys(sign_pk, sign_sk, box_pk, box_sk)
def process_subscriptions(request_context,
                          async_client_factory,
                          encryption_context=None,
                          tv_device_ids=None,
                          tv_subscriptions=None,
                          ipad_subscriptions=None):
    """
    Function to send subscription updates to the appropriate tv
    and all ipads registered to the user
    :param tv_config: tv config to send
    :param request_context: request context used to make kvstore request
    :param async_client_factory: used to create spacebridge and kvstore client
    """
    async_kvstore_client = async_client_factory.kvstore_client()
    if not tv_subscriptions:
        tv_subscriptions = yield fetch_subscriptions(
            request_context.auth_header,
            async_kvstore_client,
            user_list=[request_context.current_user],
            subscription_type=constants.DRONE_MODE_TV,
            device_ids=tv_device_ids)

    LOGGER.debug(
        'Active subscriptions%s: %s',
        ' for tv device_ids={}'.format(tv_device_ids) if tv_device_ids else '',
        tv_subscriptions)
    if tv_subscriptions:
        # create encryption context if not passed in
        if not encryption_context:
            sodium_client = SodiumClient()
            encryption_context = SplunkEncryptionContext(
                request_context.system_auth_header.session_token,
                constants.SPACEBRIDGE_APP_NAME, sodium_client)

        tv_subscription_tuples = yield build_tv_subscription_updates(
            tv_subscriptions, request_context,
            async_client_factory.kvstore_client())
        yield send_updates(tv_subscription_tuples, encryption_context,
                           async_client_factory, request_context,
                           constants.DRONE_MODE_TV)

    # Fetch active iPad subscriptions
    if not ipad_subscriptions:
        ipad_subscriptions = yield fetch_subscriptions(
            request_context.auth_header,
            async_kvstore_client,
            user_list=[request_context.current_user],
            subscription_type=constants.DRONE_MODE_IPAD)
    if ipad_subscriptions:
        ipad_subscription_tuples = yield build_ipad_subscription_updates(
            ipad_subscriptions, request_context,
            async_client_factory.kvstore_client())
        yield send_updates(ipad_subscription_tuples, encryption_context,
                           async_client_factory, request_context,
                           constants.DRONE_MODE_IPAD)
Beispiel #5
0
    def do_run(self, input_config):
        """ Spins up a websocket connection Spacebridge and begins
        the reactor loops
        """
        shard_id = default_shard_id()

        self.logger.info("Starting libsodium child process")
        sodium_logger = self.logger.getChild('sodium_client')
        sodium_logger.setLevel(logging.WARN)

        sodium_client = SodiumClient(sodium_logger)
        encryption_context = SplunkEncryptionContext(
            self.session_key, constants.SPACEBRIDGE_APP_NAME, sodium_client)

        self.logger.info(
            "Running Splunk Cloud Gateway modular input on search head, shard_id=%s",
            shard_id)

        # Fetch load balancer address if configured, otherwise use default URI
        try:
            uri = get_uri(self.session_key)
            self.logger.debug(
                "Successfully verified load_balancer_address={}".format(uri))
        except Exception as e:
            self.logger.exception(
                "Failed to verify load_balancer_address. {}".format(e))

        if not uri:
            return

        try:
            ensure_deployment_friendly_name(self.session_key)
            async_client_factory = AsyncClientFactory(uri)
            cloudgateway_message_handler = CloudgatewayMessageHandler(
                SplunkAuthHeader(self.session_key),
                logger=self.logger,
                encryption_context=encryption_context,
                async_client_factory=async_client_factory,
                shard_id=shard_id)

            client = CloudGatewayWsClient(
                encryption_context,
                message_handler=cloudgateway_message_handler,
                mode=WebsocketMode.ASYNC,
                logger=self.logger,
                config=config,
                shard_id=shard_id)

            client.connect()
        except Exception as e:
            self.logger.exception(
                "Exception connecting to cloud gateway={0}".format(e))
Beispiel #6
0
    def __init__(self, encryption_keys, sodium_client=None):
        """
        Args:
            encryption_keys ([EncryptionKeys]): User must generate encryption keys using the
            generate_keys method and provide keys in the context. It's up the user to 
            persist the keys themselves between sessions. 
            sodium_client ([type], optional): [description]. Defaults to None.
        """

        self.mode = SdkMode.STANDALONE
        if sodium_client:
            self.sodium_client = sodium_client
        else:
            self.sodium_client = SodiumClient()

        self._key_cache = encryption_keys.__dict__
def process_mpc_broadcast_request(request_context,
                                  client_single_request,
                                  single_server_response,
                                  async_client_factory):
    """
    This Method will process a MPC Broadcast Request

    :param request_context: Used to authenticate kvstore requests
    :param client_single_request: client request object protobuf
    :param single_server_response: server response object protobuf
    :param async_client factory: factory class used to generate kvstore and spacebridge clients
    """

    async_kvstore_client = async_client_factory.kvstore_client()
    device_id = client_single_request.startMPCBroadcastRequest.deviceId

    subscriptions = yield fetch_subscriptions(request_context.auth_header,
                                              async_kvstore_client,
                                              user_list=[request_context.current_user],
                                              subscription_type=constants.DRONE_MODE_TV,
                                              device_ids=[device_id])
    if not subscriptions:
        raise SpacebridgeApiRequestError('No active subscriptions for device_id={}'
                                         .format(device_id),
                                         status_code=http.BAD_REQUEST)

    async_spacebridge_client = async_client_factory.spacebridge_client()
    active_subscription = subscriptions[0]
    sodium_client = SodiumClient()
    encryption_context = SplunkEncryptionContext(request_context.system_auth_header.session_token,
                                                 constants.SPACEBRIDGE_APP_NAME,
                                                 sodium_client)
    start_broadcast = StartMPCBroadcast(device_id=device_id)
    subscription_update = DroneModeTVEvent(data_object=start_broadcast,
                                           event_type=TVEventType.MPC_BROADCAST)
    response, subscription_key = yield send_drone_mode_subscription_update(request_context.system_auth_header,
                                                                           active_subscription,
                                                                           subscription_update,
                                                                           encryption_context,
                                                                           async_spacebridge_client,
                                                                           async_kvstore_client)
    yield check_and_raise_error(response, request_context, 'MPC Broadcast Request')
    single_server_response.startMPCBroadcastResponse.SetInParent()

    LOGGER.info('Successfully sent mpc broadcast message to device_id=%s with subscription_key=%s',
                device_id,
                subscription_key)
Beispiel #8
0
    def __init__(self, session_key, app_name, sodium_client=None):
        """
        Pass a session token and create a KV Store Handler to be able to write and fetch public keys from KV Store.
        The session token itself is not exposed, only the handler.
        """

        self.mode = SdkMode.SPLUNK
        if sodium_client:
            self.sodium_client = sodium_client
        else:
            self.sodium_client = SodiumClient()

        self._key_cache = {}
        self.session_key = session_key
        self.app_name = app_name

        self.generate_keys()
Beispiel #9
0
    def do_run(self, input_config):
        """
        This will spin up a drone mode subscription manager and begins the reactor loop
        :param input_config:
        :return:
        """

        try:
            sodium_client = SodiumClient(self.logger.getChild('sodium_client'))
            encryption_context = SplunkEncryptionContext(
                self.session_key, SPACEBRIDGE_APP_NAME, sodium_client)

            self.logger.debug(
                "Running Drone Mode Subscription Manager modular input")

            # Fetch load balancer address if configured, otherwise use default URI
            try:
                uri = get_uri(self.session_key)
                self.logger.debug(
                    "Successfully verified load_balancer_address=%s", uri)
            except Exception:
                self.logger.exception(
                    "Failed to verify load_balancer_address.")

            if not uri:
                return

            subscription_manager = DroneModeSubscriptionManager(
                input_config=input_config,
                encryption_context=encryption_context,
                session_key=self.session_key,
                async_splunk_client=AsyncSplunkClient(uri),
                parent_process_monitor=ParentProcessMonitor(),
                cluster_monitor=ClusterMonitor(
                    self.logger,
                    interval=config.get_cluster_monitor_interval()))
            subscription_manager.run()

        except Exception as e:
            self.logger.exception(
                'An error occurred running the drone mode subscription modular input'
            )
            raise e
Beispiel #10
0
def send_mdm_signing_key_to_spacebridge(authtoken,
                                        mdm_public_signing_key,
                                        key_bundle=None):
    """ Send the mdm public signing key to spacebridge
        Abstraction layer for the spacebridge request. This function:
        1. Creates the mdm_credentials_bundle
        2. Serializes the bundle to bytes
        3. Signs the serialized bundle with the splapps private signing key
        4. Creates a proto object with the serialized bundle + signature and sends to spacebridge
        5. Parses the protobuf response, checking for error objects

    """

    sodium_client = SodiumClient(LOGGER.getChild("sodium_client"))
    encryption_context = SplunkEncryptionContext(
        authtoken, constants.SPACEBRIDGE_APP_NAME, sodium_client)
    sign_func = partial(sign_detached, sodium_client,
                        encryption_context.sign_private_key())
    client_id = encryption_context.sign_public_key(
        transform=encryption_context.generichash_raw)

    request_proto = http_pb2.MdmAuthenticationGrantRequest()
    client_mdm_permission = request_proto.ClientMdmPermission()
    client_mdm_permission.clientId = client_id
    client_mdm_permission.mdmPublicKeyForSigning = mdm_public_signing_key
    serialized = client_mdm_permission.SerializeToString()
    signature = sign_func(serialized)
    request_proto.clientMdmPermission = serialized
    request_proto.signature = signature
    headers = {
        'Authorization':
        encryption_context.sign_public_key(
            transform=encryption_context.generichash_hex),
        'Content-Type':
        'application/x-protobuf'
    }

    def call_grants():
        with requests_ssl_context(key_bundle) as cert:
            return requests.post('{}/api/mdm/grants'.format(
                config.get_spacebridge_domain()),
                                 headers=headers,
                                 data=request_proto.SerializeToString(),
                                 proxies=config.get_proxies(),
                                 timeout=constants.TIMEOUT_SECONDS,
                                 cert=cert.name)

    try:
        response = call_grants()
    except requests.exceptions.Timeout as e:
        raise Errors.SpacebridgeServerError(
            'Failed to receive a response from spacebridge', 500)

    sb_response_proto = http_pb2.MdmAuthenticationGrantResponse()
    sb_response_proto.ParseFromString(response.content)

    retries = 0
    while 500 <= response.status_code < 600 and retries < 3:
        wait = 2**retries
        retries = retries + 1
        time.sleep(wait)

        try:
            response = call_grants()
        except requests.exceptions.Timeout as e:
            raise Errors.SpacebridgeServerError(
                'Failed to receive a response from spacebridge', 500)

        sb_response_proto = http_pb2.MdmAuthenticationGrantResponse()
        sb_response_proto.ParseFromString(response.content)

    if sb_response_proto.HasField('error'):
        if response.status_code == 500:
            raise Errors.SpacebridgeServerError(
                'Spacebridge encountered an internal error:{}'.format(
                    sb_response_proto.error.message), 500)
        raise Errors.SpacebridgeServerError(
            'Spacebridge request error: {}'.format(
                sb_response_proto.error.message, response.status_code))

    if not (200 <= response.status_code < 300):
        raise Errors.SpacebridgeServerError(
            "Spacebridge error: {}".format(response.content),
            response.status_code)
    def do_run(self, input_config):
        shard_id = default_shard_id()

        self.logger.info("Starting libsodium child process")
        sodium_logger = self.logger.getChild('sodium_client')
        sodium_logger.setLevel(logging.WARN)
        sodium_client = SodiumClient(sodium_logger)
        self.logger.info("Loading encryption context")
        encryption_context = SplunkEncryptionContext(self.session_key,
                                                     SPACEBRIDGE_APP_NAME,
                                                     sodium_client)

        self.logger.info(
            "Running Subscription Manager modular input on search head")

        # Fetch load balancer address if configured, otherwise use default URI
        try:
            uri = get_uri(self.session_key)
            self.logger.debug(
                "Successfully verified load_balancer_address={}".format(uri))
        except Exception as e:
            self.logger.exception(
                "Failed to verify load_balancer_address. {}".format(e))

        if not uri:
            return

        try:
            minimum_iteration_time_seconds = float(input_config[
                self.input_config_key][self.minimum_iteration_time_seconds])
            warn_threshold_seconds = float(input_config[self.input_config_key][
                self.warn_threshold_seconds])
            subscription_processor_parallelism_str = input_config[
                self.input_config_key][self.subscription_processor_parallelism]
            subscription_parallelism = self._resolve_parallelism(
                subscription_processor_parallelism_str)
        except:
            self.logger.exception(
                "Failed to load required configuration values")
            return

        try:
            self.logger.info("Processing subscriptions with parallelism=%s",
                             subscription_parallelism)
            auth_header = SplunkAuthHeader(self.session_key)

            self.logger.debug(
                "Using search processor python=%s, script=%s, args=%s",
                self.python_path, self.script_path, self.subprocess_args)
            start_job = start_job_using_subprocess(self.python_path,
                                                   self.subprocess_args)

            if subscription_parallelism == self.CONFIG_VALUE_SINGLE_PROCESS:
                start_job = start_job_single_process(sodium_client,
                                                     encryption_context)

            process_manager = ProcessManager(subscription_parallelism,
                                             start_job)
            job_context = JobContext(
                auth_header, uri,
                encryption_context.get_encryption_keys().to_json())

            subscription_manager = SubscriptionManager(
                input_config=input_config,
                encryption_context=encryption_context,
                auth_header=auth_header,
                shard_id=shard_id,
                job_context=job_context,
                search_loader=loader.load_search_bundle,
                parent_process_monitor=ParentProcessMonitor(),
                minimum_iteration_time_seconds=minimum_iteration_time_seconds,
                warn_threshold_seconds=warn_threshold_seconds,
                process_manager=process_manager)

            subscription_manager.run()
        except:
            self.logger.exception(
                "Unhandled exception during subscription processing")
def process_tv_interaction_request(request_context,
                                   client_single_request,
                                   single_server_response,
                                   async_client_factory):
    """
    This Method will process a MPC Broadcast Request

    :param request_context: Used to authenticate kvstore requests
    :param client_single_request: client request object protobuf
    :param single_server_response: server response object protobuf
    :param async_client factory: factory class used to generate kvstore and spacebridge clients
    """

    async_kvstore_client = async_client_factory.kvstore_client()
    device_id = client_single_request.tvInteractionRequest.tvInteraction.device_id
    tv_interaction_proto = client_single_request.tvInteractionRequest.tvInteraction
    # determine type of interaction
    interaction_type = TVInteractionType.NONE
    speed = None
    if tv_interaction_proto.HasField(constants.SLIDESHOW_GOTO):
        interaction_type = TVInteractionType.GOTO

    elif tv_interaction_proto.HasField(constants.SLIDESHOW_STOP):
        interaction_type = TVInteractionType.STOP

    elif tv_interaction_proto.HasField(constants.SLIDESHOW_FORWARD):
        interaction_type = TVInteractionType.FORWARD

    elif tv_interaction_proto.HasField(constants.SLIDESHOW_BACK):
        interaction_type = TVInteractionType.BACK

    elif tv_interaction_proto.HasField(constants.SLIDESHOW_SPEED):
        interaction_type = TVInteractionType.SPEED
        speed = tv_interaction_proto.slideshow_speed.speed


    subscriptions = yield fetch_subscriptions(request_context.auth_header,
                                              async_kvstore_client,
                                              user_list=[request_context.current_user],
                                              subscription_type=constants.DRONE_MODE_TV,
                                              device_ids=[device_id])


    if not subscriptions:
        raise SpacebridgeApiRequestError('No active subscriptions for device_id={}'
                                         .format(device_id),
                                         status_code=http.BAD_REQUEST)

    async_spacebridge_client = async_client_factory.spacebridge_client()
    active_subscription = subscriptions[0]
    sodium_client = SodiumClient()
    encryption_context = SplunkEncryptionContext(request_context.system_auth_header.session_token,
                                                 constants.SPACEBRIDGE_APP_NAME,
                                                 sodium_client)
    tv_interaction = TVInteraction(device_id=device_id,
                                   interaction_type=interaction_type,
                                   speed=speed)
    subscription_update = DroneModeTVEvent(data_object=tv_interaction,
                                           event_type=TVEventType.TV_INTERACTION)
    response, subscription_key = yield send_drone_mode_subscription_update(request_context.system_auth_header,
                                                                           active_subscription,
                                                                           subscription_update,
                                                                           encryption_context,
                                                                           async_spacebridge_client,
                                                                           async_kvstore_client)
    yield check_and_raise_error(response, request_context, 'MPC Broadcast Request')
    single_server_response.tvInteractionResponse.SetInParent()

    LOGGER.info('Successfully sent mpc broadcast message to device_id=%s with subscription_key=%s',
                device_id,
                subscription_key)
Beispiel #13
0
 def __init__(self, command_line, command_arg):
     BaseRestHandler.__init__(self)
     self.sodium_client = SodiumClient(LOGGER.getChild('sodium_client'))
def handle_query(auth_code, device_name, user, system_authtoken):
    """
    Handler for the initial AuthenticationQueryRequest call. This function:
        1. Makes the AuthenticationQueryRequest request to the server
        2. Checks if app_type has been disabled
        3. Stores a temporary record in the kvstore

    :param auth_code: User-entered authorization code to be returned to Spacebridge
    :param device_name: Name of the new device
    :return: Confirmation code to be displayed to user, and id of temporary kvstore record to be returned later
    """

    LOGGER.info('Received new registration query request by user=%s' % user)

    # Makes the AuthenticationQueryRequest request to the server
    sodium_client = SodiumClient(LOGGER.getChild('sodium_client'))
    encryption_context = SplunkEncryptionContext(system_authtoken,
                                                 SPACEBRIDGE_APP_NAME,
                                                 sodium_client)
    client_device_info = authenticate_code(auth_code,
                                           encryption_context,
                                           resolve_app_name,
                                           config=config)
    app_name = client_device_info.app_name
    app_id = client_device_info.app_id

    LOGGER.info("client_device_info={}".format(client_device_info))

    user_devices = get_devices_for_user(user, system_authtoken)
    LOGGER.info("user_devices=%s" % user_devices)

    if any(device[DEVICE_NAME_LABEL] == device_name
           and device['device_type'] == app_name for device in user_devices):
        err_msg = (
            'Registration Error: user={} device_name={} of app_type={} already exists'
            .format(user, device_name, app_name))
        LOGGER.info(err_msg)
        raise Errors.SpacebridgeRestError(err_msg, http.CONFLICT)

    # Checks if app_type has been disabled
    if not retrieve_state_of_app(app_name, system_authtoken):
        disabled_message = 'Registration Error: Application type app_name="{}" is disabled'.format(
            app_name)
        LOGGER.info(disabled_message)
        return {
            'payload': {
                'message': disabled_message,
                'is_admin': user_is_administrator(user, system_authtoken),
                'app_name': app_name,
            },
            'status': 422,
        }

    # Stores a temporary record in the kvstore
    kvstore_unconfirmed = KvStore(UNCONFIRMED_DEVICES_COLLECTION_NAME,
                                  system_authtoken,
                                  owner=user)
    kvstore_payload = client_device_info.to_json()
    kvstore_payload['device_name'] = device_name
    kvstore_payload['device_type'] = app_name
    kvstore_payload['app_id'] = app_id
    _, content = kvstore_unconfirmed.insert_single_item(kvstore_payload)

    return {
        'payload': {
            'temp_key': json.loads(content)['_key'],
            'conf_code': client_device_info.confirmation_code
        },
        'status': http.OK,
    }
Beispiel #15
0
    def handle_saml_mdm_request(self, user, session_token, system_authtoken,
                                mdm_signing_bundle, body):
        """
        Handles the MDM SAML Registration Request.
        Validates signature sent from client, validates session token, generates a JWT token,
        and sends it encrypted using splapp's keys and the client public key
        :param user: string provided by rest handler
        :param session_token: string
        :param system_authtoken: string
        :param mdm_signing_bundle: Object
        :param body: JSON
        :return: Reponse object with payload and status
        """
        public_key = base64.b64decode(
            extract_parameter(body, PUBLIC_KEY_LABEL, BODY_LABEL))
        mdm_signature = base64.b64decode(
            extract_parameter(body, MDM_SIGNATURE_LABEL, BODY_LABEL))

        client_keys = EncryptionKeys(None, None, public_key, None)
        client_encryption_context = EncryptionContext(client_keys)

        try:
            valid_signature = yield sign_verify(
                SodiumClient(LOGGER.getChild("sodium_client")),
                base64.b64decode(
                    mdm_signing_bundle['sign_public_key'].encode('utf8')),
                client_encryption_context.encrypt_public_key(), mdm_signature)
        except Exception as e:
            LOGGER.exception(
                "Exception verifying signature from client for user={}".format(
                    user))
            defer.returnValue({
                'payload': {
                    'token': "",
                    'user': user,
                    'status': http.UNAUTHORIZED
                },
                'status': http.OK
            })

        async_splunk_client = self.async_client_factory.splunk_client()
        valid_request = yield valid_session_token(user, session_token,
                                                  async_splunk_client)

        LOGGER.info(
            "Received new mdm registration request by user={}".format(user))

        if valid_signature and valid_request:
            try:
                credentials = SplunkJWTCredentials(user)
                credentials.load_jwt_token(SplunkAuthHeader(system_authtoken))
                LOGGER.info("Successfully fetched jwt token")
            except Exception as e:
                LOGGER.exception(
                    "Exception fetching jwt token for user={} with message={}".
                    format(user, e))
                defer.returnValue({
                    'payload': {
                        'token': "",
                        'user': user,
                        'status': 422
                    },
                    'status': http.OK
                })

            splapp_encryption_context = SplunkEncryptionContext(
                system_authtoken, constants.SPACEBRIDGE_APP_NAME,
                SodiumClient(LOGGER.getChild("sodium_client")))

            # Encrypt session token using splapp keys
            secured_session_token = splapp_encryption_context.secure_session_token(
                credentials.get_credentials())
            # Encrypt session token using client's given public key
            encrypted_jwt_token = yield encrypt_for_send(
                SodiumClient(LOGGER.getChild("sodium_client")),
                client_encryption_context.encrypt_public_key(),
                secured_session_token)
            base64_encrypted_jwt_token = base64.b64encode(encrypted_jwt_token)

            defer.returnValue({
                'payload': {
                    'token': base64_encrypted_jwt_token,
                    'user': user,
                    'status': http.OK
                },
                'status': http.OK
            })
        else:
            LOGGER.info(
                "Error: Mismatched user={} and session token".format(user))
            defer.returnValue({
                'payload': {
                    'token': "",
                    'user': user,
                    'status': http.UNAUTHORIZED
                },
                'status': http.OK
            })
 def __init__(self, command_line, command_arg):
     BaseRestHandler.__init__(self)
     self.sodium_client = SodiumClient()
Beispiel #17
0
def handle_query(auth_code, device_name, user, system_authtoken):
    """
    Handler for the initial AuthenticationQueryRequest call. This function:
        1. Makes the AuthenticationQueryRequest request to the server
        2. Checks if app_type has been disabled
        3. Stores a temporary record in the kvstore

    :param auth_code: User-entered authorization code to be returned to Spacebridge
    :param device_name: Name of the new device
    :return: Confirmation code to be displayed to user, and id of temporary kvstore record to be returned later
    """

    LOGGER.info('Received new registration query request by user=%s' % user)

    # Makes the AuthenticationQueryRequest request to the server
    sodium_client = SodiumClient(LOGGER.getChild('sodium_client'))
    encryption_context = SplunkEncryptionContext(system_authtoken,
                                                 SPACEBRIDGE_APP_NAME,
                                                 sodium_client)
    client_device_info = authenticate_code(auth_code,
                                           encryption_context,
                                           resolve_app_name,
                                           config=config)
    app_name = client_device_info.app_name
    app_id = client_device_info.app_id

    platform = client_device_info.platform

    # if platform not set and we know platform based on app id, use that.
    if not platform and app_id in APP_ID_TO_PLATFORM_MAP:
        platform = get_app_platform(app_id)

    LOGGER.info("client_device_info={}".format(client_device_info))

    user_devices = get_devices_for_user(user, system_authtoken)
    LOGGER.info("user_devices=%s" % user_devices)

    if any(device[DEVICE_NAME_LABEL] == device_name
           and device['device_type'] == app_name for device in user_devices):
        err_msg = (
            'Registration Error: user={} device_name={} of app_type={} already exists'
            .format(user, device_name, app_name))
        LOGGER.info(err_msg)
        raise Errors.SpacebridgeRestError(err_msg, HTTPStatus.CONFLICT)

    # Stores a temporary record in the kvstore
    kvstore_unconfirmed = KvStore(UNCONFIRMED_DEVICES_COLLECTION_NAME,
                                  system_authtoken,
                                  owner=user)
    kvstore_payload = client_device_info.to_json()
    kvstore_payload['device_name'] = device_name
    kvstore_payload['device_type'] = app_name
    kvstore_payload['app_name'] = app_name
    kvstore_payload['app_id'] = app_id
    kvstore_payload['platform'] = platform
    _, content = kvstore_unconfirmed.insert_single_item(kvstore_payload)

    return {
        'payload': {
            'temp_key': json.loads(content)['_key'],
            'conf_code': client_device_info.confirmation_code
        },
        'status': HTTPStatus.OK,
    }
def run_search_process(job_contexts, sodium_client):
    d = task.deferLater(reactor, 0, _run, job_contexts, sodium_client)

    def handle_success(result):
        LOGGER.debug("Search job process finished")
        reactor.stop()

    def handle_error(error):
        LOGGER.error("Search job finished with error=%s", error)
        reactor.stop()

    d.addCallback(handle_success)
    d.addErrback(handle_error)
    LOGGER.debug("Starting reactor")
    reactor.run()
    sys.exit(0)


if __name__ == "__main__":
    # entry point for single search processing
    LOGGER.debug("Starting subscription os process")
    try:
        SODIUM_CLIENT = SodiumClient()
        for line in fileinput.input():
            pickle_format = base64.b64decode(line)
            input_contexts = pickle.loads(pickle_format)
            run_search_process(input_contexts, SODIUM_CLIENT)
    except Exception as e:
        LOGGER.exception("Failed to start subscription os process")
        raise e
Beispiel #19
0
def send_push_notifications(request_context, notification, recipient_devices,
                            async_kvstore_client, async_spacebridge_client,
                            async_splunk_client):
    """
    Given a notification object and a list of device ids, sends a post request to the Spacebridge notifications
    api for each device id
    :param request_context:
    :param notification: notification object to be sent
    :param recipient_devices: list of device id strings
    :param async_kvstore_client: AsyncKVStoreClient
    :param async_spacebridge_client: AsyncSpacebridgeClient
    :return:
    """

    sodium_client = SodiumClient(LOGGER.getChild('sodium_client'))
    sign_keys_response = yield async_splunk_client.async_get_sign_credentials(
        request_context.auth_header)

    if sign_keys_response[0] == http.OK:

        encryption_keys = EncryptionKeys(
            b64decode(sign_keys_response[1]['sign_public_key']),
            b64decode(sign_keys_response[1]['sign_private_key']), None, None)
        encryption_context = EncryptionContext(encryption_keys, sodium_client)

    else:
        LOGGER.exception(
            "Unable to fetch encryption keys with error_code={}".format(
                sign_keys_response[0]))
        raise EncryptionKeyError(sign_keys_response[1], sign_keys_response[0])

    sender_id = encryption_context.sign_public_key(
        transform=encryption_context.generichash_raw)
    sender_id_hex = py23.encode_hex_str(sender_id)

    headers = {
        'Content-Type': 'application/x-protobuf',
        'Authorization': sender_id_hex
    }
    recipient_devices = [device.encode("utf8") for device in recipient_devices]
    deferred_responses = []

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

    for device_id in recipient_devices:
        device_id_raw = b64decode(device_id)

        try:
            _, receiver_encrypt_public_key = yield public_keys_for_device(
                device_id_raw, request_context.auth_header,
                async_kvstore_client)

            encryptor = partial(encrypt_for_send, sodium_client,
                                receiver_encrypt_public_key)

            LOGGER.info("Sending notification alert_id=%s, device_id=%s" %
                        (notification.alert_id, device_id))
            notification_request = notifications.build_notification_request(
                device_id, device_id_raw, sender_id, notification, encryptor,
                signer)

            # Send post request asynchronously
            deferred_responses.append(
                async_spacebridge_client.async_send_notification_request(
                    auth_header=SpacebridgeAuthHeader(sender_id),
                    data=notification_request.SerializeToString(),
                    headers=headers))

        except KeyNotFoundError:
            LOGGER.info("Public key not found for device_id=%s" % device_id)
        except SodiumOperationError:
            LOGGER.warn("Sodium operation failed! device_id=%s" % device_id)

    # Wait until all the post requests have returned
    deferred_list = yield defer.DeferredList(deferred_responses)
    responses = yield [response[1] for response in deferred_list]
    results = [(recipient_devices[i], responses[i].code, responses[i].text())
               for i in range(len(responses))]

    LOGGER.info(("Finished sending push notifications with responses=%s" %
                 str(results)))