def from_rest_request(request): return RequestContext( auth_header=SplunkAuthHeader(request[SESSION][AUTHTOKEN]), request_id=str(uuid.uuid4()), current_user=request[SESSION][USER], system_auth_header=SplunkAuthHeader(request[SYSTEM_AUTHTOKEN]) if SYSTEM_AUTHTOKEN in request else None, user_agent=request.get(HEADERS, {}).get(HEADER_USER_AGENT))
def __init__(self, session_key): """ Delete Tokens Sync constructor :param session_key: session key passed by modular input """ self.session_key = session_key self.system_auth_header = SplunkAuthHeader(self.session_key)
async def get_http_port_number(async_splunk_client, system_authtoken): response = await async_splunk_client.async_get_server_settings( SplunkAuthHeader(system_authtoken)) response_json = await response.json() http_port_number = response_json[constants.ENTRY][0][constants.CONTENT][ constants.HTTP_PORT] return http_port_number
async def get(self, request): """ REST handler to fetch all apps visible to current user """ authtoken = request[SESSION][AUTHTOKEN] user = request[SESSION][USER] auth_header = SplunkAuthHeader(authtoken) request_context = RequestContext(auth_header, current_user=user, system_auth_header=auth_header) try: async_splunk_client = self.async_client_factory.splunk_client() app_list = await fetch_app_names(request_context, async_splunk_client) payload = [{ APP_NAME: app.app_name, DISPLAY_APP_NAME: app.display_app_name } for app in app_list] return { 'payload': payload, 'status': HTTPStatus.OK, } except Exception as e: return {'error': e.message}
def __init__(self, session_key): """ Registered Users Sync constructor :param session_key: session key passed by modular input :param async_kvstore_client: """ self.session_key = session_key self.system_auth_header = SplunkAuthHeader(self.session_key)
def __init__(self, session_key, clean_up_time): """ Subscription Clean Up constructor :param session_key: session key passed by modular input :param clean_up_time: configurable time given in days """ self.session_key = session_key self.clean_up_time = clean_up_time self.system_auth_header = SplunkAuthHeader(self.session_key)
async def do_trigger(alert_payload): auth_header = SplunkAuthHeader(alert_payload[SESSION_KEY]) # Use default URI for alerts try: uri = rest.makeSplunkdUri() except Exception as e: LOGGER.exception("Failed to generate default URI") if not uri: return mtls_spacebridge_client = None mtls_enabled = config.get_mtls_enabled() if mtls_enabled: mtls_spacebridge_client = build_mtls_spacebridge_client(alert_payload[SESSION_KEY]) async_client_factory = AsyncClientFactory(uri, spacebridge_client=mtls_spacebridge_client) async_kvstore_client = async_client_factory.kvstore_client() async_splunk_client = async_client_factory.splunk_client() async_spacebridge_client = async_client_factory.spacebridge_client() alert_sid = alert_payload[SEARCH_ID] preprocess_payload(alert_payload) # Default to empty string so urllib.quote doesn't fail if user doesn't exist user = alert_payload[RESULT].get(USER, '') request_context = RequestContext(auth_header=auth_header, is_alert=True, current_user=user) LOGGER.info("get_registered_devices alert_sid=%s", alert_sid) registered_devices = await get_registered_devices(request_context, async_kvstore_client, alert_payload) LOGGER.info("get_registered_devices ok alert_sid=%s", alert_sid) alert = await build_alert(request_context, alert_payload, async_splunk_client, async_kvstore_client) LOGGER.info("persist_alert alert_id=%s", alert_sid) response = persist_alert(alert, auth_header.session_token) LOGGER.info("persist_alert ok succeeded alert_id=%s", alert_sid) # If we get a proper response from KV Store, then we get the key of the stored alert # and create a (device_id, alert_id, timestamp) triplet for each device that should # receive the alert if response is not None and "_key" in response.keys(): alert_id = response["_key"] alert.notification.alert_id = alert_id # Persisting (recipient device, alert id) pairs and sending push notifications happens simultaneously via async LOGGER.info("persist_recipient_devices alert_id=%s", alert_id) await persist_recipient_devices(request_context, alert_id, registered_devices, alert_payload, async_kvstore_client) LOGGER.info("persist_recipient_devices ok alert_id=%s", alert_id) LOGGER.info("send_push_notifications starting registered_devices=%s", len(registered_devices)) await send_push_notifications( request_context, alert.notification, registered_devices, async_kvstore_client, async_spacebridge_client, async_splunk_client)
def do_trigger(reactor, alert_payload): auth_header = SplunkAuthHeader(alert_payload[SESSION_KEY]) # Use default URI for alerts try: uri = rest.makeSplunkdUri() except Exception as e: LOGGER.exception("Failed to generate default URI. {}".format(e)) if not uri: return async_client_factory = AsyncClientFactory(uri) async_kvstore_client = async_client_factory.kvstore_client() async_splunk_client = async_client_factory.splunk_client() async_spacebridge_client = async_client_factory.spacebridge_client() alert_sid = alert_payload[SEARCH_ID] preprocess_payload(alert_payload) request_context = RequestContext(auth_header=auth_header, is_alert=True) LOGGER.info("get_registered_devices alert_sid=%s" % alert_sid) registered_devices = yield get_registered_devices(request_context, async_kvstore_client, alert_payload) LOGGER.info("get_registered_devices ok alert_sid=%s" % alert_sid) alert = yield build_alert(request_context, alert_payload, async_splunk_client, async_kvstore_client) LOGGER.info("persist_alert alert_id=%s" % alert_sid) response = persist_alert(alert, auth_header.session_token) LOGGER.info("persist_alert ok succeeded alert_id=%s" % alert_sid) # If we get a proper response from KV Store, then we get the key of the stored alert # and create a (device_id, alert_id, timestamp) triplet for each device that should # receive the alert if response is not None and "_key" in response.keys(): alert_id = response["_key"] alert.notification.alert_id = alert_id # Persisting (recipient device, alert id) pairs and sending push notifications happens simultaneously via async LOGGER.info("persist_recipient_devices alert_id=%s" % alert_id) persist_recipient_devices(request_context, alert_id, registered_devices, alert_payload, async_kvstore_client) LOGGER.info("persist_recipient_devices ok alert_id=%s" % alert_id) LOGGER.info("send_push_notifications starting registered_devices=%s" % len(registered_devices)) yield send_push_notifications(request_context, alert.notification, registered_devices, async_kvstore_client, async_spacebridge_client, async_splunk_client)
async def get(self, request): """ Handler which retrieves kvstore app_list data for the current user """ """ REST handler to fetch the selected app list for the current user """ authtoken = request[SESSION][AUTHTOKEN] user = request[SESSION][USER] auth_header = SplunkAuthHeader(authtoken) request_context = RequestContext(auth_header, current_user=user, system_auth_header=auth_header) try: """ This method will process a DashboardAppListGetRequest. This will return the list of app_names found under the dashboard_app_list key in the user_meta KVStore collection. :param request_context: :param client_single_request: :param single_server_response: :param async_client_factory: :return: """ # async clients async_kvstore_client = self.async_client_factory.kvstore_client() async_splunk_client = self.async_client_factory.splunk_client() # Get dashboard_meta collection if key exists selected_apps = await fetch_dashboard_app_list_with_default( request_context=request_context, async_kvstore_client=async_kvstore_client, async_splunk_client=async_splunk_client) app_list = await fetch_app_names(request_context, async_splunk_client) app_dict = get_app_dict(app_list) # This filters out apps that are invalid from displaying in the app selection tab payload = [{ APP_NAME: app, DISPLAY_APP_NAME: app_dict[app] } for app in selected_apps if app in app_dict] return { 'payload': payload, 'status': HTTPStatus.OK, } except Exception as e: return {'error': e.message}
def is_valid_session_token(user, session_token): """ Method to validate that the user provided session token matches the user :param user: string :param session_token: string :return: boolean """ response = get_current_context(SplunkAuthHeader(session_token)) context_user = response[constants.ENTRY][0][constants.CONTENT][ constants.USERNAME] if user == context_user: return True else: return False
async def async_is_valid_session_token(user, session_token, async_splunk_client): """ Method to validate that the user provided session token matches the user :param user: string :param session_token: string :param async_splunk_client: AsyncSplunkClient :return: boolean """ response = await async_splunk_client.async_get_current_context(SplunkAuthHeader(session_token)) response_json = await response.json() context_user = response_json[constants.ENTRY][0][constants.CONTENT][constants.USERNAME] if user == context_user: return True else: return False
def _run_initialization(self, unused_reactor, async_client_factory): """ Runs each initialization step required for the AR collections to run properly. :param unused_reactor: A Twisted reactor passed as an argument when called with task.react :param async_client_factory: An AsyncClientFactory for instantiating other async clients """ try: auth_header = SplunkAuthHeader(self.session_key) kvstore = async_client_factory.kvstore_client() permissions = async_client_factory.ar_permissions_client() splunk = async_client_factory.splunk_client() yield init_workspace_component_ids(kvstore, auth_header) yield init_capabilities_collection(kvstore, auth_header) yield cleanup_deleted_roles(splunk, kvstore, permissions, auth_header) except Exception as e: LOGGER.exception( 'Unhandled exception during AR modular input steps.') raise e defer.returnValue(True)
def __init__(self, input_config, encryption_context, session_key, async_splunk_client, parent_process_monitor=None, cluster_monitor=None, async_client_factory=None, async_kvstore_client=None, async_spacebridge_client=AsyncSpacebridgeClient()): """ Subscription Manager constructor :param input_config: :param encryption_context: :param session_key: :param async_kvstore_client: :param async_splunk_client: :param async_spacebridge_client: """ self.input_config = input_config self.encryption_context = encryption_context self.session_key = session_key self.parent_process_monitor = parent_process_monitor self.cluster_monitor = cluster_monitor self.async_splunk_client = async_splunk_client self.async_spacebridge_client = async_spacebridge_client self.system_auth_header = SplunkAuthHeader(self.session_key) if not async_client_factory: uri = get_uri(self.session_key) async_client_factory = AsyncClientFactory(uri) self.async_client_factory = async_client_factory if not async_kvstore_client: async_kvstore_client = self.async_client_factory.kvstore_client() self.async_kvstore_client = async_kvstore_client self.request_context = RequestContext( auth_header=self.system_auth_header, current_user=constants.ADMIN, system_auth_header=self.system_auth_header)
async def clean_up_old_entries(timestamp, async_kvstore_client, session_token): """ Delete entries in the devices to roles mapping collection older than input timestamp """ try: query = { constants.OR_OPERATOR: [{ constants.TIMESTAMP: { constants.LESS_THAN_OPERATOR: timestamp } }, { constants.TIMESTAMP: { constants.GREATER_THAN_OPERATOR: timestamp } }] } params = {constants.QUERY: json.dumps(query)} r = await async_kvstore_client.async_kvstore_delete_request( collection=constants.DEVICE_ROLES_COLLECTION_NAME, auth_header=SplunkAuthHeader(session_token), params=params) LOGGER.debug("finished deleting old entry with code=%s" % str(r.code)) except: LOGGER.exception("exception deleting old entries")
async def update(session_token, async_kvstore_client, async_splunk_client): """ Update the devices to role mapping collection in KV Store """ timestamp = get_current_timestamp() splunk_auth_header = SplunkAuthHeader(session_token) # for each user, fetch user to roles mapping response_code, user_to_role_dict = await async_splunk_client.async_get_users_roles_mapping( splunk_auth_header) LOGGER.debug("Fetched user role mapping with response code=%s, %s" % (str(response_code), str(user_to_role_dict))) # For each user fetch devices registered to that user registered_devices_jsn = await get_registered_devices( splunk_auth_header, list(user_to_role_dict.keys()), async_kvstore_client) # Construct kvstore payload batch_payload = create_payloads(registered_devices_jsn, user_to_role_dict, timestamp) batch_post_to_kvstore(session_token, batch_payload) await clean_up_old_entries(timestamp, async_kvstore_client, session_token)
async def post(self, request): """ Handler which creates a new app_list data entry in kvstore for the current user """ authtoken = request[SESSION][AUTHTOKEN] user = request[SESSION][USER] auth_header = SplunkAuthHeader(authtoken) request_context = RequestContext(auth_header, current_user=user, system_auth_header=auth_header) async_splunk_client = self.async_client_factory.splunk_client() async_kvstore_client = self.async_client_factory.kvstore_client() total_app_list = await fetch_app_names(request_context, async_splunk_client) total_app_name_list = [app.app_name for app in total_app_list] selected_app_names = validate_write_request(request, total_app_list) # validate all app names for app_name in selected_app_names: if app_name not in total_app_name_list: error_message = f"The appName={app_name} is invalid. Unable to set appName list." return {'error': error_message} # Store names in kvstore dashboard_app_list = await set_dashboard_app_list( request_context=request_context, app_names=selected_app_names, async_kvstore_client=async_kvstore_client, async_splunk_client=async_splunk_client) return { 'payload': dashboard_app_list.app_names, 'status': HTTPStatus.OK, }
def handle_saml_confirmation(auth_code, user, session_token, system_authtoken, body): """ Handler for the final DevicePairingConfirmationRequest call. This function: 1. Authenticates the supplied user name 2. Retrieves temporary record from the kvstore 3. Checks if app_type has been disabled since registration 4. Makes the DevicePairingConfirmationRequest request to the server 5. Creates a new permanent record for the device in the kvstore 6. Deletes the temporary kvstore record :param auth_code: User-entered authorization code to be returned to Spacebridge :param user: User provided by rest handler :param body: Parsed JSON body of the incoming POST request :param kvstore_unconfirmed: Access object for the temporary registration kvstore :param system_authtoken: System-level access token for writing to the kvstore :return: Success message """ # Authenticates the supplied user name kvstore_temp = KvStore(constants.UNCONFIRMED_DEVICES_COLLECTION_NAME, system_authtoken, owner=user) encryption_context = SplunkEncryptionContext( system_authtoken, constants.SPACEBRIDGE_APP_NAME) LOGGER.info( 'Received new registration confirmation request by user={}'.format( user)) # Retrieves temporary record from the kvstore temp_key = extract_parameter(body, KVSTORE_TEMPORARY_ID_LABEL, BODY_LABEL) r, temp_record = kvstore_temp.get_item_by_key(temp_key) temp_record = json.loads(temp_record) device_id = temp_record[DEVICE_ID_LABEL] device_id_raw = base64.b64decode(device_id) device_registration = {'_key': base64.urlsafe_b64encode(device_id_raw)} device_public_keys = {'_key': base64.urlsafe_b64encode(device_id_raw)} for k in temp_record.keys(): if k in DEVICE_REGISTRATION_ATTRS: device_registration[k] = temp_record[k] if k in DEVICE_PUBLIC_KEYS_ATTRS: device_public_keys[k] = temp_record[k] # Checks if app_type has been disabled since registration app_name = temp_record[DEVICE_TYPE_LABEL] if not retrieve_state_of_app(app_name, system_authtoken): disabled_message = 'Registration Error: Application type app_name="%s" is disabled' % app_name LOGGER.info(disabled_message) return { 'payload': { 'message': disabled_message, 'app_name': app_name, }, 'status': 422, } device_encryption_info = DeviceInfo( base64.b64decode(temp_record['encrypt_public_key']), base64.b64decode(temp_record['sign_public_key']), base64.b64decode(temp_record['device_id']), "NA", app_id=temp_record['app_id'], app_name=temp_record['device_type']) deployment_friendly_name = get_deployment_friendly_name(system_authtoken) try: credentials = SplunkJWTCredentials(user) credentials.load_jwt_token(SplunkAuthHeader(session_token)) LOGGER.info("Successfully fetched jwt token") except Exception as e: LOGGER.info( "Failed to fetch jwt token for user={} with message={}".format( user, e.message)) jwt_error_message = 'Registration Error: Failed to fetch jwt token for user={}'.format( user) return { 'payload': { 'message': jwt_error_message, 'app_name': app_name, }, 'status': 422, } pair_device(auth_code, credentials, device_encryption_info, encryption_context, server_name=deployment_friendly_name, config=config, server_app_id=constants.SPLAPP_APP_ID) # Creates a new permanent record for the device in the kvstore kvstore_user = KvStore(constants.REGISTERED_DEVICES_COLLECTION_NAME, system_authtoken, owner=user) kvstore_user.insert_single_item(device_registration) # Adds the user to the list of users with registered devices, if not already there kvstore_users = KvStore(constants.REGISTERED_USERS_COLLECTION_NAME, system_authtoken) kvstore_users.insert_or_update_item_containing_key({'_key': user}) kvstore_nobody = KvStore(constants.DEVICE_PUBLIC_KEYS_COLLECTION_NAME, system_authtoken) kvstore_nobody.insert_single_item(device_public_keys) # Deletes the temporary kvstore record kvstore_temp.delete_item_by_key(temp_key) LOGGER.info( 'Device registration confirmed. Device with device_name=\"%s\" was recorded in the kvstore.' % temp_record[DEVICE_NAME_LABEL]) return { 'payload': 'Device registration successful', 'status': 201, }
def __init__(self, session_token, logger): self.session_token = session_token self.auth_header = SplunkAuthHeader(session_token) self.logger = logger
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 })