from enum import IntEnum from flask_hashids import HashidMixin from typing import Union from app import db class Permission(IntEnum): ''' Defines User permissions as integers by the power of 2. User permission can be evaluated using the bitwise operator &. ''' ADMINISTRATE = 1 CONTRIBUTE = 2 USE_API = 4 @staticmethod def get(permission: Union['Permission', int, str]) -> 'Permission': if isinstance(permission, Permission): return permission if isinstance(permission, int): return Permission(permission) if isinstance(permission, str): return Permission[permission] raise TypeError('permission must be Permission, int, or str') class Role(HashidMixin, db.Model): __tablename__ = 'roles' # Primary key id = db.Column(db.Integer, primary_key=True) # Fields name = db.Column(db.String(64), unique=True) default = db.Column(db.Boolean, default=False, index=True) permissions = db.Column(db.Integer, default=0) # Relationships users = db.relationship('User', back_populates='role', lazy='dynamic') def __repr__(self): return f'' def has_permission(self, permission: Union[Permission, int, str]): p = Permission.get(permission) return self.permissions & p.value == p.value def add_permission(self, permission: Union[Permission, int, str]): p = Permission.get(permission) if not self.has_permission(p): self.permissions += p.value def remove_permission(self, permission: Union[Permission, int, str]): p = Permission.get(permission) if self.has_permission(p): self.permissions -= p.value def reset_permissions(self): self.permissions = 0 def to_json_serializeable(self, backrefs=False, relationships=False): json_serializeable = { 'id': self.hashid, 'default': self.default, 'name': self.name, 'permissions': [ x.name for x in Permission if self.has_permission(x.value) ] } if backrefs: pass if relationships: json_serializeable['users'] = { x.hashid: x.to_json_serializeable(relationships=True) for x in self.users } return json_serializeable @staticmethod def insert_defaults(): roles = { 'User': [], 'API user': [Permission.USE_API], 'Contributor': [Permission.CONTRIBUTE], 'Administrator': [ Permission.ADMINISTRATE, Permission.CONTRIBUTE, Permission.USE_API ], 'System user': [] } default_role_name = 'User' for role_name, permissions in roles.items(): role = Role.query.filter_by(name=role_name).first() if role is None: role = Role(name=role_name) role.reset_permissions() for permission in permissions: role.add_permission(permission) role.default = role.name == default_role_name db.session.add(role) db.session.commit()