async def login() -> Union[str, "Response"]: error: str = "" username: str = "" password: str = "" csrf_token: uuid.UUID = uuid.uuid4() if request.method == "GET": session["csrf_token"] = str(csrf_token) if request.args.get("next"): session["next"] = request.args.get("next") if request.method == "POST": form: dict = await request.form username = form.get("username", "") password = form.get("password", "") if not username or not password: error = "Please enter username and password" if (session.get("csrf_token") != form.get("csrf_token") and not current_app.testing): error = "Invalid POST contents" conn = current_app.dbc # check if the user exists user = await get_user_by_username(conn, form.get("username")) if not user: error = "User not found" # check the password elif not pbkdf2_sha256.verify(password, user.get("password")): error = "User not found" if not error: # login the user if not current_app.testing: del session["csrf_token"] session["user_id"] = user.get("id") session["username"] = user.get("username") if "next" in session: next = session.get("next") session.pop("next") return redirect(next) else: return redirect(url_for("home_app.init")) else: session["csrf_token"] = str(csrf_token) return await render_template("user/login.html", error=error, username=username, csrf_token=csrf_token)
async def settings_profile_post(): # if not authenticated; render login if not 'authenticated' in session: return await flash('error', 'You must be logged in to access profile settings!', 'login') # form data form = await request.form username = form.get('username') email = form.get('email') # no data has changed; deny post if username == session['user_data']['name'] and email == session['user_data']['email']: return await flash('error', 'No changes have been made.', 'settings/profile') # Usernames must: # - be within 2-15 characters in length # - not contain both ' ' and '_', one is fine # - not be in the config's `disallowed_names` list # - not already be taken by another player if not _username_rgx.match(username): return await flash('error', 'Your new username syntax is invalid.', 'settings/profile') if '_' in username and ' ' in username: return await flash('error', 'Your new username may contain "_" or " ", but not both.', 'settings/profile') if username in glob.config.disallowed_names: return await flash('error', 'Your new username isn\'t allowed; pick another.', 'settings/profile') if await glob.db.fetch('SELECT 1 FROM users WHERE name = %s AND NOT name = %s', [username, session['user_data']['name']]): return await flash('error', 'Your new username already taken by another user.', 'settings/profile') # Emails must: # - match the regex `^[^@\s]{1,200}@[^@\s\.]{1,30}\.[^@\.\s]{1,24}$` # - not already be taken by another player if not _email_rgx.match(email): return await flash('error', 'Your new email syntax is invalid.', 'settings/profile') if await glob.db.fetch('SELECT 1 FROM users WHERE email = %s AND NOT email = %s', [email, session['user_data']['email']]): return await flash('error', 'Your new email already taken by another user.', 'settings/profile') # username change successful if username != session['user_data']['name'] and session['user_data']['is_donator']: await glob.db.execute('UPDATE users SET name = %s, safe_name = %s WHERE safe_name = %s', [username, get_safe_name(username), get_safe_name(session['user_data']['name'])]) # email change successful if email != session['user_data']['email']: safe_name = get_safe_name(username) if username != session['user_data']['name'] else get_safe_name(session['user_data']['name']) await glob.db.execute('UPDATE users SET email = %s WHERE safe_name = %s', [email, safe_name]) # logout session.pop('authenticated', None) session.pop('user_data', None) return await flash('success', 'Your username/email have been changed! Please login again.', 'login')
def deauth_user(self, user_id: int = None) -> None: """ Deauthenticates a user. If an id isn't given, then we are able to delete the session cookies from the user. """ if user_id is None: if session.get("DISCORD_ID") in self.authed_users.keys(): del self.authed_users[session["DISCORD_ID"]] session.pop("DISCORD_ID", None) session.pop("DISCORD_TOKEN", None) else: self.authed_users.pop(user_id, None)
async def logout(): if 'authenticated' not in session: return await flash('error', "You can't logout if you aren't logged in!", 'login') if glob.config.debug: log(f'{session["user_data"]["name"]} logged out.', Ansi.LGREEN) # clear session data session.pop('authenticated', None) session.pop('user_data', None) # render login return await flash('success', 'Successfully logged out!', 'login')
async def login() -> Union[str, "Response"]: error: str = "" username: str = "" password: str = "" csrf_token: uuid.UUID = uuid.uuid4() if request.method == "GET": session["csrf_token"] = str(csrf_token) if request.args.get("next"): session["next"] = request.args.get("next") if request.method == "POST": form: dict = await request.form username = form.get("username", "") password = form.get("password", "") if not username or not password: error = "Please enter username and password" if (session.get("csrf_token") != form.get("csrf_token") and not current_app.testing): error = "Invalid POST contents" # check if the user exists user = await User().login(username=username, password=password) if not user: error = "User not found" if user and not error: # login the user if not current_app.testing: del session["csrf_token"] session["user_uid"] = user.uid session["username"] = user.username if "next" in session: next = session["next"] session.pop("next") return redirect(next) else: return redirect(url_for("chat_app.index")) else: session["csrf_token"] = str(csrf_token) return await render_template("user/login.html", error=error, username=username, csrf_token=csrf_token)
def revoke(self): """This method clears current discord token, state and all session data from quart `session <https://pgjones.gitlab.io/quart/reference/source/quart.sessions.html#quart.sessions.Session>`_. Which means user will have to go through discord authorization token grant flow again. Also tries to remove the user from internal cache if they exist. """ self.users_cache.pop(self.user_id, None) for session_key in self.SESSION_KEYS: try: session.pop(session_key) except KeyError: pass
async def logout(): if not 'authenticated' in session: return await flash("You can't logout if you aren't logged in!", 'login') if glob.config.debug: log( f'Logout successful! {session["user_data"]["name"]} is now logged out.', Ansi.LGREEN) # clear session data session.pop('authenticated', None) session.pop('user_data', None) # redirect to login return await flash('Successfully logged out!', 'login')
async def logout(self) -> None: try: await self.revoke_token() except aiohttp.ClientError: self.logger.warn("failed to revoke token!", exc_info=True) http_session.pop(self.SESSION_TOKEN, None) http_session.pop(self.SESSION_ADDRESS, None) http_session.pop(self.SESSION_CACHED_SCOPE, None)
async def logout(): session.pop('logged_in', None) return None
async def logout(): if logged_in(): session.pop('restaurant_id') return redirect(url_for('login'))
async def settings_profile_post(): form = await request.form new_name = form.get('username', type=str) new_email = form.get('email', type=str) if new_name is None or new_email is None: return await flash('error', 'Invalid parameters.', 'home') old_name = session['user_data']['name'] old_email = session['user_data']['email'] # no data has changed; deny post if ( new_name == old_name and new_email == old_email ): return await flash('error', 'No changes have been made.', 'settings/profile') if new_name != old_name: if not session['user_data']['is_donator'] or not session['user_data']['is_staff']: return await flash('error', 'Username changes are currently a supporter perk.', 'settings/profile') # Usernames must: # - be within 2-15 characters in length # - not contain both ' ' and '_', one is fine # - not be in the config's `disallowed_names` list # - not already be taken by another player if not regexes.username.match(new_name): return await flash('error', 'Your new username syntax is invalid.', 'settings/profile') if '_' in new_name and ' ' in new_name: return await flash('error', 'Your new username may contain "_" or " ", but not both.', 'settings/profile') if new_name in glob.config.disallowed_names: return await flash('error', "Your new username isn't allowed; pick another.", 'settings/profile') if await glob.db.fetch('SELECT 1 FROM users WHERE name = %s', [new_name]): return await flash('error', 'Your new username already taken by another user.', 'settings/profile') safe_name = utils.get_safe_name(new_name) # username change successful await glob.db.execute( 'UPDATE users ' 'SET name = %s, safe_name = %s ' 'WHERE id = %s', [new_name, safe_name, session['user_data']['id']] ) if new_email != old_email: # Emails must: # - match the regex `^[^@\s]{1,200}@[^@\s\.]{1,30}\.[^@\.\s]{1,24}$` # - not already be taken by another player if not regexes.email.match(new_email): return await flash('error', 'Your new email syntax is invalid.', 'settings/profile') if await glob.db.fetch('SELECT 1 FROM users WHERE email = %s', [new_email]): return await flash('error', 'Your new email already taken by another user.', 'settings/profile') # email change successful await glob.db.execute( 'UPDATE users ' 'SET email = %s ' 'WHERE id = %s', [new_email, session['user_data']['id']] ) # logout session.pop('authenticated', None) session.pop('user_data', None) return await flash('success', 'Your username/email have been changed! Please login again.', 'login')
async def logout(): for k in list(session): print(k) session.pop(k) await flash("Logged Out", "error") return redirect(url_for('index'))
async def settings_password_post(): form = await request.form old_password = form.get('old_password') new_password = form.get('new_password') repeat_password = form.get('repeat_password') # new password and repeat password don't match; deny post if new_password != repeat_password: return await flash('error', "Your new password doesn't match your repeated password!", 'settings/password') # new password and old password match; deny post if old_password == new_password: return await flash('error', 'Your new password cannot be the same as your old password!', 'settings/password') # Passwords must: # - be within 8-32 characters in length # - have more than 3 unique characters # - not be in the config's `disallowed_passwords` list if not 8 < len(new_password) <= 32: return await flash('error', 'Your new password must be 8-32 characters in length.', 'settings/password') if len(set(new_password)) <= 3: return await flash('error', 'Your new password must have more than 3 unique characters.', 'settings/password') if new_password.lower() in glob.config.disallowed_passwords: return await flash('error', 'Your new password was deemed too simple.', 'settings/password') # cache and other password related information pw_cache = glob.cache['pw'] pw_hash = (await glob.db.fetchval( 'SELECT pw ' 'FROM users ' 'WHERE id = %s', [session['user_data']['id']]) ).encode('ISO-8859-1').decode('unicode-escape').encode('ISO-8859-1') pw_md5 = hashlib.md5(old_password.encode()).hexdigest().encode() # check old password against db # intentionally slow, will cache to speed up if pw_hash in pw_cache: if pw_md5 != pw_cache[pw_hash]: # ~0.1ms if glob.config.debug: log(f"{session['user_data']['name']}'s change pw failed - pw incorrect.", Ansi.LYELLOW) return await flash('error', 'Your old password is incorrect.', 'settings/password') else: # ~200ms k = HKDFExpand(algorithm=hashes.SHA256(), length=32, info=b'', backend=backend()) try: k.verify(pw_hash, pw_md5) except: if glob.config.debug: log(f"{session['user_data']['name']}'s change pw failed - pw incorrect.", Ansi.LYELLOW) return await flash('error', 'Your old password is incorrect.', 'settings/password') # remove old password from cache if pw_hash in pw_cache: del pw_cache[pw_hash] # calculate new md5 & bcrypt pw pw_md5 = hashlib.md5(new_password.encode()).hexdigest().encode() k = HKDFExpand(algorithm=hashes.SHA256(), length=32, info=b'', backend=backend()) pw_hash_new = k.derive(pw_md5).decode('unicode-escape') # update password in cache and db pw_cache[pw_hash_new] = pw_md5 await glob.db.execute( 'UPDATE users ' 'SET pw = %s ' 'WHERE safe_name = %s', [pw_hash_new, utils.get_safe_name(session['user_data']['name'])] ) # logout session.pop('authenticated', None) session.pop('user_data', None) return await flash('success', 'Your password has been changed! Please log in again.', 'login')
async def logout(): '''Logout''' session.pop('tg_id', None) return redirect(url_for('index'))
async def settings_password_post(): # if not authenticated; render login if not 'authenticated' in session: return await flash('error', 'You must be logged in to access password settings!', 'login') # form data form = await request.form old_password = form.get('old_password') new_password = form.get('new_password') repeat_password = form.get('repeat_password') # cache and other password related information bcrypt_cache = glob.cache['bcrypt'] pw_bcrypt = (await glob.db.fetch('SELECT pw_bcrypt FROM users WHERE safe_name = %s', [get_safe_name(session['user_data']['name'])]))['pw_bcrypt'].encode() pw_md5 = hashlib.md5(old_password.encode()).hexdigest().encode() # check old password against db # intentionally slow, will cache to speed up if pw_bcrypt in bcrypt_cache: if pw_md5 != bcrypt_cache[pw_bcrypt]: # ~0.1ms if glob.config.debug: log(f'{session["user_data"]["name"]}\'s change pw failed - pw incorrect.', Ansi.LYELLOW) return await flash('error', 'Your old password is incorrect.', 'settings/password') else: # ~200ms if not bcrypt.checkpw(pw_md5, pw_bcrypt): if glob.config.debug: log(f'{session["user_data"]["name"]}\'s change pw failed - pw incorrect.', Ansi.LYELLOW) return await flash('error', 'Your old password is incorrect.', 'settings/password') # new password and old password match; deny post if old_password.lower() == new_password.lower(): return await flash('error', 'Your new password cannot be the same as your old password!', 'settings/password') # new password and repeat password don't match; deny post if new_password != repeat_password: return await flash('error', "Your new password doesn't match your repeated password!", 'settings/password') # Passwords must: # - be within 8-32 characters in length # - have more than 3 unique characters # - not be in the config's `disallowed_passwords` list if not 8 < len(new_password) <= 32: return await flash('error', 'Your new password must be 8-32 characters in length.', 'settings/password') if len(set(new_password)) <= 3: return await flash('error', 'Your new password must have more than 3 unique characters.', 'settings/password') if new_password.lower() in glob.config.disallowed_passwords: return await flash('error', 'Your new password was deemed too simple.', 'settings/password') # remove old password from cache if pw_bcrypt in bcrypt_cache: del bcrypt_cache[pw_bcrypt] # update password in cache and db pw_md5 = hashlib.md5(new_password.encode()).hexdigest().encode() pw_bcrypt = bcrypt.hashpw(pw_md5, bcrypt.gensalt()) bcrypt_cache[pw_bcrypt] = pw_md5 await glob.db.execute('UPDATE users SET pw_bcrypt = %s WHERE safe_name = %s', [pw_bcrypt, get_safe_name(session['user_data']['name'])]) # logout session.pop('authenticated', None) session.pop('user_data', None) return await flash('success', 'Your password has been changed! Please login again.', 'login')
def __get_state(): return session.pop("DISCORD_OAUTH2_STATE", str())
async def auth(self): if not self.client_secret: raise Exception( "client_secret required to initiate confidential flow.") if "error" in request.args: error = request.args["error"] description = request.args.get("error_description", "") self.app.logger.error(f"redirect error '{error}' {description}") return f"{error}, check logs", 500 for expect in ["code", "session_state"]: if expect not in request.args: msg = f"Missing '{expect}' argument on" raise Exception(msg) # with the authorization code we can fetch an access token url = self._openid_configuration["token_endpoint"] data = { "grant_type": "authorization_code", "code": request.args["code"], "redirect_uri": self.redirect_uri, "client_id": self.client_id, "client_secret": self.client_secret } try: resp = await self.json_post(url, data=data, raise_status=False, json=False) if "error" in resp: raise Exception( f"{resp['error']}: {resp.get('error_description', 'unknown error')}" ) except Exception as ex: raise Exception(ex) access_token = resp.get("access_token", "") token_type = resp.get("token_type", "").lower() if token_type != "bearer": raise Exception("unsupported token type") if not access_token: self.app.logger.error(f"Access token not in response for {url}") raise Exception("unknown error, check logs") # verify jwt header access_token_header = jwt.get_unverified_header(access_token) for expect in ["kid", "alg"]: if expect not in access_token_header: raise Exception(f"Invalid JWT header for token {access_token}") # verify we actually have the key id key_id = access_token_header['kid'] if key_id not in self._get_keyids: # unknown kid, attempt to refresh OIDC keys keys = await self.fetch_pubkeys( self._openid_configuration['jwks_uri']) if not keys or key_id not in [k['kid'] for k in keys]: raise Exception("Could not validate token; unknown kid") self._openid_keys = keys if self._nonce: nonce = session.get(self._nonce_session_key) for token in [v for k, v in resp.items() if k.endswith("_token")]: token_decoded = OpenID.decode_token(token) if "nonce" not in token_decoded: raise Exception(f"Missing nonce in {token}") if token_decoded['nonce'] != nonce: raise Exception("Bad nonce") session.pop(self._nonce_session_key) return await self._fn_after_token(resp)
async def logout(): session.pop('logged_in', None) await flash('You were logged out') return redirect(url_for('posts'))
async def logout(): session.pop("logged_in", None) await flash("You were logged out") return redirect(url_for("posts"))
async def logout(): # it just pops the user from session if("user" in session): session.pop("user") # then we go back to index return redirect(url_for('index'))
def logout(): # clear session cookie session.pop('logged_in', None) return redirect(url_for('index'))
async def logout(self): # this currently only kills the cookie stuff, we may want to invalidate # the token on th server side, toos http_session.pop(self.SESSION_TOKEN, None) http_session.pop(self.SESSION_ADDRESS, None)