def post(self, request): """ Handler which creates a new app_list data entry in kvstore for the current user """ # Check payload is valid # create kvstore entry app_list = validate_write_request(request) params = {KEY: DASHBOARD_APP_LIST, APP_NAMES: app_list} kvstore = KvStore(USER_META_COLLECTION_NAME, request[SESSION][AUTHTOKEN], owner=request[SESSION][USER]) try: kvstore.insert_or_update_item_containing_key(params) return { 'payload': 'Successfully wrote app_list={} for user={}'.format( app_list, request[SESSION][USER]), 'status': http.OK, } except Exception as e: raise Errors.SpacebridgeRestError( 'Error: failed to write kvstore entry with id={}'.format( DASHBOARD_APP_LIST), 400)
def set_deployment_friendly_name(auth_token, name): """ Given an auth token and name, set the deployment friendly name in the 'meta' collection :param auth_token: A valid splunk system auth token :param name: the string representation of the mame you want to give the deployment :return: """ kvstore = KvStore(constants.META_COLLECTION_NAME, auth_token, owner=constants.NOBODY) deployment_info = { '_key': constants.DEPLOYMENT_INFO, constants.DEPLOYMENT_FRIENDLY_NAME: name } kvstore.insert_or_update_item_containing_key(deployment_info)
def post(self, request): """ register a new companion app. Must have valid signature""" app_bundle = json.loads(request['payload']) session_token = request['session']['authtoken'] # Validate if provided bundle has all the required fields self.validate_app_bundle(app_bundle) # Validate provided signature self.validate_signature(app_bundle) # Write bundle to KV Store kvstore_payload = self.build_kvstore_payload(app_bundle) kvstore_client = KVStoreCollectionAccessObject(collection=self.COMPANION_APPS_COLLECTION_NAME, session_key=session_token) r, content = kvstore_client.insert_or_update_item_containing_key(kvstore_payload) payload = json.loads(content.decode('utf-8')) if r.status == HTTPStatus.OK else content.decode('utf-8') return { 'payload': payload, 'status': r.status }
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 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, }