def generate(self):
        spacebridge_server = config.get_spacebridge_domain()

        url = "{}/health_check".format(spacebridge_server)

        proxies = config.get_proxies()

        # Unset proxy, if unsetProxy = True
        if not self.useProxy:
            proxies = {}

        # Load data from REST API
        try:
            response = requests.get(url, proxies=proxies, timeout=15)

            response.raise_for_status()
            healthy = {'https_sync': True}

        except requests.exceptions.HTTPError as err:
            healthy = {'https_sync': False, 'message': err.message}
        except ProxyError as err:
            healthy = {'https_sync': False, 'message': err.message}
        except requests.ConnectionError as err:
            healthy = {'https_sync': False, 'message': err.message}

        yield healthy
def delete_device_from_spacebridge(device_id, system_authtoken):
    """
    Deletes device from spacebridge
    :param device_id:
    :param system_authtoken:
    :return: response from spacebridge
    """
    sodium_client = SodiumClient(LOGGER.getChild("sodium_client"))
    encryption_context = SplunkEncryptionContext(
        system_authtoken, constants.SPACEBRIDGE_APP_NAME, sodium_client)
    public_key_hash = encryption_context.sign_public_key(
        transform=encryption_context.generichash_hex)

    unregister_proto = http_pb2.DeviceUnregistrationRequest()
    unregister_proto.deviceId = b64decode(device_id)
    unregister_proto.deploymentId = encryption_context.sign_public_key(
        transform=encryption_context.generichash_raw)

    headers = {
        'Authorization': public_key_hash,
        'Content-Type': 'application/x-protobuf'
    }

    try:
        response = requests.delete("%s/api/session" %
                                   config.get_spacebridge_domain(),
                                   headers=headers,
                                   proxies=config.get_proxies(),
                                   data=unregister_proto.SerializeToString())
    except Exception:
        LOGGER.exception(
            "Exception attempting sending delete device request to Spacebridge"
        )
        raise Errors.SpacebridgeServerError('Unable to reach Spacebridge', 503)

    LOGGER.info(
        "Received response=%s on delete device from Spacebridge request" %
        response.status_code)

    spacebridge_response = http_pb2.DeviceUnregistrationResponse()
    spacebridge_response.ParseFromString(response.content)

    LOGGER.info('Spacebridge response: %s' % str(spacebridge_response))

    if spacebridge_response.HasField(
            'error'
    ) and spacebridge_response.error.code != http_pb2.HttpError.Code.Value(
            'ERROR_ROUTING_UNDELIVERABLE'):
        raise Errors.SpacebridgeServerError(
            "Spacebridge error on delete device request=%s" %
            spacebridge_response.error.message)

    return response
    def generate(self):

        url = self.url
        proxies = config.get_proxies()

        # Unset proxy, if unsetProxy = True
        if not self.useProxy:
            proxies = {}

        # Load data from REST API
        try:
            response = requests.get(url, proxies=proxies, timeout=15)

            response.raise_for_status()
            healthy = {'connected': True}

        except requests.exceptions.HTTPError as err:
            healthy = {'connected': False, 'message': err.message}
        except ProxyError as err:
            healthy = {'connected': False, 'message': err.message}

        yield healthy
def handle_confirmation(user, authtoken, system_authtoken, body):
    """
    Handler for the NLP service registration call. This function:
        1. Gets an auth code from the NLP server
        2. Call the reqistration/query endpoint to get a temp key
        3. Use that temp key to register (registration/confirmation)

    :param auth_code: User-entered authorization code to be returned to Spacebridge
    :param user: User running the query
    :param body: Parsed JSON body of the incoming POST request
    :param system_authtoken: System-level access token for writing to the kvstore
    :return: Success message
    """

    if nlp_utils.is_nlp_enabled(system_authtoken, user):
        return {
            'payload': 'NLP feature flag is not enabled',
            'status': 500,
        }

    LOGGER.info(
        'Received NLP service registration request by user={}'.format(user))
    device_name = NLP_DEVICE_NAME_PREFIX + str(uuid.uuid4())
    nlp_auth_url = get_nlp_auth_url(user, system_authtoken)
    deployment_info = nlp_utils.get_deployment_info(authtoken)
    deployment_id = deployment_info['deployment_id']

    auth_code_url = '{}/registration/authcode/{}'.format(
        nlp_auth_url, deployment_id)

    LOGGER.info('authCode URL={}'.format(auth_code_url))
    proxies = config.get_proxies()
    r = requests.get(auth_code_url, proxies=proxies, verify=True)
    if not r.status_code == requests.codes.ok:
        return {
            'payload': 'Failed to get Auth Code',
            'status': 500,
        }

    auth_code = r.content

    # enable NLP APP
    set_state_of_app(constants.NLP, authtoken, system_authtoken, True)

    delete_all_nlp_devices(authtoken, system_authtoken)
    params = {AUTH_CODE_LABEL: auth_code, DEVICE_NAME_LABEL: device_name}
    serverResponse, serverContent = rest.simpleRequest("registration/query",
                                                       getargs=params,
                                                       sessionKey=authtoken)
    reg_query_result = json.loads(serverContent)
    if not KVSTORE_TEMPORARY_ID_LABEL in reg_query_result:
        return {
            'payload': 'Invalid registration data',
            'status': 500,
        }
    temp_key = reg_query_result[KVSTORE_TEMPORARY_ID_LABEL]

    LOGGER.info(
        'NLP service registration confirmed, device_name=\"{}\" temp_key=\"{}\"'
        .format(device_name, temp_key))
    return register_device(auth_code, user, system_authtoken, temp_key)
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
def send_mdm_signing_key_to_spacebridge(authtoken, mdm_public_signing_key):
    """ Send the mdm public signing key to spacebridge
        Abstraction layer for the spacebridge request. This function:
        1. Creates the mdm_credentials_bundle
        2. Serializes the bundle to bytes
        3. Signs the serialized bundle with the splapps private signing key
        4. Creates a proto object with the serialized bundle + signature and sends to spacebridge
        5. Parses the protobuf response, checking for error objects

    """

    sodium_client = SodiumClient(LOGGER.getChild("sodium_client"))
    encryption_context = SplunkEncryptionContext(
        authtoken, constants.SPACEBRIDGE_APP_NAME, sodium_client)
    sign_func = partial(sign_detached, sodium_client,
                        encryption_context.sign_private_key())
    client_id = encryption_context.sign_public_key(
        transform=encryption_context.generichash_raw)

    request_proto = http_pb2.MdmAuthenticationGrantRequest()
    client_mdm_permission = request_proto.ClientMdmPermission()
    client_mdm_permission.clientId = client_id
    client_mdm_permission.mdmPublicKeyForSigning = mdm_public_signing_key
    serialized = client_mdm_permission.SerializeToString()
    signature = sign_func(serialized)
    request_proto.clientMdmPermission = serialized
    request_proto.signature = signature
    headers = {
        'Authorization':
        encryption_context.sign_public_key(
            transform=encryption_context.generichash_hex),
        'Content-Type':
        'application/x-protobuf'
    }

    try:
        response = requests.post('{}/api/mdm/grants'.format(
            config.get_spacebridge_domain()),
                                 headers=headers,
                                 data=request_proto.SerializeToString(),
                                 proxies=config.get_proxies(),
                                 timeout=constants.TIMEOUT_SECONDS)
    except requests.exceptions.Timeout as e:
        raise Errors.SpacebridgeServerError(
            'Failed to receive a response from spacebridge', 500)

    sb_response_proto = http_pb2.MdmAuthenticationGrantResponse()
    sb_response_proto.ParseFromString(response.content)

    retries = 0
    while 500 <= response.status_code < 600 and retries < 3:
        wait = 2**retries
        retries = retries + 1
        time.sleep(wait)

        try:
            response = requests.post('{}/api/mdm/grants'.format(
                config.get_spacebridge_domain()),
                                     headers=headers,
                                     data=request_proto.SerializeToString(),
                                     proxies=config.get_proxies(),
                                     timeout=constants.TIMEOUT_SECONDS)
        except requests.exceptions.Timeout as e:
            raise Errors.SpacebridgeServerError(
                'Failed to receive a response from spacebridge', 500)

        sb_response_proto = http_pb2.MdmAuthenticationGrantResponse()
        sb_response_proto.ParseFromString(response.content)

    if sb_response_proto.HasField('error'):
        if response.status_code == 500:
            raise Errors.SpacebridgeServerError(
                'Spacebridge encountered an internal error:{}'.format(
                    sb_response_proto.error.message), 500)
        raise Errors.SpacebridgeServerError(
            'Spacebridge request error: {}'.format(
                sb_response_proto.error.message, response.status_code))

    if not (200 <= response.status_code < 300):
        raise Errors.SpacebridgeServerError(
            "Spacebridge error: {}".format(response.content),
            response.status_code)
def device_pairing_confirmation_request(auth_header, auth_code, username,
                                        device_id, encrypt_public_key,
                                        sign_public_key,
                                        session_token_encrypted, encrypt,
                                        deployment_friendly_name):
    """ Abstraction layer for the spacebridge request. This function:
        1. Creates the encrypted_credentials_bundle
        2. Generates a protobuf object from the supplied dictionary, proto_object
        3. Makes the registration confirmation PUT request to the spacebridge endpoint
        4. Parses the protobuf response, checking for error objects

    :param auth_code: Authorization code of the device being registered
    :param proto_object: Dict containing protobuf values
    :param username: User username for the encrypted_credentials_bundle
    :param password: User password for the encrypted_credentials_bundle
    :return: None
    """

    # Creates the encrypted_credentials_bundle
    credentials_bundle_proto = http_pb2.CredentialsBundle()
    credentials_bundle_proto.sessionToken = session_token_encrypted
    credentials_bundle_proto.userName = username
    credentials_bundle_proto.deploymentName = deployment_friendly_name
    credentials_bundle = credentials_bundle_proto.SerializeToString()

    encrypted_credentials_bundle = encrypt(credentials_bundle)

    # Generates a protobuf object from the supplied dictionary, proto_object
    sb_request_proto = http_pb2.DevicePairingConfirmationRequest()
    sb_request_proto.authenticationCode = auth_code
    sb_request_proto.deviceId = device_id
    sb_request_proto.deploymentPublicKeyForEncryption = encrypt_public_key
    sb_request_proto.deploymentPublicKeyForSigning = sign_public_key
    sb_request_proto.encryptedCredentialsBundle = encrypted_credentials_bundle
    LOGGER.info("Registration Bundle deploymentPublicKey= %s" %
                str(sb_request_proto.deploymentPublicKey))
    LOGGER.info("Registration Bundle deviceId= %s" %
                str(sb_request_proto.deviceId))

    # Makes the registration confirmation PUT request to the spacebridge endpoint
    try:
        response = requests.put('%s/api/registrations/%s' %
                                (config.get_spacebridge_domain(), auth_code),
                                headers={
                                    'Content-Type': 'application/x-protobuf',
                                    'Authorization': str(auth_header)
                                },
                                data=sb_request_proto.SerializeToString(),
                                proxies=config.get_proxies())
    except Exception:
        raise Errors.SpacebridgeServerError('Unable to reach Spacebridge', 503)

    # Parses the protobuf response, checking for error objects
    sb_response_proto = http_pb2.DevicePairingConfirmationResponse()
    sb_response_proto.ParseFromString(response.content)

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

    if not (200 <= response.status_code < 300):
        raise Errors.SpacebridgeServerError(
            "Spacebridge error: %s" % str(response.content),
            response.status_code)
Beispiel #8
0
def forward_request(self, request):
    method = request['method']
    path = request['path_info']
    params = request['query']
    request_headers = request['headers']

    data = None
    if 'payload' in request:
        data = request['payload']

    user = request['session']['user']
    user_token = request['session']['authtoken']
    system_authtoken = request['system_authtoken']

    resp_status = ()
    resp_body = {}
    url = None
    try:
        resp_status = (None, None)

        url = build_url(self, user, user_token, path)
        headers = get_headers(self, user, user_token, system_authtoken,
                              request_headers)
        proxies = config.get_proxies()

        LOGGER.info("Request method={} url={} data={} params={}".format(
            method, url, data, params))

        if data is not None:
            data = data.encode('utf-8')

        response = requests.request(method=method,
                                    url=url,
                                    data=data,
                                    params=params,
                                    headers=headers,
                                    proxies=proxies,
                                    timeout=self.timeout,
                                    verify=True)
        resp_status = (response.status_code, response.content)
        resp_body = json.loads(response.content)
        LOGGER.info("Response method={} url={} response_status={}".format(
            method, url, data, resp_status))
        if not is_valid_response(self, user, user_token, response.headers):
            LOGGER.info(
                "Response sign verification failed for method={} url={} response_status={}"
                .format(method, url, data, resp_status))
            return {
                'payload': {
                    "status": "signature verification failed"
                },
                'status': 401,
            }
        else:
            LOGGER.info(
                "Response sign verification success for method={} url={} response_status={}"
                .format(method, url, data, resp_status))
            return {
                'payload': resp_body,
                'status': resp_status[0],
            }

    except Exception as e:
        resp_body = {
            'url': url,
            'code': resp_status[0],
            'message': resp_status[1],
            'detail': str(e)
        }
        LOGGER.error(
            "FORWARDING REQUEST EXCEPTION {} for response status {} resp_body {}"
            .format(e, resp_status[0], resp_body))
        return {
            'payload': resp_body,
            'status': resp_status[0],
        }