コード例 #1
0
ファイル: helper.py プロジェクト: DwarfCraft/splunk_app
def extract_boolean(obj, key, source_name):
    """
    Validates that a value exists within a dictionary and is a string representing a boolean,
    then returns it. Throws a relevant error otherwise.
    """
    try:
        result = obj[key]
    except KeyError:
        raise Errors.SpacebridgeRestError(
            'Error: Request requires %s parameter "%s"' % (source_name, key),
            400)

    if result == 'true' or result is True:
        return True
    if result == 'false' or result is False:
        return False

    error_message = 'Error: Request requires %s parameter "%s" to be a boolean literal ("true" or "false")' % \
                    (source_name, key)

    if not isinstance(result, str) and not py23.py2_check_unicode(result):
        raise Errors.SpacebridgeRestError(error_message, 400)

    if not result:
        error_message = 'Error: Request requires %s parameter "%s" to not be empty' % (
            source_name, key)
        raise Errors.SpacebridgeRestError(error_message, 400)

    raise Errors.SpacebridgeRestError(error_message, 400)
コード例 #2
0
def validate_write_request(request, total_app_list):
    """
    Common validation for put and post
    methods

    :param request: The HTTP request
    :return app_list: The app list to write to kvstore
    """

    params = {
        k: unquote(v)
        for k, v in dict(parse_qsl(request['payload'])).iteritems()
    }
    app_list = params.get(APP_LIST)
    if app_list is None:
        raise Errors.SpacebridgeRestError(
            'Error: Put request must have an app_list', 400)

    jsonified_app_list = json.loads(app_list)
    invalid_app_list = invalid_apps(total_app_list, jsonified_app_list)

    if invalid_app_list:
        raise Errors.SpacebridgeRestError(
            'Error: Could not find app(s)={}'.format(invalid_app_list), 400)

    return app_list
コード例 #3
0
ファイル: app_list.py プロジェクト: DwarfCraft/splunk_app
    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)
コード例 #4
0
ファイル: feature.py プロジェクト: DwarfCraft/splunk_app
def get_state_of_app(app_name, authtoken):
    """
    Returns whether a given app_type is enabled or not. This function:
        1. Validates the app_type for correctness
        2. Attempts to retrieve the record of the app from the kvstore
        3. Creates a new record if one does not already exist
        4. Returns the state of the app_type

    :param app_name: Name of app in kvstore
    :param authtoken: System level auth token
    :return: State of specified app_type
    """

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

    # Attempts to retrieve the record of the app from the kvstore. Creates a new record if one does not already exist
    app_state = retrieve_state_of_app(app_name, authtoken)
    LOGGER.info('Retrieved state of app for app_type={}, application_state={}'.format(app_name, app_state))

    # Returns the state of the app_type
    return {
        'payload': {
            app_name: app_state,
        },
        'status': 200,
    }
コード例 #5
0
ファイル: helper.py プロジェクト: DwarfCraft/splunk_app
def extract_parameter(obj, key, source_name):
    """
    Validates that a value exists within a dictionary and is non-empty, then returns it. Throws a
    relevant error otherwise.
    """
    try:
        result = obj[key]
    except KeyError:
        raise Errors.SpacebridgeRestError(
            'Error: Request requires %s parameter "%s"' % (source_name, key),
            400)

    if (isinstance(result, str)
            or py23.py2_check_unicode(result)) and not result:
        error_message = 'Error: Request requires %s parameter "%s" to not be empty' % (
            source_name, key)
        raise Errors.SpacebridgeRestError(error_message, 400)
    return result
コード例 #6
0
ファイル: feature.py プロジェクト: DwarfCraft/splunk_app
def get_state_of_apps(feature_names, authtoken):
    result = {}
    for feature_name in feature_names:
        if feature_name not in FEATURE_TYPES:
            raise Errors.SpacebridgeRestError('Error: Invalid feature_type=%s' % feature_name, 400)
        result[feature_name] = retrieve_state_of_app(feature_name, authtoken)
    return {
        'payload': result,
        'status': 200,
    }
コード例 #7
0
ファイル: app.py プロジェクト: AndyViry56/splunkversion_test
def get_state_of_apps(app_names, authtoken):
    result = {}
    for app_name in app_names:
        if app_name not in APP_TYPES:
            raise Errors.SpacebridgeRestError('Error: Invalid app_type=%s' % app_name, 400)
        result[app_name] = retrieve_state_of_app(app_name, authtoken)
    return {
        'payload': result,
        'status': 200,
    }
コード例 #8
0
    def get(self, request):
        """
        Handler which generates QR codes using a deep link generated from a dashboard ID, and
        returns it to the client as a binary body of the specified type. This function:
            1. Extracts request data
            2. Validates the requested file type
            3. Validates and transforms the dashboard ID into a deep link
            4. Generates and returns the QR code
        """
        # Extracts request data
        user = request['session']['user']
        dashboard_id = extract_parameter(request['query'], DASHBOARD_ID_LABEL,
                                         'query')
        file_type = request['path_info'].split('.')[-1]
        id_type = 'a' if 'isAr' in request['query'] else 's'

        # Validates the requested file type
        if file_type not in get_valid_file_types(with_names=False):
            raise Errors.SpacebridgeRestError(
                'QR code was requested with invalid file_type=%s' % file_type)

        # Validates and transforms the dashboard ID into a deep link
        deep_link = shorten_dashboard_id_from_url(dashboard_id)
        if deep_link == dashboard_id:  # Nothing was shortened so it must be an invalid dashboard_id
            raise Errors.SpacebridgeRestError(
                'QR code was requested for invalid dashboard_id=%s' %
                dashboard_id)
        deep_link = 'https://spl.mobi/%s%s/%s' % (
            id_type, QR_CODE_DASHBOARD_VERSION, deep_link)

        LOGGER.info('Generated QR code of deep_link=%s for user=%s' %
                    (deep_link, user))

        # Generates and returns the QR code
        return {
            'binary': generate_qr_code(deep_link, file_type),
            'status': 200,
        }
コード例 #9
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,
    }
コード例 #10
0
ファイル: feature.py プロジェクト: DwarfCraft/splunk_app
    def get(self, request):
        """
        Returns whether a given app_type is enabled or not

        :param query[feature_name] <string> or query[feature_names] <json array>
        """
        authtoken = request['system_authtoken']

        if FEATURE_NAMES_LABEL in request[QUERY_LABEL]:
            try:
                feature_names = json.loads(request[QUERY_LABEL][FEATURE_NAMES_LABEL])
            except ValueError:
                raise Errors.SpacebridgeRestError('Error: "feature_names" query parameter must be a valid JSON array', 400)

            return get_state_of_apps(feature_names, authtoken)

        feature_name = extract_parameter(request[QUERY_LABEL], FEATURE_NAME_LABEL, QUERY_LABEL)
        return get_state_of_app(feature_name, authtoken)
コード例 #11
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,
    }
コード例 #12
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,
    }
コード例 #13
0
def authentication_query_request(auth_code, encryption_context):
    """ Abstraction layer for the spacebridge request. This function:
        1. Makes the registration query GET request to the spacebridge endpoint
        2. Parses the protobuf response
        3. Packs the response values into a response object. Binary objects are encoded to ensure kvstore compatibility

    :param auth_code: Authorization code of the device being registered
    :return: response object containing "public_key", "device_id", and "conf_code"
    """

    # Makes the registration query GET request to the spacebridge endpoint
    try:
        headers = {
            'Authorization':
            encryption_context.sign_public_key(
                transform=encryption_context.generichash_hex)
        }
        response = requests.get('%s/api/registrations/%s' %
                                (config.get_spacebridge_domain(), auth_code),
                                headers=headers,
                                proxies=config.get_proxies())
    except Exception:
        LOGGER.exception("Exception contacting spacebridge")
        raise Errors.SpacebridgeServerError('Unable to reach Spacebridge', 503)

    # Parses the protobuf response
    spacebridge_response = http_pb2.AuthenticationQueryResponse()
    spacebridge_response.ParseFromString(response.content)

    if spacebridge_response.HasField('error'):
        if response.status_code == 500:
            raise Errors.SpacebridgeServerError(
                'Spacebridge encountered an internal error: %s' %
                spacebridge_response.error.message, 500)

        raise Errors.SpacebridgeServerError(
            'Spacebridge request error: %s' %
            spacebridge_response.error.message, response.status_code)

    if not str(response.status_code).startswith('2'):
        raise Errors.SpacebridgeServerError(
            "Spacebridge error: %s" % str(response.content),
            response.status_code)

    # Packs the response values into a response object. Binary objects are encoded to ensure kvstore compatibility
    encrypt_public_key = spacebridge_response.payload.publicKeyForEncryption
    sign_public_key = spacebridge_response.payload.publicKeyForSigning
    response = {
        'encrypt_public_key':
        py23.b64encode_to_str(encrypt_public_key),
        'sign_public_key':
        py23.b64encode_to_str(sign_public_key),
        'device_id':
        py23.b64encode_to_str(spacebridge_response.payload.deviceId),
        'conf_code':
        encryption_context.generichash_hex(sign_public_key).upper()[:8],
    }

    try:
        response[APP_TYPE_LABEL] = translate_app_name(
            http_pb2.AppType.Name(spacebridge_response.payload.appType))
    except ValueError as err:
        # When app_type is 'APPTYPE_INVALID'
        raise Errors.SpacebridgeRestError('Registration Error: %s' % str(err),
                                          501)

    return response