def check_advantage(*args, **kwargs): if "is_maintenance": if strtobool(os.getenv("STORE_MAINTENANCE", "false")): return flask.render_template("advantage/maintenance.html") is_test_backend = flask.request.args.get("test_backend", False) stripe_publishable_key = os.getenv( "STRIPE_LIVE_PUBLISHABLE_KEY", "pk_live_68aXqowUeX574aGsVck8eiIE", ) api_url = flask.current_app.config["CONTRACTS_LIVE_API_URL"] if is_test_backend: stripe_publishable_key = os.getenv( "STRIPE_TEST_PUBLISHABLE_KEY", "pk_test_yndN9H0GcJffPe0W58Nm64cM00riYG4N46", ) api_url = flask.current_app.config["CONTRACTS_TEST_API_URL"] user_token = flask.session.get("authentication_token") guest_token = flask.session.get("guest_authentication_token") kwargs["test_backend"] = is_test_backend kwargs["api_url"] = api_url kwargs["stripe_publishable_key"] = stripe_publishable_key kwargs["token"] = user_token or guest_token kwargs["token_type"] = "Macaroon" if user_token else "Bearer" if "view_need_user" in check_list and not user_info(flask.session): if flask.request.path != "/advantage": return flask.redirect("/advantage") return flask.render_template( "advantage/index-no-login.html", is_test_backend=is_test_backend, ) if "need_user" in check_list: if not user_info(flask.session): return ( flask.jsonify({"error": "authentication required"}), 401, ) if "need_user_or_guest" in check_list: if not user_info(flask.session) and not guest_token: return ( flask.jsonify({"error": "authentication required"}), 401, ) return func(*args, **kwargs)
def decorated_function(*args, **kwargs): # UA under maintenance if strtobool(os.getenv("STORE_MAINTENANCE", "false")): return flask.render_template("advantage/maintenance.html") # if logged in, get rid of guest token if user_info(flask.session): if flask.session.get("guest_authentication_token"): flask.session.pop("guest_authentication_token") test_backend = flask.request.args.get("test_backend", "false") is_test_backend = strtobool(test_backend) user_token = flask.session.get("authentication_token") guest_token = flask.session.get("guest_authentication_token") if permission == "user" and response == "html": if not user_info(flask.session): if flask.request.path != "/advantage": return flask.redirect( "/advantage?test_backend=true" if is_test_backend else "/advantage") return flask.render_template( "advantage/index-no-login.html", is_test_backend=is_test_backend, ) if permission == "user" and response == "json": if not user_info(flask.session): message = {"error": "authentication required"} return flask.jsonify(message), 401 if permission == "user_or_guest" and response == "json": if not user_info(flask.session) and not guest_token: message = {"error": "authentication required"} return flask.jsonify(message), 401 # init API instance g.api = UAContractsAPI( session=session, authentication_token=(user_token or guest_token), token_type=("Macaroon" if user_token else "Bearer"), api_url=get_api_url(is_test_backend), ) if response == "html": g.api.set_is_for_view(True) return func(*args, **kwargs)
def context(): return { "current_year": current_year, "descending_years": descending_years, "format_date": format_date, "get_json_feed": get_json_feed, "modify_query": modify_query, "month_name": month_name, "months_list": months_list, "get_navigation": get_navigation, "get_stripe_publishable_key": os.getenv( "STRIPE_PUBLISHABLE_KEY", "pk_live_68aXqowUeX574aGsVck8eiIE", ), "product": flask.request.args.get("product", ""), "request": flask.request, "releases": releases(), "user_info": user_info(flask.session), "utm_campaign": flask.request.args.get("utm_campaign", ""), "utm_content": flask.request.args.get("utm_content", ""), "utm_medium": flask.request.args.get("utm_medium", ""), "utm_source": flask.request.args.get("utm_source", ""), "CAPTCHA_TESTING_API_KEY": CAPTCHA_TESTING_API_KEY, "http_host": flask.request.host, }
def post_anonymised_customer_info(): user_token = flask.session.get("authentication_token") guest_token = flask.session.get("guest_authentication_token") is_test_backend = flask.request.args.get("test_backend", False) api_url = flask.current_app.config["CONTRACTS_LIVE_API_URL"] if is_test_backend: api_url = flask.current_app.config["CONTRACTS_TEST_API_URL"] if user_info(flask.session) or guest_token: advantage = AdvantageContracts( session, user_token or guest_token, token_type=("Macaroon" if user_token else "Bearer"), api_url=api_url, ) if not flask.request.is_json: return flask.jsonify({"error": "JSON required"}), 400 account_id = flask.request.json.get("account_id") if not account_id: return flask.jsonify({"error": "account_id required"}), 400 address = flask.request.json.get("address") if not address: return flask.jsonify({"error": "address required"}), 400 tax_id = flask.request.json.get("tax_id") return advantage.put_anonymous_customer_info(account_id, address, tax_id) else: return flask.jsonify({"error": "authentication required"}), 401
def blender_shop_view(advantage_mapper, **kwargs): account = None if user_info(flask.session): try: account = advantage_mapper.get_purchase_account("blender") except UAContractsUserHasNoAccount: # There is no purchase account yet for this user. # One will need to be created later; expected condition. pass except AccessForbiddenError: return flask.render_template("account/forbidden.html") all_subscriptions = [] if account: all_subscriptions = advantage_mapper.get_account_subscriptions( account_id=account.id, marketplace="blender", ) current_subscriptions = [ subscription for subscription in all_subscriptions if subscription.status in ["active", "locked"] ] listings = advantage_mapper.get_product_listings("blender") previous_purchase_ids = extract_last_purchase_ids(current_subscriptions) return flask.render_template( "advantage/subscribe/blender/index.html", account=account, previous_purchase_ids=previous_purchase_ids, product_listings=to_dict(listings), )
def post_customer_info(): if user_info(flask.session): advantage = AdvantageContracts( session, flask.session["authentication_token"], api_url=flask.current_app.config["CONTRACTS_API_URL"], ) if not flask.request.is_json: return flask.jsonify({"error": "JSON required"}), 400 payment_method_id = flask.request.json.get("payment_method_id") if not payment_method_id: return flask.jsonify({"error": "payment_method_id required"}), 400 account_id = flask.request.json.get("account_id") if not account_id: return flask.jsonify({"error": "account_id required"}), 400 address = flask.request.json.get("address") name = flask.request.json.get("name") tax_id = flask.request.json.get("tax_id") return advantage.put_customer_info( account_id, payment_method_id, address, name, tax_id ) else: return flask.jsonify({"error": "authentication required"}), 401
def cube_require_login_cube_study(): if flask.request.path.startswith("/cube/study"): user = user_info(flask.session) if not user: return flask.redirect("/login?next=" + flask.request.path) if not is_authorized(user): flask.abort(403)
def account_query(): """ A JSON endpoint to request login status """ return flask.jsonify({ "account": user_info(flask.session), })
def ua_contracts_validation_error(error): sentry.captureException( extra={ "user_info": user_info(flask.session), "request_url": error.request.url, "request_body": error.request.json, "response_body": error.response.messages, }) return flask.jsonify({"errors": error.response.messages}), 422
def advantage_thanks_view(**kwargs): email = kwargs.get("email") if user_info(flask.session): return flask.redirect("/advantage") return flask.render_template( "advantage/subscribe/thank-you.html", email=email, )
def accept_renewal(renewal_id): if user_info(flask.session): advantage = AdvantageContracts( session, flask.session["authentication_token"], api_url=flask.current_app.config["CONTRACTS_API_URL"], ) return advantage.accept_renewal(renewal_id) else: return flask.jsonify({"error": "authentication required"}), 401
def get_purchase(purchase_id): user_token = flask.session.get("authentication_token") guest_token = flask.session.get("guest_authentication_token") if user_info(flask.session) or guest_token: advantage = AdvantageContracts( session, user_token or guest_token, token_type=("Macaroon" if user_token else "Bearer"), api_url=flask.current_app.config["CONTRACTS_API_URL"], ) return advantage.get_purchase(purchase_id) else: return flask.jsonify({"error": "authentication required"}), 401
def ua_contracts_api_error_view(error): sentry.captureException( extra={ "user_info": user_info(flask.session), "request_url": error.request.url, "request_headers": error.request.headers, "response_headers": error.response.headers, "response_body": error.response.json(), }) if error.response.status_code == 401: empty_session(flask.session) return flask.redirect(flask.request.url) return flask.render_template("500.html"), 500
def ua_contracts_api_error(error): sentry.captureException( extra={ "user_info": user_info(flask.session), "request_url": error.request.url, "request_headers": error.request.headers, "response_headers": error.response.headers, "response_body": error.response.json(), }) if error.response.status_code == 401: empty_session(flask.session) return ( flask.jsonify({"errors": error.response.json()["message"]}), error.response.status_code or 500, )
def ensure_purchase_account(): """ Returns an object with the ID of an account a user can make purchases on. If the user is not logged in, the object also contains an auth token required for subsequent calls to the contract API. """ is_test_backend = flask.request.args.get("test_backend", False) api_url = flask.current_app.config["CONTRACTS_LIVE_API_URL"] if is_test_backend: api_url = flask.current_app.config["CONTRACTS_TEST_API_URL"] if not flask.request.is_json: return flask.jsonify({"error": "JSON required"}), 400 auth_token = None if user_info(flask.session): auth_token = flask.session["authentication_token"] advantage = AdvantageContracts( session, auth_token, api_url=api_url, ) request = flask.request.json try: account = advantage.ensure_purchase_account( email=request.get("email"), account_name=request.get("account_name"), payment_method_id=request.get("payment_method_id"), ) except UnauthorizedError as err: # This kind of errors are handled js side. return err.asdict(), 200 except HTTPError as err: flask.current_app.extensions["sentry"].captureException() return err.response.content, 500 # The guest authentication token is included in the response only when the # user is not logged in. token = account.get("token") if token: flask.session["guest_authentication_token"] = token return flask.jsonify(account), 200
def accept_renewal(renewal_id): is_test_backend = flask.request.args.get("test_backend", False) api_url = flask.current_app.config["CONTRACTS_LIVE_API_URL"] if is_test_backend: api_url = flask.current_app.config["CONTRACTS_TEST_API_URL"] if user_info(flask.session): advantage = AdvantageContracts( session, flask.session["authentication_token"], api_url=api_url, ) return advantage.accept_renewal(renewal_id) else: return flask.jsonify({"error": "authentication required"}), 401
def context(): return { "current_year": current_year, "descending_years": descending_years, "format_date": format_date, "get_json_feed": get_json_feed, "modify_query": modify_query, "month_name": month_name, "months_list": months_list, "get_navigation": get_navigation, "product": flask.request.args.get("product", ""), "request": flask.request, "releases": releases(), "user_info": user_info(flask.session), "utm_campaign": flask.request.args.get("utm_campaign", ""), "utm_medium": flask.request.args.get("utm_medium", ""), "utm_source": flask.request.args.get("utm_source", ""), "CAPTCHA_TESTING_API_KEY": CAPTCHA_TESTING_API_KEY, }
def ensure_purchase_account(**kwargs): """ Returns an object with the ID of an account a user can make purchases on. If the user is not logged in, the object also contains an auth token required for subsequent calls to the contract API. """ api_url = kwargs.get("api_url") email = kwargs.get("email") account_name = kwargs.get("account_name") payment_method_id = kwargs.get("payment_method_id") country = kwargs.get("country") token = None if user_info(flask.session): token = flask.session["authentication_token"] advantage = UAContractsAPI(session, token, api_url=api_url) try: account = advantage.ensure_purchase_account( email=email, account_name=account_name, payment_method_id=payment_method_id, country=country, ) except UnauthorizedError as err: # This kind of errors are handled js side. return err.asdict(), 200 except HTTPError as err: flask.current_app.extensions["sentry"].captureException() return err.response.content, 500 # The guest authentication token is included in the response only when the # user is not logged in. token = account.get("token") if token: flask.session["guest_authentication_token"] = token return flask.jsonify(account), 200
def post_stripe_invoice_id(tx_type, tx_id, invoice_id): user_token = flask.session.get("authentication_token") guest_token = flask.session.get("guest_authentication_token") is_test_backend = flask.request.args.get("test_backend", False) api_url = flask.current_app.config["CONTRACTS_LIVE_API_URL"] if is_test_backend: api_url = flask.current_app.config["CONTRACTS_TEST_API_URL"] if user_info(flask.session) or guest_token: advantage = AdvantageContracts( session, user_token or guest_token, token_type=("Macaroon" if user_token else "Bearer"), api_url=api_url, ) return advantage.post_stripe_invoice_id(tx_type, tx_id, invoice_id) else: return flask.jsonify({"error": "authentication required"}), 401
def cube_study_labs_button(edx_api, **kwargs): sso_user = user_info(flask.session) study_labs = "course-v1:CUBE+study_labs+2020" edx_user = edx_api.get_user(sso_user["email"]) enrollments = [ enrollment["course_details"]["course_id"] for enrollment in edx_api.get_enrollments(edx_user["username"]) if enrollment["is_active"] ] text = "Purchase study labs access" redirect_url = "/cube/microcerts" if study_labs in enrollments: text = "Access study labs" prepare_materials_path = quote_plus(f"/courses/{study_labs}/course/") redirect_url = ( f"{edx_api.base_url}/auth/login/tpa-saml/" f"?auth_entry=login&idp=ubuntuone&next={prepare_materials_path}") return flask.jsonify({"text": text, "redirect_url": redirect_url})
def advantage_shop_view(): g.api.set_convert_response(True) account = None if user_info(flask.session): try: account = g.api.get_purchase_account() except UAContractsUserHasNoAccount: # There is no purchase account yet for this user. # One will need to be created later; expected condition. pass all_subscriptions = [] if account: all_subscriptions = g.api.get_account_subscriptions( account_id=account.id, marketplace="canonical-ua", ) current_subscriptions = [ subscription for subscription in all_subscriptions if subscription.status in ["active", "locked"] ] is_trialling = any(subscription for subscription in current_subscriptions if subscription.in_trial) listings = g.api.get_product_listings("canonical-ua") previous_purchase_ids = extract_last_purchase_ids(current_subscriptions) user_listings = set_listings_trial_status(listings, all_subscriptions) return flask.render_template( "advantage/subscribe/index.html", account=account, previous_purchase_ids=previous_purchase_ids, product_listings=user_listings, is_trialling=is_trialling, )
def post_build(): """ Once they submit the build form on /core/build, kick off the build with Launchpad """ opt_in = flask.request.values.get("canonicalUpdatesOptIn") full_name = flask.request.values.get("FullName") names = full_name.split(" ") email = flask.request.values.get("Email") board = flask.request.values.get("board") system = flask.request.values.get("system") snaps = flask.request.values.get("snaps", "").split(",") arch = flask.request.values.get("arch") if not user_info(flask.session): flask.abort(401) launchpad = Launchpad( username=os.environ["LAUNCHPAD_IMAGE_BUILD_USER"], token=os.environ["LAUNCHPAD_IMAGE_BUILD_TOKEN"], secret=os.environ["LAUNCHPAD_IMAGE_BUILD_SECRET"], session=session, auth_consumer=os.environ["LAUNCHPAD_IMAGE_BUILD_AUTH_CONSUMER"], ) context = {} # Submit user to marketo session.post( "https://pages.ubuntu.com/index.php/leadCapture/save", data={ "canonicalUpdatesOptIn": opt_in, "FirstName": " ".join(names[:-1]), "LastName": names[-1] if len(names) > 1 else "", "Email": email, "formid": "3546", "lpId": "2154", "subId": "30", "munchkinId": "066-EOV-335", "imageBuilderStatus": "NULL", }, ) # Ensure webhook is created if flask.request.host == "ubuntu.com": launchpad.create_update_system_build_webhook( system=system, delivery_url="https://ubuntu.com/core/build/notify", secret=flask.current_app.config["SECRET_KEY"], ) # Kick off image build try: response = launchpad.build_image( board=board, system=system, snaps=snaps, author_info={"name": full_name, "email": email, "board": board}, gpg_passphrase=flask.current_app.config["SECRET_KEY"], arch=arch, ) context["build_info"] = launchpad.session.get( response.headers["Location"] ).json() except HTTPError as http_error: if http_error.response.status_code == 400: return ( flask.render_template( "core/build/error.html", build_error=http_error.response.content.decode(), ), 400, ) else: raise http_error return flask.render_template("core/build/index.html", **context)
def advantage_view(): accounts = None personal_account = None enterprise_contracts = {} entitlements = {} open_subscription = flask.request.args.get("subscription", None) stripe_publishable_key = os.getenv( "STRIPE_PUBLISHABLE_KEY", "pk_live_68aXqowUeX574aGsVck8eiIE" ) if user_info(flask.session): advantage = AdvantageContracts( session, flask.session["authentication_token"], api_url=flask.current_app.config["CONTRACTS_API_URL"], ) try: accounts = advantage.get_accounts() except HTTPError as http_error: if http_error.response.status_code == 401: # We got an unauthorized request, so we likely # need to re-login to refresh the macaroon flask.current_app.extensions["sentry"].captureException( extra={ "session_keys": flask.session.keys(), "request_url": http_error.request.url, "request_headers": http_error.request.headers, "response_headers": http_error.response.headers, "response_body": http_error.response.json(), "response_code": http_error.response.json()["code"], "response_message": http_error.response.json()[ "message" ], } ) empty_session(flask.session) return flask.render_template("advantage/index.html") raise http_error for account in accounts: account["contracts"] = advantage.get_account_contracts(account) for contract in account["contracts"]: contract["token"] = advantage.get_contract_token(contract) machines = advantage.get_contract_machines(contract).get( "machines" ) contract["machineCount"] = 0 if machines: contract["machineCount"] = len(machines) if contract["contractInfo"].get("origin", "") == "free": personal_account = account personal_account["free_token"] = contract["token"] for entitlement in contract["contractInfo"][ "resourceEntitlements" ]: if entitlement["type"] == "esm-infra": entitlements["esm-infra"] = True elif entitlement["type"] == "esm-apps": entitlements["esm-apps"] = True elif entitlement["type"] == "livepatch": entitlements["livepatch"] = True elif entitlement["type"] == "fips": entitlements["fips"] = True elif entitlement["type"] == "cc-eal": entitlements["cc-eal"] = True personal_account["entitlements"] = entitlements else: entitlements = {} for entitlement in contract["contractInfo"][ "resourceEntitlements" ]: contract["supportLevel"] = "-" if entitlement["type"] == "esm-infra": entitlements["esm-infra"] = True elif entitlement["type"] == "esm-apps": entitlements["esm-apps"] = True elif entitlement["type"] == "livepatch": entitlements["livepatch"] = True elif entitlement["type"] == "fips": entitlements["fips"] = True elif entitlement["type"] == "cc-eal": entitlements["cc-eal"] = True elif entitlement["type"] == "support": contract["supportLevel"] = entitlement[ "affordances" ]["supportLevel"] contract["entitlements"] = entitlements created_at = dateutil.parser.parse( contract["contractInfo"]["createdAt"] ) contract["contractInfo"][ "createdAtFormatted" ] = created_at.strftime("%d %B %Y") contract["contractInfo"]["status"] = "active" if "effectiveTo" in contract["contractInfo"]: effective_to = dateutil.parser.parse( contract["contractInfo"]["effectiveTo"] ) contract["contractInfo"][ "effectiveToFormatted" ] = effective_to.strftime("%d %B %Y") time_now = datetime.utcnow().replace(tzinfo=pytz.utc) if effective_to < time_now: contract["contractInfo"]["status"] = "expired" contract["contractInfo"][ "expired_restart_date" ] = time_now - timedelta(days=1) date_difference = effective_to - time_now contract["expiring"] = date_difference.days <= 30 contract["contractInfo"][ "daysTillExpiry" ] = date_difference.days contract["renewal"] = make_renewal( advantage, contract["contractInfo"] ) enterprise_contract = enterprise_contracts.setdefault( contract["accountInfo"]["name"], [] ) # If a subscription id is present and this contract # matches add it to the start of the list if contract["contractInfo"]["id"] == open_subscription: enterprise_contract.insert(0, contract) else: enterprise_contract.append(contract) return flask.render_template( "advantage/index.html", accounts=accounts, enterprise_contracts=enterprise_contracts, personal_account=personal_account, open_subscription=open_subscription, stripe_publishable_key=stripe_publishable_key, )
def advantage_shop_view(): account = previous_purchase_id = None is_test_backend = flask.request.args.get("test_backend", False) stripe_publishable_key = os.getenv("STRIPE_LIVE_PUBLISHABLE_KEY", "pk_live_68aXqowUeX574aGsVck8eiIE") api_url = flask.current_app.config["CONTRACTS_LIVE_API_URL"] if is_test_backend: stripe_publishable_key = os.getenv( "STRIPE_TEST_PUBLISHABLE_KEY", "pk_test_yndN9H0GcJffPe0W58Nm64cM00riYG4N46", ) api_url = flask.current_app.config["CONTRACTS_TEST_API_URL"] if user_info(flask.session): advantage = AdvantageContracts( session, flask.session["authentication_token"], api_url=api_url, ) if flask.session.get("guest_authentication_token"): flask.session.pop("guest_authentication_token") try: account = advantage.get_purchase_account() except HTTPError as err: code = err.response.status_code if code == 401: # We got an unauthorized request, so we likely # need to re-login to refresh the macaroon flask.current_app.extensions["sentry"].captureException( extra={ "session_keys": flask.session.keys(), "request_url": err.request.url, "request_headers": err.request.headers, "response_headers": err.response.headers, "response_body": err.response.json(), "response_code": err.response.json()["code"], "response_message": err.response.json()["message"], }) empty_session(flask.session) return flask.render_template( "advantage/subscribe/index.html", account=None, previous_purchase_id=None, product_listings=[], stripe_publishable_key=stripe_publishable_key, is_test_backend=is_test_backend, ) if code != 404: raise # There is no purchase account yet for this user. # One will need to be created later, but this is an expected # condition. else: advantage = AdvantageContracts(session, None, api_url=api_url) if account is not None: resp = advantage.get_account_subscriptions_for_marketplace( account["id"], "canonical-ua") subs = resp.get("subscriptions") if subs: previous_purchase_id = subs[0].get("lastPurchaseID") listings_response = advantage.get_marketplace_product_listings( "canonical-ua") product_listings = listings_response.get("productListings") if not product_listings: # For the time being, no product listings means the shop has not been # activated, so fallback to shopify. This should become an error later. return flask.redirect("https://buy.ubuntu.com/") products = {pr["id"]: pr for pr in listings_response["products"]} listings = [] for listing in product_listings: if "price" not in listing: continue listing["product"] = products[listing["productID"]] listings.append(listing) return flask.render_template( "advantage/subscribe/index.html", account=account, previous_purchase_id=previous_purchase_id, product_listings=listings, stripe_publishable_key=stripe_publishable_key, is_test_backend=is_test_backend, )
def post_advantage_subscriptions(preview, **kwargs): api_url = kwargs.get("api_url") token = kwargs.get("token") token_type = kwargs.get("token_type") account_id = kwargs.get("account_id") previous_purchase_id = kwargs.get("previous_purchase_id") period = kwargs.get("period") products = kwargs.get("products") resizing = kwargs.get("resizing", False) advantage = UAContractsAPI( session, token, token_type=token_type, api_url=api_url ) current_subscription = {} if user_info(flask.session): subscriptions = advantage.get_account_subscriptions( account_id=account_id, marketplace="canonical-ua", filters={"status": "active"}, ) for subscription in subscriptions: if subscription["subscription"]["period"] == period: current_subscription = subscription # If there is a subscription we get the current metric # value for each product listing so we can generate a # purchase request with updated quantities later. # If we resize we want to override the existing value subscribed_quantities = {} if not resizing and current_subscription: for item in current_subscription["purchasedProductListings"]: product_listing_id = item["productListing"]["id"] subscribed_quantities[product_listing_id] = item["value"] purchase_items = [] for product in products: product_listing_id = product["product_listing_id"] metric_value = product["quantity"] + subscribed_quantities.get( product_listing_id, 0 ) purchase_items.append( { "productListingID": product_listing_id, "metric": "active-machines", "value": metric_value, } ) purchase_request = { "accountID": account_id, "purchaseItems": purchase_items, "previousPurchaseID": previous_purchase_id, } try: if not preview: purchase = advantage.purchase_from_marketplace( marketplace="canonical-ua", purchase_request=purchase_request ) else: purchase = advantage.preview_purchase_from_marketplace( marketplace="canonical-ua", purchase_request=purchase_request ) except CannotCancelLastContractError as error: raise UAContractsAPIError(error) return flask.jsonify(purchase), 200
def cube_microcerts(): assertions = {} enrollments = [] passed_courses = 0 sso_user = user_info(flask.session) edx_url = (f"{edx_api.base_url}/auth/login/tpa-saml/" "?auth_entry=login&idp=ubuntuone&next=") edx_user = edx_api.get_user(sso_user["email"]) if sso_user else None if edx_user: assertions = { assertion["badgeclass"]: assertion for assertion in badgr_api.get_assertions( CUBE_CONTENT["badgr-issuer"], edx_user["email"])["result"] } enrollments = [ enrollment["course_details"]["course_id"] for enrollment in edx_api.get_enrollments(edx_user["username"]) if enrollment["is_active"] ] certified_badge = {} if CUBE_CONTENT["certified-badge"] in assertions: assertion = assertions.pop(CUBE_CONTENT["certified-badge"]) if not assertion["revoked"]: certified_badge["image"] = assertion["image"] certified_badge["share_url"] = assertion["openBadgeId"] study_labs = CUBE_CONTENT["study-labs"] courses = copy.deepcopy(CUBE_CONTENT["courses"]) for course in courses: attempts = [] if edx_user: attempts = edx_api.get_course_attempts( course["id"], edx_user["username"])["proctored_exam_attempts"] assertion = assertions.get(course["badge"]["class"]) course["status"] = "not-enrolled" if assertion and not assertion["revoked"]: course["badge"]["url"] = assertion["image"] course["status"] = "passed" passed_courses += 1 elif attempts: course["status"] = ("in-progress" if not attempts[0]["completed_at"] else "failed") elif course["id"] in enrollments: course["status"] = "enrolled" course_id = course["id"] courseware_name = course_id.split("+")[1] course["take_url"] = edx_url + quote_plus( f"/courses/{course_id}/courseware/2020/start/?child=first") course["study_lab"] = edx_url + quote_plus( f"/courses/{study_labs}/courseware/{courseware_name}/?child=first") study_labs_url = edx_url + quote_plus(f"/courses/{study_labs}/course/") return flask.render_template( "cube/microcerts.html", **{ "edx_user": edx_user, "edx_register_url": f"{edx_url}%2F", "sso_user": sso_user, "certified_badge": certified_badge, "modules": courses, "passed_courses": passed_courses, "has_enrollments": len(enrollments) > 0, "has_study_labs": study_labs in enrollments, "study_labs_url": study_labs_url, }, )
def advantage_shop_view(**kwargs): is_test_backend = kwargs.get("test_backend") api_url = kwargs.get("api_url") stripe_publishable_key = kwargs.get("stripe_publishable_key") token = kwargs.get("token") account = None previous_purchase_ids = {"monthly": "", "yearly": ""} advantage = UAContractsAPI( session, None, api_url=api_url, is_for_view=True ) if user_info(flask.session): advantage = UAContractsAPI( session, token, api_url=api_url, is_for_view=True ) if flask.session.get("guest_authentication_token"): flask.session.pop("guest_authentication_token") try: account = advantage.get_purchase_account() except UAContractsUserHasNoAccount: # There is no purchase account yet for this user. # One will need to be created later, but this is an expected # condition. pass if account: subscriptions = advantage.get_account_subscriptions( account_id=account["id"], marketplace="canonical-ua", filters={"status": "active"}, ) for subscription in subscriptions: period = subscription["subscription"]["period"] previous_purchase_ids[period] = subscription["lastPurchaseID"] listings = advantage.get_product_listings("canonical-ua") product_listings = listings.get("productListings") if not product_listings: # For the time being, no product listings means the shop has not been # activated, so fallback to shopify. This should become an error later. return flask.redirect("https://buy.ubuntu.com/") products = {product["id"]: product for product in listings["products"]} website_listing = [] for listing in product_listings: if "price" not in listing: continue listing["product"] = products[listing["productID"]] website_listing.append(listing) return flask.render_template( "advantage/subscribe/index.html", is_test_backend=is_test_backend, stripe_publishable_key=stripe_publishable_key, account=account, previous_purchase_ids=previous_purchase_ids, product_listings=website_listing, )
def cube_microcerts(): assertions = {} enrollments = [] passed_courses = 0 sso_user = user_info(flask.session) if not is_authorized(sso_user): flask.abort(403) edx_user = edx_api.get_user(sso_user["email"]) if sso_user else None if edx_user: assertions = { assertion["badgeclass"]: assertion for assertion in badgr_api.get_assertions( CUBE_CONTENT["badgr-issuer"], edx_user["email"])["result"] } enrollments = [ enrollment["course_details"]["course_id"] for enrollment in edx_api.get_enrollments(edx_user["username"]) if enrollment["is_active"] ] certified_badge = {} if CUBE_CONTENT["certified-badge"] in assertions: assertion = assertions.pop(CUBE_CONTENT["certified-badge"]) if not assertion["revoked"]: certified_badge["image"] = assertion["image"] certified_badge["share_url"] = assertion["openBadgeId"] courses = copy.deepcopy(CUBE_CONTENT["courses"]) for course in courses: attempts = [] if edx_user: attempts = edx_api.get_course_attempts( course["id"], edx_user["username"])["proctored_exam_attempts"] assertion = assertions.get(course["badge"]["class"]) course["status"] = "not-enrolled" if assertion and not assertion["revoked"]: course["badge"]["url"] = assertion["image"] course["status"] = "passed" passed_courses += 1 elif attempts: course["status"] = ("in-progress" if not attempts[0]["completed_at"] else "failed") elif course["id"] in enrollments: course["status"] = "enrolled" course["take_url"] = (f"{edx_api.base_url}/courses/{course['id']}" "/courseware/2020/start/?child=first") course["prepare_url"] = (f"{edx_api.base_url}/courses/" f"{CUBE_CONTENT['prepare-course']}/course/") has_prepare_material = CUBE_CONTENT["prepare-course"] in enrollments prepare_material_url = ( f"{edx_api.base_url}/courses/{CUBE_CONTENT['prepare-course']}/course/") return flask.render_template( "cube/microcerts.html", **{ "user": sso_user, "certified_badge": certified_badge, "modules": courses, "passed_courses": passed_courses, "has_enrollments": len(enrollments) > 0, "has_prepare_material": has_prepare_material, "prepare_material_url": prepare_material_url, }, )
def post_advantage_subscriptions(preview): is_test_backend = flask.request.args.get("test_backend", False) api_url = flask.current_app.config["CONTRACTS_LIVE_API_URL"] if is_test_backend: api_url = flask.current_app.config["CONTRACTS_TEST_API_URL"] user_token = flask.session.get("authentication_token") guest_token = flask.session.get("guest_authentication_token") if user_info(flask.session) or guest_token: advantage = AdvantageContracts( session, user_token or guest_token, token_type=("Macaroon" if user_token else "Bearer"), api_url=api_url, ) else: return flask.jsonify({"error": "authentication required"}), 401 payload = flask.request.json if not payload: return flask.jsonify({}), 400 account_id = payload.get("account_id") previous_purchase_id = payload.get("previous_purchase_id") last_subscription = {} if not guest_token: try: subscriptions = ( advantage.get_account_subscriptions_for_marketplace( account_id=account_id, marketplace="canonical-ua")) except HTTPError: flask.current_app.extensions["sentry"].captureException( extra={"payload": payload}) return ( flask.jsonify( {"error": "could not retrieve account subscriptions"}), 500, ) if subscriptions: last_subscription = subscriptions["subscriptions"][0] # If there is a subscription we get the current metric # value for each product listing so we can generate a # purchase request with updated quantities later. subscribed_quantities = {} if "purchasedProductListings" in last_subscription: for item in last_subscription["purchasedProductListings"]: product_listing_id = item["productListing"]["id"] subscribed_quantities[product_listing_id] = item["value"] purchase_items = [] for product in payload.get("products"): product_listing_id = product["product_listing_id"] metric_value = product["quantity"] + subscribed_quantities.get( product_listing_id, 0) purchase_items.append({ "productListingID": product_listing_id, "metric": "active-machines", "value": metric_value, }) purchase_request = { "accountID": account_id, "purchaseItems": purchase_items, "previousPurchaseID": previous_purchase_id, } try: if not preview: purchase = advantage.purchase_from_marketplace( marketplace="canonical-ua", purchase_request=purchase_request) else: purchase = advantage.preview_purchase_from_marketplace( marketplace="canonical-ua", purchase_request=purchase_request) except HTTPError as http_error: flask.current_app.extensions["sentry"].captureException( extra={ "purchase_request": purchase_request, "api_response": http_error.response.json(), }) return ( flask.jsonify({"error": "could not complete this purchase"}), 500, ) return flask.jsonify(purchase), 200
def cube_home(): sso_user = user_info(flask.session) if not is_authorized(sso_user): flask.abort(403) return flask.render_template("cube/index.html")