You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

202 lines
6.6 KiB

  1. from datetime import datetime
  2. import hashlib
  3. from werkzeug.security import generate_password_hash, check_password_hash
  4. from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
  5. from itsdangerous import BadSignature
  6. from flask import current_app
  7. from flask_login import UserMixin, AnonymousUserMixin
  8. from . import db, login_manager
  9. class Permission:
  10. FOLLOW = 1
  11. COMMENT = 2
  12. WRITE = 4
  13. MODERATE = 8
  14. ADMIN = 16
  15. class Role(db.Model):
  16. __tablename__ = 'roles'
  17. id = db.Column(db.Integer, primary_key=True)
  18. name = db.Column(db.String(64), unique=True)
  19. default = db.Column(db.Boolean, default=False, index=True)
  20. permissions = db.Column(db.Integer)
  21. users = db.relationship('User', backref='role', lazy='dynamic')
  22. def __init__(self, **kwargs):
  23. super(Role, self).__init__(**kwargs)
  24. if self.permissions is None:
  25. self.permissions = 0
  26. @staticmethod
  27. def insert_roles():
  28. roles = {
  29. 'User': [Permission.FOLLOW, Permission.COMMENT, Permission.WRITE],
  30. 'Moderator': [Permission.FOLLOW, Permission.COMMENT,
  31. Permission.WRITE, Permission.MODERATE],
  32. 'Administrator': [Permission.FOLLOW, Permission.COMMENT,
  33. Permission.WRITE, Permission.MODERATE,
  34. Permission.ADMIN]
  35. }
  36. default_role = 'User'
  37. for r in roles:
  38. role = Role.query.filter_by(name=r).first()
  39. if role is None:
  40. role = Role(name=r)
  41. role.reset_permissions()
  42. for perm in roles[r]:
  43. role.add_permission(perm)
  44. role.default = (role.name == default_role)
  45. db.session.add(role)
  46. db.session.commit()
  47. def add_permission(self, perm):
  48. if not self.has_permission(perm):
  49. self.permissions += perm
  50. def remove_permission(self, perm):
  51. if self.has_permission(perm):
  52. self.permissions -= perm
  53. def reset_permissions(self):
  54. self.permissions = 0
  55. def has_permission(self, perm):
  56. return self.permissions & perm == perm
  57. def __repr__(self):
  58. return '<Role %r>' % self.name
  59. class User(UserMixin, db.Model):
  60. __tablename__ = 'users'
  61. id = db.Column(db.Integer, primary_key=True)
  62. email = db.Column(db.String(64), unique=True, index=True)
  63. username = db.Column(db.String(64), unique=True, index=True)
  64. role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
  65. password_hash = db.Column(db.String(128))
  66. confirmed = db.Column(db.Boolean, default=False)
  67. name = db.Column(db.String(64))
  68. location = db.Column(db.String(64))
  69. about_me = db.Column(db.Text())
  70. member_since = db.Column(db.DateTime(), default=datetime.utcnow)
  71. last_seen = db.Column(db.DateTime(), default=datetime.utcnow)
  72. avatar_hash = db.Column(db.String(32))
  73. def __init__(self, **kwargs):
  74. super(User, self).__init__(**kwargs)
  75. if self.role is None:
  76. if self.email == current_app.config['FLASKY_ADMIN']:
  77. self.role = Role.query.filter_by(name='Administrator').first()
  78. else:
  79. self.role = Role.query.filter_by(default=True).first()
  80. if self.email is not None and self.avatar_hash is None:
  81. self.avatar_hash = self.gravatar_hash()
  82. @property
  83. def password(self):
  84. raise AttributeError('Password is not a readable attribute')
  85. @password.setter
  86. def password(self, password):
  87. self.password_hash = generate_password_hash(password)
  88. def verify_password(self, password):
  89. return check_password_hash(self.password_hash, password)
  90. def generate_confirmation_token(self, expiration=3600):
  91. s = Serializer(current_app.config['SECRET_KEY'], expiration)
  92. return s.dumps({'confirm': self.id}).decode('utf-8')
  93. def confirm(self, token):
  94. s = Serializer(current_app.config['SECRET_KEY'])
  95. try:
  96. data = s.loads(token.encode('utf-8'))
  97. except BadSignature:
  98. return False
  99. if data.get('confirm') != self.id:
  100. return False
  101. self.confirmed = True
  102. db.session.add(self)
  103. return True
  104. def generate_reset_token(self, expiration=3600):
  105. s = Serializer(current_app.config['SECRET_KEY'], expiration)
  106. return s.dumps({'reset': self.id}).decode('utf-8')
  107. @staticmethod
  108. def reset_password(token, new_password):
  109. s = Serializer(current_app.config['SECRET_KEY'])
  110. try:
  111. data = s.loads(token.encode('utf-8'))
  112. except BadSignature:
  113. return False
  114. user = User.query.get(data.get('reset'))
  115. if user is None:
  116. return False
  117. user.password = new_password
  118. db.session.add(user)
  119. return True
  120. def generate_email_change_token(self, new_email, expiration=3600):
  121. s = Serializer(current_app.config['SECRET_KEY'], expiration)
  122. return s.dumps(
  123. {'change_email': self.id, 'new_email': new_email}).decode('utf-8')
  124. def change_email(self, token):
  125. s = Serializer(current_app.config['SECRET_KEY'])
  126. try:
  127. data = s.loads(token.encode('utf-8'))
  128. except BadSignature:
  129. return False
  130. if data.get('change_email') != self.id:
  131. return False
  132. new_email = data.get('new_email')
  133. if new_email is None:
  134. return False
  135. if self.query.filter_by(email=new_email).first() is not None:
  136. return False
  137. self.email = new_email
  138. self.gravatar_hash = self.gravatar_hash()
  139. db.session.add(self)
  140. return True
  141. def can(self, perm):
  142. return self.role is not None and self.role.has_permission(perm)
  143. def is_administrator(self):
  144. return self.can(Permission.ADMIN)
  145. def ping(self):
  146. self.last_seen = datetime.utcnow()
  147. db.session.add(self)
  148. db.session.commit()
  149. def gravatar_hash(self):
  150. return hashlib.md5(self.email.lower().encode('utf-8')).hexdigest()
  151. def gravatar(self, size=100, default='identicon', rating='g'):
  152. url = 'https://secure.gravatar.com/avatar'
  153. hash = self.avatar_hash or self.gravatar_hash()
  154. return '{url}/{hash}?s={size}&d={default}&r={rating}'.format(
  155. url=url, hash=hash, size=size, default=default, rating=rating)
  156. def __repr__(self):
  157. return '<User %r>' % self.username
  158. class AnonymousUser(AnonymousUserMixin):
  159. def can(self, perm):
  160. return False
  161. def is_administrator(self):
  162. return False
  163. login_manager.anonymous_user = AnonymousUser
  164. @login_manager.user_loader
  165. def load_user(user_id):
  166. return User.query.get(int(user_id))