def ar_dashboard_exists(authtoken, dashboard_name): """ Checks if a dashboard exists by searching the kvstore for it. Returns True or False. """ kvstore = KvStore(constants.AR_DASHBOARDS_COLLECTION_NAME, authtoken, owner=NOBODY) try: kvstore.get_item_by_key(dashboard_name) except splunk.RESTException as err: if err.statusCode == 404: return False raise err return True
def get_mdm_update_timestamp(request, auth_token, retry=False): """ Return the generation time of the mdm signing public key :param auth_token: A valid splunk system auth token :return: The last time a mdm public signing key was generated (epoch time) """ kvstore = KvStore(constants.USER_META_COLLECTION_NAME, auth_token, owner=request[constants.SESSION][constants.USER]) parsed = {} try: r, jsn = kvstore.get_item_by_key(constants.MDM_KEYPAIR_GENERATION_TIME) parsed = json.loads(jsn) LOGGER.info("mdm keypair last generated info={}".format( parsed[constants.TIMESTAMP])) except splunk.RESTException as e: # If we get a 503, KV Store is not up yet, so try again in 5 seconds. if e.statusCode == HTTPStatus.SERVICE_UNAVAILABLE: if not retry: time.sleep(5) return get_mdm_update_timestamp(auth_token, True) if e.statusCode != HTTPStatus.NOT_FOUND: raise e return parsed.get(constants.TIMESTAMP, None)
def get_deployment_friendly_name(auth_token, retry=False): """ Return the current splunk deployment friendly name. :param auth_token: A valid splunk system auth token :return: The current friendly deployment name, None if not set """ kvstore = KvStore(constants.META_COLLECTION_NAME, auth_token, owner=constants.NOBODY) parsed = {} try: r, jsn = kvstore.get_item_by_key(constants.DEPLOYMENT_INFO) parsed = json.loads(jsn) LOGGER.info("current deployment info=%s" % str(parsed)) except splunk.RESTException as e: # If we get a 503, KV Store is not up yet, so try again in 5 seconds. if e.statusCode == HTTPStatus.SERVICE_UNAVAILABLE: if not retry: time.sleep(5) return get_deployment_friendly_name(auth_token, True) if e.statusCode != HTTPStatus.NOT_FOUND: raise e return parsed.get(constants.DEPLOYMENT_FRIENDLY_NAME, None)
def delete_device(user, device_owner, device_key, system_authtoken, user_authtoken): """ Deletes a specific device from the kvstore. This function: 1. Checks if the user has the necessary privileges to delete the given device 2. Attempts to delete the device from the kvstore :param user: User making the deletion request :param device_owner: User who owns the device being deleted :param device_key: kvstore _key of the device being deleted :param system_authtoken: Authorization token with system-level privileges. Used to allow users to delete their own devices even when they don't have unrestricted kvstore write access :param user_authtoken: Authorization token with the same permissions as "user" :return: Success message """ # Checks if the user has the necessary privileges to delete the given device kvstore_user = KvStore(constants.REGISTERED_DEVICES_COLLECTION_NAME, user_authtoken, owner=device_owner) if user == device_owner: kvstore_user = KvStore(constants.REGISTERED_DEVICES_COLLECTION_NAME, system_authtoken, owner=device_owner) kvstore_nobody = KvStore(constants.DEVICE_PUBLIC_KEYS_COLLECTION_NAME, system_authtoken) r, record = kvstore_user.get_item_by_key(device_key) record = json.loads(record) # Attempts to delete the device from the kvstore kvstore_user.delete_item_by_key(device_key) try: kvstore_nobody.delete_item_by_key(device_key) except splunk.RESTException: LOGGER.info("public for device not found, device_id=%s" % device_key) LOGGER.info('device_key=%s (of device_owner=%s) deleted from kvstore by user=%s' % (device_key, device_owner, user)) delete_device_from_spacebridge(record['device_id'], system_authtoken) LOGGER.info('device key=%s (of device_owner=%s) deleted from spacebridge by user=%s' % (device_key, device_owner, user)) if not user_has_registered_devices(device_owner, system_authtoken): kvstore_nobody = KvStore(constants.REGISTERED_USERS_COLLECTION_NAME, system_authtoken) kvstore_nobody.delete_item_by_key(device_owner) return { 'payload': 'Device with key %s successfully deleted' % device_key, 'status': 200, }
def get_app_list(request): """ Returns a json object containing the app_list data :param request: The http request :return: json app list data """ try: kvstore = KvStore(USER_META_COLLECTION_NAME, request[SESSION][AUTHTOKEN], owner=request[SESSION][USER]) r, entry = kvstore.get_item_by_key(DASHBOARD_APP_LIST) entry = json.loads(entry) app_names = json.loads(entry[APP_NAMES]) if app_names is None: app_names = [] except Exception as e: LOGGER.exception('get_app_list failed') app_names = [] return app_names
def is_valid_response(self, user, user_token, response_headers): if "authorization" in response_headers and 'deployment-info' in response_headers: rAuthToken = base64.b64decode(response_headers['authorization']) deployment_info = base64.b64decode(response_headers['deployment-info']) nlp_device_info = get_nlp_device(user, user_token) if '_key' in nlp_device_info: device_key = nlp_device_info['_key'] kvstore = KvStore(constants.DEVICE_PUBLIC_KEYS_COLLECTION_NAME, user_token, owner="nobody") r, record = kvstore.get_item_by_key(device_key) parsed = json.loads(record) sender_sign_public_key = base64.b64decode(parsed['sign_public_key']) sign = sign_verify(self.sodium_client, sender_sign_public_key, deployment_info, rAuthToken) return sign else: LOGGER.info("authorization doesn't exist failed to verify response") return False
def handle_confirmation(auth_code, user, session_token, system_authtoken, body): """ Handler for the final DevicePairingConfirmationRequest call. This function: 1. Authenticates the supplied username and password 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 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 username and password kvstore_temp = KvStore(constants.UNCONFIRMED_DEVICES_COLLECTION_NAME, system_authtoken, owner=user) encryption_context = SplunkEncryptionContext(system_authtoken, constants.SPACEBRIDGE_APP_NAME) username = extract_parameter(body, USERNAME_LABEL, BODY_LABEL) password = extract_parameter(body, PASSWORD_LABEL, BODY_LABEL) try: # use what Splunk thinks the username is to generate the session token auth = BasicAuthHeader(username, password) content = get_current_context(auth) username = content[constants.ENTRY][0][constants.CONTENT][constants.USERNAME] except SpacebridgePermissionsError as e: LOGGER.exception('Invalid credentials passed to current-context API') raise e LOGGER.info('Received new registration confirmation request by user=%s for device_owner=%s' % (user, username)) # 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': py23.urlsafe_b64encode_to_str(device_id_raw)} device_public_keys = {'_key': py23.urlsafe_b64encode_to_str(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(username, password=password) credentials.load_jwt_token(SplunkAuthHeader(session_token)) LOGGER.info("Successfully fetched jwt token") except Exception as e: LOGGER.info("Failed to fetch jwt token with message={}. Using basic credentials instead.".format(e)) credentials = SimpleUserCredentials(username, password) 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=username) 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': username}) 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 get(self, request): """ Lists one or more subscriptions based on provided json/query payload parameters, OR key provided on path Request Parameters users optional list of user to fetch subscriptions for subscription_types optional list of subscription types to filter on device_ids optional list of device_ids to filter on You can either pass these in the json body, or pass them in like https://localhost:8089/services/ssg/subscription/?users=user1&users=user2 Returns: list of subscriptions found Example: [ { "device_id": "3", "expired_time": "12345678", "last_update_time": "123456", "shard_id": "shard", "subscription_key": "keyy", "subscription_type": "Splunk TV", "ttl_seconds": "10", "user": "******", "version": 2, "visualization_id": "", "_user": "******", "_key": "5f04e4d1ed7a6aab3e6f2e91" } ] """ session_key = request[constants.SESSION][constants.AUTHTOKEN] path = request.get(PATH_INFO) kvstore_object = KVStore( collection=constants.SUBSCRIPTIONS_COLLECTION_NAME, session_key=session_key) if path: # search using path as key LOGGER.debug('Fetching subscription with key %s', path) try: response_header, response = kvstore_object.get_item_by_key( path) except splunk.RESTException as e: if e.statusCode == HTTPStatus.NOT_FOUND: return { constants.PAYLOAD: { 'Error': 'Not found' }, constants.STATUS: HTTPStatus.NOT_FOUND, } raise e else: # use json parameters or search for all payload = json.loads(request.get(constants.PAYLOAD, '{}')) query_params = request.get(constants.QUERY) users = payload.get(USERS) subscription_types = payload.get(SUBSCRIPTION_TYPES) device_ids = payload.get(DEVICE_IDS) # if not in json, try to pull from query params if not users and query_params: users = get_list_from_query(query_params, USERS) if not subscription_types and query_params: subscription_types = get_list_from_query( query_params, SUBSCRIPTION_TYPES) if not device_ids and query_params: device_ids = get_list_from_query(query_params, DEVICE_IDS) query = {} if users: query = build_containedin_clause(constants.USER, users) if subscription_types: query = { constants.AND_OPERATOR: [ build_containedin_clause(constants.SUBSCRIPTION_TYPE, subscription_types), query ] } if device_ids: query = { constants.AND_OPERATOR: [ build_containedin_clause(constants.DEVICE_ID, device_ids), query ] } if query: LOGGER.debug('Fetching subscription(s) with query %s', query) response_header, response = kvstore_object.get_items_by_query( query) else: LOGGER.debug('Fetching all subscriptions') response_header, response = kvstore_object.get_all_items() payload = json.loads(response) return { constants.PAYLOAD: payload, constants.STATUS: response_header.status, }
def register_device(auth_code, user, system_authtoken, temp_key): """ Handler for the final DevicePairingConfirmationRequest call. This function: 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 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 """ 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=%s' % (user)) # Retrieves temporary record from the kvstore 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) pair_device(auth_code, SimpleUserCredentials("dummy", "dummy"), device_encryption_info, encryption_context, server_name=deployment_friendly_name, config=config) # 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 run(self): """ Attempts to sync the migration. If the kvstore is not yet available, schedules a non-blocking retry attempt in 5 seconds """ LOGGER.info("Attempting Migration from Splunk Cloud Gateway to Splunk Secure Gateway") try: meta_collection = KvStore(META_COLLECTION_NAME, self.session_key, owner=NOBODY) migration_info = {KEY: MIGRATION_DONE, STATUS: '0'} meta_keys = meta_collection.get_collection_keys() if MIGRATION_DONE in meta_keys: migration_status = meta_collection.get_item_by_key(MIGRATION_DONE) else: migration_status = '0' if migration_status.isdigit() and int(migration_status): LOGGER.debug("Migration modular input will not run because migration from Splunk Cloud Gateway " "is already done") else: # List of all collections _, collections_content = get_all_collections(self.session_key, app_name=constants.CLOUDGATEWAY_APP_NAME) all_collections = json.loads(collections_content)['entry'] app_collections = [x for x in all_collections if x['acl']['app'] == CLOUDGATEWAY_APP_NAME] app_collection_names = {x['name'] for x in app_collections if x['name'] not in collections_not_to_migrate} # Special case this collection because it doesn't show up in the SCG /collections/config endpoint app_collection_names.add("user_meta") registered_users_kvstore = KvStore(constants.REGISTERED_USERS_COLLECTION_NAME, self.session_key, app=constants.CLOUDGATEWAY_APP_NAME, owner=NOBODY) _, users_content = registered_users_kvstore.get_collection_keys() registered_user_records = json.loads(users_content) all_registered_users = [registered_user_record[u'_key'] for registered_user_record in registered_user_records] if NOBODY not in all_registered_users: all_registered_users.append(NOBODY) for collection in app_collection_names: # Iterate through all users and insert data per user for user in all_registered_users: cloud_gateway_kvstore = KvStore(collection, self.session_key, app=constants.CLOUDGATEWAY_APP_NAME, owner=user) _, scg_content = cloud_gateway_kvstore.get_all_items() scg_content = json.loads(scg_content) if scg_content: if collection == constants.REGISTERED_DEVICES_COLLECTION_NAME: scg_content = [resolve_device_platform_and_type(device) for device in scg_content] secure_gateway_kvstore = KvStore(collection, self.session_key, app=constants.SPACEBRIDGE_APP_NAME, owner=user) _, ssg_content = secure_gateway_kvstore.insert_multiple_items(scg_content) # Copying passwords.conf to Splunk Secure Gateway for key in password_keys: try: value = fetch_sensitive_data(self.session_key, key, app=CLOUDGATEWAY_APP_NAME) update_or_create_sensitive_data(self.session_key, key, value) except splunk.ResourceNotFound: LOGGER.debug('key=%s not found in storage/passwords', key) migration_info[STATUS] = '1' meta_collection.insert_or_update_item_containing_key(migration_info) except splunk.RESTException as e: if e.statusCode == HTTPStatus.SERVICE_UNAVAILABLE: LOGGER.info("KVStore is not yet setup. Retrying migration in 5 seconds") time.sleep(TIMEOUT_SECONDS) self.run() else: raise e
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 = { constants.KEY: py23.urlsafe_b64encode_to_str(device_id_raw) } device_public_keys = { constants.KEY: py23.urlsafe_b64encode_to_str(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] 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) valid_request = is_valid_session_token(user, session_token) if valid_request: try: credentials = SplunkJWTCredentials(user) credentials.load_jwt_token(SplunkAuthHeader(system_authtoken)) LOGGER.info( "Successfully fetched jwt token for SAML auth user with username={}" .format(user)) 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( {constants.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]) else: LOGGER.info("Error: Mismatched user={} and session token".format(user)) 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, } return { 'payload': 'Device registration successful', 'status': 201, }