Esempio n. 1
0
def persist_alert(alert, session_key):
    """
    Takes an alert (json object), a server uri and a splunk session key and posts each
    alert into KV Store.

    Currently does it synchronously but we should optimize by making each post request async.

    Arguments:
        server_uri {string} -- uri of the server
        alert {Alert} -- Alert object to be persisted to KV store
        session_key {string} -- session key given by Splunk. Used for auth.
    """

    mobile_alerts_accessor = KvStoreAccessor(collection=constants.MOBILE_ALERTS_COLLECTION_NAME,
                                             owner="nobody",
                                             session_key=session_key)

    try:
        r, response = mobile_alerts_accessor.insert_single_item_as_json(jsonpickle.encode(alert))

        LOGGER.info("Response from KV Store: " + response.decode('ascii'))
        return json.loads(response)

    except Exception:
        LOGGER.exception("Error writing alert to KV Store")
        return {}
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)
Esempio n. 3
0
def retrieve_state_of_app(app_name, authtoken):
    """
    Single function to encapsulate all app_type state retrieval behaviour. This function:
        1. Attempts to retrieve the record of the app from the kvstore
        2. Creates a new record if one does not already exist

    :param app_name: Name of app in kvstore
    :param authtoken: System level auth token (in case new record needs to be created)
    :return:
    """

    # Attempts to retrieve the record of the app from the kvstore
    kvstore = KvStore(constants.APPLICATION_TYPES_COLLECTION_NAME,
                      authtoken,
                      owner=NOBODY)
    r, app_record = kvstore.get_items_by_query({'application_name': app_name})
    app_record = json.loads(app_record)

    # Creates a new record if one does not already exist
    if not app_record:
        kvstore.insert_single_item({
            'application_name': app_name,
            'application_enabled': False
        })
        return False
    return app_record[0][APPLICATION_ENABLED_LABEL]
Esempio n. 4
0
    def sync(self):
        """
        Goes through every user to identify those with registered devices, and syncs to KVstore. The list of
        old_registered_users must be fetched first to avoid a possible race condition.
        :return:
        """
        old_registered_users = get_all_mobile_users(self.session_key)
        all_splunk_users = get_all_users(self.session_key) + ['nobody']
        registered_users = [
            user for user in all_splunk_users
            if user_has_registered_devices(user, self.session_key)
        ]

        kvstore_users = KvStore(constants.REGISTERED_USERS_COLLECTION_NAME,
                                self.session_key)
        try:
            [
                kvstore_users.insert_single_item({u'_key': user})
                for user in registered_users
                if user not in old_registered_users
            ]
            [
                kvstore_users.delete_item_by_key(user)
                for user in old_registered_users
                if user not in registered_users
            ]
            LOGGER.info(
                "Completed Registered Users Sync in process PID={}".format(
                    os.getpid()))
        except:
            LOGGER.exception(
                "Exception performing RegisteredUsersSync for collection={}".
                format(constants.REGISTERED_USERS_COLLECTION_NAME))
Esempio n. 5
0
    def put(self, request):
        """
        Handler which updates app_list data entry in kvstore for the
        current user
        """
        # Check payload is valid
        # update 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.update_item_by_key(DASHBOARD_APP_LIST, params)
            return {
                'payload':
                'Successfully updated kvstore entry with id={}'.format(
                    DASHBOARD_APP_LIST),
                'status':
                http.OK,
            }
        except Exception as e:
            raise Errors.SpacebridgeRestError(
                'Error: failed to update kvstore entry with id={}'.format(
                    DASHBOARD_APP_LIST), 400)
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 run(self):
        """
        Goes through each alerts related collection and deletes items older than ttl_days
        """
        LOGGER.info("Running alerts ttl utility")

        try:
            for kvstore_ttl_resource in self.ttl_resource_list:

                timestamp_attribute_name = kvstore_ttl_resource.time_attribute_name
                collection = kvstore_ttl_resource.collection_name
                ttl_num_seconds = day_to_seconds(
                    kvstore_ttl_resource.ttl_num_days)

                collection_handler = KVStoreCollectionAccessObject(
                    collection=kvstore_ttl_resource.collection_name,
                    session_key=self.session_key,
                    timestamp_attribute_name=timestamp_attribute_name)

                self.delete_invalid_entries(collection, collection_handler,
                                            timestamp_attribute_name)
                try:
                    resp = collection_handler.delete_expired_items(
                        expired_time=ttl_num_seconds)
                    LOGGER.info(
                        "Successfully performed TTL for collection=%s with response=%s"
                        % (collection, str(resp)))
                except:
                    LOGGER.exception(
                        "Exception performing TTL for collection=%s" %
                        collection)
        except:
            LOGGER.exception("Failure encountered during Alerts TTL ")
Esempio n. 8
0
    def calculate(self):
        kvstore_client = KVStoreCollectionAccessObject(
            MOBILE_ALERTS_COLLECTION_NAME, self.session_token, owner=NOBODY)

        r, jsn = kvstore_client.get_collection_keys()
        collection_keys = json.loads(jsn)
        collection_size = len(collection_keys)
        return {self.METRIC_NAME: collection_size}
    def _clean_user_namespaced_items(self):
        users = get_all_mobile_users(self.session_key)

        timestamp_before = time_utils.get_current_timestamp() - self.clean_up_time
        LOGGER.debug('Deleting credentials older than last_update_time=%s, users=%s', timestamp_before, len(users))
        for owner in users:
            credentials = KVStoreCollectionAccessObject(collection=SUBSCRIPTION_CREDENTIALS_COLLECTION_NAME,
                                                        owner=owner,
                                                        session_key=self.session_key)
            credentials.delete_expired_items(expired_time=timestamp_before, expiration_attribute_name=LAST_UPDATE_TIME)
Esempio n. 10
0
    def calculate(self):
        kvstore_client = KVStoreCollectionAccessObject(APPLICATION_TYPES_COLLECTION_NAME,
                                                       self.session_token,
                                                       owner=NOBODY)
        r, app_states = kvstore_client.get_all_items()
        metrics = {}

        for app_state in json.loads(app_states):
            metrics[app_state["application_name"]] = app_state["application_enabled"]

        return {self.METRIC_NAME: metrics}
Esempio n. 11
0
def delete_all_devices_of_type_for_user(user, app_type, authtoken, system_authtoken):
    """
    Removes all devices of a given type from a single user in the kvstore
    """
    kvstore = KvStore(constants.REGISTERED_DEVICES_COLLECTION_NAME, authtoken, owner=user)
    r, devices = kvstore.get_all_items()
    devices = json.loads(devices)
    kvstore.delete_items_by_query({'device_type': app_type})

    for device in devices:
        if device['device_type'] == app_type:
            delete_device_from_spacebridge(device['device_id'], system_authtoken)
    def get(self, request):
        """ get registered companion apps """

        system_authtoken = request['system_authtoken']
        kvstore_client = KVStoreCollectionAccessObject(collection=self.COMPANION_APPS_COLLECTION_NAME,
                                                       session_key=system_authtoken)
        r, content = kvstore_client.get_all_items()
        payload = json.loads(content.decode('utf-8')) if r.status == HTTPStatus.OK else content.decode('utf-8')

        return {
            'payload':  payload,
            'status': r.status
        }
def get_devices_for_user(user, authtoken):
    """
    Gets devices belonging to a user from the kvstore
    :param user: Username to retrieve devices for
    :param authtoken: Authorization token to supply to the kvstore interface
    :return: List of devices
    """
    kvstore = KvStore(constants.REGISTERED_DEVICES_COLLECTION_NAME,
                      authtoken,
                      owner=user)
    _, devices_record = kvstore.get_items_by_query(query={},
                                                   sort="device_name")
    LOGGER.debug("user={}, devices={}".format(user, devices_record))
    return json.loads(devices_record)
Esempio n. 14
0
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_all_mobile_users(authtoken):
    """
    Returns a list of all Splunk users with registered mobile devices

    :param authtoken: Authorization token
    :return: List of users
    """
    kvstore = KvStore(constants.REGISTERED_USERS_COLLECTION_NAME, authtoken)
    _, content = kvstore.get_collection_keys()
    registered_user_records = json.loads(content)
    return [
        registered_user_record[u'_key']
        for registered_user_record in registered_user_records
    ]
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 sync(self):
        """
        Gets all registered users. Gets all tokens per user, sorted by expiry date. Deletes all tokens except the one
        with the most recent expiration date and any that are being used as subscription credentials.
        """

        all_registered_users = get_all_mobile_users(self.session_key)
        try:
            for user in all_registered_users:
                tokens = get_all_secure_gateway_tokens(self.session_key, user)
                current_time = get_current_timestamp()

                index_to_delete = min(3, len(tokens))
                for i in range(0, index_to_delete - 1):
                    if token[i]['content']['claims']['exp'] < current_time:
                        index_to_delete = i
                        break

                tokens_to_delete = tokens[index_to_delete:]

                kvstore_subscription_credentials = KvStore(
                    constants.SUBSCRIPTION_CREDENTIALS_COLLECTION_NAME,
                    self.session_key,
                    owner=user)
                response, credentials = kvstore_subscription_credentials.get_all_items(
                )
                credentials = json.loads(credentials)
                jwt_credential = [
                    c for c in credentials if 'session_type' in c
                    and c['session_type'] == constants.JWT_TOKEN_TYPE[0]
                ]
                if jwt_credential:
                    subscription_token_info = calculate_token_info(
                        jwt_credential['session_key'])
                else:
                    subscription_token_info = None

                for token in tokens_to_delete:
                    # if that token does not exist in subscription credentials
                    if not subscription_token_info or token[
                            'name'] != subscription_token_info['id']:
                        delete_token_by_id(self.session_key, user,
                                           token['name'])
        except:
            LOGGER.exception("Exception performing DeleteTokensSync")
    def delete(self, request):
        """ delete a companion app using comma separated list of app_ids.
        e.g. app_ids=id1,id2,id3
        """
        session_token = request['session']['authtoken']
        app_ids = extract_parameter(request['query'], self.APP_IDS_LABEL, "query")
        app_ids_lst = app_ids.strip().split(',')

        query = {constants.OR_OPERATOR: [{constants.KEY: app_id} for app_id in app_ids_lst]}
        kvstore_client = KVStoreCollectionAccessObject(collection=self.COMPANION_APPS_COLLECTION_NAME,
                                                       session_key=session_token)

        r, content = kvstore_client.delete_items_by_query(query)

        return {
            'payload': content.decode('utf-8'),
            'status': r.status
        }
Esempio n. 19
0
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 run(self):
        """
        Goes through each subscription and deletes items older than expiration_time + clean_up_time
        """
        LOGGER.debug("Running Subscription Clean Up")

        subscriptions = KVStoreCollectionAccessObject(
            collection=SUBSCRIPTIONS_COLLECTION_NAME,
            session_key=self.session_key)

        searches = KVStoreCollectionAccessObject(
            collection=SEARCHES_COLLECTION_NAME, session_key=self.session_key)

        search_updates = KVStoreCollectionAccessObject(
            collection=SEARCH_UPDATES_COLLECTION_NAME,
            session_key=self.session_key)

        # clean all expired subscriptions
        self._clean_expired_subscriptions(subscriptions)

        # Build kvstore query to return any records not in valid subscription list
        # All subscriptions should be valid after above cleaning
        not_keys_query = not_subscription_keys_query(subscriptions)

        # Delete any search_updates, subscription_credentials that don't belong to valid subscriptions
        search_updates.delete_items_by_query(not_keys_query)
        self._clean_user_namespaced_items(not_keys_query)

        # Clean up any searches that have not been updated in a multiple of the clean_up time
        self._clean_expired_searches(searches)

        LOGGER.debug("Completed Subscription Clean up")
Esempio n. 21
0
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 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
        }
Esempio n. 23
0
def set_state_of_app(app_name, authtoken, system_authtoken, new_state):
    """
    Updates whether the specified app is enabled or not. This function:
        1. Validates the app_type for correctness
        2. If app is being disabled, delete all registered devices
        3. Attempts to retrieve the record of the app from the kvstore
        4. Updates or creates the new kvstore entry depending if one exists already
        5. Returns the state of the app_type

    :param app_name: Name of app in kvstore
    :param authtoken: User's authorization token
    :param system_authtoken: System authorization token
    :param new_state: Boolean signifying whether to enable the app
    :return: Success message
    """

    # Validates the app_type for correctness
    if app_name not in APP_TYPES:
        raise Errors.SpacebridgeRestError(
            'Error: Invalid app_type=%s' % app_name, 400)

    # If app is being disabled, delete all registered devices
    if not new_state:
        delete_all_devices_of_type(app_name, authtoken, system_authtoken)

    # Attempts to retrieve the record of the app from the kvstore
    kvstore = KvStore(constants.APPLICATION_TYPES_COLLECTION_NAME,
                      authtoken,
                      owner=NOBODY)
    r, app_record = kvstore.get_items_by_query({'application_name': app_name})
    app_record = json.loads(app_record)
    new_app = {'application_name': app_name, 'application_enabled': new_state}

    # Updates or creates the new kvstore entry depending if one exists already
    if app_record:
        kvstore.update_item_by_key(app_record[0]['_key'], new_app)
    else:
        kvstore.insert_single_item(new_app)

    result_string = 'Application app_type=%s is now new_state=%s' % (
        app_name, 'enabled' if new_state else 'disabled')

    LOGGER.info(result_string)

    # Returns the state of the app_type
    return {
        'payload': result_string,
        'status': 200,
    }
Esempio n. 24
0
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,
    }
Esempio n. 25
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,
    }
Esempio n. 26
0
def delete_device(user,
                  device_owner,
                  device_key,
                  system_authtoken,
                  user_authtoken,
                  key_bundle=None):
    """
    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,
                                   key_bundle)
    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,
    }
Esempio n. 27
0
    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
Esempio n. 28
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

    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 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,
    }
Esempio n. 30
0
    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,
        }