class LogMod(ModBase, db.Model): """Represents a change made to a mod.""" __tablename__ = "log_mod" # The user that made this change. user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=True) user = db.relationship('User', backref='changes') # Date this change was made. date = db.Column(db.DateTime, default=datetime.datetime.utcnow) cur_id = db.Column(db.Integer, db.ForeignKey('mod.id'), nullable=True) current = db.relationship("Mod", backref=backref("logs", order_by='LogMod.date')) # Index within this mod's list of versions. index = db.Column(db.Integer, nullable=False) authors = db.relationship("ModAuthor", secondary=authored_by_table) mod_vsns = db.relationship("LogModVersion", back_populates="mod") def blank(self, **kwargs): return LogMod(**kwargs) def blank_child(self, **kwargs): return LogModVersion(**kwargs) def copy_from(self, other): if hasattr(other, 'cur_id'): self.cur_id = other.cur_id self.cur_id = other.id super(ModBase, self).copy_from(other)
def mk_authored_by_table(mod_table): """Generates an association table between mods and the author table""" return db.Table( '{}_authored_by'.format(mod_table), db.Model.metadata, db.Column('mod_id', db.Integer, db.ForeignKey('{}.id'.format(mod_table), ondelete='CASCADE'), primary_key=True), db.Column('author_id', db.Integer, db.ForeignKey('author.id', ondelete='CASCADE'), primary_key=True))
def mk_for_game_vsn_table(mod_vsn_table): """Generates an assocation table between mod versions and the game version table""" return db.Table( '{}_for_game_version'.format(mod_vsn_table), db.Model.metadata, db.Column('mod_vsn_id', db.Integer, db.ForeignKey('{}.id'.format(mod_vsn_table), ondelete='CASCADE'), primary_key=True), db.Column('game_vsn_id', db.Integer, db.ForeignKey('game_version.id', ondelete='CASCADE'), primary_key=True), )
class LogModFile(ModFileBase, db.Model): __tablename__ = "log_mod_file" version_id = db.Column(db.Integer, db.ForeignKey('log_mod_version.id')) version = db.relationship("LogModVersion", back_populates="files") cur_id = db.Column(db.Integer, db.ForeignKey('mod_file.id'), nullable=True) current = db.relationship("ModFile") def blank(self, **kwargs): return LogModFile(**kwargs) def copy_from(self, other): if hasattr(other, 'cur_id'): self.cur_id = other.cur_id self.cur_id = other.id super(ModFileBase, self).copy_from(other)
class ModFile(ModFileBase, db.Model): __tablename__ = "mod_file" version_id = db.Column(db.Integer, db.ForeignKey('mod_version.id')) version = db.relationship("ModVersion", back_populates="files") # Whether we're providing our own download links for this file. redist = db.Column(db.Boolean) def blank(self, **kwargs): return ModFile(**kwargs)
class ModVersion(ModVersionBase, db.Model): __tablename__ = "mod_version" mod_id = db.Column(db.Integer, db.ForeignKey('mod.id')) mod = db.relationship("Mod", back_populates="mod_vsns") game_vsns = db.relationship( "GameVersion", secondary=for_game_vsn_table, backref="mod_vsns") files = db.relationship("ModFile", back_populates="version") def blank(self, **kwargs): return ModVersion(**kwargs) def blank_child(self, **kwargs): return ModFile(**kwargs)
class LogModVersion(ModVersionBase, db.Model): __tablename__ = "log_mod_version" mod_id = db.Column(db.Integer, db.ForeignKey('log_mod.id')) mod = db.relationship("LogMod", back_populates="mod_vsns") game_vsns = db.relationship("GameVersion", secondary=for_game_vsn_table) files = db.relationship("LogModFile", back_populates="version") cur_id = db.Column(db.Integer, db.ForeignKey('mod_version.id'), nullable=True) current = db.relationship("ModVersion") def blank(self, **kwargs): return LogModVersion(**kwargs) def blank_child(self, **kwargs): return LogModFile(**kwargs) def copy_from(self, other): if hasattr(other, 'cur_id'): self.cur_id = other.cur_id self.cur_id = other.id super(ModVersionBase, self).copy_from(other)
class UserSetting(db.Model): # type: ignore """ Table for storing user settings. Each setting the user has changed from the defaults is stored as a separate row containing a key, value pair in this table. This schema allows for new settings to be added without requiring a database migration. """ key = db.Column(db.String(40), nullable=False, primary_key=True) value = db.Column(db.JSON, nullable=False) user_id = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True) user = db.relationship('User')
class StoredFile(db.Model): """Represents a file stored in some sort of storage medium.""" __tablename__ = 'stored_file' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(80), nullable=False) sha256 = db.Column(db.String(130), nullable=False) upload_by_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=True) upload_by = db.relationship('User') # Path to this file within the B2 bucket. Null if file is not on B2. b2_path = db.Column(db.String(300), nullable=True) def b2_download_url(self): """Gets the URL to download this file from the archive's B2 bucket.""" if self.b2_path: return urljoin(app.config['B2_PUBLIC_URL'], self.b2_path)
class ResetToken(db.Model): """Represents a one time use key that allows a user to reset (or set) their password.""" id = db.Column(db.Integer, primary_key=True) token = db.Column(GUID(), nullable=False, unique=True) created = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) active = db.Column(db.Boolean, nullable=False, default=True) user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) user = db.relationship('User', backref=db.backref('reset_token', uselist=False, cascade='all, delete-orphan')) def __init__(self, *args, token=None, **kwargs): if not token: token = uuid.uuid4() super(ResetToken, self).__init__(*args, token=token, **kwargs) def expired(self): return not self.active or \ self.created < datetime.utcnow() - app.config['PASSWD_RESET_EXPIRE_TIME']
class Session(db.Model): """Represents a user's login session.""" id = db.Column(db.Integer, primary_key=True) # Session ID stored in the user's browser cookies. sess_id = db.Column(GUID(), nullable=False, unique=True) login_ip = db.Column(db.String(128), nullable=False) login_date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) last_seen = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) # User this session is logged in as. user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) user = db.relationship('User', backref=db.backref('sessions', lazy=True, order_by=last_seen.desc())) active = db.Column(db.Boolean, nullable=False, default=True) def __init__(self, *args, sess_id=None, **kwargs): if not sess_id: sess_id = uuid.uuid4() super(Session, self).__init__(*args, sess_id=sess_id, **kwargs) def expired(self): return not self.active or \ self.last_seen < datetime.utcnow() - app.config['SERV_SESSION_EXPIRE_TIME'] def disable(self): self.active = False def touch(self): """Updates this session's last seen date.""" now = datetime.utcnow() self.last_seen = now self.user.last_seen = now
def stored_id(cls): return db.Column(db.Integer, db.ForeignKey('stored_file.id'), nullable=True)
class Session(db.Model): """Represents a user's login session. A session may not be considered fully authenticated if `authed_2fa` is false. """ id = db.Column(db.Integer, primary_key=True) # Session ID stored in the user's browser cookies. sess_id = db.Column(GUID(), nullable=False, unique=True) login_ip = db.Column(db.String(128), nullable=False) login_date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) last_seen = db.Column(db.DateTime, nullable=False, default=datetime.utcnow) # User this session is logged in as. user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) user = db.relationship('User', backref=db.backref('sessions', lazy=True, order_by=last_seen.desc())) authed_2fa = db.Column(db.Boolean, nullable=False, default=False) active = db.Column(db.Boolean, nullable=False, default=True) def __init__(self, *args, sess_id=None, **kwargs): if not sess_id: sess_id = uuid.uuid4() super(Session, self).__init__(*args, sess_id=sess_id, **kwargs) def expired(self): if not self.active: return True if self.authed_2fa: return self.last_seen < datetime.utcnow( ) - app.config['SERV_SESSION_EXPIRE_TIME'] else: return self.last_seen < datetime.utcnow() - \ app.config['SERV_PARTIAL_SESSION_EXPIRE_TIME'] def auth_2fa(self, code): """Authenticates the user's second factor with the given code. Returns true on success, false on failure. If successful, this session will be marked as fully authenticated. If the user's 2 factor is disabled, this has no effect. """ if self.user.totp_secret and self.user.validate_otp(code): self.authed_2fa = True db.session.commit() return True else: return False def disable(self): self.active = False def touch(self): """Updates this session's last seen date.""" now = datetime.utcnow() self.last_seen = now self.user.last_seen = now