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.

177 lines
5.6 KiB

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