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.

188 lines
6.0 KiB

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