Пример #1
0
def _delete_baselines(baseline_ids):
    """
    delete baselines
    """
    account_number = view_helpers.get_account_number(request)
    query = SystemBaseline.query.filter(
        SystemBaseline.account == account_number,
        SystemBaseline.id.in_(baseline_ids))

    full_results = query.all()
    if len(full_results) < len(baseline_ids):
        fetched_ids = {str(result.id) for result in full_results}
        missing_ids = set(baseline_ids) - fetched_ids
        raise HTTPError(
            HTTPStatus.NOT_FOUND,
            message="ids [%s] not available to delete" %
            ", ".join(missing_ids),
        )

    query.delete(synchronize_session="fetch")
    db.session.commit()
Пример #2
0
def copy_baseline_by_id(baseline_id, display_name):
    """
    create a new baseline given an existing ID
    """
    ensure_rbac_write()
    validate_uuids([baseline_id])

    # ensure display_name is not null
    if not display_name:
        message = "no value given for display_name"
        current_app.logger.audit(message, request=request, success=False)
        raise HTTPError(HTTPStatus.BAD_REQUEST, message=message)

    account_number = view_helpers.get_account_number(request)

    _check_for_existing_display_name(display_name, account_number)
    _check_for_whitespace_in_display_name(display_name)

    message = "counted baselines"
    current_app.logger.audit(message, request=request)

    query = SystemBaseline.query.filter(
        SystemBaseline.account == account_number,
        SystemBaseline.id == baseline_id)

    copy_baseline = query.first_or_404()
    db.session.expunge(copy_baseline)
    make_transient(copy_baseline)
    copy_baseline.id = None
    copy_baseline.created_on = None
    copy_baseline.modified_on = None
    copy_baseline.display_name = display_name
    db.session.add(copy_baseline)

    db.session.commit()

    message = "created baselines"
    current_app.logger.audit(message, request=request)

    return copy_baseline.to_json()
Пример #3
0
def ensure_entitled(request, app_name, logger):
    """
    check if the request is entitled. We run this on all requests and bail out
    if the URL is whitelisted. Returning 'None' allows the request to go through.
    """

    auth_key = get_key_from_headers(request.headers)

    # check if the request comes from our own drift service
    if auth_key:
        auth = json.loads(base64.b64decode(auth_key))
        if auth.get("identity", {}).get("type", None) == "System":
            request_shared_secret = request.headers.get("x-rh-drift-internal-api", None)
            if request_shared_secret and request_shared_secret == drift_shared_secret:
                return  # shared secret set and is correct

    entitlement_key = "insights"
    if enable_smart_mgmt_check:
        entitlement_key = "smart_management"

    # TODO: Blueprint.before_request was not working as expected, using
    # before_app_request and checking URL here instead.
    if _is_mgmt_url(request.path) or _is_openapi_url(request.path, app_name):
        return  # allow request

    if auth_key:
        entitlements = json.loads(base64.b64decode(auth_key)).get("entitlements", {})
        if entitlement_key in entitlements:
            if entitlements[entitlement_key].get("is_entitled"):
                logger.debug("enabled entitlement found on header")
                return  # allow request
    else:
        logger.debug("identity header not sent for request")

    # if we got here, reject the request
    logger.debug("entitlement not found for account.")
    raise HTTPError(
        HTTPStatus.BAD_REQUEST, message="Entitlement not found for account."
    )
Пример #4
0
def get_hsps_by_inventory_id(inventory_id, limit, offset):
    """
    return a list of historical system profiles for a given inventory id
    """
    validate_uuids([inventory_id])
    account_number = view_helpers.get_account_number(request)

    message = "read historical system profiles"
    current_app.logger.audit(message, request=request)

    query_results = db_interface.get_hsps_by_inventory_id(
        inventory_id, account_number, limit, offset)
    valid_profiles = _filter_old_hsps(query_results)

    if not valid_profiles:
        message = "no historical profiles found for inventory_id %s" % inventory_id
        current_app.logger.audit(message, request=request, success=False)
        raise HTTPError(
            HTTPStatus.NOT_FOUND,
            message=message,
        )

    # TODO: request just these three fields from the DB, instead of fetching
    # the full records, then slicing and sorting

    profile_metadata = []
    for profile in valid_profiles:
        profile_metadata.append({
            "captured_date": profile.captured_date,
            "id": profile.id,
            "system_id": profile.inventory_id,
        })
    sorted_profile_metadata = sorted(profile_metadata,
                                     key=lambda p: p["captured_date"],
                                     reverse=True)

    result = {"profiles": sorted_profile_metadata}
    return {"data": [result]}
Пример #5
0
def validate_uuids(system_ids):
    """
    helper method to test if a UUID is properly formatted. Will raise an
    exception if the format is wrong.
    """
    malformed_ids = []
    for system_id in system_ids:
        # the UUID() check was missing some characters, so adding regex first
        if not re.match(
            r"^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$",
            system_id.lower(),
        ):
            malformed_ids.append(system_id)
        else:
            try:
                UUID(system_id)
            except ValueError:
                malformed_ids.append(system_id)
    if malformed_ids:
        raise HTTPError(
            HTTPStatus.BAD_REQUEST,
            message="malformed UUIDs requested (%s)" % ", ".join(malformed_ids),
        )
Пример #6
0
def list_systems_with_baseline(baseline_id):
    validate_uuids([baseline_id])
    account_number = view_helpers.get_account_number(request)

    query = SystemBaseline.query.filter(
        SystemBaseline.account == account_number,
        SystemBaseline.id == baseline_id)
    baseline = query.first_or_404()

    message = "read baseline"
    current_app.logger.audit(message, request=request, success=True)

    try:
        system_ids = baseline.mapped_system_ids()
    except ValueError as error:
        message = str(error)
        current_app.logger.audit(message, request=request, success=False)
        raise HTTPError(HTTPStatus.BAD_REQUEST, message=message)
    except Exception:
        message = "Unknown error when reading mapped system ids"
        current_app.logger.audit(message, request=request, success=False)
        raise

    return {"system_ids": system_ids}
Пример #7
0
def handle_errors(http_errors=None, other_errors=None):
    # raises merged errors
    if http_errors or other_errors:
        if any(
                isinstance(other_error, ServiceError)
                for other_error in other_errors) or any(
                    http_error.code >= 500 for http_error in http_errors):
            http_code = HTTPStatus.INTERNAL_SERVER_ERROR
        elif any(http_error.code == HTTPStatus.BAD_REQUEST
                 for http_error in http_errors):
            http_code = HTTPStatus.BAD_REQUEST
        elif any(
                isinstance(other_error, ItemNotReturned)
                for other_error in other_errors) or any(
                    http_error.code == HTTPStatus.NOT_FOUND
                    for http_error in http_errors):
            http_code = HTTPStatus.NOT_FOUND
        else:
            http_code = HTTPStatus.INTERNAL_SERVER_ERROR

        message = ", ".join(
            [str(http_error.reason) for http_error in http_errors] +
            [str(other_error.message) for other_error in other_errors])
        raise HTTPError(http_code, message=message)
Пример #8
0
def update_baseline(baseline_id, system_baseline_patch):
    """
    update a baseline
    """
    ensure_rbac_write()
    validate_uuids([baseline_id])

    account_number = view_helpers.get_account_number(request)
    _check_for_whitespace_in_display_name(
        system_baseline_patch["display_name"])

    # this query is a bit different than what's in _check_for_existing_display_name,
    # since it's OK if the display name is used by the baseline we are updating
    existing_display_name_query = SystemBaseline.query.filter(
        SystemBaseline.account == account_number,
        SystemBaseline.id != baseline_id,
        SystemBaseline.display_name == system_baseline_patch["display_name"],
    )

    if existing_display_name_query.count() > 0:
        message = ("display_name '%s' already used for this account" %
                   system_baseline_patch["display_name"])
        current_app.logger.audit(message, request=request, success=False)
        raise HTTPError(
            HTTPStatus.BAD_REQUEST,
            message=message,
        )

    query = SystemBaseline.query.filter(
        SystemBaseline.account == account_number,
        SystemBaseline.id == baseline_id)

    baseline = query.first_or_404()
    message = "read baselines"
    current_app.logger.audit(message, request=request)

    try:
        updated_facts = jsonpatch.apply_patch(
            baseline.baseline_facts, system_baseline_patch["facts_patch"])
        _validate_facts(updated_facts)
        baseline.baseline_facts = updated_facts
    except FactValidationError as error:
        message = error.message
        current_app.logger.audit(message, request=request, success=False)
        raise HTTPError(HTTPStatus.BAD_REQUEST, message=message)
    except (jsonpatch.JsonPatchException, jsonpointer.JsonPointerException):
        message = "unable to apply patch to baseline"
        current_app.logger.audit(message, request=request, success=False)
        raise HTTPError(HTTPStatus.BAD_REQUEST, message=message)

    baseline.display_name = system_baseline_patch["display_name"]

    baseline.baseline_facts = _sort_baseline_facts(baseline.baseline_facts)
    db.session.add(baseline)

    db.session.commit()

    message = "updated baselines"
    current_app.logger.audit(message, request=request)

    # pull baseline again so we have the correct updated timestamp and fact count
    query = SystemBaseline.query.filter(
        SystemBaseline.account == account_number,
        SystemBaseline.id == baseline_id)
    return [query.first().to_json()]
Пример #9
0
def create_baseline(system_baseline_in):
    """
    create a baseline
    """
    ensure_rbac_write()
    account_number = view_helpers.get_account_number(request)

    if "values" in system_baseline_in and "value" in system_baseline_in:
        message = "'values' and 'value' cannot both be defined for system baseline"
        current_app.logger.audit(message, request=request, success=False)
        raise HTTPError(
            HTTPStatus.BAD_REQUEST,
            message=message,
        )

    _check_for_existing_display_name(system_baseline_in["display_name"],
                                     account_number)
    _check_for_whitespace_in_display_name(system_baseline_in["display_name"])

    message = "counted baselines"
    current_app.logger.audit(message, request=request)

    baseline_facts = []
    if "baseline_facts" in system_baseline_in:
        if "inventory_uuid" in system_baseline_in:
            message = (
                "Both baseline facts and inventory id provided, can clone only one."
            )
            current_app.logger.audit(message, request=request, success=False)
            raise HTTPError(
                HTTPStatus.BAD_REQUEST,
                message=message,
            )
        if "hsp_uuid" in system_baseline_in:
            message = "Both baseline facts and hsp id provided, can clone only one."
            current_app.logger.audit(message, request=request, success=False)
            raise HTTPError(
                HTTPStatus.BAD_REQUEST,
                message=message,
            )
        baseline_facts = system_baseline_in["baseline_facts"]
    elif "hsp_uuid" in system_baseline_in:
        if "inventory_uuid" in system_baseline_in:
            message = "Both hsp id and system id provided, can clone only one."
            current_app.logger.audit(message, request=request, success=False)
            raise HTTPError(
                HTTPStatus.BAD_REQUEST,
                message=message,
            )
        validate_uuids([system_baseline_in["hsp_uuid"]])
        auth_key = get_key_from_headers(request.headers)
        try:
            hsp = fetch_historical_sys_profiles(
                [system_baseline_in["hsp_uuid"]],
                auth_key,
                current_app.logger,
                get_event_counters(),
            )[0]
            message = "read historical system profiles"
            current_app.logger.audit(message, request=request)
        except ItemNotReturned:
            message = "hsp UUID %s not available" % system_baseline_in[
                "hsp_uuid"]
            current_app.logger.audit(message, request=request, success=False)
            raise HTTPError(
                HTTPStatus.NOT_FOUND,
                message=message,
            )
        except RBACDenied as error:
            message = error.message
            current_app.logger.audit(message, request=request, success=False)
            raise HTTPError(HTTPStatus.FORBIDDEN, message=message)

        system_name = "clone_from_hsp_unused"
        baseline_facts = _parse_from_sysprofile(hsp["system_profile"],
                                                system_name,
                                                current_app.logger)
    elif "inventory_uuid" in system_baseline_in:
        validate_uuids([system_baseline_in["inventory_uuid"]])
        auth_key = get_key_from_headers(request.headers)
        try:
            system_with_profile = fetch_systems_with_profiles(
                [system_baseline_in["inventory_uuid"]],
                auth_key,
                current_app.logger,
                get_event_counters(),
            )[0]
            message = "read system with profiles"
            current_app.logger.audit(message, request=request)
        except ItemNotReturned:
            message = ("inventory UUID %s not available" %
                       system_baseline_in["inventory_uuid"])
            current_app.logger.audit(message, request=request, success=False)
            raise HTTPError(
                HTTPStatus.NOT_FOUND,
                message=message,
            )
        except RBACDenied as error:
            message = error.message
            current_app.logger.audit(message, request=request, success=False)
            raise HTTPError(HTTPStatus.FORBIDDEN, message=message)

        system_name = profile_parser.get_name(system_with_profile)
        baseline_facts = _parse_from_sysprofile(
            system_with_profile["system_profile"], system_name,
            current_app.logger)

    try:
        _validate_facts(baseline_facts)
    except FactValidationError as error:
        message = error.message
        current_app.logger.audit(message, request=request, success=False)
        raise HTTPError(HTTPStatus.BAD_REQUEST, message=message)

    baseline = SystemBaseline(
        account=account_number,
        display_name=system_baseline_in["display_name"],
        baseline_facts=baseline_facts,
    )
    baseline.baseline_facts = _sort_baseline_facts(baseline.baseline_facts)
    db.session.add(baseline)

    db.session.commit(
    )  # commit now so we get a created/updated time before json conversion

    message = "creat baselines"
    current_app.logger.audit(message, request=request)

    return baseline.to_json()
Пример #10
0
def comparison_report(
    system_ids,
    baseline_ids,
    historical_sys_profile_ids,
    reference_id,
    auth_key,
    data_format,
    short_circuit,
):
    """
    return a comparison report

    If short_circuit is true, this is a call to see if a single system has
    drifted from a single baseline in order to trigger a Notification if
    necessary, so the only facts that will be compared will be those present
    on the baseline.  If the system has drifted from the baseline, the report
    will contain the key 'drift_event_notify' set to True, otherwise False.
    """
    if len(system_ids + baseline_ids + historical_sys_profile_ids) == 0:
        message = "must specify at least one of system, baseline, or HSP"
        current_app.logger.audit(str(HTTPStatus.BAD_REQUEST) + " " + message,
                                 request=request,
                                 success=False)
        raise HTTPError(
            HTTPStatus.BAD_REQUEST,
            message=message,
        )
    if len(system_ids) > len(set(system_ids)):
        message = "duplicate UUID specified in system_ids list"
        current_app.logger.audit(str(HTTPStatus.BAD_REQUEST) + " " + message,
                                 request=request,
                                 success=False)
        raise HTTPError(
            HTTPStatus.BAD_REQUEST,
            message=message,
        )

    if len(baseline_ids) > len(set(baseline_ids)):
        message = "duplicate UUID specified in baseline_ids list"
        current_app.logger.audit(str(HTTPStatus.BAD_REQUEST) + " " + message,
                                 request=request,
                                 success=False)
        raise HTTPError(
            HTTPStatus.BAD_REQUEST,
            message=message,
        )

    if system_ids:
        validate_uuids(system_ids)
    if baseline_ids:
        validate_uuids(baseline_ids)
    if historical_sys_profile_ids:
        validate_uuids(historical_sys_profile_ids)
    if reference_id:
        validate_uuids([reference_id])
        if reference_id not in (system_ids + baseline_ids +
                                historical_sys_profile_ids):
            message = "reference id %s does not match any ids from query" % reference_id
            current_app.logger.audit(
                str(HTTPStatus.BAD_REQUEST) + " " + message,
                request=request,
                success=False,
            )
            raise HTTPError(
                HTTPStatus.BAD_REQUEST,
                message=message,
            )

    try:
        systems_with_profiles = []
        baseline_results = []
        hsp_results = []

        with PT_CR_API_REQUESTS.time():
            try:
                if system_ids:
                    # can raise RBACDenied exception
                    message = "reading systems with profiles"
                    current_app.logger.audit(message, request=request)
                    systems_with_profiles = fetch_systems_with_profiles(
                        system_ids, auth_key, current_app.logger,
                        get_event_counters())

                if baseline_ids:
                    # can raise RBACDenied exception
                    message = "reading baselines"
                    current_app.logger.audit(message, request=request)
                    baseline_results = fetch_baselines(baseline_ids, auth_key,
                                                       current_app.logger)
                    ensure_correct_system_count(baseline_ids, baseline_results)

                if historical_sys_profile_ids:
                    # can raise RBACDenied exception
                    message = "reading historical system profiles"
                    current_app.logger.audit(message, request=request)
                    hsp_results = fetch_historical_sys_profiles(
                        historical_sys_profile_ids,
                        auth_key,
                        current_app.logger,
                        get_event_counters(),
                    )
            except RBACDenied as error:
                message = error.message
                current_app.logger.audit(str(HTTPStatus.FORBIDDEN) + " " +
                                         message,
                                         request=request)
                raise HTTPError(HTTPStatus.FORBIDDEN, message=message)

        with PT_CR_BUILD_COMPARISON.time():
            comparisons = info_parser.build_comparisons(
                systems_with_profiles,
                baseline_results,
                hsp_results,
                reference_id,
                short_circuit,
            )

        metrics.systems_compared.observe(len(system_ids))
        if data_format == "csv":
            output = make_response(_csvify(comparisons))
            output.headers[
                "Content-Disposition"] = "attachment; filename=export.csv"
            output.headers["Content-type"] = "text/csv"
            return output
        else:
            return jsonify(comparisons)

    except ItemNotReturned as error:
        message = error.message
        current_app.logger.audit(str(HTTPStatus.NOT_FOUND) + " " + message,
                                 request=request,
                                 success=False)
        raise HTTPError(HTTPStatus.NOT_FOUND, message=message)
Пример #11
0
def comparison_report(
    system_ids,
    baseline_ids,
    historical_sys_profile_ids,
    reference_id,
    auth_key,
    data_format,
):
    """
    return a comparison report
    """
    if len(system_ids + baseline_ids + historical_sys_profile_ids) == 0:
        raise HTTPError(
            HTTPStatus.BAD_REQUEST,
            message="must specify at least one of system, baseline, or HSP",
        )
    if len(system_ids) > len(set(system_ids)):
        raise HTTPError(
            HTTPStatus.BAD_REQUEST,
            message="duplicate UUID specified in system_ids list",
        )

    if len(baseline_ids) > len(set(baseline_ids)):
        raise HTTPError(
            HTTPStatus.BAD_REQUEST,
            message="duplicate UUID specified in baseline_ids list",
        )

    if system_ids:
        validate_uuids(system_ids)
    if baseline_ids:
        validate_uuids(baseline_ids)
    if historical_sys_profile_ids:
        validate_uuids(historical_sys_profile_ids)
    if reference_id:
        validate_uuids([reference_id])
        if reference_id not in (system_ids + baseline_ids +
                                historical_sys_profile_ids):
            raise HTTPError(
                HTTPStatus.BAD_REQUEST,
                message="reference id %s does not match any ids from query" %
                reference_id,
            )

    try:
        systems_with_profiles = []
        baseline_results = []
        hsp_results = []

        if system_ids:
            systems_with_profiles = fetch_systems_with_profiles(
                system_ids, auth_key, current_app.logger, get_event_counters())

        if baseline_ids:
            baseline_results = fetch_baselines(baseline_ids, auth_key,
                                               current_app.logger)
            ensure_correct_system_count(baseline_ids, baseline_results)

        if historical_sys_profile_ids:
            hsp_results = fetch_historical_sys_profiles(
                historical_sys_profile_ids,
                auth_key,
                current_app.logger,
                get_event_counters(),
            )

        comparisons = info_parser.build_comparisons(systems_with_profiles,
                                                    baseline_results,
                                                    hsp_results, reference_id)
        metrics.systems_compared.observe(len(system_ids))
        if data_format == "csv":
            output = make_response(_csvify(comparisons))
            output.headers[
                "Content-Disposition"] = "attachment; filename=export.csv"
            output.headers["Content-type"] = "text/csv"
            return output
        else:
            return jsonify(comparisons)

    except ItemNotReturned as error:
        raise HTTPError(HTTPStatus.NOT_FOUND, message=error.message)
Пример #12
0
def create_baseline(system_baseline_in):
    """
    create a baseline
    """
    account_number = view_helpers.get_account_number(request)

    if "values" in system_baseline_in and "value" in system_baseline_in:
        raise HTTPError(
            HTTPStatus.BAD_REQUEST,
            message=
            "'values' and 'value' cannot both be defined for system baseline",
        )

    query = SystemBaseline.query.filter(
        SystemBaseline.account == account_number,
        SystemBaseline.display_name == system_baseline_in["display_name"],
    )

    if query.count() > 0:
        raise HTTPError(
            HTTPStatus.BAD_REQUEST,
            message="display_name '%s' already used for this account" %
            system_baseline_in["display_name"],
        )

    baseline_facts = []
    if "baseline_facts" in system_baseline_in:
        baseline_facts = system_baseline_in["baseline_facts"]
    elif "inventory_uuid" in system_baseline_in:
        auth_key = get_key_from_headers(request.headers)
        system_with_profile = fetch_systems_with_profiles(
            [system_baseline_in["inventory_uuid"]],
            auth_key,
            current_app.logger,
            get_event_counters(),
        )[0]

        system_name = profile_parser.get_name(system_with_profile)
        parsed_profile = profile_parser.parse_profile(
            system_with_profile["system_profile"], system_name,
            current_app.logger)
        facts = []
        for fact in parsed_profile:
            if fact not in ["id", "name"] and parsed_profile[fact] not in [
                    "N/A",
                    "None",
                    None,
            ]:
                facts.append({"name": fact, "value": parsed_profile[fact]})

        baseline_facts = group_baselines(facts)

    baseline = SystemBaseline(
        account=account_number,
        display_name=system_baseline_in["display_name"],
        baseline_facts=baseline_facts,
    )
    db.session.add(baseline)
    db.session.commit(
    )  # commit now so we get a created/updated time before json conversion

    return baseline.to_json()
Пример #13
0
def create_baseline(system_baseline_in):
    """
    create a baseline
    """
    account_number = view_helpers.get_account_number(request)

    if "values" in system_baseline_in and "value" in system_baseline_in:
        raise HTTPError(
            HTTPStatus.BAD_REQUEST,
            message=
            "'values' and 'value' cannot both be defined for system baseline",
        )

    _check_for_existing_display_name(system_baseline_in["display_name"],
                                     account_number)

    baseline_facts = []
    if "baseline_facts" in system_baseline_in:
        baseline_facts = system_baseline_in["baseline_facts"]
    elif "inventory_uuid" in system_baseline_in:
        _validate_uuids([system_baseline_in["inventory_uuid"]])
        auth_key = get_key_from_headers(request.headers)
        try:
            system_with_profile = fetch_systems_with_profiles(
                [system_baseline_in["inventory_uuid"]],
                auth_key,
                current_app.logger,
                get_event_counters(),
            )[0]
        except ItemNotReturned:
            raise HTTPError(
                HTTPStatus.BAD_REQUEST,
                message="inventory UUID %s not available" %
                system_baseline_in["inventory_uuid"],
            )

        system_name = profile_parser.get_name(system_with_profile)
        parsed_profile = profile_parser.parse_profile(
            system_with_profile["system_profile"], system_name,
            current_app.logger)
        facts = []
        for fact in parsed_profile:
            if fact not in ["id", "name"] and parsed_profile[fact] not in [
                    "N/A",
                    "None",
                    None,
            ]:
                facts.append({"name": fact, "value": parsed_profile[fact]})

        baseline_facts = group_baselines(facts)

    try:
        _validate_facts(baseline_facts)
    except FactValidationError as e:
        raise HTTPError(HTTPStatus.BAD_REQUEST, message=e.message)

    baseline = SystemBaseline(
        account=account_number,
        display_name=system_baseline_in["display_name"],
        baseline_facts=baseline_facts,
    )
    baseline.baseline_facts = _sort_baseline_facts(baseline.baseline_facts)
    db.session.add(baseline)
    db.session.commit(
    )  # commit now so we get a created/updated time before json conversion

    return baseline.to_json()
Пример #14
0
def comparison_report(
    system_ids,
    baseline_ids,
    historical_sys_profile_ids,
    reference_id,
    auth_key,
    data_format,
    short_circuit,
):
    """
    return a comparison report

    If short_circuit is true, this is a call to see if a single system has
    drifted from a single baseline in order to trigger a Notification if
    necessary, so the only facts that will be compared will be those present
    on the baseline.  If the system has drifted from the baseline, the report
    will contain the key 'drift_event_notify' set to True, otherwise False.
    """
    if len(system_ids + baseline_ids + historical_sys_profile_ids) == 0:
        message = "must specify at least one of system, baseline, or HSP"
        current_app.logger.audit(str(HTTPStatus.BAD_REQUEST) + " " + message,
                                 request=request,
                                 success=False)
        raise HTTPError(
            HTTPStatus.BAD_REQUEST,
            message=message,
        )
    if len(system_ids) > len(set(system_ids)):
        message = "duplicate UUID specified in system_ids list"
        current_app.logger.audit(str(HTTPStatus.BAD_REQUEST) + " " + message,
                                 request=request,
                                 success=False)
        raise HTTPError(
            HTTPStatus.BAD_REQUEST,
            message=message,
        )

    if len(baseline_ids) > len(set(baseline_ids)):
        message = "duplicate UUID specified in baseline_ids list"
        current_app.logger.audit(str(HTTPStatus.BAD_REQUEST) + " " + message,
                                 request=request,
                                 success=False)
        raise HTTPError(
            HTTPStatus.BAD_REQUEST,
            message=message,
        )

    with PT_CR_VALIDATE_UUID.time():
        if system_ids:
            validate_uuids(system_ids)
        if baseline_ids:
            validate_uuids(baseline_ids)
        if historical_sys_profile_ids:
            validate_uuids(historical_sys_profile_ids)
        if reference_id:
            validate_uuids([reference_id])
            if reference_id not in (system_ids + baseline_ids +
                                    historical_sys_profile_ids):
                message = "reference id %s does not match any ids from query" % reference_id
                current_app.logger.audit(
                    str(HTTPStatus.BAD_REQUEST) + " " + message,
                    request=request,
                    success=False,
                )
                raise HTTPError(
                    HTTPStatus.BAD_REQUEST,
                    message=message,
                )

    def get_systems_with_profiles(app, timer, system_ids, auth_key, logger,
                                  counters):
        with timer.time():
            with app.app_context():
                # can raise RBACDenied exception
                message = "reading systems with profiles"
                current_app.logger.audit(message, request=request)
                systems_with_profiles = fetch_systems_with_profiles(
                    system_ids, auth_key, current_app.logger, counters)
        return systems_with_profiles

    def get_baselines(app, timer, baseline_ids, auth_key, logger):
        with timer.time():
            with app.app_context():
                # can raise RBACDenied exception
                message = "reading baselines"
                current_app.logger.audit(message, request=request)
                baseline_results = fetch_baselines(baseline_ids, auth_key,
                                                   logger)
                ensure_correct_system_count(baseline_ids, baseline_results)
        return baseline_results

    def get_historical_sys_profiles(app, timer, historical_sys_profile_ids,
                                    auth_key, logger, counters):
        with timer.time():
            with app.app_context():
                # can raise RBACDenied exception
                message = "reading historical system profiles"
                current_app.logger.audit(message, request=request)
                hsp_results = fetch_historical_sys_profiles(
                    historical_sys_profile_ids,
                    auth_key,
                    logger,
                    counters,
                )
        return hsp_results

    try:
        api_results = {}
        http_errors = []
        other_errors = []

        with PT_CR_API_REQUESTS.time():
            futures = {}
            with concurrent.futures.ThreadPoolExecutor() as executor:
                app = current_app._get_current_object()
                if system_ids:
                    # can raise RBACDenied exception
                    future = executor.submit(
                        get_systems_with_profiles,
                        app,
                        PT_CR_API_SYSTEM_PROFILE_REQUESTS,
                        system_ids,
                        auth_key,
                        current_app.logger,
                        get_event_counters(),
                    )
                    futures[future] = "systems_with_profiles"
                if baseline_ids:
                    # can raise RBACDenied exception
                    future = executor.submit(
                        get_baselines,
                        app,
                        PT_CR_API_BASELINE_REQUESTS,
                        baseline_ids,
                        auth_key,
                        current_app.logger,
                    )
                    futures[future] = "baseline_results"
                if historical_sys_profile_ids:
                    # can raise RBACDenied exception
                    future = executor.submit(
                        get_historical_sys_profiles,
                        app,
                        PT_CR_API_HSP_REQUESTS,
                        historical_sys_profile_ids,
                        auth_key,
                        current_app.logger,
                        get_event_counters(),
                    )
                    futures[future] = "hsp_results"

                for future in concurrent.futures.as_completed(futures):
                    try:
                        api_results[futures[future]] = future.result()
                    except RBACDenied as error:
                        message = error.message
                        current_app.logger.audit(str(HTTPStatus.FORBIDDEN) +
                                                 " " + message,
                                                 request=request)
                        raise HTTPError(HTTPStatus.FORBIDDEN, message=message)
                    except HTTPError as error:
                        http_errors.append(error)
                    except (ItemNotReturned, ServiceError) as error:
                        other_errors.append(error)

            handle_errors(http_errors=http_errors,
                          other_errors=other_errors)  # raises merged error

        systems_with_profiles = api_results.get("systems_with_profiles", [])
        baseline_results = api_results.get("baseline_results", [])
        hsp_results = api_results.get("hsp_results", [])

        with PT_CR_BUILD_COMPARISON.time():
            comparisons = info_parser.build_comparisons(
                systems_with_profiles,
                baseline_results,
                hsp_results,
                reference_id,
                short_circuit,
            )

        metrics.systems_compared.observe(len(system_ids))
        if data_format == "csv":
            output = make_response(_csvify(comparisons))
            output.headers[
                "Content-Disposition"] = "attachment; filename=export.csv"
            output.headers["Content-type"] = "text/csv"
            return output
        else:
            return jsonify(comparisons)

    except ItemNotReturned as error:
        message = error.message
        current_app.logger.audit(str(HTTPStatus.NOT_FOUND) + " " + message,
                                 request=request,
                                 success=False)
        raise HTTPError(HTTPStatus.NOT_FOUND, message=message)