fix(fastapi): fix SessionID (and ResetKey) generation

Signed-off-by: Kevin Morris <kevr@0cost.org>
This commit is contained in:
Kevin Morris 2021-11-27 20:04:26 -08:00
parent d658627e99
commit 47feb72f48
No known key found for this signature in database
GPG key ID: F7E46DED420788F3
4 changed files with 12 additions and 35 deletions

View file

@ -24,42 +24,15 @@ DRIVERS = {
"mysql": "mysql+mysqldb" "mysql": "mysql+mysqldb"
} }
# Global introspected object memo.
introspected = dict()
# Some types we don't get access to in this module. # Some types we don't get access to in this module.
Base = NewType("Base", "aurweb.models.declarative_base.Base") Base = NewType("Base", "aurweb.models.declarative_base.Base")
def make_random_value(table: str, column: str): def make_random_value(table: str, column: str, length: int):
""" Generate a unique, random value for a string column in a table. """ Generate a unique, random value for a string column in a table.
This can be used to generate for example, session IDs that
align with the properties of the database column with regards
to size.
Internally, we use SQLAlchemy introspection to look at column
to decide which length to use for random string generation.
:return: A unique string that is not in the database :return: A unique string that is not in the database
""" """
global introspected
# Make sure column is converted to a string for memo interaction.
scolumn = str(column)
# If the target column is not yet introspected, store its introspection
# object into our global `introspected` memo.
if scolumn not in introspected:
from sqlalchemy import inspect
target_column = scolumn.split('.')[-1]
col = list(filter(lambda c: c.name == target_column,
inspect(table).columns))[0]
introspected[scolumn] = col
col = introspected.get(scolumn)
length = col.type.length
string = aurweb.util.make_random_string(length) string = aurweb.util.make_random_string(length)
while query(table).filter(column == string).first(): while query(table).filter(column == string).first():
string = aurweb.util.make_random_string(length) string = aurweb.util.make_random_string(length)

View file

@ -1,8 +1,7 @@
from sqlalchemy.exc import IntegrityError from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import backref, relationship from sqlalchemy.orm import backref, relationship
from aurweb import schema from aurweb import db, schema
from aurweb.db import make_random_value, query
from aurweb.models.declarative import Base from aurweb.models.declarative import Base
from aurweb.models.user import User as _User from aurweb.models.user import User as _User
@ -19,8 +18,8 @@ class Session(Base):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super().__init__(**kwargs) super().__init__(**kwargs)
user_exists = query( user_exists = db.query(
query(_User).filter(_User.ID == self.UsersID).exists() db.query(_User).filter(_User.ID == self.UsersID).exists()
).scalar() ).scalar()
if not user_exists: if not user_exists:
raise IntegrityError( raise IntegrityError(
@ -31,4 +30,4 @@ class Session(Base):
def generate_unique_sid(): def generate_unique_sid():
return make_random_value(Session, Session.SessionID) return db.make_random_value(Session, Session.SessionID, 32)

View file

@ -230,3 +230,7 @@ class User(Base):
def __repr__(self): def __repr__(self):
return "<User(ID='%s', AccountType='%s', Username='%s')>" % ( return "<User(ID='%s', AccountType='%s', Username='%s')>" % (
self.ID, str(self.AccountType), self.Username) self.ID, str(self.AccountType), self.Username)
def generate_unique_resetkey():
return db.make_random_value(User, User.ResetKey, 32)

View file

@ -16,6 +16,7 @@ from aurweb.exceptions import ValidationError
from aurweb.l10n import get_translator_for_request from aurweb.l10n import get_translator_for_request
from aurweb.models import account_type as at from aurweb.models import account_type as at
from aurweb.models.ssh_pub_key import get_fingerprint from aurweb.models.ssh_pub_key import get_fingerprint
from aurweb.models.user import generate_unique_resetkey
from aurweb.scripts.notify import ResetKeyNotification, WelcomeNotification from aurweb.scripts.notify import ResetKeyNotification, WelcomeNotification
from aurweb.templates import make_context, make_variable_context, render_template from aurweb.templates import make_context, make_variable_context, render_template
from aurweb.users import update, validate from aurweb.users import update, validate
@ -92,7 +93,7 @@ async def passreset_post(request: Request,
status_code=HTTPStatus.SEE_OTHER) status_code=HTTPStatus.SEE_OTHER)
# If we got here, we continue with issuing a resetkey for the user. # If we got here, we continue with issuing a resetkey for the user.
resetkey = db.make_random_value(models.User, models.User.ResetKey) resetkey = generate_unique_resetkey()
with db.begin(): with db.begin():
user.ResetKey = resetkey user.ResetKey = resetkey
@ -291,7 +292,7 @@ async def account_register_post(request: Request,
# Create a user with no password with a resetkey, then send # Create a user with no password with a resetkey, then send
# an email off about it. # an email off about it.
resetkey = db.make_random_value(models.User, models.User.ResetKey) resetkey = generate_unique_resetkey()
# By default, we grab the User account type to associate with. # By default, we grab the User account type to associate with.
atype = db.query(models.AccountType, atype = db.query(models.AccountType,