Exemplo n.º 1
0
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)
Exemplo n.º 2
0
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')
Exemplo n.º 3
0
 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)
Exemplo n.º 4
0
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')
Exemplo n.º 5
0
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)
Exemplo n.º 6
0
    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
Exemplo n.º 7
0
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)
Exemplo n.º 9
0
async def logout():
    session.pop('logged_in', None)
    return None
Exemplo n.º 10
0
async def logout():
    if logged_in():
        session.pop('restaurant_id')

    return redirect(url_for('login'))
Exemplo n.º 11
0
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')
Exemplo n.º 12
0
async def logout():
    for k in list(session):
        print(k)
        session.pop(k)
    await flash("Logged Out", "error")
    return redirect(url_for('index'))
Exemplo n.º 13
0
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')
Exemplo n.º 14
0
async def logout():
    '''Logout'''
    session.pop('tg_id', None)
    return redirect(url_for('index'))
Exemplo n.º 15
0
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')
Exemplo n.º 16
0
 def __get_state():
     return session.pop("DISCORD_OAUTH2_STATE", str())
Exemplo n.º 17
0
    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)
Exemplo n.º 18
0
async def logout():
    session.pop('logged_in', None)
    await flash('You were logged out')
    return redirect(url_for('posts'))
Exemplo n.º 19
0
async def logout():
    session.pop("logged_in", None)
    await flash("You were logged out")
    return redirect(url_for("posts"))
Exemplo n.º 20
0
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'))
Exemplo n.º 21
0
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)