def delete_image(user_id, image_id):
    """
    DELETE the image and all resources for it. Returns 204 - No Content on success

    :param user_id:
    :param image_id:
    :return:
    """
    db = get_session()
    try:
        log.info(
            "Deleting image {}/{} and all associated resources".format(
                user_id, image_id
            )
        )
        img = db.query(Image).get((image_id, user_id))
        if img:
            get_vulnerabilities_provider().delete_image_vulnerabilities(
                image=img, db_session=db
            )
            try:
                conn_timeout = ApiRequestContextProxy.get_service().configuration.get(
                    "catalog_client_conn_timeout", DEFAULT_CACHE_CONN_TIMEOUT
                )
                read_timeout = ApiRequestContextProxy.get_service().configuration.get(
                    "catalog_client_read_timeout", DEFAULT_CACHE_READ_TIMEOUT
                )
                mgr = EvaluationCacheManager(
                    img, None, None, conn_timeout, read_timeout
                )
                mgr.flush()
            except Exception as ex:
                log.exception(
                    "Could not delete evaluations for image {}/{} in the cache. May be orphaned".format(
                        user_id, image_id
                    )
                )

            db.delete(img)
            db.commit()
        else:
            db.rollback()

        # Idempotently return 204. This isn't properly RESTY, but idempotency on delete makes clients much cleaner.
        return None, 204
    except HTTPException:
        raise
    except Exception as e:
        log.exception(
            "Error processing DELETE request for image {}/{}".format(user_id, image_id)
        )
        db.rollback()
        return (
            make_response_error(
                "Error deleting image {}/{}: {}".format(user_id, image_id, e),
                in_httpcode=500,
            ),
            500,
        )
Esempio n. 2
0
def ping():
    """
    GET /

    :return: 200 status with api version string
    """
    return ApiRequestContextProxy.get_service().__service_api_version__, 200
Esempio n. 3
0
def create_object(bucket, archiveid, bodycontent):
    httpcode = 500
    try:
        account_name = ApiRequestContextProxy.namespace()
        obj_mgr = anchore_engine.subsys.object_store.manager.get_manager()

        jsonbytes = anchore_utils.ensure_bytes(json.dumps(bodycontent))
        rc = obj_mgr.put(account_name, bucket, archiveid, jsonbytes)

        my_svc = ApiRequestContextProxy.get_service()
        if my_svc is not None:
            resource_url = my_svc.service_record[
                'base_url'] + "/" + my_svc.service_record[
                    'version'] + "/archive/" + bucket + "/" + archiveid
        else:
            resource_url = "N/A"

        return_object = resource_url
        httpcode = 200

    except Exception as err:
        return_object = anchore_engine.common.helpers.make_response_error(
            err, in_httpcode=httpcode)

    return return_object, httpcode
Esempio n. 4
0
def validate_schema(notification):
    """
    Check if the notification conforms to the Schema outlined in the Swagger Spec.
    Also only do this for the types we know (policy_eval, vuln_update, tag_update, analysis_update)

    :param notification: notification object to deliver
    """
    ret = False

    notification_type = notification.get("data",
                                         {}).get("notification_type", None)
    if notification_type not in NOTIFICATION_MAPPING.keys():
        logger.debug(
            "Not doing Schema validation for Notification Type: {}".format(
                notification_type))
        return ret
    elif not notification_type:
        logger.warn("Notification Type not resolved: {}".format(notification))
        return ret

    notification_schema_definition = NOTIFICATION_MAPPING.get(
        notification_type, "NotificationBase")

    spec = ApiRequestContextProxy.get_service().api_spec
    schema = spec.get("definitions", {}).get(notification_schema_definition)
    try:
        jsonschema.validate(notification, schema)
        ret = True
    except jsonschema.ValidationError as e:
        logger.error(
            "Notification does not pass validation, still delivering for backwards compatibility: {}"
            .format(e))
        ret = False

    return ret
Esempio n. 5
0
def create_archive(bucket, archiveid, bodycontent):
    httpcode = 500
    try:
        accountName = ApiRequestContextProxy.namespace()
        archive_sys = archive.get_manager()

        try:
            jsonbytes = anchore_utils.ensure_bytes(json.dumps(bodycontent))
            my_svc = ApiRequestContextProxy.get_service()
            if my_svc is not None:
                resource_url = (my_svc.service_record["base_url"] + "/" +
                                my_svc.service_record["version"] +
                                "/archive/" + bucket + "/" + archiveid)
            else:
                resource_url = "N/A"

            rc = archive_sys.put(accountName, bucket, archiveid, jsonbytes)
            return_object = resource_url
            httpcode = 200
        except Exception as err:
            httpcode = 500
            raise err

    except Exception as err:
        return_object = anchore_engine.common.helpers.make_response_error(
            err, in_httpcode=httpcode)

    return return_object, httpcode
Esempio n. 6
0
def action_provider(op_id):
    """
    Lazy lookup of the action associated with the operation id via the request context reference
    to the parent service, which provides the map of ops->actions (via the swagger doc)
    :param op_id:
    :return:
    """
    return ApiRequestContextProxy.get_service().action_for_operation(op_id)
Esempio n. 7
0
def get_image_content(image_digest, content_type):
    httpcode = 500

    try:
        return_object = ApiRequestContextProxy.get_service().get_image_content(
            ApiRequestContextProxy.namespace(), content_type, image_digest)
        httpcode = 200
    except Exception as err:
        logger.exception("Failed to lookup image content")
        return_object = make_response_error(err, in_httpcode=httpcode)
        httpcode = return_object["httpcode"]
    return return_object, httpcode
Esempio n. 8
0
def get_oauth_token(grant_type="password",
                    username=None,
                    password=None,
                    client_id="anonymous"):
    """
    POST /oauth/token

    Requires the resource-owners credentials in the Authorization Header.

    This is a bit of a mix of the ResourceOwnerPasswordGrant flow and the ImplicitGrant flow since
    this function will populate the necessary fields to perform a password grant if the Authorization
    header is set and no content body is provided

    Note: the parameters above are embedded within the connexion request object, but must be specified in the
    method signature in order for connexion to route the request to this method. So it may appear that they are unused,
    but have no fear, they are!

    :return:
    """

    # Short-circuit if no oauth/token configured
    try:
        tok_mgr = token_manager()
        authz = ApiRequestContextProxy.get_service()._oauth_app
    except Exception as e:
        raise AccessDeniedError("Oauth not enabled in configuration",
                                detail={})

    # Add some default properties if not set in the request
    try:
        if request.content_length == 0 or not request.form:
            logger.debug(
                "Handling converting empty body into form-based grant request")

            if not request.data and not request.form:
                setattr(
                    request,
                    "form",
                    ImmutableMultiDict([
                        ("username", request.authorization.username),
                        ("password", request.authorization.password),
                        ("grant_type", "password"),
                        ("client_id", "anonymous"),
                    ]),
                )

        resp = authz.create_token_response()
        logger.debug("Token resp: {}".format(resp))
        return resp
    except:
        logger.debug_exception("Error authenticating")
        raise
def ingress_image(ingress_request):
    """
    :param ingress_request json object specifying the identity of the image to sync
    :return: status result for image load
    """

    req = ImageIngressRequest.from_json(ingress_request)
    if not req.user_id:
        raise ValueError("user_id")
    if not req.image_id:
        raise ValueError("image_id")

    try:
        # Try this synchronously for now to see how slow it really is
        conn_timeout = ApiRequestContextProxy.get_service().configuration.get(
            "catalog_client_conn_timeout", DEFAULT_CACHE_CONN_TIMEOUT
        )
        read_timeout = ApiRequestContextProxy.get_service().configuration.get(
            "catalog_client_read_timeout", DEFAULT_CACHE_READ_TIMEOUT
        )
        t = ImageLoadTask(
            req.user_id,
            req.image_id,
            url=req.fetch_url,
            content_conn_timeout=conn_timeout,
            content_read_timeout=read_timeout,
        )
        result = t.execute()
        resp = ImageIngressResponse()
        if not result:
            resp.status = "loaded"
        else:
            # We're doing a sync call above, so just send loaded. It should be 'accepted' once async works.
            resp.status = "loaded"
        resp.problems = list()
        return resp.to_json(), 200
    except Exception as e:
        log.exception("Error loading image into policy engine")
        return make_response_error(e, in_httpcode=500), 500
Esempio n. 10
0
def create_object_in_storage(bucket: str,
                             archiveid: str,
                             bodycontent: str,
                             is_json: bool = False):
    """
    Creates an object in storeage with the object storage manager.
    Takes param to determine if it should be stored as json or not.
    Used by two endpoints so that a single endpoint does not return multiple different content types
    """
    http_code = 500
    try:
        account_name = ApiRequestContextProxy.namespace()
        obj_mgr = anchore_engine.subsys.object_store.manager.get_manager()

        if is_json:
            bodycontent = anchore_utils.ensure_bytes(json.dumps(bodycontent))

        obj_mgr.put(account_name, bucket, archiveid, bodycontent)
    except Exception as err:
        return_object = anchore_engine.common.helpers.make_response_error(
            err, in_httpcode=http_code)
        return return_object, http_code

    my_svc = ApiRequestContextProxy.get_service()
    if my_svc is None:
        resource_url = "N/A"
    else:
        try:
            path_parts = [
                my_svc.service_record["base_url"],
                my_svc.service_record["version"],
                "archive",
                bucket,
                archiveid,
            ]
        except KeyError as err:
            return_object = anchore_engine.common.helpers.make_response_error(
                err, in_httpcode=http_code)
            return return_object, http_code
        resource_url = "/".join(path_parts)
    return_object = resource_url
    http_code = 200
    return return_object, http_code
Esempio n. 11
0
def get_oauth_token():
    """
    POST /oauth/token

    Requires the resource-owners credentials in the Authorization Header.

    This is a bit of a mix of the ResourceOwnerPasswordGrant flow and the ImplicitGrant flow since
    this function will populate the necessary fields to perform a password grant if the Authorization
    header is set and no content body is provided

    :return:
    """

    # Short-circuit if no oauth/token configured
    try:
        tok_mgr = token_manager()
        authz = ApiRequestContextProxy.get_service()._oauth_app
    except Exception as e:
        raise AccessDeniedError('Oauth not enabled in configuration',
                                detail={})

    # Add some default properties if not set in the request
    try:
        if request.content_length == 0 or not request.form:
            logger.debug(
                'Handling converting empty body into form-based grant request')

            if not request.data and not request.form:
                setattr(
                    request, 'form',
                    ImmutableMultiDict([
                        ('username', request.authorization.username),
                        ('password', request.authorization.password),
                        ('grant_type', 'password'), ('client_id', 'anonymous')
                    ]))

        resp = authz.create_token_response()
        logger.debug('Token resp: {}'.format(resp))
        return resp
    except:
        logger.debug_exception('Error authenticating')
        raise
def check_user_image_inline(user_id, image_id, tag, bundle):
    """
    Execute a policy evaluation using the info in the request body including the bundle content

    :param user_id:
    :param image_id:
    :param tag:
    :param bundle:
    :return:
    """

    timer = time.time()
    db = get_session()
    cache_mgr = None

    try:
        # Input validation
        if tag is None:
            # set tag value to a value that only matches wildcards
            tag = "*/*:*"

        try:
            img_obj = db.query(Image).get((image_id, user_id))
        except:
            return make_response_error("Image not found", in_httpcode=404), 404

        if not img_obj:
            log.info(
                "Request for evaluation of image that cannot be found: user_id = {}, image_id = {}"
                .format(user_id, image_id))
            return make_response_error("Image not found", in_httpcode=404), 404

        if evaluation_cache_enabled:
            timer2 = time.time()
            try:
                try:
                    conn_timeout = (
                        ApiRequestContextProxy.get_service().configuration.get(
                            "catalog_client_conn_timeout",
                            DEFAULT_CACHE_CONN_TIMEOUT))
                    read_timeout = (
                        ApiRequestContextProxy.get_service().configuration.get(
                            "catalog_client_read_timeout",
                            DEFAULT_CACHE_READ_TIMEOUT))
                    cache_mgr = EvaluationCacheManager(img_obj, tag, bundle,
                                                       conn_timeout,
                                                       read_timeout)
                except ValueError as err:
                    log.warn(
                        "Could not leverage cache due to error in bundle data: {}"
                        .format(err))
                    cache_mgr = None

                if cache_mgr is None:
                    log.info(
                        "Could not initialize cache manager for policy evaluation, skipping cache usage"
                    )
                else:
                    cached_result = cache_mgr.refresh()
                    if cached_result:
                        metrics.counter_inc(
                            name="anchore_policy_evaluation_cache_hits")
                        metrics.histogram_observe(
                            "anchore_policy_evaluation_cache_access_latency",
                            time.time() - timer2,
                            status="hit",
                        )
                        log.info(
                            "Returning cached result of policy evaluation for {}/{}, with tag {} and bundle {} with digest {}. Last evaluation: {}"
                            .format(
                                user_id,
                                image_id,
                                tag,
                                cache_mgr.bundle_id,
                                cache_mgr.bundle_digest,
                                cached_result.get("last_modified"),
                            ))
                        return cached_result
                    else:
                        metrics.counter_inc(
                            name="anchore_policy_evaluation_cache_misses")
                        metrics.histogram_observe(
                            "anchore_policy_evaluation_cache_access_latency",
                            time.time() - timer2,
                            status="miss",
                        )
                        log.info(
                            "Policy evaluation not cached, or invalid, executing evaluation for {}/{} with tag {} and bundle {} with digest {}"
                            .format(
                                user_id,
                                image_id,
                                tag,
                                cache_mgr.bundle_id,
                                cache_mgr.bundle_digest,
                            ))

            except Exception as ex:
                log.exception(
                    "Unexpected error operating on policy evaluation cache. Skipping use of cache."
                )

        else:
            log.info("Policy evaluation cache disabled. Executing evaluation")

        # Build bundle exec.
        problems = []
        executable_bundle = None
        try:
            # Allow deprecated gates here to support upgrade cases from old policy bundles.
            executable_bundle = build_bundle(bundle,
                                             for_tag=tag,
                                             allow_deprecated=True)
            if executable_bundle.init_errors:
                problems = executable_bundle.init_errors
        except InitializationError as e:
            log.exception(
                "Bundle construction and initialization returned errors")
            problems = e.causes

        eval_result = None
        if not problems:
            # Execute bundle
            try:
                eval_result = executable_bundle.execute(
                    img_obj, tag,
                    ExecutionContext(db_session=db, configuration={}))
            except Exception as e:
                log.exception(
                    "Error executing policy bundle {} against image {} w/tag {}: {}"
                    .format(bundle["id"], image_id, tag, e))
                return (
                    make_response_error(
                        "Internal bundle evaluation error",
                        details={
                            "message":
                            "Cannot execute given policy against the image due to errors executing the policy bundle: {}"
                            .format(e)
                        },
                        in_httpcode=500,
                    ),
                    500,
                )
        else:
            # Construct a failure eval with details on the errors and mappings to send to client
            eval_result = build_empty_error_execution(img_obj,
                                                      tag,
                                                      executable_bundle,
                                                      errors=problems,
                                                      warnings=[])
            if (executable_bundle and executable_bundle.mapping
                    and len(executable_bundle.mapping.mapping_rules) == 1):
                eval_result.executed_mapping = executable_bundle.mapping.mapping_rules[
                    0]

        resp = PolicyEvaluation()
        resp.user_id = user_id
        resp.image_id = image_id
        resp.tag = tag
        resp.bundle = bundle
        resp.matched_mapping_rule = (eval_result.executed_mapping.json() if
                                     eval_result.executed_mapping else False)
        resp.last_modified = int(time.time())
        resp.final_action = eval_result.bundle_decision.final_decision.name
        resp.final_action_reason = eval_result.bundle_decision.reason
        resp.matched_whitelisted_images_rule = (
            eval_result.bundle_decision.whitelisted_image.json()
            if eval_result.bundle_decision.whitelisted_image else False)
        resp.matched_blacklisted_images_rule = (
            eval_result.bundle_decision.blacklisted_image.json()
            if eval_result.bundle_decision.blacklisted_image else False)
        resp.result = eval_result.as_table_json()
        resp.created_at = int(time.time())
        resp.evaluation_problems = [
            problem_from_exception(i) for i in eval_result.errors
        ]
        resp.evaluation_problems += [
            problem_from_exception(i) for i in eval_result.warnings
        ]
        if resp.evaluation_problems:
            for i in resp.evaluation_problems:
                log.warn(
                    "Returning evaluation response for image {}/{} w/tag {} and bundle {} that contains error: {}"
                    .format(user_id, image_id, tag, bundle["id"],
                            json.dumps(i.to_json())))
            metrics.histogram_observe(
                "anchore_policy_evaluation_time_seconds",
                time.time() - timer,
                status="fail",
            )
        else:
            metrics.histogram_observe(
                "anchore_policy_evaluation_time_seconds",
                time.time() - timer,
                status="success",
            )

        result = resp.to_json()

        # Never let the cache block returning results
        try:
            if evaluation_cache_enabled and cache_mgr is not None:
                cache_mgr.save(result)
        except Exception as ex:
            log.exception(
                "Failed saving policy result in cache. Skipping and continuing."
            )

        db.commit()

        return result

    except HTTPException as e:
        db.rollback()
        log.exception("Caught exception in execution: {}".format(e))
        raise
    except Exception as e:
        db.rollback()
        log.exception("Failed processing bundle evaluation: {}".format(e))
        return (
            make_response_error(
                "Unexpected internal error",
                details={"message": str(e)},
                in_httpcode=500,
            ),
            500,
        )
    finally:
        db.close()
Esempio n. 13
0
def version_check():
    """
    :return:
    """
    return version_response(ApiRequestContextProxy.get_service().versions), 200