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.
 
 

152 lines
3.7 KiB

from __future__ import annotations
import enum
from typing import Dict
from typing import Optional
from typing import Sequence
from flask_security import RoleMixin
from flask_security import SQLAlchemyUserDatastore
from flask_security import UserMixin
from werkzeug.security import check_password_hash
from werkzeug.security import generate_password_hash
from ..database import BaseModel
from ..database import db
roles_users = db.Table(
"roles_users",
db.Column("user_id", db.Integer(), db.ForeignKey("user.id")),
db.Column("role_id", db.Integer(), db.ForeignKey("role.id")),
)
class RoleType(enum.Enum):
"""The type of a role"""
admin: int = 1
ROLE_DESCRIPTIONS: Dict[RoleType, str] = {
RoleType.admin: "Has admin rights",
}
class Role(BaseModel, RoleMixin):
id: int # type: ignore
"""The DB id"""
name: str = db.Column(db.String(80), unique=True)
"""The name of the role, see :class:`RoleType`"""
description: str = db.Column(db.String(255))
"""A short description of the role"""
def __init__(
self,
name: str,
description: str,
):
"""
:param name: The name of the role, see :class:`RoleType`
:param description: A short description of the role
"""
self.name = name
self.description = description
@classmethod
def get_role(
cls,
role_type: RoleType,
) -> Role:
"""Fetches the role from the DB or creates it if necessary
:param role_type: The type of the role
:return: The role instance
"""
role_: Optional[Role] = cls.query.filter_by(name=role_type.name).first()
if role_ is None:
role_ = cls(
name=role_type.name,
description=ROLE_DESCRIPTIONS[role_type],
)
role_.save()
return role_
class User(BaseModel, UserMixin):
id: int # type: ignore
"""The DB id"""
email: str = db.Column(db.String(255), unique=True)
"""The email address"""
password: str = db.Column(db.String(512))
"""The password as hash"""
active: bool = db.Column(db.Boolean())
"""Is the user activated?"""
roles: Sequence[Role] = db.relationship(
"Role",
secondary=roles_users,
backref=db.backref("users", lazy="dynamic"),
)
"""The roles which the user has"""
def __init__(
self,
email: str,
password: str,
active: bool = True,
roles: Sequence[Role] = None,
):
"""
:param email: The email address
:param password: The password as plain text
:param active: *True* if the user is active
:param roles: The Roles of the user.
"""
self.email = email
self.set_password(password=password)
self.active = active
self.roles = roles or [Role.get_role(RoleType.admin)]
def set_password(
self,
password: str,
):
"""Create hashed password."""
self.password = generate_password_hash(
password=password,
)
def check_password(
self,
password: str,
) -> bool:
"""Check hashed password
:param password: The password in plain text
:return: True if equal
"""
return bool(
check_password_hash(
pwhash=self.password,
password=password,
),
)
def __repr__(self):
return (
f"<{self.__class__.__name__} "
f"email={self.email!r}, "
f"active={self.active!r}, "
f"roles={self.roles!r}>"
)
user_datastore = SQLAlchemyUserDatastore(db, User, Role)