def make_twitch_blueprint( client_id=None, client_secret=None, *, scope=None, redirect_url=None, redirect_to=None, login_url=None, authorized_url=None, session_class=ClientIdHeaderOAuth2Session, storage=None, rule_kwargs=None, ): """Make a blueprint for authenticating with Twitch using OAuth 2. This requires a client ID and client secret from Twitch. You should either pass them to this constructor, or make sure that your Flask application config defines them, using the variables :envvar:`TWITCH_OAUTH_CLIENT_ID` and :envvar:`TWITCH_OAUTH_CLIENT_SECRET`. Args: client_id (str): The client ID for your application on Twitch. Defaults to app config "TWITCH_OAUTH_CLIENT_ID". client_secret (str): The client Secret for your application on Twitch. Defaults to app config "TWITCH_OAUTH_CLIENT_SECRET". scope (list, optional): Comma-separated list of scopes for the OAuth token. Defaults to None. redirect_url (str): the URL to redirect to after the authentication dance is complete redirect_to (str): if ``redirect_url`` is not defined, the name of the view to redirect to after the authentication dance is complete. The actual URL will be determined by :func:`flask.url_for` login_url (str, optional): the URL path for the ``login`` view. Defaults to ``/twitch`` authorized_url (str, optional): the URL path for the ``authorized`` view. Defaults to ``/twitch/authorized``. session_class (class, optional): The class to use for creating a Requests session. Defaults to :class:`~flask_dance.contrib.twitch.ClientIdHeaderOAuth2Session`. storage: A token storage class, or an instance of a token storage class, to use for this blueprint. Defaults to :class:`~flask_dance.consumer.storage.session.SessionStorage`. rule_kwargs (dict, optional): Additional arguments that should be passed when adding the login and authorized routes. Defaults to ``None``. Returns: :class:`~flask_dance.consumer.OAuth2ConsumerBlueprint` A :doc:`blueprint <flask:blueprints>` to attach to your Flask app. """ twitch_bp = OAuth2ConsumerBlueprint( "twitch", __name__, client_id=client_id, client_secret=client_secret, scope=scope, base_url="https://api.twitch.tv/helix/", authorization_url="https://id.twitch.tv/oauth2/authorize", token_url="https://id.twitch.tv/oauth2/token", token_url_params={"include_client_id": True}, redirect_url=redirect_url, redirect_to=redirect_to, login_url=login_url, authorized_url=authorized_url, session_class=session_class, storage=storage, rule_kwargs=rule_kwargs, ) twitch_bp.from_config["client_id"] = "TWITCH_OAUTH_CLIENT_ID" twitch_bp.from_config["client_secret"] = "TWITCH_OAUTH_CLIENT_SECRET" # TODO: The key won't auto renew. See https://github.com/singingwolfboy/flask-dance/issues/35 # I think this will work but needs a test. twitch_bp.auto_refresh_url = twitch_bp.token_url twitch_bp.auto_refresh_kwargs = { "client_id": twitch_bp.client_id, "client_secret": twitch_bp.client_secret, } @twitch_bp.before_app_request def set_applocal_session(): ctx = stack.top ctx.twitch_oauth = twitch_bp.session return twitch_bp
def make_github_blueprint( client_id=None, client_secret=None, scope=None, redirect_url=None, redirect_to=None, login_url=None, authorized_url=None, session_class=None, backend=None, storage=None, ): """ Make a blueprint for authenticating with GitHub using OAuth 2. This requires a client ID and client secret from GitHub. You should either pass them to this constructor, or make sure that your Flask application config defines them, using the variables GITHUB_OAUTH_CLIENT_ID and GITHUB_OAUTH_CLIENT_SECRET. Args: client_id (str): The client ID for your application on GitHub. client_secret (str): The client secret for your application on GitHub scope (str, optional): comma-separated list of scopes for the OAuth token redirect_url (str): the URL to redirect to after the authentication dance is complete redirect_to (str): if ``redirect_url`` is not defined, the name of the view to redirect to after the authentication dance is complete. The actual URL will be determined by :func:`flask.url_for` login_url (str, optional): the URL path for the ``login`` view. Defaults to ``/github`` authorized_url (str, optional): the URL path for the ``authorized`` view. Defaults to ``/github/authorized``. session_class (class, optional): The class to use for creating a Requests session. Defaults to :class:`~flask_dance.consumer.requests.OAuth2Session`. storage: A token storage class, or an instance of a token storage class, to use for this blueprint. Defaults to :class:`~flask_dance.consumer.storage.session.SessionStorage`. :rtype: :class:`~flask_dance.consumer.OAuth2ConsumerBlueprint` :returns: A :ref:`blueprint <flask:blueprints>` to attach to your Flask app. """ github_bp = OAuth2ConsumerBlueprint( "github", __name__, client_id=client_id, client_secret=client_secret, scope=scope, base_url="https://api.github.com/", authorization_url="https://github.com/login/oauth/authorize", token_url="https://github.com/login/oauth/access_token", redirect_url=redirect_url, redirect_to=redirect_to, login_url=login_url, authorized_url=authorized_url, session_class=session_class, backend=backend, storage=storage, ) github_bp.from_config["client_id"] = "GITHUB_OAUTH_CLIENT_ID" github_bp.from_config["client_secret"] = "GITHUB_OAUTH_CLIENT_SECRET" @github_bp.before_app_request def set_applocal_session(): ctx = stack.top ctx.github_oauth = github_bp.session return github_bp
def create_app(oidc_blueprint=None): app = Flask(__name__) app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_port=1, x_prefix=1) app.secret_key = "8210f566-4981-11ea-92d1-f079596e599b" app.config.from_json('config.json') settings = Settings(app.config) cred = Credentials(settings.db_url) infra = Infrastructures(settings.db_url) toscaTemplates = utils.loadToscaTemplates(settings.toscaDir) toscaInfo = utils.extractToscaInfo(settings.toscaDir, settings.toscaParamsDir, toscaTemplates) app.jinja_env.filters['tojson_pretty'] = utils.to_pretty_json app.logger.debug("TOSCA INFO: " + json.dumps(toscaInfo)) loglevel = app.config.get("LOG_LEVEL") if app.config.get( "LOG_LEVEL") else "INFO" numeric_level = getattr(logging, loglevel.upper(), None) if not isinstance(numeric_level, int): raise ValueError('Invalid log level: %s' % loglevel) logging.basicConfig(level=numeric_level) oidc_base_url = app.config['OIDC_BASE_URL'] oidc_token_url = oidc_base_url + '/token' oidc_refresh_url = oidc_base_url + '/token' oidc_authorization_url = oidc_base_url + '/authorize' if not oidc_blueprint: oidc_blueprint = OAuth2ConsumerBlueprint( "oidc", __name__, client_id=app.config['OIDC_CLIENT_ID'], client_secret=app.config['OIDC_CLIENT_SECRET'], scope=app.config['OIDC_SCOPES'], base_url=oidc_base_url, token_url=oidc_token_url, auto_refresh_url=oidc_refresh_url, authorization_url=oidc_authorization_url, redirect_to='home') app.register_blueprint(oidc_blueprint, url_prefix="/login") @app.before_request def before_request_checks(): if 'external_links' not in session: session['external_links'] = settings.external_links g.analytics_tag = settings.analytics_tag g.settings = settings def authorized_with_valid_token(f): @wraps(f) def decorated_function(*args, **kwargs): try: if not oidc_blueprint.session.authorized or 'username' not in session: return redirect(url_for('login')) if oidc_blueprint.session.token['expires_in'] < 20: app.logger.debug("Force refresh token") oidc_blueprint.session.get('/userinfo') except (InvalidTokenError, TokenExpiredError): flash("Token expired.", 'warning') return redirect(url_for('login')) return f(*args, **kwargs) return decorated_function @app.route('/settings') @authorized_with_valid_token def show_settings(): return render_template('settings.html', oidc_url=settings.oidcUrl, im_url=settings.imUrl) @app.route('/login') def login(): session.clear() return render_template('home.html', oidc_name=settings.oidcName) @app.route('/') def home(): if not oidc_blueprint.session.authorized: return redirect(url_for('login')) try: account_info = oidc_blueprint.session.get( urlparse(settings.oidcUrl)[2] + "/userinfo") except (InvalidTokenError, TokenExpiredError): flash("Token expired.", 'warning') return redirect(url_for('login')) if account_info.ok: account_info_json = account_info.json() session["vos"] = None if 'eduperson_entitlement' in account_info_json: session["vos"] = utils.getUserVOs( account_info_json['eduperson_entitlement']) if settings.oidcGroups: user_groups = [] if 'groups' in account_info_json: user_groups = account_info_json['groups'] elif 'eduperson_entitlement' in account_info_json: user_groups = account_info_json['eduperson_entitlement'] if not set(settings.oidcGroups).issubset(user_groups): app.logger.debug( "No match on group membership. User group membership: " + json.dumps(user_groups)) message = Markup( 'You need to be a member of the following groups: {0}. <br>' ' Please, visit <a href="{1}">{1}</a> and apply for the requested ' 'membership.'.format(json.dumps(settings.oidcGroups), settings.oidcUrl)) raise Forbidden(description=message) session['userid'] = account_info_json['sub'] if 'name' in account_info_json: session['username'] = account_info_json['name'] else: session['username'] = "" if 'given_name' in account_info_json: session['username'] = account_info_json['given_name'] if 'family_name' in account_info_json: session[ 'username'] += " " + account_info_json['family_name'] if session['username'] == "": session['username'] = account_info_json['sub'] if 'email' in account_info_json: session['gravatar'] = utils.avatar(account_info_json['email'], 26) else: session['gravatar'] = utils.avatar(account_info_json['sub'], 26) return render_template('portfolio.html', templates=toscaInfo) else: flash("Error getting User info: \n" + account_info.text, 'error') return render_template('home.html', oidc_name=settings.oidcName) @app.route('/vminfo/<infid>/<vmid>') @authorized_with_valid_token def showvminfo(infid=None, vmid=None): access_token = oidc_blueprint.session.token['access_token'] auth_data = utils.getUserAuthData(access_token, cred, session["userid"]) headers = {"Authorization": auth_data, "Accept": "application/json"} url = "%s/infrastructures/%s/vms/%s" % (settings.imUrl, infid, vmid) response = requests.get(url, headers=headers) vminfo = {} state = "" nets = "" deployment = "" if not response.ok: flash("Error retrieving VM info: \n" + response.text, 'error') else: app.logger.debug("VM Info: %s" % response.text) vminfo = utils.format_json_radl(response.json()["radl"]) if "cpu.arch" in vminfo: del vminfo["cpu.arch"] if "state" in vminfo: state = vminfo["state"] del vminfo["state"] if "provider.type" in vminfo: deployment = vminfo["provider.type"] del vminfo["provider.type"] if "provider.host" in vminfo: if "provider.port" in vminfo: deployment += ": %s:%s" % (vminfo["provider.host"], vminfo["provider.port"]) del vminfo["provider.port"] else: deployment += ": " + vminfo["provider.host"] del vminfo["provider.host"] cont = 0 while "net_interface.%s.ip" % cont in vminfo: if cont > 0: nets += Markup('<br/>') nets += Markup('<i class="fa fa-network-wired"></i>') nets += " %s: %s" % (cont, vminfo["net_interface.%s.ip" % cont]) del vminfo["net_interface.%s.ip" % cont] cont += 1 cont = 0 while "net_interface.%s.connection" % cont in vminfo: del vminfo["net_interface.%s.connection" % cont] cont += 1 for elem in vminfo: if elem.endswith("size") and isinstance(vminfo[elem], int): vminfo[elem] = "%d GB" % (vminfo[elem] / 1073741824) return render_template('vminfo.html', infid=infid, vmid=vmid, vminfo=vminfo, state=state, nets=nets, deployment=deployment) @app.route('/managevm/<op>/<infid>/<vmid>') @authorized_with_valid_token def managevm(op=None, infid=None, vmid=None): access_token = oidc_blueprint.session.token['access_token'] auth_data = utils.getUserAuthData(access_token, cred, session["userid"]) headers = {"Authorization": auth_data, "Accept": "application/json"} op = op.lower() if op in ["stop", "start", "reboot"]: url = "%s/infrastructures/%s/vms/%s/%s" % (settings.imUrl, infid, vmid, op) response = requests.put(url, headers=headers) elif op == "terminate": url = "%s/infrastructures/%s/vms/%s" % (settings.imUrl, infid, vmid) response = requests.delete(url, headers=headers) else: flash("Error: invalid operation: %s." % op, 'error') return redirect(url_for('showinfrastructures')) if response.ok: flash("Operation '%s' successfully made on VM ID: %s" % (op, vmid), 'info') else: flash( "Error making %s op on VM %s: \n%s" % (op, vmid, response.text), 'error') if op == "terminate": return redirect(url_for('showinfrastructures')) else: return redirect(url_for('showvminfo', infid=infid, vmid=vmid)) @app.route('/infrastructures') @authorized_with_valid_token def showinfrastructures(): access_token = oidc_blueprint.session.token['access_token'] auth_data = utils.getUserAuthData(access_token, cred, session["userid"]) headers = {"Authorization": auth_data, "Accept": "application/json"} url = "%s/infrastructures" % settings.imUrl response = requests.get(url, headers=headers) infrastructures = {} if not response.ok: flash("Error retrieving infrastructure list: \n" + response.text, 'error') else: app.logger.debug("Infrastructures: %s" % response.text) state_res = response.json() if "uri-list" in state_res: inf_id_list = [elem["uri"] for elem in state_res["uri-list"]] else: inf_id_list = [] for inf_id in inf_id_list: url = "%s/state" % inf_id response = requests.get(url, headers=headers) if not response.ok: flash( "Error retrieving infrastructure %s state: \n%s" % (inf_id, response.text), 'warning') else: inf_state = response.json() infrastructures[os.path.basename( inf_id)] = inf_state['state'] try: infra_name = infra.get_infra( os.path.basename(inf_id))["name"] except Exception: infra_name = "" infrastructures[os.path.basename( inf_id)]['name'] = infra_name return render_template('infrastructures.html', infrastructures=infrastructures) @app.route('/reconfigure/<infid>') @authorized_with_valid_token def infreconfigure(infid=None): access_token = oidc_blueprint.session.token['access_token'] auth_data = utils.getUserAuthData(access_token, cred, session["userid"]) headers = {"Authorization": auth_data} url = "%s/infrastructures/%s/reconfigure" % (settings.imUrl, infid) response = requests.put(url, headers=headers) if response.ok: flash("Infrastructure successfuly reconfigured.", "info") else: flash("Error reconfiguring Infrastructure: \n" + response.text, "error") return redirect(url_for('showinfrastructures')) @app.route('/template/<infid>') @authorized_with_valid_token def template(infid=None): access_token = oidc_blueprint.session.token['access_token'] auth_data = utils.getUserAuthData(access_token, cred, session["userid"]) headers = {"Authorization": auth_data} url = "%s/infrastructures/%s/tosca" % (settings.imUrl, infid) response = requests.get(url, headers=headers) if not response.ok: flash("Error getting template: \n" + response.text, "error") template = "" else: template = response.text return render_template('deptemplate.html', template=template) @app.route('/log/<infid>') @authorized_with_valid_token def inflog(infid=None): access_token = oidc_blueprint.session.token['access_token'] auth_data = utils.getUserAuthData(access_token, cred, session["userid"]) headers = {"Authorization": auth_data} url = "%s/infrastructures/%s/contmsg" % (settings.imUrl, infid) response = requests.get(url, headers=headers, verify=False) if not response.ok: log = "Not found" else: log = response.text return render_template('inflog.html', log=log) @app.route('/vmlog/<infid>/<vmid>') @authorized_with_valid_token def vmlog(infid=None, vmid=None): access_token = oidc_blueprint.session.token['access_token'] auth_data = utils.getUserAuthData(access_token, cred, session["userid"]) headers = {"Authorization": auth_data} url = "%s/infrastructures/%s/vms/%s/contmsg" % (settings.imUrl, infid, vmid) response = requests.get(url, headers=headers, verify=False) if not response.ok: log = "Not found" else: log = response.text return render_template('inflog.html', log=log, vmid=vmid) @app.route('/outputs/<infid>') @authorized_with_valid_token def infoutputs(infid=None): access_token = oidc_blueprint.session.token['access_token'] auth_data = utils.getUserAuthData(access_token, cred, session["userid"]) headers = {"Authorization": auth_data} url = "%s/infrastructures/%s/outputs" % (settings.imUrl, infid) response = requests.get(url, headers=headers, verify=False) if not response.ok: outputs = {} else: outputs = response.json()["outputs"] for elem in outputs: if isinstance(outputs[elem], str) and (outputs[elem].startswith('http://') or outputs[elem].startswith('https://')): outputs[elem] = Markup( "<a href='%s' target='_blank'>%s</a>" % (outputs[elem], outputs[elem])) return render_template('outputs.html', infid=infid, outputs=outputs) @app.route('/delete/<infid>/<force>') @authorized_with_valid_token def infdel(infid=None, force=0): access_token = oidc_blueprint.session.token['access_token'] auth_data = utils.getUserAuthData(access_token, cred, session["userid"]) headers = {"Authorization": auth_data} url = "%s/infrastructures/%s?async=1" % (settings.imUrl, infid) if force: url += "&force=1" response = requests.delete(url, headers=headers) if not response.ok: flash("Error deleting infrastructure: " + response.text, "error") else: flash("Infrastructure '%s' successfuly deleted." % infid, "info") try: infra.delete_infra(infid) except Exception as ex: flash("Error deleting infrastructure name: %s" + str(ex), "warning") return redirect(url_for('showinfrastructures')) @app.route('/configure') @authorized_with_valid_token def configure(): selected_tosca = request.args['selected_tosca'] app.logger.debug("Template: " + json.dumps(toscaInfo[selected_tosca])) vos = utils.getStaticVOs() vos.extend(appdb.get_vo_list()) vos = list(set(vos)) if session["vos"]: vos = [vo for vo in vos if vo in session["vos"]] return render_template('createdep.html', template=toscaInfo[selected_tosca], selectedTemplate=selected_tosca, vos=vos) @app.route('/sites/<vo>') def getsites(vo=None): res = "" appdb_sites = appdb.get_sites(vo) for site_name, (_, critical, _) in appdb_sites.items(): if critical: critical = " (WARNING: %s state!)" % critical res += '<option name="selectedSite" value=%s>%s%s</option>' % ( site_name, site_name, critical) for site_name, _ in utils.getStaticSites(vo).items(): # avoid site duplication if site_name not in appdb_sites: res += '<option name="selectedSite" value=%s>%s</option>' % ( site_name, site_name) return res @app.route('/images/<site>/<vo>') def getimages(site=None, vo=None): res = "" if vo == "local": access_token = oidc_blueprint.session.token['access_token'] for image_name, image_id in utils.get_site_images( site, vo, access_token, cred, session["userid"]): res += '<option name="selectedSiteImage" value=%s>%s</option>' % ( image_id, image_name) else: for image in appdb.get_images(site, vo): res += '<option name="selectedImage" value=%s>%s</option>' % ( image, image) return res def add_image_to_template(template, image): # Add the image to all compute nodes for node in list( template['topology_template']['node_templates'].values()): if node["type"] == "tosca.nodes.indigo.Compute": node["capabilities"]["os"]["properties"]["image"] = image app.logger.debug(yaml.dump(template, default_flow_style=False)) return template def add_auth_to_template(template, auth_data): # Add the auth_data ElasticCluster node for node in list( template['topology_template']['node_templates'].values()): if node["type"] == "tosca.nodes.ec3.ElasticCluster": if "properties" not in node: node["properties"] = {} node["properties"]["im_auth"] = auth_data app.logger.debug(yaml.dump(template, default_flow_style=False)) return template def set_inputs_to_template(template, inputs): # Add the image to all compute nodes for name, value in template['topology_template']['inputs'].items(): if name in inputs: if value["type"] == "integer": value["default"] = int(inputs[name]) elif value["type"] == "float": value["default"] = float(inputs[name]) elif value["type"] == "boolean": if inputs[name].lower() in ['yes', 'true', '1']: value["default"] = True else: value["default"] = False # Special case for ports, convert a comma separated list of ints # to a PortSpec map elif value["type"] == "map" and name == "ports": ports = inputs[name].split(",") ports_value = {} for port in ports: # Should we also open UDP? ports_value["port_%s" % port] = { "protocol": "tcp", "source": int(port) } value["default"] = ports_value else: value["default"] = inputs[name] app.logger.debug(yaml.dump(template, default_flow_style=False)) return template @app.route('/submit', methods=['POST']) @authorized_with_valid_token def createdep(): form_data = request.form.to_dict() vo = form_data['extra_opts.selectedVO'] site = form_data['extra_opts.selectedSite'] access_token = oidc_blueprint.session.token['access_token'] auth_data = utils.getUserAuthData(access_token, cred, session["userid"], vo, site) app.logger.debug("Form data: " + json.dumps(request.form.to_dict())) with io.open(settings.toscaDir + request.args.get('template')) as stream: template = yaml.full_load(stream) if form_data['extra_opts.selectedImage'] != "": image = "appdb://%s/%s?%s" % ( form_data['extra_opts.selectedSite'], form_data['extra_opts.selectedImage'], form_data['extra_opts.selectedVO']) elif form_data['extra_opts.selectedSiteImage'] != "": site_url = utils.get_ost_image_url( form_data['extra_opts.selectedSite']) image = "ost://%s/%s" % ( site_url, form_data['extra_opts.selectedSiteImage']) else: flash("No correct image selected.", "error") return redirect(url_for('showinfrastructures')) template = add_image_to_template(template, image) template = add_auth_to_template(template, auth_data) inputs = { k: v for (k, v) in form_data.items() if not k.startswith("extra_opts.") } app.logger.debug("Parameters: " + json.dumps(inputs)) template = set_inputs_to_template(template, inputs) payload = yaml.dump(template, default_flow_style=False, sort_keys=False) headers = {"Authorization": auth_data, "Content-Type": "text/yaml"} url = "%s/infrastructures?async=1" % settings.imUrl response = requests.post(url, headers=headers, data=payload) if not response.ok: flash("Error creating infrastrucrure: \n" + response.text, "error") else: try: inf_id = os.path.basename(response.text) infra.write_infra(inf_id, {"name": form_data['infra_name']}) except Exception as ex: flash("Error storing Infrastructure name: %s" % str(ex), "warning") return redirect(url_for('showinfrastructures')) @app.route('/manage_creds') @authorized_with_valid_token def manage_creds(): sites = {} try: sites = utils.getCachedSiteList() except Exception as e: flash("Error retrieving sites list: \n" + str(e), 'warning') return render_template('service_creds.html', sites=sites) @app.route('/write_creds', methods=['GET', 'POST']) @authorized_with_valid_token def write_creds(): serviceid = request.args.get('service_id', "") servicename = request.args.get('service_name', "") app.logger.debug("service_id={}".format(serviceid)) if request.method == 'GET': res = {} projects = {} try: res = cred.get_cred(servicename, session["userid"]) projects = utils.getStaticSitesProjectIDs(serviceid) projects.update(appdb.get_project_ids(serviceid)) if session["vos"]: filter_projects = {} for vo, project in projects.items(): if vo in session["vos"]: filter_projects[vo] = project projects = filter_projects except Exception as ex: flash("Error reading credentials %s!" % ex, 'error') return render_template('modal_creds.html', service_creds=res, service_id=serviceid, service_name=servicename, projects=projects) else: app.logger.debug("Form data: " + json.dumps(request.form.to_dict())) creds = request.form.to_dict() try: cred.write_creds(servicename, session["userid"], creds) flash("Credentials successfully written!", 'info') except Exception as ex: flash("Error writing credentials %s!" % ex, 'error') return redirect(url_for('manage_creds')) @app.route('/delete_creds') @authorized_with_valid_token def delete_creds(): serviceid = request.args.get('service_id', "") try: cred.delete_cred(serviceid, session["userid"]) flash("Credentials successfully deleted!", 'info') except Exception as ex: flash("Error deleting credentials %s!" % ex, 'error') return redirect(url_for('manage_creds')) @app.route('/addresourcesform/<infid>') @authorized_with_valid_token def addresourcesform(infid=None): access_token = oidc_blueprint.session.token['access_token'] auth_data = utils.getUserAuthData(access_token, cred, session["userid"]) headers = {"Authorization": auth_data, "Accept": "text/plain"} url = "%s/infrastructures/%s/radl" % (settings.imUrl, infid) response = requests.get(url, headers=headers) if response.ok: try: radl = radl_parse.parse_radl(response.text) except Exception as ex: flash("Error parsing RADL: \n%s" % str(ex), 'error') return render_template('addresource.html', infid=infid, systems=radl.systems) else: flash("Error getting RADL: \n%s" % (response.text), 'error') return redirect(url_for('showinfrastructures')) @app.route('/addresources/<infid>', methods=['POST']) @authorized_with_valid_token def addresources(infid=None): access_token = oidc_blueprint.session.token['access_token'] auth_data = utils.getUserAuthData(access_token, cred, session["userid"]) headers = {"Authorization": auth_data, "Accept": "text/plain"} form_data = request.form.to_dict() url = "%s/infrastructures/%s/radl" % (settings.imUrl, infid) response = requests.get(url, headers=headers) if response.ok: try: radl = radl_parse.parse_radl(response.text) radl.deploys = [] for system in radl.systems: if "%s_num" % system.name in form_data: vm_num = int(form_data["%s_num" % system.name]) if vm_num > 0: radl.deploys.append(deploy(system.name, vm_num)) except Exception as ex: flash( "Error parsing RADL: \n%s\n%s" % (str(ex), response.text), 'error') headers = { "Authorization": auth_data, "Accept": "application/json" } url = "%s/infrastructures/%s" % (settings.imUrl, infid) response = requests.post(url, headers=headers, data=str(radl)) if response.ok: num = len(response.json()["uri-list"]) flash("%d nodes added successfully" % num, 'info') else: flash("Error adding nodesL: \n%s" % (response.text), 'error') return redirect(url_for('showinfrastructures')) else: flash("Error getting RADL: \n%s" % (response.text), 'error') return redirect(url_for('showinfrastructures')) @app.route('/logout') def logout(): session.clear() oidc_blueprint.session.get("/logout") return redirect(url_for('login')) @app.errorhandler(403) def forbidden(error): return render_template('error_pages/403.html', message=error.description) @app.errorhandler(404) def page_not_found(error): app.logger.error('Page not found: %s', (request.path)) return render_template('error_pages/404.html'), 404 @app.errorhandler(500) def internal_server_error(error): app.logger.error('Server Error: %s', (error)) return render_template( 'error_pages/500.html', support_email=app.config.get('SUPPORT_EMAIL')), 500 return app
def make_kkbox_blueprint(client_id=None, client_secret=None, authorization_url_params=None, scope=None, redirect_url=None, redirect_to=None, login_url=None, authorized_url=None, rerequest_declined_permissions=False, session_class=None, backend=None): """ Make a blueprint for authenticating with KKBOX using OAuth 2. This requires a client ID and client secret from KKBOX. You should either pass them to this constructor, or make sure that your Flask application config defines them, using the variables KKBOX_OAUTH_CLIENT_ID and KKBOX_OAUTH_CLIENT_SECRET. Args: client_id (str): The client ID for your application on KKBOX. client_secret (str): The client secret for your application on KKBOX scope (str, optional): comma-separated list of scopes for the OAuth token redirect_url (str): the URL to redirect to after the authentication dance is complete redirect_to (str): if ``redirect_url`` is not defined, the name of the view to redirect to after the authentication dance is complete. The actual URL will be determined by :func:`flask.url_for` login_url (str, optional): the URL path for the ``login`` view. Defaults to ``/kkbox`` authorized_url (str, optional): the URL path for the ``authorized`` view. Defaults to ``/kkbox/authorized``. rerequest_declined_permissions (bool, optional): should the blueprint ask again for declined permissions. Defaults to ``False`` session_class (class, optional): The class to use for creating a Requests session. Defaults to :class:`~flask_dance.consumer.requests.OAuth2Session`. backend: A storage backend class, or an instance of a storage backend class, to use for this blueprint. Defaults to :class:`~flask_dance.consumer.backend.session.SessionBackend`. :rtype: :class:`~flask_dance.consumer.OAuth2ConsumerBlueprint` :returns: A :ref:`blueprint <flask:blueprints>` to attach to your Flask app. """ kkbox_bp = OAuth2ConsumerBlueprint( "kkbox", __name__, client_id=client_id, client_secret=client_secret, scope=scope, base_url="https://account.kkbox.com/oauth2/", authorization_url="https://account.kkbox.com/oauth2/authorize/", authorization_url_params=authorization_url_params, token_url="https://account.kkbox.com/oauth2/token/", redirect_url=redirect_url, redirect_to=redirect_to, login_url=login_url, authorized_url=authorized_url, session_class=session_class, backend=backend, ) kkbox_bp.from_config["client_id"] = "KKBOX_OAUTH_CLIENT_ID" kkbox_bp.from_config["client_secret"] = "KKBOX_OAUTH_CLIENT_SECRET" @kkbox_bp.before_app_request def set_applocal_session(): ctx = stack.top ctx.facebook_oauth = kkbox_bp.session return kkbox_bp
def make_meetup_blueprint( key=None, secret=None, scope=None, redirect_url=None, redirect_to=None, login_url=None, authorized_url=None, session_class=None, backend=None, storage=None, ): """ Make a blueprint for authenticating with Meetup using OAuth 2. This requires an OAuth consumer from Meetup. You should either pass the key and secret to this constructor, or make sure that your Flask application config defines them, using the variables MEETUP_OAUTH_KEY and MEETUP_OAUTH_SECRET. Args: key (str): The OAuth consumer key for your application on Meetup secret (str): The OAuth consumer secret for your application on Meetup scope (str, optional): comma-separated list of scopes for the OAuth token redirect_url (str): the URL to redirect to after the authentication dance is complete redirect_to (str): if ``redirect_url`` is not defined, the name of the view to redirect to after the authentication dance is complete. The actual URL will be determined by :func:`flask.url_for` login_url (str, optional): the URL path for the ``login`` view. Defaults to ``/meetup`` authorized_url (str, optional): the URL path for the ``authorized`` view. Defaults to ``/meetup/authorized``. session_class (class, optional): The class to use for creating a Requests session. Defaults to :class:`~flask_dance.consumer.requests.OAuth2Session`. storage: A token storage class, or an instance of a token storage class, to use for this blueprint. Defaults to :class:`~flask_dance.consumer.storage.session.SessionStorage`. :rtype: :class:`~flask_dance.consumer.OAuth2ConsumerBlueprint` :returns: A :ref:`blueprint <flask:blueprints>` to attach to your Flask app. """ scope = scope or ["basic"] meetup_bp = OAuth2ConsumerBlueprint( "meetup", __name__, client_id=key, client_secret=secret, scope=scope, base_url="https://api.meetup.com/2/", authorization_url="https://secure.meetup.com/oauth2/authorize", token_url="https://secure.meetup.com/oauth2/access", redirect_url=redirect_url, redirect_to=redirect_to, login_url=login_url, authorized_url=authorized_url, session_class=session_class or MeetupOAuth2Session, backend=backend, storage=storage, ) meetup_bp.from_config["client_id"] = "MEETUP_OAUTH_KEY" meetup_bp.from_config["client_secret"] = "MEETUP_OAUTH_SECRET" @meetup_bp.before_app_request def set_applocal_session(): ctx = stack.top ctx.meetup_oauth = meetup_bp.session return meetup_bp
def make_linkedin_blueprint( client_id=None, client_secret=None, scope=None, redirect_url=None, redirect_to=None, login_url=None, authorized_url=None, session_class=None, storage=None, ): """ Make a blueprint for authenticating with LinkedIn using OAuth 2. This requires a client ID and client secret from LinkedIn. You should either pass them to this constructor, or make sure that your Flask application config defines them, using the variables :envvar:`LINKEDIN_OAUTH_CLIENT_ID` and :envvar:`LINKEDIN_OAUTH_CLIENT_SECRET`. Args: client_id (str): The client ID for your application on LinkedIn. client_secret (str): The client secret for your application on LinkedIn scope (str, optional): comma-separated list of scopes for the OAuth token redirect_url (str): the URL to redirect to after the authentication dance is complete redirect_to (str): if ``redirect_url`` is not defined, the name of the view to redirect to after the authentication dance is complete. The actual URL will be determined by :func:`flask.url_for` login_url (str, optional): the URL path for the ``login`` view. Defaults to ``/linkedin`` authorized_url (str, optional): the URL path for the ``authorized`` view. Defaults to ``/linkedin/authorized``. session_class (class, optional): The class to use for creating a Requests session. Defaults to :class:`~flask_dance.consumer.requests.OAuth2Session`. storage: A token storage class, or an instance of a token storage class, to use for this blueprint. Defaults to :class:`~flask_dance.consumer.storage.session.SessionStorage`. :rtype: :class:`~flask_dance.consumer.OAuth2ConsumerBlueprint` :returns: A :ref:`blueprint <flask:blueprints>` to attach to your Flask app. """ linkedin_bp = OAuth2ConsumerBlueprint( "linkedin", __name__, client_id=client_id, client_secret=client_secret, scope=scope, base_url="https://api.linkedin.com/v2/", authorization_url="https://www.linkedin.com/oauth/v2/authorization", token_url="https://www.linkedin.com/oauth/v2/accessToken", token_url_params={"include_client_id": True}, redirect_url=redirect_url, redirect_to=redirect_to, login_url=login_url, authorized_url=authorized_url, session_class=session_class, storage=storage, ) linkedin_bp.from_config["client_id"] = "LINKEDIN_OAUTH_CLIENT_ID" linkedin_bp.from_config["client_secret"] = "LINKEDIN_OAUTH_CLIENT_SECRET" @linkedin_bp.before_app_request def set_applocal_session(): ctx = stack.top ctx.linkedin_oauth = linkedin_bp.session return linkedin_bp
def make_google_blueprint( client_id=None, client_secret=None, scope=None, offline=False, reprompt_consent=False, redirect_url=None, redirect_to=None, login_url=None, authorized_url=None, session_class=None, backend=None, hosted_domain=None, ): """ Make a blueprint for authenticating with Google using OAuth 2. This requires a client ID and client secret from Google. You should either pass them to this constructor, or make sure that your Flask application config defines them, using the variables GOOGLE_OAUTH_CLIENT_ID and GOOGLE_OAUTH_CLIENT_SECRET. Args: client_id (str): The client ID for your application on Google client_secret (str): The client secret for your application on Google scope (str, optional): comma-separated list of scopes for the OAuth token. Defaults to the "https://www.googleapis.com/auth/userinfo.profile" scope. offline (bool): Whether to request `offline access <https://developers.google.com/accounts/docs/OAuth2WebServer#offline>`_ for the OAuth token. Defaults to False reprompt_consent (bool): If True, force Google to re-prompt the user for their consent, even if the user has already given their consent. Defaults to False redirect_url (str): the URL to redirect to after the authentication dance is complete redirect_to (str): if ``redirect_url`` is not defined, the name of the view to redirect to after the authentication dance is complete. The actual URL will be determined by :func:`flask.url_for` login_url (str, optional): the URL path for the ``login`` view. Defaults to ``/google`` authorized_url (str, optional): the URL path for the ``authorized`` view. Defaults to ``/google/authorized``. session_class (class, optional): The class to use for creating a Requests session. Defaults to :class:`~flask_dance.consumer.requests.OAuth2Session`. backend: A storage backend class, or an instance of a storage backend class, to use for this blueprint. Defaults to :class:`~flask_dance.consumer.backend.session.SessionBackend`. hosted_domain (str, optional): The domain of the G Suite user. Used to indicate that the account selection UI should be optimized for accounts at this domain. Note that this only provides UI optimization, and requires response validation (see warning). .. _google_hosted_domain_warning: .. warning:: The ``hosted_domain`` argument **only provides UI optimization**. Don't rely on this argument to control who can access your application. You must verify that the ``hd`` claim of the response ID token matches the ``hosted_domain`` argument passed to ``make_google_blueprint``. For example: .. code-block:: python from flask import session, abort from flask_dance.consumer import oauth_authorized from flask_dance.contrib.google import make_google_blueprint, google import requests google_bp = make_google_blueprint( client_id="foo", client_secret="bar", scope=["profile", "email"], hosted_domain="example.com" ) @oauth_authorized.connect_via(google_bp) def logged_in(blueprint, token): resp_json = google.get("/oauth2/v2/userinfo").json() if resp_json["hd"] != blueprint.authorization_url_params["hd"]: requests.post( "https://accounts.google.com/o/oauth2/revoke", params={"token": token["access_token"]} ) session.clear() abort(403) :rtype: :class:`~flask_dance.consumer.OAuth2ConsumerBlueprint` :returns: A :ref:`blueprint <flask:blueprints>` to attach to your Flask app. """ scope = scope or ["https://www.googleapis.com/auth/userinfo.profile"] authorization_url_params = {} auto_refresh_url = None if offline: authorization_url_params["access_type"] = "offline" auto_refresh_url = "https://accounts.google.com/o/oauth2/token" if reprompt_consent: authorization_url_params["approval_prompt"] = "force" if hosted_domain: authorization_url_params["hd"] = hosted_domain google_bp = OAuth2ConsumerBlueprint( "google", __name__, client_id=client_id, client_secret=client_secret, scope=scope, base_url="https://www.googleapis.com/", authorization_url="https://accounts.google.com/o/oauth2/auth", token_url="https://accounts.google.com/o/oauth2/token", auto_refresh_url=auto_refresh_url, redirect_url=redirect_url, redirect_to=redirect_to, login_url=login_url, authorized_url=authorized_url, authorization_url_params=authorization_url_params, session_class=session_class, backend=backend, ) google_bp.from_config["client_id"] = "GOOGLE_OAUTH_CLIENT_ID" google_bp.from_config["client_secret"] = "GOOGLE_OAUTH_CLIENT_SECRET" @google_bp.before_app_request def set_applocal_session(): ctx = stack.top ctx.google_oauth = google_bp.session return google_bp
def make_salesforce_blueprint( client_id=None, client_secret=None, *, scope=None, reprompt_consent=False, hostname=None, is_sandbox=False, redirect_url=None, redirect_to=None, login_url=None, authorized_url=None, session_class=None, storage=None, ): """ Make a blueprint for authenticating with Salesforce using OAuth 2. This requires a client ID and client secret from Salesforce. You should either pass them to this constructor, or make sure that your Flask application config defines them, using the variables :envvar:`SALESFORCE_OAUTH_CLIENT_ID` and :envvar:`SALESFORCE_OAUTH_CLIENT_SECRET`. Args: client_id (str): The client ID for your application on Salesforce. client_secret (str): The client secret for your application on Salesforce. scope (str, optional): comma-separated list of scopes for the OAuth token. reprompt_consent (bool): If True, force Salesforce to re-prompt the user for their consent, even if the user has already given their consent. Defaults to False. hostname (str, optional): The hostname of your Salesforce instance. By default, Salesforce uses ``login.salesforce.com`` for production instances and ``test.salesforce.com`` for sandboxes. is_sandbox (bool): If hostname is not defined specify whether Salesforce instance is a sandbox. Defaults to False. redirect_url (str): the URL to redirect to after the authentication dance is complete. redirect_to (str): if ``redirect_url`` is not defined, the name of the view to redirect to after the authentication dance is complete. The actual URL will be determined by :func:`flask.url_for`. login_url (str, optional): the URL path for the ``login`` view. Defaults to ``/salesforce``. authorized_url (str, optional): the URL path for the ``authorized`` view. Defaults to ``/salesforce/authorized``. session_class (class, optional): The class to use for creating a Requests session. Defaults to :class:`~flask_dance.consumer.requests.OAuth2Session`. storage: A token storage class, or an instance of a token storage class, to use for this blueprint. Defaults to :class:`~flask_dance.consumer.storage.session.SessionStorage`. :rtype: :class:`~flask_dance.consumer.OAuth2ConsumerBlueprint` :returns: A :doc:`blueprint <flask:blueprints>` to attach to your Flask app. """ authorization_url_params = {} if reprompt_consent: authorization_url_params["prompt"] = "consent" if not hostname: hostname = "test.salesforce.com" if is_sandbox else "login.salesforce.com" salesforce_bp = OAuth2ConsumerBlueprint( "salesforce", __name__, client_id=client_id, client_secret=client_secret, scope=scope, base_url=f"https://{hostname}/", authorization_url=f"https://{hostname}/services/oauth2/authorize", token_url=f"https://{hostname}/services/oauth2/token", redirect_url=redirect_url, redirect_to=redirect_to, login_url=login_url, authorized_url=authorized_url, authorization_url_params=authorization_url_params, session_class=session_class, storage=storage, ) salesforce_bp.from_config["client_id"] = "SALESFORCE_OAUTH_CLIENT_ID" salesforce_bp.from_config[ "client_secret"] = "SALESFORCE_OAUTH_CLIENT_SECRET" @salesforce_bp.before_app_request def set_applocal_session(): ctx = stack.top ctx.salesforce_oauth = salesforce_bp.session return salesforce_bp
def make_atlassian_blueprint( client_id=None, client_secret=None, scope=None, reprompt_consent=False, redirect_url=None, redirect_to=None, login_url=None, authorized_url=None, session_class=None, storage=None, ): """ Make a blueprint for authenticating with Atlassian using OAuth 2. This requires a client ID and client secret from Atlassian. You should either pass them to this constructor, or make sure that your Flask application config defines them, using the variables :envvar:`ATLASSIAN_OAUTH_CLIENT_ID` and :envvar:`ATLASSIAN_OAUTH_CLIENT_SECRET`. Args: client_id (str): The client ID for your application on Atlassian. client_secret (str): The client secret for your application on Atlassian. scope (str, optional): comma-separated list of scopes for the OAuth token. reprompt_consent (bool): If True, force Atlassian to re-prompt the user for their consent, even if the user has already given their consent. Defaults to False. redirect_url (str): the URL to redirect to after the authentication dance is complete. redirect_to (str): if ``redirect_url`` is not defined, the name of the view to redirect to after the authentication dance is complete. The actual URL will be determined by :func:`flask.url_for`. login_url (str, optional): the URL path for the ``login`` view. Defaults to ``/atlassian``. authorized_url (str, optional): the URL path for the ``authorized`` view. Defaults to ``/atlassian/authorized``. session_class (class, optional): The class to use for creating a Requests session. Defaults to :class:`~flask_dance.consumer.requests.OAuth2Session`. storage: A token storage class, or an instance of a token storage class, to use for this blueprint. Defaults to :class:`~flask_dance.consumer.storage.session.SessionStorage`. :rtype: :class:`~flask_dance.consumer.OAuth2ConsumerBlueprint` :returns: A :ref:`blueprint <flask:blueprints>` to attach to your Flask app. """ authorization_url_params = {"audience": "api.atlassian.com"} if reprompt_consent: authorization_url_params["prompt"] = "consent" atlassian_bp = OAuth2ConsumerBlueprint( "atlassian", __name__, client_id=client_id, client_secret=client_secret, scope=scope, base_url="https://api.atlassian.com/", authorization_url="https://auth.atlassian.com/authorize", token_url="https://auth.atlassian.com/oauth/token", redirect_url=redirect_url, redirect_to=redirect_to, login_url=login_url, authorized_url=authorized_url, authorization_url_params=authorization_url_params, session_class=session_class, storage=storage, ) atlassian_bp.from_config["client_id"] = "ATLASSIAN_OAUTH_CLIENT_ID" atlassian_bp.from_config["client_secret"] = "ATLASSIAN_OAUTH_CLIENT_SECRET" @atlassian_bp.before_app_request def set_applocal_session(): ctx = stack.top ctx.atlassian_oauth = atlassian_bp.session return atlassian_bp
def make_azure_blueprint(client_id=None, client_secret=None, scope=None, redirect_url=None, redirect_to=None, login_url=None, authorized_url=None, session_class=None, backend=None, tenant="common"): """ Make a blueprint for authenticating with Azure AD using OAuth 2. This requires a client ID and client secret from Azure AD. You should either pass them to this constructor, or make sure that your Flask application config defines them, using the variables AZURE_OAUTH_CLIENT_ID and AZURE_OAUTH_CLIENT_SECRET. Args: client_id (str): The client ID for your application on Azure AD. client_secret (str): The client secret for your application on Azure AD scope (str, optional): comma-separated list of scopes for the OAuth token redirect_url (str): the URL to redirect to after the authentication dance is complete redirect_to (str): if ``redirect_url`` is not defined, the name of the view to redirect to after the authentication dance is complete. The actual URL will be determined by :func:`flask.url_for` login_url (str, optional): the URL path for the ``login`` view. Defaults to ``/azure`` authorized_url (str, optional): the URL path for the ``authorized`` view. Defaults to ``/azure/authorized``. session_class (class, optional): The class to use for creating a Requests session. Defaults to :class:`~flask_dance.consumer.requests.OAuth2Session`. backend: A storage backend class, or an instance of a storage backend class, to use for this blueprint. Defaults to :class:`~flask_dance.consumer.backend.session.SessionBackend`. tenant: Determine which accounts are allowed to authenticate with Azure. `See the Azure documentation for more information about this parameter. <https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols#endpoints>`_ Defaults to ``common``. :rtype: :class:`~flask_dance.consumer.OAuth2ConsumerBlueprint` :returns: A :ref:`blueprint <flask:blueprints>` to attach to your Flask app. """ scope = scope or ["openid", "email", "profile", "User.Read"] azure_bp = OAuth2ConsumerBlueprint( "azure", __name__, client_id=client_id, client_secret=client_secret, scope=scope, base_url="https://graph.microsoft.com", authorization_url= "https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize". format(tenant=tenant), token_url="https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token" .format(tenant=tenant), redirect_url=redirect_url, redirect_to=redirect_to, login_url=login_url, authorized_url=authorized_url, session_class=session_class, backend=backend, ) azure_bp.from_config["client_id"] = "AZURE_OAUTH_CLIENT_ID" azure_bp.from_config["client_secret"] = "AZURE_OAUTH_CLIENT_SECRET" @azure_bp.before_app_request def set_applocal_session(): ctx = stack.top ctx.azure_oauth = azure_bp.session return azure_bp
def make_facebook_blueprint(client_id=None, client_secret=None, scope=None, redirect_url=None, redirect_to=None, login_url=None, authorized_url=None, session_class=None, backend=None): """ Make a blueprint for authenticating with Facebook using OAuth 2. This requires a client ID and client secret from Facebook. You should either pass them to this constructor, or make sure that your Flask application config defines them, using the variables FACEBOOK_OAUTH_CLIENT_ID and FACEBOOK_OAUTH_CLIENT_SECRET. Args: client_id (str): The client ID for your application on Facebook. client_secret (str): The client secret for your application on Facebook scope (str, optional): comma-separated list of scopes for the OAuth token redirect_url (str): the URL to redirect to after the authentication dance is complete redirect_to (str): if ``redirect_url`` is not defined, the name of the view to redirect to after the authentication dance is complete. The actual URL will be determined by :func:`flask.url_for` login_url (str, optional): the URL path for the ``login`` view. Defaults to ``/facebook`` authorized_url (str, optional): the URL path for the ``authorized`` view. Defaults to ``/facebook/authorized``. session_class (class, optional): The class to use for creating a Requests session. Defaults to :class:`~flask_dance.consumer.requests.OAuth2Session`. backend: A storage backend class, or an instance of a storage backend class, to use for this blueprint. Defaults to :class:`~flask_dance.consumer.backend.session.SessionBackend`. :rtype: :class:`~flask_dance.consumer.OAuth2ConsumerBlueprint` :returns: A :ref:`blueprint <flask:blueprints>` to attach to your Flask app. """ facebook_bp = OAuth2ConsumerBlueprint( "facebook", __name__, client_id=client_id, client_secret=client_secret, scope=scope, base_url="https://graph.facebook.com/", authorization_url="https://www.facebook.com/dialog/oauth", token_url="https://graph.facebook.com/oauth/access_token", redirect_url=redirect_url, redirect_to=redirect_to, login_url=login_url, authorized_url=authorized_url, session_class=session_class, backend=backend, ) facebook_bp.from_config["client_id"] = "FACEBOOK_OAUTH_CLIENT_ID" facebook_bp.from_config["client_secret"] = "FACEBOOK_OAUTH_CLIENT_SECRET" @facebook_bp.before_app_request def set_applocal_session(): ctx = stack.top ctx.facebook_oauth = facebook_bp.session return facebook_bp
from os import environ from flask_dance.consumer import OAuth2ConsumerBlueprint onshape = OAuth2ConsumerBlueprint( "onshape", __name__, base_url="https://cad.onshape.com/api/", authorization_url="https://oauth.onshape.com/oauth/authorize", token_url="https://oauth.onshape.com/oauth/token", ) onshape.from_config["client_id"] = "ONSHAPE_CLIENT_ID" onshape.from_config["client_secret"] = "ONSHAPE_CLIENT_SECRET"
def make_figshare_blueprint( client_id=None, client_secret=None, scope=None, redirect_url=None, redirect_to=None, login_url=None, authorized_url=None, rerequest_declined_permissions=False, session_class=None, storage=None, ): """ Make a blueprint for authenticating with Facebook using OAuth 2. This requires a client ID and client secret from Facebook. You should either pass them to this constructor, or make sure that your Flask application config defines them, using the variables :envvar:`FIGSHARE_OAUTH_CLIENT_ID` and :envvar:`FIGSHARE_OAUTH_CLIENT_SECRET`. Args: client_id (str): The client ID for your application on Figshare. client_secret (str): The client secret for your application on Figshare scope (str, optional): comma-separated list of scopes for the OAuth token redirect_url (str): the URL to redirect to after the authentication dance is complete redirect_to (str): if ``redirect_url`` is not defined, the name of the view to redirect to after the authentication dance is complete. The actual URL will be determined by :func:`flask.url_for` login_url (str, optional): the URL path for the ``login`` view. Defaults to ``/figshare`` authorized_url (str, optional): the URL path for the ``authorized`` view. Defaults to ``/figshare/authorized``. rerequest_declined_permissions (bool, optional): should the blueprint ask again for declined permissions. Defaults to ``False`` session_class (class, optional): The class to use for creating a Requests session. Defaults to :class:`~flask_dance.consumer.requests.OAuth2Session`. storage: A token storage class, or an instance of a token storage class, to use for this blueprint. Defaults to :class:`~flask_dance.consumer.storage.session.SessionStorage`. :rtype: :class:`~flask_dance.consumer.OAuth2ConsumerBlueprint` :returns: A :ref:`blueprint <flask:blueprints>` to attach to your Flask app. """ authorization_url_params = {} if rerequest_declined_permissions: authorization_url_params["auth_type"] = "rerequest" figshare_bp = OAuth2ConsumerBlueprint( "figshare", __name__, client_id=client_id, client_secret=client_secret, scope=scope, base_url="https://api.figshare.com/v2", authorization_url='https://figshare.com/account/applications/authorize', authorization_url_params=authorization_url_params, token_url='https://api.figshare.com/v2/token', redirect_url=redirect_url, redirect_to=redirect_to, login_url=login_url, authorized_url=authorized_url, session_class=session_class, storage=storage, ) figshare_bp.from_config["client_id"] = "FIGSHARE_OAUTH_CLIENT_ID" figshare_bp.from_config["client_secret"] = "FIGSHARE_OAUTH_CLIENT_SECRET" @figshare_bp.before_app_request def set_applocal_session(): ctx = stack.top ctx.figshare_oauth = figshare_bp.session return figshare_bp
def create_app(oidc_blueprint=None): app = Flask(__name__) app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_port=1, x_prefix=1) app.secret_key = "8210f566-4981-11ea-92d1-f079596e599b" app.config.from_json('config.json') settings = Settings(app.config) if 'CREDS_KEY' in os.environ: key = os.environ['CREDS_KEY'] else: key = None CSRFProtect(app) cred = Credentials(settings.db_url, key) infra = Infrastructures(settings.db_url) im = InfrastructureManager(settings.imUrl) # To Reload internally the site cache scheduler = APScheduler() scheduler.api_enabled = False scheduler.init_app(app) scheduler.start() toscaTemplates = utils.loadToscaTemplates(settings.toscaDir) toscaInfo = utils.extractToscaInfo(settings.toscaDir, settings.toscaParamsDir, toscaTemplates) app.jinja_env.filters['tojson_pretty'] = utils.to_pretty_json app.logger.debug("TOSCA INFO: " + json.dumps(toscaInfo)) loglevel = app.config.get("LOG_LEVEL") if app.config.get( "LOG_LEVEL") else "INFO" numeric_level = getattr(logging, loglevel.upper(), None) if not isinstance(numeric_level, int): raise ValueError('Invalid log level: %s' % loglevel) logging.basicConfig(level=numeric_level) oidc_base_url = app.config['OIDC_BASE_URL'] oidc_token_url = oidc_base_url + '/token' oidc_refresh_url = oidc_base_url + '/token' oidc_authorization_url = oidc_base_url + '/authorize' if not oidc_blueprint: oidc_blueprint = OAuth2ConsumerBlueprint( "oidc", __name__, client_id=app.config['OIDC_CLIENT_ID'], client_secret=app.config['OIDC_CLIENT_SECRET'], scope=app.config['OIDC_SCOPES'], base_url=oidc_base_url, token_url=oidc_token_url, auto_refresh_url=oidc_refresh_url, authorization_url=oidc_authorization_url, redirect_to='home') app.register_blueprint(oidc_blueprint, url_prefix="/login") @app.before_request def before_request_checks(): if 'external_links' not in session: session['external_links'] = settings.external_links g.analytics_tag = settings.analytics_tag g.settings = settings def authorized_with_valid_token(f): @wraps(f) def decorated_function(*args, **kwargs): if settings.debug_oidc_token: oidc_blueprint.session.token = { 'access_token': settings.debug_oidc_token } else: try: if not oidc_blueprint.session.authorized or 'username' not in session: return redirect(url_for('login')) if oidc_blueprint.session.token['expires_in'] < 20: app.logger.debug("Force refresh token") oidc_blueprint.session.get('/userinfo') except (InvalidTokenError, TokenExpiredError): flash("Token expired.", 'warning') return redirect(url_for('login')) return f(*args, **kwargs) return decorated_function @app.route('/settings') @authorized_with_valid_token def show_settings(): imUrl = "%s (v. %s)" % (settings.imUrl, im.get_version()) return render_template('settings.html', oidc_url=settings.oidcUrl, im_url=imUrl) @app.route('/login') def login(): session.clear() return render_template('home.html', oidc_name=settings.oidcName) @app.route('/') def home(): if settings.debug_oidc_token: session["vos"] = None session['userid'] = "userid" session['username'] = "******" session['gravatar'] = "" return render_template('portfolio.html', templates=toscaInfo) else: if not oidc_blueprint.session.authorized: return redirect(url_for('login')) try: account_info = oidc_blueprint.session.get( urlparse(settings.oidcUrl)[2] + "/userinfo") except (InvalidTokenError, TokenExpiredError): flash("Token expired.", 'warning') return redirect(url_for('login')) if account_info.ok: account_info_json = account_info.json() session["vos"] = None if 'eduperson_entitlement' in account_info_json: session["vos"] = utils.getUserVOs( account_info_json['eduperson_entitlement']) if settings.oidcGroups: user_groups = [] if 'groups' in account_info_json: user_groups = account_info_json['groups'] elif 'eduperson_entitlement' in account_info_json: user_groups = account_info_json[ 'eduperson_entitlement'] if not set(settings.oidcGroups).issubset(user_groups): app.logger.debug( "No match on group membership. User group membership: " + json.dumps(user_groups)) message = Markup( 'You need to be a member of the following groups: {0}. <br>' ' Please, visit <a href="{1}">{1}</a> and apply for the requested ' 'membership.'.format( json.dumps(settings.oidcGroups), settings.oidcUrl)) raise Forbidden(description=message) session['userid'] = account_info_json['sub'] if 'name' in account_info_json: session['username'] = account_info_json['name'] else: session['username'] = "" if 'given_name' in account_info_json: session['username'] = account_info_json['given_name'] if 'family_name' in account_info_json: session['username'] += " " + account_info_json[ 'family_name'] if session['username'] == "": session['username'] = account_info_json['sub'] if 'email' in account_info_json: session['gravatar'] = utils.avatar( account_info_json['email'], 26) else: session['gravatar'] = utils.avatar( account_info_json['sub'], 26) return render_template('portfolio.html', templates=toscaInfo) else: flash("Error getting User info: \n" + account_info.text, 'error') return render_template('home.html', oidc_name=settings.oidcName) @app.route('/vminfo') @authorized_with_valid_token def showvminfo(): access_token = oidc_blueprint.session.token['access_token'] vmid = request.args['vmId'] infid = request.args['infId'] auth_data = utils.getUserAuthData(access_token, cred, session["userid"]) try: response = im.get_vm_info(infid, vmid, auth_data) except Exception as ex: flash("Error: %s." % ex, 'error') vminfo = {} state = "" nets = "" disks = "" deployment = "" if not response.ok: flash("Error retrieving VM info: \n" + response.text, 'error') else: app.logger.debug("VM Info: %s" % response.text) radl_json = response.json()["radl"] outports = utils.get_out_ports(radl_json) vminfo = utils.format_json_radl(radl_json) if "cpu.arch" in vminfo: del vminfo["cpu.arch"] if "state" in vminfo: state = vminfo["state"] del vminfo["state"] if "provider.type" in vminfo: deployment = vminfo["provider.type"] del vminfo["provider.type"] if "provider.host" in vminfo: if "provider.port" in vminfo: deployment += ": %s:%s" % (vminfo["provider.host"], vminfo["provider.port"]) del vminfo["provider.port"] else: deployment += ": " + vminfo["provider.host"] del vminfo["provider.host"] if "disk.0.os.name" in vminfo: del vminfo["disk.0.os.name"] cont = 0 while "net_interface.%s.connection" % cont in vminfo: if "net_interface.%s.ip" % cont in vminfo: if cont > 0: nets += Markup('<br/>') nets += Markup('<i class="fa fa-network-wired"></i>') nets += Markup( ' <span class="badge badge-secondary">%s</span>' % cont) nets += ": %s" % vminfo["net_interface.%s.ip" % cont] del vminfo["net_interface.%s.ip" % cont] if "net_interface.%s.dns_name" % cont in vminfo: nets += " (%s)" % vminfo["net_interface.%s.dns_name" % cont] del vminfo["net_interface.%s.dns_name" % cont] cont += 1 cont = 0 while "net_interface.%s.connection" % cont in vminfo: del vminfo["net_interface.%s.connection" % cont] cont += 1 for elem in vminfo: if elem.endswith("size") and isinstance(vminfo[elem], int): vminfo[elem] = "%d GB" % (vminfo[elem] / 1073741824) cont = 0 while "disk.%s.size" % cont in vminfo or "disk.%s.image.url" % cont in vminfo: if cont > 0: disks += Markup('<br/>') disks += Markup( '<i class="fa fa-database"></i> <span class="badge badge-secondary">' '%s</span><br/>' % cont) prop_map = { "size": "Size", "image.url": "URL", "device": "Device", "mount_path": "Mount Path", "fstype": "F.S. type", "os.flavour": "O.S. Flavor", "os.version": "O.S. Version" } for name, label in prop_map.items(): prop = "disk.%s.%s" % (cont, name) if prop in vminfo: disks += Markup(' ') disks += "- %s: %s" % (label, vminfo[prop]) disks += Markup('<br/>') del vminfo[prop] cont += 1 str_outports = "" for port in outports: str_outports += Markup( '<i class="fas fa-project-diagram"></i> <span class="badge ' 'badge-secondary">%s</span>' % port.get_remote_port()) if not port.is_range(): if port.get_remote_port() != port.get_local_port(): str_outports += Markup( ' <i class="fas fa-long-arrow-alt-right">' '</i> <span class="badge badge-secondary">%s</span>' % port.get_local_port()) else: str_outports += Markup( ' : </i> <span class="badge badge-secondary">%s</span>' % port.get_local_port()) str_outports += Markup('<br/>') return render_template('vminfo.html', infid=infid, vmid=vmid, vminfo=vminfo, outports=str_outports, state=state, nets=nets, deployment=deployment, disks=disks) @app.route('/managevm/<op>/<infid>/<vmid>', methods=['POST']) @authorized_with_valid_token def managevm(op=None, infid=None, vmid=None): access_token = oidc_blueprint.session.token['access_token'] auth_data = utils.getUserAuthData(access_token, cred, session["userid"]) try: if op == "reconfigure": response = im.reconfigure_inf(infid, auth_data, [vmid]) if op == "resize": form_data = request.form.to_dict() cpu = int(form_data['cpu']) memory = int(form_data['memory']) system_name = form_data['system_name'] radl = "system %s (cpu.count >= %d and memory.size >= %dg)" % ( system_name, cpu, memory) response = im.resize_vm(infid, vmid, radl, auth_data) else: response = im.manage_vm(op, infid, vmid, auth_data) except Exception as ex: flash("Error: %s." % ex, 'error') return redirect(url_for('showinfrastructures')) if response.ok: flash("Operation '%s' successfully made on VM ID: %s" % (op, vmid), 'info') else: flash( "Error making %s op on VM %s: \n%s" % (op, vmid, response.text), 'error') if op == "terminate": return redirect(url_for('showinfrastructures')) else: return redirect(url_for('showvminfo', infId=infid, vmId=vmid)) @app.route('/infrastructures') @authorized_with_valid_token def showinfrastructures(): access_token = oidc_blueprint.session.token['access_token'] reload_infid = None if 'reload' in request.args: reload_infid = request.args['reload'] auth_data = "type = InfrastructureManager; token = %s" % access_token inf_list = [] try: inf_list = im.get_inf_list(auth_data) except Exception as ex: flash("Error: %s." % ex, 'error') infrastructures = {} for inf_id in inf_list: infrastructures[inf_id] = {} try: infra_data = infra.get_infra(inf_id) except Exception: infra_data = {} infrastructures[inf_id] = {'name': '', 'state': {}} if 'name' in infra_data: infrastructures[inf_id]['name'] = infra_data["name"] if 'state' in infra_data: infrastructures[inf_id]['state'] = infra_data["state"] return render_template('infrastructures.html', infrastructures=infrastructures, reload=reload_infid) @app.route('/infrastructures/state') @authorized_with_valid_token def infrastructure_state(): access_token = oidc_blueprint.session.token['access_token'] infid = request.args['infid'] if not infid: return {"state": "error", "vm_states": {}} auth_data = utils.getUserAuthData(access_token, cred, session["userid"]) try: state = im.get_inf_state(infid, auth_data) try: infra.write_infra(infid, {"state": state}) except Exception as ex: app.logger.error("Error saving infrastructure state: %s" % ex) return state except Exception: return {"state": "error", "vm_states": {}} @app.route('/template/<infid>') @authorized_with_valid_token def template(infid=None): access_token = oidc_blueprint.session.token['access_token'] auth_data = utils.getUserAuthData(access_token, cred, session["userid"]) template = "" try: response = im.get_inf_property(infid, 'tosca', auth_data) if not response.ok: raise Exception(response.text) template = response.text except Exception as ex: flash("Error getting template: \n%s" % ex, "error") return render_template('deptemplate.html', template=template) def add_colors(log): """Add color in error messages in logs.""" res = "" lines = log.split('\n') for n, line in enumerate(lines): if "ERROR executing task" in line or ( "fatal: " in line and "...ignoring" not in lines[n + 1]): res += Markup( '<span class="bg-danger text-white">%s</span><br>' % line) else: res += Markup("%s<br>" % line) return res @app.route('/log/<infid>') @authorized_with_valid_token def inflog(infid=None): access_token = oidc_blueprint.session.token['access_token'] auth_data = utils.getUserAuthData(access_token, cred, session["userid"]) log = "Not found" try: response = im.get_inf_property(infid, 'contmsg', auth_data) if not response.ok: raise Exception(response.text) log = add_colors(response.text) except Exception as ex: flash("Error: %s." % ex, 'error') return render_template('inflog.html', log=log) @app.route('/vmlog/<infid>/<vmid>') @authorized_with_valid_token def vmlog(infid=None, vmid=None): access_token = oidc_blueprint.session.token['access_token'] auth_data = utils.getUserAuthData(access_token, cred, session["userid"]) log = "Not found" try: response = im.get_vm_contmsg(infid, vmid, auth_data) if not response.ok: raise Exception(response.text) log = add_colors(response.text) except Exception as ex: flash("Error: %s." % ex, 'error') return render_template('inflog.html', log=log, vmid=vmid) @app.route('/outputs/<infid>') @authorized_with_valid_token def infoutputs(infid=None): access_token = oidc_blueprint.session.token['access_token'] auth_data = utils.getUserAuthData(access_token, cred, session["userid"]) outputs = {} try: response = im.get_inf_property(infid, 'outputs', auth_data) if not response.ok: raise Exception(response.text) outputs = response.json()["outputs"] for elem in outputs: if isinstance(outputs[elem], str) and (outputs[elem].startswith('http://') or outputs[elem].startswith('https://')): outputs[elem] = Markup( "<a href='%s' target='_blank'>%s</a>" % (outputs[elem], outputs[elem])) except Exception as ex: flash("Error: %s." % ex, 'error') return render_template('outputs.html', infid=infid, outputs=outputs) @app.route('/configure') @authorized_with_valid_token def configure(): selected_tosca = request.args['selected_tosca'] app.logger.debug("Template: " + json.dumps(toscaInfo[selected_tosca])) creds = cred.get_creds(session['userid'], 1) return render_template('createdep.html', template=toscaInfo[selected_tosca], selectedTemplate=selected_tosca, creds=creds) @app.route('/vos') def getvos(): res = "" vos = utils.getStaticVOs() vos.extend(appdb.get_vo_list()) vos = list(set(vos)) if "vos" in session and session["vos"]: vos = [vo for vo in vos if vo in session["vos"]] for vo in vos: res += '<option name="selectedVO" value=%s>%s</option>' % (vo, vo) return res @app.route('/sites/<vo>') def getsites(vo=None): res = "" static_sites = utils.getStaticSites(vo) for site_name, site in static_sites.items(): res += '<option name="selectedSite" value=%s>%s</option>' % ( site['url'], site_name) appdb_sites = appdb.get_sites(vo) for site_name, site in appdb_sites.items(): # avoid site duplication if site_name not in static_sites: if site["state"]: site["state"] = " (WARNING: %s state!)" % site["state"] res += '<option name="selectedSite" value=%s>%s%s</option>' % ( site['url'], site_name, site["state"]) return res @app.route('/images/<cred_id>') @authorized_with_valid_token def getimages(cred_id=None): res = "" local = request.args.get('local', None) if local: access_token = oidc_blueprint.session.token['access_token'] auth_data = utils.getUserAuthData(access_token, cred, session["userid"], cred_id) try: response = im.get_cloud_images(cred_id, auth_data) if not response.ok: raise Exception(response.text) for image in response.json()["images"]: res += '<option name="selectedSiteImage" value=%s>%s</option>' % ( image['uri'], image['name']) except Exception as ex: res += '<option name="selectedSiteImage" value=%s>%s</option>' % ( ex, ex) else: site, _, vo = utils.get_site_info(cred_id, cred, session["userid"]) for image_name, image_id in appdb.get_images(site['id'], vo): res += '<option name="selectedImage" value=%s>%s</option>' % ( image_id, image_name) return res @app.route('/usage/<cred_id>') @authorized_with_valid_token def getusage(cred_id=None): access_token = oidc_blueprint.session.token['access_token'] auth_data = utils.getUserAuthData(access_token, cred, session["userid"], cred_id) try: response = im.get_cloud_quotas(cred_id, auth_data) if not response.ok: raise Exception(response.text) return json.dumps(response.json()["quotas"]) except Exception as ex: return "Error loading site quotas: %s!" % str(ex), 400 def add_image_to_template(template, image): # Add the image to all compute nodes for node in list( template['topology_template']['node_templates'].values()): if node["type"] == "tosca.nodes.indigo.Compute": node["capabilities"]["os"]["properties"]["image"] = image app.logger.debug(yaml.dump(template, default_flow_style=False)) return template def add_auth_to_template(template, auth_data): # Add the auth_data ElasticCluster node for node in list( template['topology_template']['node_templates'].values()): if node["type"] == "tosca.nodes.ec3.ElasticCluster": if "properties" not in node: node["properties"] = {} node["properties"]["im_auth"] = auth_data app.logger.debug(yaml.dump(template, default_flow_style=False)) return template def add_record_name_to_template(template, replace_name): # Add a random name in the DNS record name for node in list( template['topology_template']['node_templates'].values()): if node["type"] == "tosca.nodes.ec3.DNSRegistry": node["properties"]["record_name"] = node["properties"][ "record_name"].replace("*", replace_name) return template def set_inputs_to_template(template, inputs): # Add the image to all compute nodes for name, value in template['topology_template']['inputs'].items(): if name in inputs: if value["type"] == "integer": value["default"] = int(inputs[name]) elif value["type"] == "float": value["default"] = float(inputs[name]) elif value["type"] == "boolean": if inputs[name].lower() in ['yes', 'true', '1']: value["default"] = True else: value["default"] = False # Special case for ports, convert a comma separated list of ints # to a PortSpec map elif value["type"] == "map" and name == "ports": ports = inputs[name].split(",") ports_value = {} for port in ports: # Should we also open UDP? ports_value["port_%s" % port] = { "protocol": "tcp", "source": int(port) } value["default"] = ports_value else: value["default"] = inputs[name] app.logger.debug(yaml.dump(template, default_flow_style=False)) return template @app.route('/submit', methods=['POST']) @authorized_with_valid_token def createdep(): form_data = request.form.to_dict() app.logger.debug("Form data: " + json.dumps(request.form.to_dict())) cred_id = form_data['extra_opts.selectedCred'] cred_data = cred.get_cred(cred_id, session["userid"]) access_token = oidc_blueprint.session.token['access_token'] image = None if cred_data['type'] in [ 'fedcloud', 'OpenStack', 'OpenNebula', 'Linode', 'Orange', 'GCE' ]: if form_data['extra_opts.selectedImage'] != "": site, _, vo = utils.get_site_info(cred_id, cred, session["userid"]) image = "appdb://%s/%s?%s" % ( site['name'], form_data['extra_opts.selectedImage'], vo) elif form_data['extra_opts.selectedSiteImage'] != "": image = form_data['extra_opts.selectedSiteImage'] else: image_id = form_data['extra_opts.imageID'] protocol_map = { 'EC2': 'aws', 'Kubernetes': 'docker', 'Azure': 'azr' } image = "%s://%s" % (protocol_map.get(cred_data['type']), image_id) if not image: flash("No correct image specified.", "error") return redirect(url_for('showinfrastructures')) auth_data = utils.getUserAuthData(access_token, cred, session["userid"], cred_id) with io.open(settings.toscaDir + request.args.get('template')) as stream: template = yaml.full_load(stream) template = add_image_to_template(template, image) template = add_image_to_template(template, image) template = add_auth_to_template(template, auth_data) # Specially added for OSCAR clusters template = add_record_name_to_template(template, utils.generate_random_name()) inputs = { k: v for (k, v) in form_data.items() if not k.startswith("extra_opts.") } app.logger.debug("Parameters: " + json.dumps(inputs)) template = set_inputs_to_template(template, inputs) payload = yaml.dump(template, default_flow_style=False, sort_keys=False) try: response = im.create_inf(payload, auth_data) if not response.ok: raise Exception(response.text) try: inf_id = os.path.basename(response.text) infra.write_infra( inf_id, { "name": form_data['infra_name'], "state": { "state": "pending", "vm_states": {} } }) except Exception as ex: flash("Error storing Infrastructure name: %s" % str(ex), "warning") except Exception as ex: flash("Error creating infrastrucrure: \n%s." % ex, 'error') return redirect(url_for('showinfrastructures')) @app.route('/manage_creds') @authorized_with_valid_token def manage_creds(): creds = {} try: creds = cred.get_creds(session["userid"]) except Exception as e: flash("Error retrieving credentials: \n" + str(e), 'warning') return render_template('service_creds.html', creds=creds) @app.route('/write_creds', methods=['GET', 'POST']) @authorized_with_valid_token def write_creds(): cred_id = request.args.get('cred_id', "") cred_type = request.args.get('cred_type', "") app.logger.debug("service_id={}".format(cred_id)) if request.method == 'GET': res = {} try: if cred_id: res = cred.get_cred(cred_id, session["userid"]) cred_type = res['type'] except Exception as ex: flash("Error reading credentials %s!" % ex, 'error') return render_template('modal_creds.html', creds=res, cred_id=cred_id, cred_type=cred_type) else: app.logger.debug("Form data: " + json.dumps(request.form.to_dict())) creds = request.form.to_dict() if 'cred_id' in creds: cred_id = creds['cred_id'] del creds['cred_id'] if 'password' in request.files: if request.files['password'].filename != "": creds['password'] = request.files['password'].read( ).decode() try: if 'password' in creds and creds['password'] in [None, '']: del creds['password'] if 'csrf_token' in creds: del creds['csrf_token'] cred.write_creds(creds["id"], session["userid"], creds, cred_id in [None, '']) flash("Credentials successfully written!", 'info') except db.IntegrityError: flash("Error writing credentials: Duplicated Credential ID!", 'error') except Exception as ex: flash("Error writing credentials: %s!" % ex, 'error') return redirect(url_for('manage_creds')) @app.route('/delete_creds') @authorized_with_valid_token def delete_creds(): cred_id = request.args.get('cred_id', "") try: cred.delete_cred(cred_id, session["userid"]) flash("Credentials successfully deleted!", 'info') except Exception as ex: flash("Error deleting credentials %s!" % ex, 'error') return redirect(url_for('manage_creds')) @app.route('/enable_creds') @authorized_with_valid_token def enable_creds(): cred_id = request.args.get('cred_id', "") enable = request.args.get('enable', 0) try: cred.enable_cred(cred_id, session["userid"], enable) except Exception as ex: flash("Error updating credentials %s!" % ex, 'error') return redirect(url_for('manage_creds')) @app.route('/addresourcesform/<infid>') @authorized_with_valid_token def addresourcesform(infid=None): access_token = oidc_blueprint.session.token['access_token'] auth_data = utils.getUserAuthData(access_token, cred, session["userid"]) try: response = im.get_inf_property(infid, 'radl', auth_data) if not response.ok: raise Exception(response.text) systems = [] try: radl = radl_parse.parse_radl(response.text) systems = radl.systems except Exception as ex: flash("Error parsing RADL: \n%s" % str(ex), 'error') return render_template('addresource.html', infid=infid, systems=systems) except Exception as ex: flash("Error getting RADL: \n%s" % ex, 'error') return redirect(url_for('showinfrastructures')) @app.route('/addresources/<infid>', methods=['POST']) @authorized_with_valid_token def addresources(infid=None): access_token = oidc_blueprint.session.token['access_token'] form_data = request.form.to_dict() auth_data = utils.getUserAuthData(access_token, cred, session["userid"]) try: response = im.get_inf_property(infid, 'radl', auth_data) except Exception as ex: flash("Error: %s." % ex, 'error') if response.ok: radl = None try: radl = radl_parse.parse_radl(response.text) radl.deploys = [] for system in radl.systems: sys_dep = deploy(system.name, 0) if "%s_num" % system.name in form_data: vm_num = int(form_data["%s_num" % system.name]) if vm_num > 0: sys_dep.vm_number = vm_num radl.deploys.append(sys_dep) except Exception as ex: flash( "Error parsing RADL: \n%s\n%s" % (str(ex), response.text), 'error') if radl: try: response = im.addresource_inf(infid, str(radl), auth_data) if not response.ok: raise Exception(response.text) num = len(response.json()["uri-list"]) flash("%d nodes added successfully" % num, 'info') except Exception as ex: flash("Error adding nodes: \n%s\n%s" % (ex, response.text), 'error') return redirect(url_for('showinfrastructures')) else: flash("Error getting RADL: \n%s" % (response.text), 'error') return redirect(url_for('showinfrastructures')) @app.route('/manage_inf/<infid>/<op>', methods=['POST']) @authorized_with_valid_token def manage_inf(infid=None, op=None): access_token = oidc_blueprint.session.token['access_token'] auth_data = utils.getUserAuthData(access_token, cred, session["userid"]) reload = None try: if op in ["start", "stop"]: response = im.manage_inf(op, infid, auth_data) if not response.ok: raise Exception(response.text) flash( "Operation '%s' successfully made on Infrastructure ID: %s" % (op, infid), 'info') reload = infid elif op == "delete": form_data = request.form.to_dict() force = False if 'force' in form_data and form_data['force'] != "0": force = True # Specially added for OSCAR clusters success, msg = utils.delete_dns_record(infid, im, auth_data) if not success: app.logger.error('Error deleting DNS record: %s', (msg)) response = im.delete_inf(infid, force, auth_data) if not response.ok: raise Exception(response.text) flash("Infrastructure '%s' successfuly deleted." % infid, "info") try: infra_data = infra.get_infra(infid) infra_data["state"]["state"] = "deleting" infra.write_infra(infid, infra_data) scheduler.add_job('delete_infra_%s' % infid, delete_infra, trigger='interval', seconds=60, args=(infid, )) except Exception as dex: app.logger.error( 'Error setting infra state to deleting.: %s', (dex)) elif op == "reconfigure": response = im.reconfigure_inf(infid, auth_data) if not response.ok: raise Exception(response.text) flash("Reconfiguration process successfuly started.", "info") except Exception as ex: flash("Error in '%s' operation: %s." % (op, ex), 'error') return redirect(url_for('showinfrastructures', reload=reload)) @app.route('/logout') def logout(): session.clear() oidc_blueprint.session.get("/logout") return redirect(url_for('login')) @app.errorhandler(403) def forbidden(error): return render_template('error_pages/403.html', message=error.description) @app.errorhandler(404) def page_not_found(error): app.logger.error('Page not found: %s', (request.path)) return render_template('error_pages/404.html'), 404 @app.errorhandler(500) def internal_server_error(error): app.logger.error('Server Error: %s', (error)) return render_template( 'error_pages/500.html', support_email=app.config.get('SUPPORT_EMAIL')), 500 # Reload internally the site cache @scheduler.task('interval', id='reload_sites', seconds=5) def reload_sites(): scheduler.modify_job('reload_sites', trigger='interval', seconds=settings.appdb_cache_timeout - 30) with app.app_context(): app.logger.debug('Reload Site List.') g.settings = settings utils.getCachedSiteList(True) def delete_infra(infid): infra.delete_infra(infid) scheduler.delete_job('delete_infra_%s' % infid) return app
def make_google_blueprint(client_id=None, client_secret=None, scope=None, offline=False, reprompt_consent=False, redirect_url=None, redirect_to=None, login_url=None, authorized_url=None, session_class=None, backend=None): """ Make a blueprint for authenticating with Google using OAuth 2. This requires a client ID and client secret from Google. You should either pass them to this constructor, or make sure that your Flask application config defines them, using the variables GOOGLE_OAUTH_CLIENT_ID and GOOGLE_OAUTH_CLIENT_SECRET. Args: client_id (str): The client ID for your application on Google client_secret (str): The client secret for your application on Google scope (str, optional): comma-separated list of scopes for the OAuth token. Defaults to the "https://www.googleapis.com/auth/userinfo.profile" scope. offline (bool): Whether to request `offline access <https://developers.google.com/accounts/docs/OAuth2WebServer#offline>`_ for the OAuth token. Defaults to False reprompt_consent (bool): If True, force Google to re-prompt the user for their consent, even if the user has already given their consent. Defaults to False redirect_url (str): the URL to redirect to after the authentication dance is complete redirect_to (str): if ``redirect_url`` is not defined, the name of the view to redirect to after the authentication dance is complete. The actual URL will be determined by :func:`flask.url_for` login_url (str, optional): the URL path for the ``login`` view. Defaults to ``/google`` authorized_url (str, optional): the URL path for the ``authorized`` view. Defaults to ``/google/authorized``. session_class (class, optional): The class to use for creating a Requests session. Defaults to :class:`~flask_dance.consumer.requests.OAuth2Session`. backend: A storage backend class, or an instance of a storage backend class, to use for this blueprint. Defaults to :class:`~flask_dance.consumer.backend.session.SessionBackend`. :rtype: :class:`~flask_dance.consumer.OAuth2ConsumerBlueprint` :returns: A :ref:`blueprint <flask:blueprints>` to attach to your Flask app. """ scope = scope or ["https://www.googleapis.com/auth/userinfo.profile"] authorization_url_params = {} if offline: authorization_url_params["access_type"] = "offline" if reprompt_consent: authorization_url_params["approval_prompt"] = "force" google_bp = OAuth2ConsumerBlueprint( "google", __name__, client_id=client_id, client_secret=client_secret, scope=scope, base_url="https://www.googleapis.com/", authorization_url="https://accounts.google.com/o/oauth2/auth", token_url="https://accounts.google.com/o/oauth2/token", auto_refresh_url="https://accounts.google.com/o/oauth2/token", redirect_url=redirect_url, redirect_to=redirect_to, login_url=login_url, authorized_url=authorized_url, authorization_url_params=authorization_url_params, session_class=session_class, backend=backend, ) google_bp.from_config["client_id"] = "GOOGLE_OAUTH_CLIENT_ID" google_bp.from_config["client_secret"] = "GOOGLE_OAUTH_CLIENT_SECRET" @google_bp.before_app_request def set_applocal_session(): ctx = stack.top ctx.google_oauth = google_bp.session return google_bp
import os from flask import redirect, url_for from flask_dance.consumer import OAuth2ConsumerBlueprint from flask_login import current_user, login_required from .app import app from .models import Event, ANONYMOUS_EMAIL, User splitwise_blueprint = OAuth2ConsumerBlueprint( "splitwise", __name__, client_id=os.environ.get("SPLITWISE_KEY"), client_secret=os.environ.get("SPLITWISE_SECRET"), base_url="https://secure.splitwise.com/", token_url="https://secure.splitwise.com/oauth/token", authorization_url="https://secure.splitwise.com/oauth/authorize", ) app.register_blueprint(splitwise_blueprint) splitwise = splitwise_blueprint.session @app.route("/splitwise/allow") @login_required def allow_splitwise(): if not splitwise.authorized: return redirect(url_for("splitwise.login")) resp = splitwise.get("/api/v3.0/get_current_user") data = resp.json() splitwise_id = str(data["user"]["id"])
from utils.launch_tasks import start_task API_VERSION = config.API_VERSION CURRENT_DATE = datetime.strftime(datetime.now(), '%Y%m%d') os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' app = Flask(__name__) app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1) app.secret_key = config.OAUTH2['SECRET_KEY'] oauth_blueprint = OAuth2ConsumerBlueprint( "oauth", __name__, client_id=config.OAUTH2['CLIENT_ID'], client_secret=config.OAUTH2['SECRET_KEY'], base_url=config.OAUTH2['BASE_URL'], token_url=config.OAUTH2['TOKEN_URL'], authorization_url=config.OAUTH2['AUTHORIZATION_URL'], redirect_url='/', scope='default') app.register_blueprint(oauth_blueprint) def get_user() -> dict: auth = session.get('oauth_oauth_token') if auth: return auth.get('user') return None
def make_authentiq_blueprint( client_id=None, client_secret=None, scope="openid profile", redirect_url=None, redirect_to=None, login_url=None, authorized_url=None, session_class=None, backend=None, storage=None, hostname="connect.authentiq.io", ): """ Make a blueprint for authenticating with authentiq using OAuth 2. This requires a client ID and client secret from authentiq. You should either pass them to this constructor, or make sure that your Flask application config defines them, using the variables AUTHENTIQ_OAUTH_CLIENT_ID and AUTHENTIQ_OAUTH_CLIENT_SECRET. Args: client_id (str): The client ID for your application on Authentiq. client_secret (str): The client secret for your application on Authentiq. scope (str, optional): comma-separated list of scopes for the OAuth token. redirect_url (str): the URL to redirect to after the authentication dance is complete. redirect_to (str): if ``redirect_url`` is not defined, the name of the view to redirect to after the authentication dance is complete. The actual URL will be determined by :func:`flask.url_for`. login_url (str, optional): the URL path for the ``login`` view. Defaults to ``/authentiq``. authorized_url (str, optional): the URL path for the ``authorized`` view. Defaults to ``/authentiq/authorized``. session_class (class, optional): The class to use for creating a Requests session. Defaults to :class:`~flask_dance.consumer.requests.OAuth2Session`. storage: A token storage class, or an instance of a token storage class, to use for this blueprint. Defaults to :class:`~flask_dance.consumer.storage.session.SessionStorage`. hostname (str, optional): If using a private instance of authentiq CE/EE, specify the hostname, default is ``connect.authentiq.io`` :rtype: :class:`~flask_dance.consumer.OAuth2ConsumerBlueprint` :returns: A :ref:`blueprint <flask:blueprints>` to attach to your Flask app. """ authentiq_bp = OAuth2ConsumerBlueprint( "authentiq", __name__, client_id=client_id, client_secret=client_secret, scope=scope, base_url="https://{hostname}/".format(hostname=hostname), authorization_url="https://{hostname}/authorize".format(hostname=hostname), token_url="https://{hostname}/token".format(hostname=hostname), redirect_url=redirect_url, redirect_to=redirect_to, login_url=login_url, authorized_url=authorized_url, session_class=session_class, backend=backend, storage=storage, ) authentiq_bp.from_config["client_id"] = "AUTHENTIQ_OAUTH_CLIENT_ID" authentiq_bp.from_config["client_secret"] = "AUTHENTIQ_OAUTH_CLIENT_SECRET" @authentiq_bp.before_app_request def set_applocal_session(): ctx = stack.top ctx.authentiq_oauth = authentiq_bp.session return authentiq_bp
logging.basicConfig(level=logging.DEBUG) app = Flask(__name__) app.wsgi_app = ProxyFix(app.wsgi_app) app.secret_key="30bb7cf2-1fef-4d26-83f0-8096b6dcc7a3" app.config.from_json('config.json') iam_base_url=app.config['IAM_BASE_URL'] iam_token_url=iam_base_url + '/token' iam_refresh_url=iam_base_url + '/token' iam_authorization_url=iam_base_url + '/authorize' iam_blueprint = OAuth2ConsumerBlueprint( "iam", __name__, client_id=app.config['IAM_CLIENT_ID'], client_secret=app.config['IAM_CLIENT_SECRET'], scope='openid email profile offline_access', base_url=iam_base_url, token_url=iam_token_url, auto_refresh_url=iam_refresh_url, authorization_url=iam_authorization_url, redirect_to='home' ) app.register_blueprint(iam_blueprint, url_prefix="/login") from app import routes, errors if __name__ == "__main__": app.run(host='0.0.0.0')
def make_digitalocean_blueprint( client_id=None, client_secret=None, scope=None, redirect_url=None, redirect_to=None, login_url=None, authorized_url=None, session_class=None, storage=None, ): """ Make a blueprint for authenticating with Digital Ocean using OAuth 2. This requires a client ID and client secret from Digital Ocean. You should either pass them to this constructor, or make sure that your Flask application config defines them, using the variables :envvar:`DIGITALOCEAN_OAUTH_CLIENT_ID` and :envvar:`DIGITALOCEAN_OAUTH_CLIENT_SECRET`. Args: client_id (str): Client ID for your application on Digital Ocean client_secret (str): Client secret for your Digital Ocean application scope (str, optional): comma-separated list of scopes for the OAuth token. redirect_url (str): the URL to redirect to after the authentication dance is complete redirect_to (str): if ``redirect_url`` is not defined, the name of the view to redirect to after the authentication dance is complete. The actual URL will be determined by :func:`flask.url_for` login_url (str, optional): the URL path for the ``login`` view. Defaults to ``/digitalocean`` authorized_url (str, optional): the URL path for the ``authorized`` view. Defaults to ``/digitalocean/authorized``. session_class (class, optional): The class to use for creating a Requests session. Defaults to :class:`~flask_dance.consumer.requests.OAuth2Session`. storage: A token storage class, or an instance of a token storage class, to use for this blueprint. Defaults to :class:`~flask_dance.consumer.storage.session.SessionStorage`. :rtype: :class:`~flask_dance.consumer.OAuth2ConsumerBlueprint` :returns: A :ref:`blueprint <flask:blueprints>` to attach to your Flask app. """ digitalocean_bp = OAuth2ConsumerBlueprint( "digitalocean", __name__, client_id=client_id, client_secret=client_secret, scope=scope.replace(",", " ") if scope else None, base_url="https://cloud.digitalocean.com/v1/oauth", authorization_url="https://cloud.digitalocean.com/v1/oauth/authorize", token_url="https://cloud.digitalocean.com/v1/oauth/token", redirect_url=redirect_url, redirect_to=redirect_to, login_url=login_url, authorized_url=authorized_url, session_class=session_class, storage=storage, ) digitalocean_bp.from_config["client_id"] = "DIGITALOCEAN_OAUTH_CLIENT_ID" digitalocean_bp.from_config[ "client_secret"] = "DIGITALOCEAN_OAUTH_CLIENT_SECRET" @digitalocean_bp.before_app_request def set_applocal_session(): ctx = stack.top ctx.digitalocean_oauth = digitalocean_bp.session return digitalocean_bp
def make_google_blueprint( client_id=None, client_secret=None, *, scope=None, offline=False, reprompt_consent=False, reprompt_select_account=False, redirect_url=None, redirect_to=None, login_url=None, authorized_url=None, session_class=None, storage=None, hosted_domain=None, rule_kwargs=None, ): """ Make a blueprint for authenticating with Google using OAuth 2. This requires a client ID and client secret from Google. You should either pass them to this constructor, or make sure that your Flask application config defines them, using the variables :envvar:`GOOGLE_OAUTH_CLIENT_ID` and :envvar:`GOOGLE_OAUTH_CLIENT_SECRET`. Args: client_id (str): The client ID for your application on Google client_secret (str): The client secret for your application on Google scope (str, optional): comma-separated list of scopes for the OAuth token. Defaults to the "https://www.googleapis.com/auth/userinfo.profile" scope. offline (bool): Whether to request `offline access <https://developers.google.com/accounts/docs/OAuth2WebServer#offline>`_ for the OAuth token. Defaults to False reprompt_consent (bool): If True, force Google to re-prompt the user for their consent, even if the user has already given their consent. Defaults to False reprompt_select_account (bool): If True, force Google to re-prompt the select account page, even if there is a single logged-in user. Defaults to False redirect_url (str): the URL to redirect to after the authentication dance is complete redirect_to (str): if ``redirect_url`` is not defined, the name of the view to redirect to after the authentication dance is complete. The actual URL will be determined by :func:`flask.url_for` login_url (str, optional): the URL path for the ``login`` view. Defaults to ``/google`` authorized_url (str, optional): the URL path for the ``authorized`` view. Defaults to ``/google/authorized``. session_class (class, optional): The class to use for creating a Requests session. Defaults to :class:`~flask_dance.consumer.requests.OAuth2Session`. storage: A token storage class, or an instance of a token storage class, to use for this blueprint. Defaults to :class:`~flask_dance.consumer.storage.session.SessionStorage`. hosted_domain (str, optional): The domain of the G Suite user. Used to indicate that the account selection UI should be optimized for accounts at this domain. Note that this only provides UI optimization, and requires response validation (see warning). rule_kwargs (dict, optional): Additional arguments that should be passed when adding the login and authorized routes. Defaults to ``None``. .. _google_hosted_domain_warning: .. warning:: The ``hosted_domain`` argument **only provides UI optimization**. Don't rely on this argument to control who can access your application. You must verify that the ``hd`` claim of the response ID token matches the ``hosted_domain`` argument passed to ``make_google_blueprint``. :rtype: :class:`~flask_dance.consumer.OAuth2ConsumerBlueprint` :returns: A :doc:`blueprint <flask:blueprints>` to attach to your Flask app. """ scope = scope or ["https://www.googleapis.com/auth/userinfo.profile"] authorization_url_params = {} prompt_params = [] auto_refresh_url = None if offline: authorization_url_params["access_type"] = "offline" auto_refresh_url = "https://accounts.google.com/o/oauth2/token" if reprompt_consent: prompt_params.append("consent") if reprompt_select_account: prompt_params.append("select_account") if prompt_params: prompt_params = " ".join(prompt_params) authorization_url_params["prompt"] = prompt_params if hosted_domain: authorization_url_params["hd"] = hosted_domain google_bp = OAuth2ConsumerBlueprint( "google", __name__, client_id=client_id, client_secret=client_secret, scope=scope, base_url="https://www.googleapis.com/", authorization_url="https://accounts.google.com/o/oauth2/auth", token_url="https://accounts.google.com/o/oauth2/token", auto_refresh_url=auto_refresh_url, redirect_url=redirect_url, redirect_to=redirect_to, login_url=login_url, authorized_url=authorized_url, authorization_url_params=authorization_url_params, session_class=session_class, storage=storage, rule_kwargs=rule_kwargs, ) google_bp.from_config["client_id"] = "GOOGLE_OAUTH_CLIENT_ID" google_bp.from_config["client_secret"] = "GOOGLE_OAUTH_CLIENT_SECRET" @google_bp.before_app_request def set_applocal_session(): ctx = stack.top ctx.google_oauth = google_bp.session return google_bp
def make_heroku_blueprint( client_id=None, client_secret=None, *, scope=None, api_version="3", redirect_url=None, redirect_to=None, login_url=None, authorized_url=None, session_class=None, storage=None, rule_kwargs=None, ): """ Make a blueprint for authenticating with Heroku using OAuth 2. This requires a client ID and client secret from Heroku. You should either pass them to this constructor, or make sure that your Flask application config defines them, using the variables :envvar:`HEROKU_OAUTH_CLIENT_ID` and :envvar:`HEROKU_OAUTH_CLIENT_SECRET`. Args: client_id (str): The client ID for your application on Heroku. client_secret (str): The client secret for your application on Heroku scope (str, optional): comma-separated list of scopes for the OAuth token api_version (str): The version number of the Heroku API you want to use. Defaults to version 3. redirect_url (str): the URL to redirect to after the authentication dance is complete redirect_to (str): if ``redirect_url`` is not defined, the name of the view to redirect to after the authentication dance is complete. The actual URL will be determined by :func:`flask.url_for` login_url (str, optional): the URL path for the ``login`` view. Defaults to ``/heroku`` authorized_url (str, optional): the URL path for the ``authorized`` view. Defaults to ``/heroku/authorized``. session_class (class, optional): The class to use for creating a Requests session. Defaults to :class:`~flask_dance.contrib.HerokuOAuth2Session`. storage: A token storage class, or an instance of a token storage class, to use for this blueprint. Defaults to :class:`~flask_dance.consumer.storage.session.SessionStorage`. rule_kwargs (dict, optional): Additional arguments that should be passed when adding the login and authorized routes. Defaults to ``None``. :rtype: :class:`~flask_dance.consumer.OAuth2ConsumerBlueprint` :returns: A :doc:`blueprint <flask:blueprints>` to attach to your Flask app. """ heroku_bp = OAuth2ConsumerBlueprint( "heroku", __name__, client_id=client_id, client_secret=client_secret, scope=scope, api_version=api_version, base_url="https://api.heroku.com/", authorization_url="https://id.heroku.com/oauth/authorize", token_url="https://id.heroku.com/oauth/token", redirect_url=redirect_url, redirect_to=redirect_to, login_url=login_url, authorized_url=authorized_url, session_class=session_class or HerokuOAuth2Session, storage=storage, rule_kwargs=rule_kwargs, ) heroku_bp.from_config["client_id"] = "HEROKU_OAUTH_CLIENT_ID" heroku_bp.from_config["client_secret"] = "HEROKU_OAUTH_CLIENT_SECRET" @heroku_bp.before_app_request def set_applocal_session(): g.flask_dance_heroku = heroku_bp.session return heroku_bp
def make_orcid_blueprint( client_id=None, client_secret=None, scope=None, offline=False, redirect_url=None, redirect_to=None, login_url=None, authorized_url=None, session_class=None, storage=None ): """ This function actually creates a specific orchid blueprint for use with Flask Dance Args: client_id: Override the client id provided in config client_secret: Ovveride the client secret provided in the config scope: list of scopes for the oauth offline: is this online or offline redirect_url: Ovveride the redirect_url from Flask-Login redirect_to: OVveride the redirect_to from Flask-Login login_url: OVerrid the Login_url from Flask-login authorized_url: Override the authorized_url from Flask-Dance session_class: provide a custom OAuth session storage: storage for the token Result: Flask Blueprint for app """ scope = scope or ["/authenticate", "/read-limited"] session_class = session_class or JsonOath2Session _base_url = "https://orcid.org/oauth" _token_url = "https://orcid.org/oauth/token" _authorization_url = "https://orcid.org/oauth/authorize" if os.environ.get("USE_ORCID_OAUTH_SANDBOX"): _base_url = "https://api.sandbox.orcid.org/v2.0" _token_url = "https://api.sandbox.orcid.org/oauth/token" _authorization_url = "https://sandbox.orcid.org/oauth/authorize" orcid_bp = OAuth2ConsumerBlueprint( "orcid", __name__, client_id=client_id, client_secret=client_secret, scope=scope, base_url=_base_url, token_url=_token_url, authorization_url=_authorization_url, redirect_url=redirect_url, redirect_to=redirect_to, login_url=login_url, authorized_url=authorized_url, session_class=session_class, storage=storage ) orcid_bp.from_config["client_id"] = "ORCID_OAUTH_CLIENT_ID" orcid_bp.from_config["client_secret"] = "ORCID_OAUTH_CLIENT_SECRET" @orcid_bp.before_app_request def set_applocal_session(): """ sets the orchid session in the current context """ ctx = stack.top ctx.orchid_oauth = orcid_bp.session return orcid_bp
from flask import Flask, render_template_string from flask_dance.consumer import OAuth2ConsumerBlueprint logging.basicConfig(level=logging.DEBUG) token_url = "https://app.boson.me/oauth2/token" client_id = os.getenv('client-id') client_secret = os.getenv('client-secret') boson = OAuth2ConsumerBlueprint( "boson", __name__, client_id=client_id, client_secret=client_secret, base_url="https://app.boson.me", token_url=token_url, authorization_url="https://app.boson.me/oauth2/authorize", scope=['require:phone'], auto_refresh_url=token_url, auto_refresh_kwargs={ 'client_id': client_id, 'client_secret': client_secret, }) app = Flask(__name__) app.secret_key = hexlify(os.urandom(24)) app.register_blueprint(boson, url_prefix="/login") @app.route("/") def index(): if not boson.session.authorized:
def make_discord_blueprint( client_id=None, client_secret=None, scope=None, redirect_url=None, redirect_to=None, login_url=None, authorized_url=None, session_class=None, backend=None, ): """ Make a blueprint for authenticating with Discord using OAuth 2. This requires a client ID and client secret from Discord. You should either pass them to this constructor, or make sure that your Flask application config defines them, using the variables DISCORD_OAUTH_CLIENT_ID and DISCORD_OAUTH_CLIENT_SECRET. Args: client_id (str): The client ID for your application on Discord. client_secret (str): The client secret for your application on Discord scope (list, optional): list of scopes (str) for the OAuth token redirect_url (str): the URL to redirect to after the authentication dance is complete redirect_to (str): if ``redirect_url`` is not defined, the name of the view to redirect to after the authentication dance is complete. The actual URL will be determined by :func:`flask.url_for` login_url (str, optional): the URL path for the ``login`` view. Defaults to ``/discord`` authorized_url (str, optional): the URL path for the ``authorized`` view. Defaults to ``/discord/authorized``. session_class (class, optional): The class to use for creating a Requests session. Defaults to :class:`~flask_dance.consumer.requests.OAuth2Session`. backend: A storage backend class, or an instance of a storage backend class, to use for this blueprint. Defaults to :class:`~flask_dance.consumer.backend.session.SessionBackend`. :rtype: :class:`~flask_dance.consumer.OAuth2ConsumerBlueprint` :returns: A :ref:`blueprint <flask:blueprints>` to attach to your Flask app. """ scope = scope or ["identify"] discord_bp = OAuth2ConsumerBlueprint( "discord", __name__, client_id=client_id, client_secret=client_secret, scope=scope, base_url="https://discordapp.com/", token_url="https://discordapp.com/api/oauth2/token", authorization_url="https://discordapp.com/api/oauth2/authorize", redirect_url=redirect_url, redirect_to=redirect_to, login_url=login_url, authorized_url=authorized_url, session_class=session_class, backend=backend, ) discord_bp.from_config["client_id"] = "DISCORD_OAUTH_CLIENT_ID" discord_bp.from_config["client_secret"] = "DISCORD_OAUTH_CLIENT_SECRET" @discord_bp.before_app_request def set_applocal_session(): ctx = stack.top ctx.discord_oauth = discord_bp.session return discord_bp
@login_manager.user_loader def userloader(user_id): curr = User.query.get(user_id) if curr is None: user_info = hqauth.session.get(config.USER_INFO).json() curr = process_userinfo(user_info) db.session.add(curr) db.session.commit() return curr @login_manager.unauthorized_handler def unauthorized(): flash('Please login', 'warning') return redirect(url_for('home.homepage')) hqauth = OAuth2ConsumerBlueprint('oauth', __name__, base_url=config.BASE_URL, token_url=config.ACCESS_TOKEN_URL, authorization_url=config.AUTHORIZE_URL, client_id=config.CLIENT_ID, client_secret=config.CLIENT_SECRET, scope=config.OAUTH_SCOPE, redirect_to='oauth.connect') from . import views
def make_dropbox_blueprint( app_key=None, app_secret=None, scope=None, force_reapprove=False, disable_signup=False, require_role=None, redirect_url=None, redirect_to=None, login_url=None, authorized_url=None, session_class=None, storage=None, ): """ Make a blueprint for authenticating with Dropbox using OAuth 2. This requires a client ID and client secret from Dropbox. You should either pass them to this constructor, or make sure that your Flask application config defines them, using the variables :envvar:`DROPBOX_OAUTH_CLIENT_ID` and :envvar:`DROPBOX_OAUTH_CLIENT_SECRET`. For more information about the ``force_reapprove``, ``disable_signup``, and ``require_role`` arguments, `check the Dropbox API documentation <https://www.dropbox.com/developers/documentation/http/overview>`_. Args: app_key (str): The client ID for your application on Dropbox. app_secret (str): The client secret for your application on Dropbox scope (str, optional): Comma-separated list of scopes for the OAuth token force_reapprove (bool): Force the user to approve the app again if they've already done so. disable_signup (bool): Prevent users from seeing a sign-up link on the authorization page. require_role (str): Pass the string ``work`` to require a Dropbox for Business account, or the string ``personal`` to require a personal account. redirect_url (str): the URL to redirect to after the authentication dance is complete redirect_to (str): if ``redirect_url`` is not defined, the name of the view to redirect to after the authentication dance is complete. The actual URL will be determined by :func:`flask.url_for` login_url (str, optional): the URL path for the ``login`` view. Defaults to ``/dropbox`` authorized_url (str, optional): the URL path for the ``authorized`` view. Defaults to ``/dropbox/authorized``. session_class (class, optional): The class to use for creating a Requests session. Defaults to :class:`~flask_dance.consumer.requests.OAuth2Session`. storage: A token storage class, or an instance of a token storage class, to use for this blueprint. Defaults to :class:`~flask_dance.consumer.storage.session.SessionStorage`. :rtype: :class:`~flask_dance.consumer.OAuth2ConsumerBlueprint` :returns: A :ref:`blueprint <flask:blueprints>` to attach to your Flask app. """ authorization_url_params = {} if force_reapprove: authorization_url_params["force_reapprove"] = "true" if disable_signup: authorization_url_params["disable_signup"] = "true" if require_role: authorization_url_params["require_role"] = require_role dropbox_bp = OAuth2ConsumerBlueprint( "dropbox", __name__, client_id=app_key, client_secret=app_secret, scope=scope, base_url="https://api.dropbox.com/2/", authorization_url="https://www.dropbox.com/oauth2/authorize", token_url="https://api.dropbox.com/oauth2/token", redirect_url=redirect_url, redirect_to=redirect_to, login_url=login_url, authorized_url=authorized_url, authorization_url_params=authorization_url_params, session_class=session_class, storage=storage, ) dropbox_bp.from_config["client_id"] = "DROPBOX_OAUTH_CLIENT_ID" dropbox_bp.from_config["client_secret"] = "DROPBOX_OAUTH_CLIENT_SECRET" @dropbox_bp.before_app_request def set_applocal_session(): ctx = stack.top ctx.dropbox_oauth = dropbox_bp.session return dropbox_bp
def make_strava_blueprint( client_id=None, client_secret=None, *, scope="read", redirect_url=None, redirect_to=None, login_url=None, authorized_url=None, session_class=None, storage=None, user_agent=None, rule_kwargs=None, ): """ Make a blueprint for authenticating with Strava using OAuth 2. This requires a client ID and client secret from Strava. You should either pass them to this constructor, or make sure that your Flask application config defines them, using the variables :envvar:`STRAVA_OAUTH_CLIENT_ID` and :envvar:`STRAVA_OAUTH_CLIENT_SECRET`. Args: client_id (str): The client ID for your application on Strava. client_secret (str): The client secret for your application on Strava scope (str, optional): space-separated list of scopes for the OAuth token Defaults to ``identity`` redirect_url (str): the URL to redirect to after the authentication dance is complete redirect_to (str): if ``redirect_url`` is not defined, the name of the view to redirect to after the authentication dance is complete. The actual URL will be determined by :func:`flask.url_for` login_url (str, optional): the URL path for the ``login`` view. Defaults to ``/strava`` authorized_url (str, optional): the URL path for the ``authorized`` view. Defaults to ``/strava/authorized``. session_class (class, optional): The class to use for creating a Requests session. Defaults to :class:`~flask_dance.contrib.strava.StravaOAuth2Session`. storage: A token storage class, or an instance of a token storage class, to use for this blueprint. Defaults to :class:`~flask_dance.consumer.storage.session.SessionStorage`. user_agent (str, optional): User agent for the requests to Strava API. Defaults to ``Flask-Dance/{{version}}``. rule_kwargs (dict, optional): Additional arguments that should be passed when adding the login and authorized routes. Defaults to ``None``. :rtype: :class:`~flask_dance.consumer.OAuth2ConsumerBlueprint` :returns: A :doc:`blueprint <flask:blueprints>` to attach to your Flask app. """ strava_bp = OAuth2ConsumerBlueprint( "strava", __name__, client_id=client_id, client_secret=client_secret, scope=scope, base_url="https://www.strava.com/api/v3", authorization_url="https://www.strava.com/api/v3/oauth/authorize", token_url="https://www.strava.com/api/v3/oauth/token", auto_refresh_url="https://www.strava.com/api/v3/oauth/token", redirect_url=redirect_url, redirect_to=redirect_to, login_url=login_url, authorized_url=authorized_url, session_class=session_class or StravaOAuth2Session, storage=storage, rule_kwargs=rule_kwargs, ) strava_bp.from_config["client_id"] = "STRAVA_OAUTH_CLIENT_ID" strava_bp.from_config["client_secret"] = "STRAVA_OAUTH_CLIENT_SECRET" strava_bp.user_agent = user_agent @strava_bp.before_app_request def set_applocal_session(): ctx = stack.top ctx.strava_oauth = strava_bp.session return strava_bp
def test_signal_sender_oauth_authorized(request): app, bp = make_app() bp2 = OAuth2ConsumerBlueprint("test2", __name__, client_id="client_id", client_secret="client_secret", scope="admin", state="random-string", base_url="https://example.com", authorization_url="https://example.com/oauth/authorize", token_url="https://example.com/oauth/access_token", redirect_to="index", ) app.register_blueprint(bp2, url_prefix="/login") calls = [] def callback(*args, **kwargs): calls.append((args, kwargs)) oauth_authorized.connect(callback, sender=bp) request.addfinalizer(lambda: oauth_authorized.disconnect(callback, sender=bp)) fake_token = {"access_token": "test-token"} fake_token2 = {"access_token": "test-token2"} with app.test_client() as client: with client.session_transaction() as sess: sess["test-service_oauth_state"] = "random-string" bp.session.fetch_token = mock.Mock(return_value=fake_token) bp2.session.fetch_token = mock.Mock(return_value=fake_token2) resp = client.get( "/login/test2/authorized?code=secret-code&state=random-string", ) assert len(calls) == 0 with app.test_client() as client: with client.session_transaction() as sess: sess["test-service_oauth_state"] = "random-string" bp.session.fetch_token = mock.Mock(return_value="test-token") bp2.session.fetch_token = mock.Mock(return_value="test2-token") resp = client.get( "/login/test-service/authorized?code=secret-code&state=random-string", ) assert len(calls) == 1 assert calls[0][0] == (bp,) assert calls[0][1] == {"token": "test-token"} with app.test_client() as client: with client.session_transaction() as sess: sess["test-service_oauth_state"] = "random-string" bp.session.fetch_token = mock.Mock(return_value=fake_token) bp2.session.fetch_token = mock.Mock(return_value=fake_token2) resp = client.get( "/login/test2/authorized?code=secret-code&state=random-string", ) assert len(calls) == 1 # unchanged
def make_nylas_blueprint( client_id=None, client_secret=None, scope="email", redirect_url=None, redirect_to=None, login_url=None, authorized_url=None, session_class=None, storage=None, ): """ Make a blueprint for authenticating with Nylas using OAuth 2. This requires an API ID and API secret from Nylas. You should either pass them to this constructor, or make sure that your Flask application config defines them, using the variables :envvar:`NYLAS_OAUTH_CLIENT_KEY` and :envvar:`NYLAS_OAUTH_CLIENT_SECRET`. Args: client_id (str): The client ID for your developer account on Nylas. client_secret (str): The client secret for your developer account on Nylas. scope (str, optional): comma-separated list of scopes for the OAuth token. Defaults to "email". redirect_url (str): the URL to redirect to after the authentication dance is complete redirect_to (str): if ``redirect_url`` is not defined, the name of the view to redirect to after the authentication dance is complete. The actual URL will be determined by :func:`flask.url_for` login_url (str, optional): the URL path for the ``login`` view. Defaults to ``/nylas`` authorized_url (str, optional): the URL path for the ``authorized`` view. Defaults to ``/nylas/authorized``. session_class (class, optional): The class to use for creating a Requests session. Defaults to :class:`~flask_dance.consumer.requests.OAuth2Session`. storage: A token storage class, or an instance of a token storage class, to use for this blueprint. Defaults to :class:`~flask_dance.consumer.storage.session.SessionStorage`. :rtype: :class:`~flask_dance.consumer.OAuth2ConsumerBlueprint` :returns: A :ref:`blueprint <flask:blueprints>` to attach to your Flask app. """ nylas_bp = OAuth2ConsumerBlueprint( "nylas", __name__, client_id=client_id, client_secret=client_secret, scope=scope, base_url="https://api.nylas.com/", authorization_url="https://api.nylas.com/oauth/authorize", token_url="https://api.nylas.com/oauth/token", redirect_url=redirect_url, redirect_to=redirect_to, login_url=login_url, authorized_url=authorized_url, session_class=session_class, storage=storage, ) nylas_bp.from_config["client_id"] = "NYLAS_OAUTH_CLIENT_ID" nylas_bp.from_config["client_secret"] = "NYLAS_OAUTH_CLIENT_SECRET" @nylas_bp.before_app_request def set_applocal_session(): ctx = stack.top ctx.nylas_oauth = nylas_bp.session return nylas_bp