def get_release_history(snap_name):
    try:
        release_history = publisher_api.snap_release_history(
            flask.session, snap_name)
    except StoreApiResponseErrorList as api_response_error_list:
        return _handle_error_list(api_response_error_list.errors)
    except (StoreApiError, ApiError) as api_error:
        return _handle_error(api_error)

    try:
        channel_map = publisher_api.snap_channel_map(flask.session, snap_name)
    except StoreApiResponseErrorList as api_response_error_list:
        return _handle_error_list(api_response_error_list.errors)
    except (StoreApiError, ApiError) as api_error:
        return _handle_error(api_error)

    snap = channel_map.get("snap", {})

    context = {
        "snap_name": snap_name,
        "snap_title": snap.get("title"),
        "publisher_name": snap.get("publisher", {}).get("display-name", {}),
        "release_history": release_history,
        "private": snap.get("private"),
        "default_track": snap.get("default-track"),
        "channel_map": channel_map.get("channel-map"),
        "tracks": snap.get("tracks"),
    }

    return flask.render_template("publisher/release-history.html", **context)
Esempio n. 2
0
def get_update_gh_webhooks(snap_name):
    try:
        details = publisher_api.get_snap_info(snap_name, flask.session)
    except StoreApiResponseErrorList as api_response_error_list:
        if api_response_error_list.status_code == 404:
            return flask.abort(404, "No snap named {}".format(snap_name))
        else:
            return _handle_error_list(api_response_error_list.errors)
    except (StoreApiError, ApiError) as api_error:
        return _handle_error(api_error)

    lp_snap = launchpad.get_snap_by_store_name(details["snap_name"])

    if not lp_snap:
        flask.flash(
            "This snap is not linked with a GitHub repository", "negative"
        )

        return flask.redirect(
            flask.url_for(".get_settings", snap_name=snap_name)
        )

    github = GitHub(flask.session.get("github_auth_secret"))

    try:
        github.get_user()
    except Unauthorized:
        return flask.redirect(f"/github/auth?back={flask.request.path}")

    gh_link = lp_snap["git_repository_url"][19:]
    gh_owner, gh_repo = gh_link.split("/")

    # Remove old BSI webhook if present
    old_url = f"https://build.snapcraft.io/{gh_owner}/{gh_repo}/webhook/notify"
    old_hook = github.get_hook_by_url(gh_owner, gh_repo, old_url)

    if old_hook:
        github.remove_hook(
            gh_owner,
            gh_repo,
            old_hook["id"],
        )

    # Remove current hook
    github_hook_url = f"{GITHUB_WEBHOOK_HOST_URL}{snap_name}/webhook/notify"
    snapcraft_hook = github.get_hook_by_url(gh_owner, gh_repo, github_hook_url)

    if snapcraft_hook:
        github.remove_hook(
            gh_owner,
            gh_repo,
            snapcraft_hook["id"],
        )

    # Create webhook in the repo
    github.create_hook(gh_owner, gh_repo, github_hook_url)

    flask.flash("The webhook has been created successfully", "positive")

    return flask.redirect(flask.url_for(".get_settings", snap_name=snap_name))
Esempio n. 3
0
def post_update_gh_webhooks(snap_name):
    try:
        details = api.get_snap_info(snap_name, flask.session)
    except ApiResponseErrorList as api_response_error_list:
        if api_response_error_list.status_code == 404:
            return flask.abort(404, "No snap named {}".format(snap_name))
        else:
            return _handle_error_list(api_response_error_list.errors)
    except ApiError as api_error:
        return _handle_error(api_error)

    lp_snap = launchpad.get_snap_by_store_name(details["snap_name"])
    gh_link = lp_snap["git_repository_url"][19:]
    gh_owner, gh_repo = gh_link.split("/")

    github = GitHub(flask.session.get("github_auth_secret"))
    old_url = f"https://build.snapcraft.io/{gh_owner}/{gh_repo}/webhook/notify"
    old_hook = github.get_hook_by_url(gh_owner, gh_repo, old_url)

    if old_hook:
        github.update_hook_url(
            gh_owner,
            gh_repo,
            old_hook["id"],
            f"https://snapcraft.io/{snap_name}/webhook/notify",
        )

    return flask.redirect(
        flask.url_for(".get_snap_builds", snap_name=snap_name))
Esempio n. 4
0
def login_callback():
    code = flask.request.args["code"]
    state = flask.request.args["state"]

    # Avoid CSRF attacks
    validate_csrf(state)

    discharged_token = candid.discharge_token(code)
    candid_macaroon = candid.discharge_macaroon(
        flask.session["publisher-macaroon"], discharged_token)

    # Store bakery authentication
    flask.session["macaroons"] = candid.get_serialized_bakery_macaroon(
        flask.session["publisher-macaroon"], candid_macaroon)

    try:
        publisher = publisher_api.whoami(flask.session)
    except StoreApiResponseErrorList as api_response_error_list:
        return _handle_error_list(api_response_error_list.errors)
    except (StoreApiError, ApiError) as api_error:
        return _handle_error(api_error)

    # whoami is the only Dashboard API endpoint
    # that support Candid.
    return publisher
def get_publicise_badges(snap_name):
    try:
        snap_details = publisher_api.get_snap_info(snap_name, flask.session)
    except StoreApiResponseErrorList as api_response_error_list:
        if api_response_error_list.status_code == 404:
            return flask.abort(404, "No snap named {}".format(snap_name))
        else:
            return _handle_error_list(api_response_error_list.errors)
    except (StoreApiError, ApiError) as api_error:
        return _handle_error(api_error)

    if snap_details["private"]:
        return flask.abort(404, "No snap named {}".format(snap_name))

    try:
        snap_public_details = store_api.get_item_details(snap_name,
                                                         api_version=2)
    except (StoreApiError, ApiError) as api_error:
        return _handle_error(api_error)

    context = {
        "snap_name": snap_details["snap_name"],
        "snap_title": snap_details["title"],
        "publisher_name": snap_details["publisher"]["display-name"],
        "snap_id": snap_details["snap_id"],
        "trending": snap_public_details["snap"]["trending"],
    }

    return flask.render_template("publisher/publicise/github_badges.html",
                                 **context)
def get_publicise_cards(snap_name):
    try:
        snap_details = publisher_api.get_snap_info(snap_name, flask.session)
    except StoreApiResponseErrorList as api_response_error_list:
        if api_response_error_list.status_code == 404:
            return flask.abort(404, "No snap named {}".format(snap_name))
        else:
            return _handle_error_list(api_response_error_list.errors)
    except (StoreApiError, ApiError) as api_error:
        return _handle_error(api_error)

    if snap_details["private"]:
        return flask.abort(404, "No snap named {}".format(snap_name))

    screenshots = filter_screenshots(snap_details["media"])
    has_screenshot = True if screenshots else False

    context = {
        "has_screenshot": has_screenshot,
        "snap_name": snap_details["snap_name"],
        "snap_title": snap_details["title"],
        "publisher_name": snap_details["publisher"]["display-name"],
        "snap_id": snap_details["snap_id"],
    }

    return flask.render_template("publisher/publicise/embedded_cards.html",
                                 **context)
Esempio n. 7
0
def post_build(snap_name):
    try:
        details = api.get_snap_info(snap_name, flask.session)
    except ApiResponseErrorList as api_response_error_list:
        if api_response_error_list.status_code == 404:
            return flask.abort(404, "No snap named {}".format(snap_name))
        else:
            return _handle_error_list(api_response_error_list.errors)
    except ApiError as api_error:
        return _handle_error(api_error)

    try:
        if launchpad.is_snap_building(details["snap_name"]):
            launchpad.cancel_snap_builds(details["snap_name"])

        launchpad.build_snap(details["snap_name"])
    except HTTPError as e:
        # Timeout or not found from Launchpad
        if e.response.status_code in [408, 404]:
            flask.flash("An error occurred, please try again.", "negative")
            return flask.redirect(
                flask.url_for(".get_snap_builds", snap_name=snap_name))
        raise e

    flask.flash("Build triggered", "positive")

    return flask.redirect(
        flask.url_for(".get_snap_builds", snap_name=snap_name))
Esempio n. 8
0
def get_store_snaps(store_id):
    try:
        snaps = admin_api.get_store_snaps(flask.session, store_id)
    except StoreApiResponseErrorList as api_response_error_list:
        return _handle_error_list(api_response_error_list.errors)
    except (StoreApiError, ApiError) as api_error:
        return _handle_error(api_error)

    return jsonify(snaps)
Esempio n. 9
0
def get_settings(snap_name):
    try:
        snap_details = publisher_api.get_snap_info(snap_name, flask.session)
    except StoreApiResponseErrorList as api_response_error_list:
        if api_response_error_list.status_code == 404:
            return flask.abort(404, "No snap named {}".format(snap_name))
        else:
            return _handle_error_list(api_response_error_list.errors)
    except (StoreApiError, ApiError) as api_error:
        return _handle_error(api_error)

    if "whitelist_country_codes" in snap_details:
        whitelist_country_codes = (
            snap_details["whitelist_country_codes"]
            if len(snap_details["whitelist_country_codes"]) > 0
            else []
        )
    else:
        whitelist_country_codes = []

    if "blacklist_country_codes" in snap_details:
        blacklist_country_codes = (
            snap_details["blacklist_country_codes"]
            if len(snap_details["blacklist_country_codes"]) > 0
            else []
        )
    else:
        blacklist_country_codes = []

    countries = []
    for country in pycountry.countries:
        countries.append({"key": country.alpha_2, "name": country.name})

    is_on_lp = False
    lp_snap = launchpad.get_snap_by_store_name(snap_name)
    if lp_snap:
        is_on_lp = True

    context = {
        "snap_name": snap_details["snap_name"],
        "snap_title": snap_details["title"],
        "snap_id": snap_details["snap_id"],
        "publisher_name": snap_details["publisher"]["display-name"],
        "license": license,
        "private": snap_details["private"],
        "unlisted": snap_details["unlisted"],
        "countries": countries,
        "whitelist_country_codes": whitelist_country_codes,
        "blacklist_country_codes": blacklist_country_codes,
        "price": snap_details["price"],
        "store": snap_details["store"],
        "keywords": snap_details["keywords"],
        "status": snap_details["status"],
        "is_on_lp": is_on_lp,
    }

    return flask.render_template("publisher/settings.html", **context)
Esempio n. 10
0
def login_callback():
    code = flask.request.args["code"]
    state = flask.request.args["state"]

    flask.session.pop("macaroon_root", None)
    flask.session.pop("macaroon_discharge", None)

    # Avoid CSRF attacks
    validate_csrf(state)

    discharged_token = candid.discharge_token(code)
    candid_macaroon = candid.discharge_macaroon(
        flask.session["publisher-macaroon"], discharged_token)

    # Store bakery authentication
    flask.session["macaroons"] = candid.get_serialized_bakery_macaroon(
        flask.session["publisher-macaroon"], candid_macaroon)

    try:
        publisher = publisher_api.whoami(flask.session)
        account = publisher_api.get_account(flask.session)
    except StoreApiResponseErrorList as api_response_error_list:
        return _handle_error_list(api_response_error_list.errors)
    except (StoreApiError, ApiError) as api_error:
        return _handle_error(api_error)

    flask.session["publisher"] = {
        "account_id": publisher["account"]["id"],
        "nickname": publisher["account"]["username"],
        "fullname": publisher["account"]["name"],
        "image": None,
        "email": publisher["account"]["email"],
    }

    flask.session["publisher"]["stores"] = logic.get_stores(
        account["stores"], roles=["admin", "review", "view"])

    response = flask.make_response(
        flask.redirect(
            flask.session.pop(
                "next_url",
                flask.url_for("publisher_snaps.get_account_snaps"),
            ),
            302,
        ), )

    # Set cookie to know where to redirect users for re-auth
    response.set_cookie(
        "last_login_method",
        "candid",
        expires=datetime.datetime.now() + datetime.timedelta(days=365),
    )

    return response
Esempio n. 11
0
def post_preview(snap_name):
    try:
        snap_details = publisher_api.get_snap_info(snap_name, flask.session)
    except StoreApiResponseErrorList as api_response_error_list:
        if api_response_error_list.status_code == 404:
            return flask.abort(404, "No snap named {}".format(snap_name))
        else:
            return _handle_error_list(api_response_error_list.errors)
    except (StoreApiError, ApiError) as api_error:
        return _handle_error(api_error)

    context = {
        "publisher": snap_details["publisher"]["display-name"],
        "username": snap_details["publisher"]["username"],
        "developer_validation": snap_details["publisher"]["validation"],
    }

    state = loads(flask.request.form["state"])

    for item in state:
        if item == "description":
            context[item] = parse_markdown_description(
                bleach.clean(state[item], tags=[]))
        else:
            context[item] = state[item]

    context["is_preview"] = True
    context["package_name"] = context["snap_name"]
    context["snap_title"] = context["title"]
    context["appliances"] = []

    # Images
    icons = get_icon(context["images"])
    context["screenshots"] = filter_screenshots(context["images"])
    context["icon_url"] = icons[0] if icons else None
    context["video"] = get_video(context["images"])

    # Channel map
    context["channel_map"] = []
    context["default_track"] = "latest"
    context["lowest_risk_available"] = "stable"
    context["version"] = "test"
    context["has_stable"] = True

    # metadata
    context["last_updated"] = "Preview"
    context["filesize"] = "1mb"

    # maps
    context["countries"] = preview_data.get_countries()
    context["normalized_os"] = preview_data.get_normalised_oses()

    return flask.render_template("store/snap-details.html", **context)
Esempio n. 12
0
def get_stores():
    """
    In this view we get all the stores the user is an admin or we show a 403
    """
    try:
        stores = admin_api.get_stores(flask.session)
    except StoreApiResponseErrorList as api_response_error_list:
        return _handle_error_list(api_response_error_list.errors)
    except (StoreApiError, ApiError) as api_error:
        return _handle_error(api_error)

    return jsonify(stores)
Esempio n. 13
0
def get_settings(store_id):
    try:
        stores = admin_api.get_stores(flask.session)
        store = admin_api.get_store(flask.session, store_id)
    except StoreApiResponseErrorList as api_response_error_list:
        return _handle_error_list(api_response_error_list.errors)
    except (StoreApiError, ApiError) as api_error:
        return _handle_error(api_error)

    return flask.render_template("admin/settings.html",
                                 stores=stores,
                                 store=store)
Esempio n. 14
0
def get_snap_builds(snap_name):
    try:
        details = api.get_snap_info(snap_name, flask.session)

        # API call to make users without needed permissions refresh the session
        # Users needs package_upload_request permission to use this feature
        api.get_package_upload_macaroon(session=flask.session,
                                        snap_name=snap_name,
                                        channels=["edge"])
    except ApiResponseErrorList as api_response_error_list:
        if api_response_error_list.status_code == 404:
            return flask.abort(404, "No snap named {}".format(snap_name))
        else:
            return _handle_error_list(api_response_error_list.errors)
    except ApiError as api_error:
        return _handle_error(api_error)

    context = {
        "snap_id": details["snap_id"],
        "snap_name": details["snap_name"],
        "snap_title": details["title"],
        "snap_builds_enabled": False,
        "snap_builds": [],
        "total_builds": 0,
    }

    # Get built snap in launchpad with this store name
    github = GitHub(flask.session.get("github_auth_secret"))
    lp_snap = launchpad.get_snap_by_store_name(details["snap_name"])

    if lp_snap:
        # Git repository without GitHub hostname
        context["github_repository"] = lp_snap["git_repository_url"][19:]
        github_owner, github_repo = context["github_repository"].split("/")

        context["yaml_file_exists"] = github.get_snapcraft_yaml_location(
            github_owner, github_repo)

        if not context["yaml_file_exists"]:
            flask.flash("This repository doesn't contain a snapcraft.yaml",
                        "negative")
        context = get_builds(lp_snap, context, slice(0, BUILDS_PER_PAGE))
    else:
        try:
            context["github_user"] = github.get_user()
        except Unauthorized:
            context["github_user"] = None

        if context["github_user"]:
            context["github_orgs"] = github.get_orgs()

    return flask.render_template("publisher/builds.html", **context)
Esempio n. 15
0
def snap_count():
    try:
        account_info = publisher_api.get_account(flask.session)
    except StoreApiResponseErrorList as api_response_error_list:
        return _handle_error_list(api_response_error_list.errors)
    except (StoreApiError, ApiError) as api_error:
        return _handle_error(api_error)

    user_snaps, registered_snaps = logic.get_snaps_account_info(account_info)

    context = {"count": len(user_snaps), "snaps": list(user_snaps.keys())}

    return flask.jsonify(context)
Esempio n. 16
0
def get_manage_members(store_id):
    try:
        members = admin_api.get_store_members(flask.session, store_id)

        for item in members:
            if item["email"] == flask.session["publisher"]["email"]:
                item["current_user"] = True

    except StoreApiResponseErrorList as api_response_error_list:
        return _handle_error_list(api_response_error_list.errors)
    except (StoreApiError, ApiError) as api_error:
        return _handle_error(api_error)

    return jsonify(members)
Esempio n. 17
0
def get_snaps_search(store_id):
    try:
        snaps = admin_api.get_store_snaps(
            flask.session,
            store_id,
            flask.request.args.get("q"),
            flask.request.args.get("allowed_for_inclusion"),
        )
    except StoreApiResponseErrorList as api_response_error_list:
        return _handle_error_list(api_response_error_list.errors)
    except (StoreApiError, ApiError) as api_error:
        return _handle_error(api_error)

    return jsonify(snaps)
Esempio n. 18
0
def get_stores():
    """
    In this view we get all the stores the user is an admin or we show a 403
    """
    try:
        stores = admin_api.get_stores(flask.session)
    except StoreApiResponseErrorList as api_response_error_list:
        return _handle_error_list(api_response_error_list.errors)
    except (StoreApiError, ApiError) as api_error:
        return _handle_error(api_error)

    if not stores:
        flask.abort(403)

    # We redirect to the first store snap list
    return flask.redirect(
        flask.url_for(".get_settings", store_id=stores[0]["id"]))
Esempio n. 19
0
def get_manage_members(store_id):
    try:
        stores = admin_api.get_stores(flask.session)
        store = admin_api.get_store(flask.session, store_id)
        members = admin_api.get_store_members(flask.session, store_id)
        invites = admin_api.get_store_invites(flask.session, store_id)
    except StoreApiResponseErrorList as api_response_error_list:
        return _handle_error_list(api_response_error_list.errors)
    except (StoreApiError, ApiError) as api_error:
        return _handle_error(api_error)

    return flask.render_template(
        "admin/manage_members.html",
        stores=stores,
        store=store,
        members=members,
        invites=invites,
    )
Esempio n. 20
0
def get_invites(store_id):
    try:
        stores = admin_api.get_stores(flask.session)
        store = admin_api.get_store(flask.session, store_id)
        invites = admin_api.get_store_invites(flask.session, store_id)
    except StoreApiResponseErrorList as api_response_error_list:
        return _handle_error_list(api_response_error_list.errors)
    except (StoreApiError, ApiError) as api_error:
        return _handle_error(api_error)

    pending_invites = []
    expired_invites = []
    revoked_invites = []

    for invite in invites:
        if invite["status"] == "Pending":
            pending_invites.append(invite)

        if invite["status"] == "Expired":
            expired_invites.append(invite)

        if invite["status"] == "Revoked":
            revoked_invites.append(invite)

    sorted_pending_invites = sorted(
        pending_invites, key=lambda item: item["expiration-date"]
    )

    sorted_expired_invites = sorted(
        expired_invites, key=lambda item: item["expiration-date"]
    )

    sorted_revoked_invites = sorted(
        revoked_invites, key=lambda item: item["expiration-date"]
    )

    return flask.render_template(
        "admin/invites.html",
        stores=stores,
        store=store,
        pending_invites=sorted_pending_invites,
        expired_invites=sorted_expired_invites,
        revoked_invites=sorted_revoked_invites,
    )
Esempio n. 21
0
def get_account_snaps():
    try:
        account_info = publisher_api.get_account(flask.session)
    except StoreApiResponseErrorList as api_response_error_list:
        return _handle_error_list(api_response_error_list.errors)
    except (StoreApiError, ApiError) as api_error:
        return _handle_error(api_error)

    user_snaps, registered_snaps = logic.get_snaps_account_info(account_info)

    flask_user = flask.session["publisher"]

    context = {
        "snaps": user_snaps,
        "current_user": flask_user["nickname"],
        "registered_snaps": registered_snaps,
    }

    return flask.render_template("publisher/account-snaps.html", **context)
Esempio n. 22
0
def get_snap_build(snap_name, build_id):
    try:
        details = publisher_api.get_snap_info(snap_name, flask.session)
    except StoreApiResponseErrorList as api_response_error_list:
        if api_response_error_list.status_code == 404:
            return flask.abort(404, "No snap named {}".format(snap_name))
        else:
            return _handle_error_list(api_response_error_list.errors)
    except (StoreApiError, ApiError) as api_error:
        return _handle_error(api_error)

    context = {
        "snap_id": details["snap_id"],
        "snap_name": details["snap_name"],
        "snap_title": details["title"],
        "snap_build": {},
    }

    # Get build by snap name and build_id
    lp_build = launchpad.get_snap_build(details["snap_name"], build_id)

    if lp_build:
        status = map_build_and_upload_states(
            lp_build["buildstate"], lp_build["store_upload_status"]
        )
        context["snap_build"] = {
            "id": lp_build["self_link"].split("/")[-1],
            "arch_tag": lp_build["arch_tag"],
            "datebuilt": lp_build["datebuilt"],
            "duration": lp_build["duration"],
            "logs": lp_build["build_log_url"],
            "revision_id": lp_build["revision_id"],
            "status": status,
            "title": lp_build["title"],
        }

        if context["snap_build"]["logs"]:
            context["raw_logs"] = launchpad.get_snap_build_log(
                details["snap_name"], build_id
            )

    return flask.render_template("publisher/build.html", **context)
Esempio n. 23
0
def post_build(snap_name):
    try:
        details = api.get_snap_info(snap_name, flask.session)
    except ApiResponseErrorList as api_response_error_list:
        if api_response_error_list.status_code == 404:
            return flask.abort(404, "No snap named {}".format(snap_name))
        else:
            return _handle_error_list(api_response_error_list.errors)
    except ApiError as api_error:
        return _handle_error(api_error)

    if launchpad.is_snap_building(details["snap_name"]):
        launchpad.cancel_snap_builds(details["snap_name"])

    launchpad.build_snap(details["snap_name"])

    flask.flash("Build triggered", "positive")

    return flask.redirect(
        flask.url_for(".get_snap_builds", snap_name=snap_name))
Esempio n. 24
0
def get_validate_repo(snap_name):
    try:
        details = api.get_snap_info(snap_name, flask.session)
    except ApiResponseErrorList as api_response_error_list:
        if api_response_error_list.status_code == 404:
            return flask.abort(404, "No snap named {}".format(snap_name))
        else:
            return _handle_error_list(api_response_error_list.errors)
    except ApiError as api_error:
        return _handle_error(api_error)

    owner, repo = flask.request.args.get("repo").split("/")

    return flask.jsonify(
        validate_repo(
            flask.session.get("github_auth_secret"),
            details["snap_name"],
            owner,
            repo,
        ))
Esempio n. 25
0
def post_register_name_dispute():
    try:
        snap_name = flask.request.form.get("snap-name", "")
        claim_comment = flask.request.form.get("claim-comment", "")
        api.post_register_name_dispute(flask.session, bleach.clean(snap_name),
                                       bleach.clean(claim_comment))
    except ApiResponseErrorList as api_response_error_list:
        if api_response_error_list.status_code in [400, 409]:
            return flask.render_template(
                "publisher/register-name-dispute.html",
                snap_name=snap_name,
                errors=api_response_error_list.errors,
            )
        else:
            return _handle_error_list(api_response_error_list.errors)
    except ApiError as api_error:
        return _handle_error(api_error)

    return flask.render_template(
        "publisher/register-name-dispute-success.html", snap_name=snap_name)
Esempio n. 26
0
def login_callback():
    code = flask.request.args["code"]
    state = flask.request.args["state"]

    flask.session.pop("macaroon_root", None)
    flask.session.pop("macaroon_discharge", None)

    # Avoid CSRF attacks
    validate_csrf(state)

    discharged_token = candid.discharge_token(code)
    candid_macaroon = candid.discharge_macaroon(
        flask.session["publisher-macaroon"], discharged_token)

    # Store bakery authentication
    flask.session["macaroons"] = candid.get_serialized_bakery_macaroon(
        flask.session["publisher-macaroon"], candid_macaroon)

    try:
        publisher = publisher_api.whoami(flask.session)
    except StoreApiResponseErrorList as api_response_error_list:
        return _handle_error_list(api_response_error_list.errors)
    except (StoreApiError, ApiError) as api_error:
        return _handle_error(api_error)

    flask.session["publisher"] = {
        "account_id": publisher["account"]["id"],
        "nickname": publisher["account"]["username"],
        "fullname": publisher["account"]["name"],
        "image": None,
        "email": publisher["account"]["email"],
    }

    return flask.redirect(
        flask.session.pop(
            "next_url",
            flask.url_for("publisher_snaps.get_account_snaps"),
        ),
        302,
    )
Esempio n. 27
0
def post_disconnect_repo(snap_name):
    try:
        details = publisher_api.get_snap_info(snap_name, flask.session)
    except StoreApiResponseErrorList as api_response_error_list:
        if api_response_error_list.status_code == 404:
            return flask.abort(404, "No snap named {}".format(snap_name))
        else:
            return _handle_error_list(api_response_error_list.errors)
    except StoreApiError as api_error:
        return _handle_error(api_error)

    lp_snap = launchpad.get_snap_by_store_name(snap_name)
    launchpad.delete_snap(details["snap_name"])

    # Try to remove the GitHub webhook if possible
    if flask.session.get("github_auth_secret"):
        github = GitHub(flask.session.get("github_auth_secret"))

        try:
            gh_owner, gh_repo = lp_snap["git_repository_url"][19:].split("/")

            old_hook = github.get_hook_by_url(
                gh_owner,
                gh_repo,
                f"{GITHUB_WEBHOOK_HOST_URL}{snap_name}/webhook/notify",
            )

            if old_hook:
                github.remove_hook(
                    gh_owner,
                    gh_repo,
                    old_hook["id"],
                )
        except HTTPError:
            pass

    return flask.redirect(
        flask.url_for(".get_snap_builds", snap_name=snap_name)
    )
Esempio n. 28
0
def get_snap_builds_json(snap_name):
    try:
        details = api.get_snap_info(snap_name, flask.session)
    except ApiResponseErrorList as api_response_error_list:
        if api_response_error_list.status_code == 404:
            return flask.abort(404, "No snap named {}".format(snap_name))
        else:
            return _handle_error_list(api_response_error_list.errors)
    except ApiError as api_error:
        return _handle_error(api_error)

    context = {"snap_builds": []}

    start = flask.request.args.get("start", 0, type=int)
    size = flask.request.args.get("size", 15, type=int)
    build_slice = slice(start, start + size)

    # Get built snap in launchpad with this store name
    lp_snap = launchpad.get_snap_by_store_name(details["snap_name"])

    if lp_snap:
        context.update(get_builds(lp_snap, build_slice))

    return flask.jsonify(context)
Esempio n. 29
0
def get_store_snaps(store_id):
    try:
        stores = admin_api.get_stores(flask.session)
        store = admin_api.get_store(flask.session, store_id)
        snaps = admin_api.get_store_snaps(flask.session, store_id)

        # list of all deduped store IDs that are not current store
        other_store_ids = list(dict.fromkeys([d["store"] for d in snaps]))
        other_stores = list(
            filter(lambda id: id != store["id"], other_store_ids))

        # store data for each store ID
        other_stores_data = []
        for other_store_id in other_stores:
            if other_store_id == "ubuntu":
                other_stores_data.append({
                    "id": "ubuntu",
                    "name": "Global store"
                })
            else:
                store_data = admin_api.get_store(flask.session, other_store_id)
                other_stores_data.append(store_data)

    except StoreApiResponseErrorList as api_response_error_list:
        return _handle_error_list(api_response_error_list.errors)
    except (StoreApiError, ApiError) as api_error:
        return _handle_error(api_error)

    return flask.render_template(
        "admin/snaps.html",
        stores=stores,
        store=store,
        store_json=json.dumps(store),
        snaps=json.dumps(snaps),
        other_stores_data=json.dumps(other_stores_data),
    )
Esempio n. 30
0
def post_snap_builds(snap_name):
    try:
        details = api.get_snap_info(snap_name, flask.session)
    except ApiResponseErrorList as api_response_error_list:
        if api_response_error_list.status_code == 404:
            return flask.abort(404, "No snap named {}".format(snap_name))
        else:
            return _handle_error_list(api_response_error_list.errors)
    except ApiError as api_error:
        return _handle_error(api_error)

    # Don't allow changes from Admins that are no contributors
    account_snaps = api.get_account_snaps(flask.session)

    if snap_name not in account_snaps:
        flask.flash("You do not have permissions to modify this Snap",
                    "negative")
        return flask.redirect(
            flask.url_for(".get_snap_builds", snap_name=snap_name))

    redirect_url = flask.url_for(".get_snap_builds", snap_name=snap_name)

    # Get built snap in launchpad with this store name
    github = GitHub(flask.session.get("github_auth_secret"))
    owner, repo = flask.request.form.get("github_repository").split("/")

    if not github.check_permissions_over_repo(owner, repo):
        flask.flash(
            "Your GitHub account doesn't have permissions in the repository",
            "negative",
        )
        return flask.redirect(redirect_url)

    repo_validation = validate_repo(flask.session.get("github_auth_secret"),
                                    snap_name, owner, repo)

    if not repo_validation["success"]:
        flask.flash(repo_validation["error"]["message"], "negative")
        return flask.redirect(redirect_url)

    lp_snap = launchpad.get_snap_by_store_name(details["snap_name"])
    git_url = f"https://github.com/{owner}/{repo}"

    if not lp_snap:
        lp_snap_name = md5(git_url.encode("UTF-8")).hexdigest()

        try:
            repo_exist = launchpad.get_snap(lp_snap_name)
        except HTTPError as e:
            if e.response.status_code == 404:
                repo_exist = False
            else:
                raise e

        if repo_exist:
            # The user registered the repo in BSI but didn't register a name
            # We can remove it and continue with the normal process
            if not repo_exist["store_name"]:
                # This conditional should be removed when issue 2657 is solved
                launchpad._request(path=repo_exist["self_link"][32:],
                                   method="DELETE")
            else:
                flask.flash(
                    "The specified repository is being used by another snap:"
                    f" {repo_exist['store_name']}",
                    "negative",
                )
                return flask.redirect(redirect_url)

        macaroon = api.get_package_upload_macaroon(session=flask.session,
                                                   snap_name=snap_name,
                                                   channels=["edge"
                                                             ])["macaroon"]

        launchpad.create_snap(snap_name, git_url, macaroon)

        flask.flash("The GitHub repository was linked correctly.", "positive")

        # Create webhook in the repo, it should also trigger the first build
        github_hook_url = f"https://snapcraft.io/{snap_name}/webhook/notify"

        try:
            hook = github.get_hook_by_url(owner, repo, github_hook_url)

            # We create the webhook if doesn't exist already in this repo
            if not hook:
                github.create_hook(owner, repo, github_hook_url)
        except HTTPError:
            flask.flash(
                "The GitHub Webhook could not be created. "
                "Please trigger a new build manually.",
                "caution",
            )

    elif lp_snap["git_repository_url"] != git_url:
        # In the future, create a new record, delete the old one
        raise AttributeError(
            f"Snap {snap_name} already has a build repository associated")

    return flask.redirect(redirect_url)