class Club(db.Model): __tablename__ = "clubs" __searchable_columns__ = ["name"] __search_detail_columns__ = ["website"] __search_detail_columns__ = ["email_address"] id = db.Column(Integer, autoincrement=True, primary_key=True) name = db.Column(Unicode(255), unique=True, nullable=False) email_address = db.column_property(db.Column(Unicode(255)), comparator_factory=LowerCaseComparator) owner_id = db.Column( Integer, db.ForeignKey("users.id", use_alter=True, name="users.id", ondelete="SET NULL"), ) owner = db.relationship("User", foreign_keys=[owner_id]) time_created = db.Column(DateTime, nullable=False, default=datetime.utcnow) website = db.Column(Unicode(255)) def __unicode__(self): return self.name def __repr__(self): return unicode_to_str("<Club: id=%d name='%s'>" % (self.id, self.name)) def is_writable(self, user): return user and (self.id == user.club_id or user.is_manager())
class User(db.Model): """ User definition. """ __tablename__ = "users" __searchable_columns__ = ["name"] id = db.Column(Integer, autoincrement=True, primary_key=True) # Email address and name of the user email_address = db.column_property(db.Column(Unicode(255)), comparator_factory=LowerCaseComparator) first_name = db.Column(Unicode(255), nullable=False) last_name = db.Column(Unicode(255)) # Hashed password _password = db.Column("password", Unicode(128), nullable=False) # The user's club (optional) club_id = db.Column(Integer, db.ForeignKey("clubs.id", ondelete="SET NULL")) club = db.relationship("Club", foreign_keys=[club_id], backref="members") # Tracking key, delay in minutes and other settings tracking_key = db.Column(BigInteger, nullable=False, index=True) tracking_delay = db.Column(SmallInteger, nullable=False, default=0) tracking_callsign = db.Column(Unicode(5)) # Time and IP of creation created = db.Column(DateTime, default=datetime.utcnow) created_ip = db.Column(INET) # Time and IP of the last login login_time = db.Column(DateTime) login_ip = db.Column(INET) # Password recovery information recover_key = db.Column(Integer) recover_time = db.Column(DateTime) recover_ip = db.Column(INET) # Units settings distance_unit = db.Column(SmallInteger, nullable=False, default=1) speed_unit = db.Column(SmallInteger, nullable=False, default=1) lift_unit = db.Column(SmallInteger, nullable=False, default=0) altitude_unit = db.Column(SmallInteger, nullable=False, default=0) # Other settings admin = db.Column(Boolean, nullable=False, default=False) ############################## def __init__(self, *args, **kw): self.generate_tracking_key() super(User, self).__init__(*args, **kw) ############################## def __repr__(self): return unicode_to_str("<User: email=%s, display=%s>" % (self.email_address, self.name)) def __unicode__(self): return self.name ############################## @staticmethod def by_email_address(email): """Return the user object whose email address is ``email``.""" return User.query(email_address=email).first() @staticmethod def by_credentials(email, password, *args, **kwargs): """ Return the user object whose email address is ``email`` if the password is matching. """ user = User.by_email_address(email) if user and user.validate_password(password): return user @staticmethod def by_tracking_key(key): return User.query(tracking_key=key).first() @staticmethod def by_recover_key(key): return User.query(recover_key=key).first() ############################## @hybrid_property def name(self): if not self.last_name: return self.first_name return self.first_name + u" " + self.last_name @name.expression def name_expression(cls): return case( [(cls.last_name != None, cls.first_name + u" " + cls.last_name)], else_=cls.first_name, ) def initials(self): parts = self.name.split() initials = [p[0].upper() for p in parts if len(p) > 2 and "." not in p] return "".join(initials) ############################## @hybrid_property def password(self): """Return the hashed version of the password.""" return self._password @password.setter def password(self, password): """Hash ``password`` on the fly and store its hashed version.""" self._password = self._hash_password(password) @classmethod def _hash_password(cls, password, salt=None): if salt is None: salt = os.urandom(60) assert is_unicode(password) assert is_bytes(salt) salt_hash = sha256() salt_hash.update(salt) hash = sha256() hash.update((password + salt_hash.hexdigest()).encode("utf-8")) return str_to_unicode(salt_hash.hexdigest() + hash.hexdigest()) def validate_password(self, password): """ Check the password against existing credentials. :param password: the password that was provided by the user to try and authenticate. This is the clear text version that we will need to match against the hashed one in the database. :type password: unicode object. :return: Whether the password is valid. :rtype: bool """ assert is_unicode(password) # Make sure accounts without a password can't log in if not self.password or not password: return False hash = sha256() hash.update((password + self.password[:64]).encode("utf-8")) return self.password[64:] == hash.hexdigest() ############################## def generate_tracking_key(self): self.tracking_key = struct.unpack("I", os.urandom(4))[0] @property def tracking_key_hex(self): if self.tracking_key is None: return None return "%X" % self.tracking_key @classmethod def tracking_delay_interval(cls): return cast(cast(cls.tracking_delay, String) + " minutes", Interval) ############################## @hybrid_method def is_manager(self): return self.admin ############################## def generate_recover_key(self, ip): self.recover_key = struct.unpack("I", os.urandom(4))[0] & 0x7FFFFFFF self.recover_time = datetime.utcnow() self.recover_ip = ip return self.recover_key ############################## def is_readable(self, user): """Does the current user have full read access to this object?""" return self.is_writable(user) def is_writable(self, user): return user and (self.id == user.id or (self.password is None and self.club_id == user.club_id) or user.is_manager()) ############################## def follows(self, other): assert isinstance(other, User) from skylines.model.follower import Follower return Follower.follows(self, other) @property def num_followers(self): from skylines.model.follower import Follower return Follower.query(destination=self).count() @property def num_following(self): from skylines.model.follower import Follower return Follower.query(source=self).count() ############################## def get_largest_flights(self): """ Returns a query with all flights by the user as pilot ordered by distance """ from skylines.model.flight import Flight return Flight.get_largest().filter(Flight.pilot == self) ############################## def delete(self): from skylines.model.follower import Follower from skylines.model.igcfile import IGCFile for row in db.session.query(IGCFile).filter_by(owner_id=self.id): files.delete_file(row.filename) db.session.query(IGCFile).filter_by(owner_id=self.id).delete() db.session.query(Follower).filter_by(source_id=self.id).delete() db.session.query(Follower).filter_by(destination_id=self.id).delete() db.session.delete(self)
class User(db.Model): """ User definition. """ __tablename__ = 'users' __searchable_columns__ = ['name'] id = db.Column(Integer, autoincrement=True, primary_key=True) # Email address and name of the user email_address = db.column_property(db.Column(Unicode(255)), comparator_factory=LowerCaseComparator) first_name = db.Column(Unicode(255), nullable=False) last_name = db.Column(Unicode(255)) # Hashed password _password = db.Column('password', Unicode(128), nullable=False) # The user's club (optional) club_id = db.Column(Integer, db.ForeignKey('clubs.id', ondelete='SET NULL')) club = db.relationship('Club', foreign_keys=[club_id], backref='members') # Tracking key, delay in minutes and other settings tracking_key = db.Column(BigInteger, nullable=False, index=True) tracking_delay = db.Column(SmallInteger, nullable=False, default=0) tracking_callsign = db.Column(Unicode(5)) # Time and IP of creation created = db.Column(DateTime, default=datetime.utcnow) created_ip = db.Column(INET) # Time and IP of the last login login_time = db.Column(DateTime) login_ip = db.Column(INET) # Password recovery information recover_key = db.Column(Integer) recover_time = db.Column(DateTime) recover_ip = db.Column(INET) # Units settings distance_unit = db.Column(SmallInteger, nullable=False, default=1) speed_unit = db.Column(SmallInteger, nullable=False, default=1) lift_unit = db.Column(SmallInteger, nullable=False, default=0) altitude_unit = db.Column(SmallInteger, nullable=False, default=0) # Other settings admin = db.Column(Boolean, nullable=False, default=False) ############################## def __init__(self, *args, **kw): self.generate_tracking_key() super(User, self).__init__(*args, **kw) ############################## def __repr__(self): return ('<User: email=%s, display=%s>' % (self.email_address, self.name)).encode('unicode_escape') def __unicode__(self): return self.name ############################## @staticmethod def by_email_address(email): """Return the user object whose email address is ``email``.""" return User.query(email_address=email).first() @staticmethod def by_credentials(email, password, *args, **kwargs): """ Return the user object whose email address is ``email`` if the password is matching. """ user = User.by_email_address(email) if user and user.validate_password(password): return user @staticmethod def by_tracking_key(key): return User.query(tracking_key=key).first() @staticmethod def by_recover_key(key): return User.query(recover_key=key).first() # Flask Login ################ def is_active(self): return True def is_authenticated(self): return True def is_anonymous(self): return False def get_id(self): return unicode(self.id) ############################## @hybrid_property def name(self): if not self.last_name: return self.first_name return self.first_name + u' ' + self.last_name @name.expression def name_expression(cls): return case([ (cls.last_name != None, cls.first_name + u' ' + cls.last_name), ], else_=cls.first_name) def initials(self): parts = self.name.split() initials = [p[0].upper() for p in parts if len(p) > 2 and '.' not in p] return ''.join(initials) ############################## @hybrid_property def password(self): """Return the hashed version of the password.""" return self._password @password.setter def password(self, password): """Hash ``password`` on the fly and store its hashed version.""" self._password = self._hash_password(password) @classmethod def _hash_password(cls, password): # Make sure password is a str because we cannot hash unicode objects if isinstance(password, unicode): password = password.encode('utf-8') salt = sha256() salt.update(os.urandom(60)) hash = sha256() hash.update(password + salt.hexdigest()) password = salt.hexdigest() + hash.hexdigest() # Make sure the hashed password is a unicode object at the end of the # process because SQLAlchemy _wants_ unicode objects for Unicode cols if not isinstance(password, unicode): password = password.decode('utf-8') return password def validate_password(self, password): """ Check the password against existing credentials. :param password: the password that was provided by the user to try and authenticate. This is the clear text version that we will need to match against the hashed one in the database. :type password: unicode object. :return: Whether the password is valid. :rtype: bool """ # Make sure accounts without a password can't log in if not self.password or not password: return False hash = sha256() if isinstance(password, unicode): password = password.encode('utf-8') hash.update(password + str(self.password[:64])) return self.password[64:] == hash.hexdigest() ############################## def generate_tracking_key(self): self.tracking_key = struct.unpack('I', os.urandom(4))[0] @property def tracking_key_hex(self): if self.tracking_key is None: return None return '%X' % self.tracking_key @classmethod def tracking_delay_interval(cls): return cast(cast(cls.tracking_delay, String) + ' minutes', Interval) ############################## @hybrid_method def is_manager(self): return self.admin ############################## def generate_recover_key(self, ip): self.recover_key = struct.unpack('I', os.urandom(4))[0] & 0x7fffffff self.recover_time = datetime.utcnow() self.recover_ip = ip return self.recover_key ############################## def is_readable(self, user): """Does the current user have full read access to this object?""" return self.is_writable(user) def is_writable(self, user): return user and \ (self.id == user.id or (self.password is None and self.club_id == user.club_id) or user.is_manager()) ############################## def follows(self, other): assert isinstance(other, User) from skylines.model.follower import Follower return Follower.follows(self, other) @property def num_followers(self): from skylines.model.follower import Follower return Follower.query(destination=self).count() @property def num_following(self): from skylines.model.follower import Follower return Follower.query(source=self).count() ############################## def get_largest_flights(self): """ Returns a query with all flights by the user as pilot ordered by distance """ from skylines.model.flight import Flight return Flight.get_largest().filter(Flight.pilot == self)