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 }
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
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)
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))
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)
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()
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
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)
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, }
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()
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
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)))