def two_factor_setup(): """View function for two-factor setup. This is used both for GET to fetch forms and POST to actually set configuration (and send token). There are 3 cases for setting up: 1) initial login and application requires 2FA 2) changing existing 2FA information 3) user wanting to enable or disable 2FA (assuming application doesn't require it) In order to CHANGE/ENABLE/DISABLE a 2FA information, user must be properly logged in AND have a 'fresh' authentication. For initial login when 2FA required of course user can't be logged in - in this case we need to have been sent some state via the session as part of login to show a) who and b) that they successfully authenticated. """ form_class = _security.two_factor_setup_form if request.is_json: if request.content_length: form = form_class(MultiDict(request.get_json()), meta=suppress_form_csrf()) else: form = form_class(formdata=None, meta=suppress_form_csrf()) else: form = form_class(meta=suppress_form_csrf()) if not current_user.is_authenticated: # This is the initial login case # We can also get here from setup if they want to change TODO: how? if not all(k in session for k in ["tf_user_id", "tf_state"]) or session["tf_state"] not in [ "setup_from_login", "validating_profile" ]: # illegal call on this endpoint tf_clean_session() return _tf_illegal_state(form, _security.login_url) user = _datastore.find_user(fs_uniquifier=session["tf_user_id"]) if not user: tf_clean_session() return _tf_illegal_state(form, _security.login_url) else: # Caller is changing their TFA profile. This requires a 'fresh' authentication if not check_and_update_authn_fresh( config_value("FRESHNESS"), config_value("FRESHNESS_GRACE_PERIOD"), get_request_attr("fs_authn_via"), ): return _security._reauthn_handler( config_value("FRESHNESS"), config_value("FRESHNESS_GRACE_PERIOD")) user = current_user if form.validate_on_submit(): # Before storing in DB and therefore requiring 2FA we need to # make sure it actually works. # Requiring 2FA is triggered by having BOTH tf_totp_secret and # tf_primary_method in the user record (or having the application # global config TWO_FACTOR_REQUIRED) # Until we correctly validate the 2FA - we don't set primary_method in # user model but use the session to store it. pm = form.setup.data session[ "tf_totp_secret"] = _security._totp_factory.generate_totp_secret() session["tf_primary_method"] = pm session["tf_state"] = "validating_profile" json_response = { "tf_state": "validating_profile", "tf_primary_method": pm, } if pm == "disable": tf_disable(user) after_this_request(view_commit) if not _security._want_json(request): do_flash(*get_message("TWO_FACTOR_DISABLED")) return redirect(get_url(_security.post_login_view)) elif pm == "sms" or pm == "email": if pm == "sms": user.tf_phone_number = form.sms.phone.data _datastore.put(user) after_this_request(view_commit) msg = user.tf_send_security_token( method=pm, totp_secret=session["tf_totp_secret"], phone_number=getattr(user, "tf_phone_number", None), ) if msg: # send code didn't work form.setup.errors = list() form.setup.errors.append(msg) if _security._want_json(request): return base_render_json(form, include_user=False, error_status_code=500) elif pm == "authenticator": authr_setup_values = user.tf_get_authenticator_values( session["tf_totp_secret"]) # is this insecure if we go through base_render_json? json_response.update( dict( authr_qrcode=authr_setup_values["image"], authr_key=authr_setup_values["key"], authr_username=authr_setup_values["username"], authr_issuer=authr_setup_values["issuer"], )) code_form = _security.two_factor_verify_code_form() if not _security._want_json(request): return _security.render_template( config_value("TWO_FACTOR_SETUP_TEMPLATE"), two_factor_setup_form=form, two_factor_verify_code_form=code_form, **json_response, **_ctx("tf_setup"), ) return base_render_json(form, include_user=False, additional=json_response) # We get here on GET and POST with failed validation. # For things like phone number - we've already done one POST # that succeeded and now it failed - so retain the initial info choices = config_value("TWO_FACTOR_ENABLED_METHODS") if not config_value("TWO_FACTOR_REQUIRED"): choices.append("disable") if _security._want_json(request): # Provide information application/UI might need to render their own form/input json_response = { "tf_required": config_value("TWO_FACTOR_REQUIRED"), "tf_primary_method": getattr(user, "tf_primary_method", None), "tf_phone_number": getattr(user, "tf_phone_number", None), "tf_available_methods": choices, } return base_render_json(form, include_user=False, additional=json_response) code_form = _security.two_factor_verify_code_form() return _security.render_template( config_value("TWO_FACTOR_SETUP_TEMPLATE"), two_factor_setup_form=form, two_factor_verify_code_form=code_form, choices=choices, chosen_method=form.setup.data, two_factor_required=config_value("TWO_FACTOR_REQUIRED"), **_ctx("tf_setup"), )
async def legal_privacy(): return redirect('/legal/#privacy-policy')
async def logout(): discord_session.revoke() return redirect('/' if not request.referrer else request.referrer)
async def login(): authorization_url, state = flow.authorization_url() session["state"] = state return redirect(authorization_url)
async def redirect_to_login(*_): await flash("You need to be logged in to view this page", "red") return redirect(url_for("login.login"))
async def search(): return redirect(url_for('index'))
async def index(): if not session.get('username'): return redirect(url_for("me.web_login")) return await render_template('manage.html',bot_name=bot_name, group_name=group_name)
async def Code(): data = await discord.callback() redirect_to = data.get("redirect", "/home") return redirect(redirect_to)
async def logout(): session.pop('logged_in', None) await flash('You were logged out...') return redirect(url_for('posts'))
async def index(): return redirect('/home')
async def logout(): discord.revoke() return redirect(url_for(".index"))
async def root(): return quart.redirect("/index")
async def before_request(): if not current_user.is_authenticated: return redirect(url_for('auth.login'))
def two_factor_token_validation(): """View function for two-factor token validation Two cases: 1) normal login case - everything setup correctly; normal 2FA validation In this case - user not logged in - but 'tf_state' == 'ready' or 'validating_profile' 2) validating after CHANGE/ENABLE 2FA. In this case user logged in/authenticated """ form_class = _security.two_factor_verify_code_form if request.is_json: form = form_class(MultiDict(request.get_json()), meta=suppress_form_csrf()) else: form = form_class(meta=suppress_form_csrf()) changing = current_user.is_authenticated if not changing: # This is the normal login case OR initial setup if (not all(k in session for k in ["tf_user_id", "tf_state"]) or session["tf_state"] not in ["ready", "validating_profile"] or (session["tf_state"] == "validating_profile" and not all(k in session for k in ["tf_primary_method", "tf_totp_secret"]))): # illegal call on this endpoint tf_clean_session() return _tf_illegal_state(form, _security.login_url) user = _datastore.find_user(fs_uniquifier=session["tf_user_id"]) form.user = user if not user: tf_clean_session() return _tf_illegal_state(form, _security.login_url) if session["tf_state"] == "ready": pm = user.tf_primary_method totp_secret = user.tf_totp_secret else: pm = session["tf_primary_method"] totp_secret = session["tf_totp_secret"] else: # Changing TFA profile - user is already authenticated. if (not all(k in session for k in ["tf_state", "tf_primary_method"]) or session["tf_state"] != "validating_profile"): tf_clean_session() # logout since this seems like attack-ish/logic error logout_user() return _tf_illegal_state(form, _security.login_url) pm = session["tf_primary_method"] totp_secret = session["tf_totp_secret"] form.user = current_user form.primary_method = pm form.tf_totp_secret = totp_secret if form.validate_on_submit(): # Success - log in user and clear all session variables remember = session.pop("tf_remember_login", None) completion_message = complete_two_factor_process( form.user, pm, totp_secret, changing, remember) after_this_request(view_commit) if not _security._want_json(request): after_this_request( partial( tf_set_validity_token_cookie, fs_uniquifier=form.user.fs_uniquifier, remember=remember, )) do_flash(*get_message(completion_message)) return redirect(get_post_login_redirect()) if (not config_value("TWO_FACTOR_ALWAYS_VALIDATE") and remember) and _security._want_json(request): token = generate_tf_validity_token(form.user.fs_uniquifier) json_response = {"tf_validity_token": token} return base_render_json(form, additional=json_response) # GET or not successful POST if _security._want_json(request): return base_render_json(form) # if we were trying to validate a new method if changing: setup_form = _security.two_factor_setup_form() return _security.render_template( config_value("TWO_FACTOR_SETUP_TEMPLATE"), two_factor_setup_form=setup_form, two_factor_verify_code_form=form, choices=config_value("TWO_FACTOR_ENABLED_METHODS"), **_ctx("tf_setup"), ) # if we were trying to validate an existing method else: rescue_form = _security.two_factor_rescue_form() return _security.render_template( config_value("TWO_FACTOR_VERIFY_CODE_TEMPLATE"), two_factor_rescue_form=rescue_form, two_factor_verify_code_form=form, problem=None, **_ctx("tf_token_validation"), )
def us_signin(): """ Unified sign in view. This takes an identity (as configured in USER_IDENTITY_ATTRIBUTES) and a passcode (password or OTP). Allow already authenticated users. For GET this is useful for single-page-applications on refresh - session still active but need to access user info and csrf-token. For POST - redirects to POST_LOGIN_VIEW (forms) or returns 400 (json). """ if current_user.is_authenticated and request.method == "POST": # Just redirect current_user to POST_LOGIN_VIEW (or next). # While its tempting to try to logout the current user and login the # new requested user - that simply doesn't work with CSRF. # While this is close to anonymous_user_required - it differs in that # it uses get_post_login_redirect which correctly handles 'next'. # TODO: consider changing anonymous_user_required to also call # get_post_login_redirect - not sure why it never has? if _security._want_json(request): payload = json_error_response( errors=get_message("ANONYMOUS_USER_REQUIRED")[0]) return _security._render_json(payload, 400, None, None) else: return redirect(get_post_login_redirect()) form_class = _security.us_signin_form if request.is_json: if request.content_length: form = form_class(MultiDict(request.get_json()), meta=suppress_form_csrf()) else: form = form_class(formdata=None, meta=suppress_form_csrf()) else: form = form_class(meta=suppress_form_csrf()) form.submit.data = True if form.validate_on_submit(): # Require multi-factor is it is enabled, and the method # we authenticated with requires it and either user has requested MFA or it is # required. remember_me = form.remember.data if "remember" in form else None if (config_value("TWO_FACTOR") and form.authn_via in config_value("US_MFA_REQUIRED") and (config_value("TWO_FACTOR_REQUIRED") or is_tf_setup(form.user))): return tf_login(form.user, remember=remember_me, primary_authn_via=form.authn_via) after_this_request(_commit) login_user(form.user, remember=remember_me, authn_via=[form.authn_via]) if _security._want_json(request): return base_render_json(form, include_auth_token=True) return redirect(get_post_login_redirect()) # Here on GET or failed POST validate code_methods = _compute_code_methods() if _security._want_json(request): payload = { "available_methods": config_value("US_ENABLED_METHODS"), "code_methods": code_methods, "identity_attributes": get_identity_attributes(), } return base_render_json(form, include_user=False, additional=payload) if current_user.is_authenticated: # Basically a no-op if authenticated - just perform the same # post-login redirect as if user just logged in. return redirect(get_post_login_redirect()) # On error - wipe code form.passcode.data = None return _security.render_template( config_value("US_SIGNIN_TEMPLATE"), us_signin_form=form, available_methods=config_value("US_ENABLED_METHODS"), code_methods=code_methods, skip_login_menu=True, **_security._run_ctx_processor("us_signin"))
async def nadd(): form = await request.form if form["inputbar"]: addNote(form["inputbar"]) return redirect(url_for("home"))
def us_verify_link(): """ Used to verify a magic email link. GET only """ if not all(v in request.args for v in ["email", "code"]): m, c = get_message("API_ERROR") if _security.redirect_behavior == "spa": return redirect(get_url(_security.login_error_view, qparams={c: m})) do_flash(m, c) return redirect(url_for_security("us_signin")) user = _datastore.find_user(email=request.args.get("email")) if not user or not user.active: if not user: m, c = get_message("USER_DOES_NOT_EXIST") else: m, c = get_message("DISABLED_ACCOUNT") if _security.redirect_behavior == "spa": return redirect(get_url(_security.login_error_view, qparams={c: m})) do_flash(m, c) return redirect(url_for_security("us_signin")) totp_secrets = _datastore.us_get_totp_secrets(user) if "email" not in totp_secrets or not _security._totp_factory.verify_totp( token=request.args.get("code"), totp_secret=totp_secrets["email"], user=user, window=config_value("US_TOKEN_VALIDITY"), ): m, c = get_message("INVALID_CODE") if _security.redirect_behavior == "spa": return redirect( get_url( _security.login_error_view, qparams=user.get_redirect_qparams({c: m}), )) do_flash(m, c) return redirect(url_for_security("us_signin")) if (config_value("TWO_FACTOR") and "email" in config_value("US_MFA_REQUIRED") and (config_value("TWO_FACTOR_REQUIRED") or is_tf_setup(user))): # tf_login doesn't know anything about "spa" etc. In general two-factor # isn't quite ready for SPA. So we return an error via a redirect rather # than mess up SPA applications. To be clear - this simply doesn't # work - using a magic link w/ 2FA - need to use code. if _security.redirect_behavior == "spa": return redirect( get_url( _security.login_error_view, qparams=user.get_redirect_qparams({"tf_required": 1}), )) return tf_login(user, primary_authn_via="email") login_user(user, authn_via=["email"]) after_this_request(_commit) if _security.redirect_behavior == "spa": # We do NOT send the authentication token here since the only way to # send it would be via a query param and that isn't secure. (logging and # possibly HTTP Referer header). # This means that this can only work if sessions are active which sort of # makes sense - otherwise you need to use /us-signin with a code. return redirect( get_url(_security.post_login_view, qparams=user.get_redirect_qparams())) do_flash(*get_message("PASSWORDLESS_LOGIN_SUCCESSFUL")) return redirect(get_post_login_redirect())
async def nedit(): form = await request.form print(form) if form["editNote"] and form["inputbar"]: editNote(form["editNote"], form["inputbar"]) return redirect(url_for("home"))
def logout(): # clear session cookie session.pop('logged_in', None) return redirect(url_for('index'))
async def ndel(): form = await request.form if form["deleteNote"]: delNote(form["deleteNote"]) return redirect(url_for("home"))
async def redir_to_static_site(): if "localhost" not in request.url: return redirect("https://files.pycode.tk/", status_code=301) return "test"
async def home(): return redirect('https://syllabics.app/', status_code=301)
async def logout(): session.clear() return redirect("/landing")
async def about(): return redirect('/#why-camus')
async def bot_support(): return redirect('https://discord.gg/f3MaASW')
async def chat_index(): return redirect('/', code=307)
async def legal_tos(): return redirect('/legal/#terms')
async def redir(): return redirect(request.args["path"])
async def callback(): data = await discord_session.callback() await discord_session.fetch_user() return redirect("/" if not request.referrer else request.referrer)
def reset_password(token): """View function that handles a reset password request. This is usually called via GET as part of an email link and redirects to a reset-password form It is called via POST to actually update the password (and then redirects to a post reset/login view) If in either case the token is either invalid or expired it redirects to the 'forgot-password' form. In the case of non-form based configuration: For GET normal case - redirect to RESET_VIEW?token={token}&email={email} For GET invalid case - redirect to RESET_ERROR_VIEW?error={error}&email={email} For POST normal/successful case - return 200 with new authentication token For POST error case return 400 with form.errors """ expired, invalid, user = reset_password_token_status(token) form_class = _security.reset_password_form if request.is_json: form = form_class(MultiDict(request.get_json()), meta=suppress_form_csrf()) else: form = form_class(meta=suppress_form_csrf()) form.user = user if request.method == "GET": if not user or invalid: m, c = get_message("INVALID_RESET_PASSWORD_TOKEN") if _security.redirect_behavior == "spa": return redirect( get_url(_security.reset_error_view, qparams={c: m})) do_flash(m, c) return redirect(url_for_security("forgot_password")) if expired: send_reset_password_instructions(user) m, c = get_message( "PASSWORD_RESET_EXPIRED", email=user.email, within=_security.reset_password_within, ) if _security.redirect_behavior == "spa": return redirect( get_url( _security.reset_error_view, qparams=user.get_redirect_qparams({c: m}), )) do_flash(m, c) return redirect(url_for_security("forgot_password")) # All good - for SPA - redirect to the ``reset_view`` if _security.redirect_behavior == "spa": return redirect( get_url( _security.reset_view, qparams=user.get_redirect_qparams({"token": token}), )) # for forms - render the reset password form return _security.render_template( config_value("RESET_PASSWORD_TEMPLATE"), reset_password_form=form, reset_password_token=token, **_ctx("reset_password"), ) # This is the POST case. m = None if not user or invalid: invalid = True m, c = get_message("INVALID_RESET_PASSWORD_TOKEN") if not _security._want_json(request): do_flash(m, c) if expired: send_reset_password_instructions(user) m, c = get_message( "PASSWORD_RESET_EXPIRED", email=user.email, within=_security.reset_password_within, ) if not _security._want_json(request): do_flash(m, c) if invalid or expired: if _security._want_json(request): return _security._render_json(json_error_response(m), 400, None, None) else: return redirect(url_for_security("forgot_password")) if form.validate_on_submit(): after_this_request(view_commit) update_password(user, form.password.data) if config_value("TWO_FACTOR") and ( config_value("TWO_FACTOR_REQUIRED") or (form.user.tf_totp_secret and form.user.tf_primary_method)): return tf_login(user, primary_authn_via="reset") login_user(user, authn_via=["reset"]) if _security._want_json(request): login_form = _security.login_form() login_form.user = user return base_render_json(login_form, include_auth_token=True) else: do_flash(*get_message("PASSWORD_RESET")) return redirect( get_url(_security.post_reset_view) or get_url(_security.post_login_view)) # validation failure case - for forms - we try again including the token # for non-forms - we just return errors and assume caller remembers token. if _security._want_json(request): return base_render_json(form) return _security.render_template( config_value("RESET_PASSWORD_TEMPLATE"), reset_password_form=form, reset_password_token=token, **_ctx("reset_password"), )