2024-03-05 15:02:23 +00:00
|
|
|
from enum import IntEnum
|
|
|
|
from flask_hashids import HashidMixin
|
|
|
|
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
|
2024-09-25 15:46:53 +00:00
|
|
|
def get(permission: 'Permission | int | str') -> 'Permission':
|
2024-03-05 15:02:23 +00:00
|
|
|
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'<Role {self.name}>'
|
|
|
|
|
2024-09-25 15:46:53 +00:00
|
|
|
def has_permission(self, permission: Permission | int | str):
|
2024-03-05 15:02:23 +00:00
|
|
|
p = Permission.get(permission)
|
|
|
|
return self.permissions & p.value == p.value
|
2024-09-25 15:46:53 +00:00
|
|
|
|
|
|
|
def add_permission(self, permission: Permission | int | str):
|
2024-03-05 15:02:23 +00:00
|
|
|
p = Permission.get(permission)
|
|
|
|
if not self.has_permission(p):
|
|
|
|
self.permissions += p.value
|
2024-09-25 15:46:53 +00:00
|
|
|
|
|
|
|
def remove_permission(self, permission: Permission | int | str):
|
2024-03-05 15:02:23 +00:00
|
|
|
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()
|