feature/migrations, closes #19 #20
|
@ -9,6 +9,7 @@ from sqlalchemy.exc import OperationalError
|
||||||
from werkzeug.exceptions import HTTPException
|
from werkzeug.exceptions import HTTPException
|
||||||
|
|
||||||
from flaschengeist import logger
|
from flaschengeist import logger
|
||||||
|
from flaschengeist.models import Plugin
|
||||||
from flaschengeist.utils.hook import Hook
|
from flaschengeist.utils.hook import Hook
|
||||||
from flaschengeist.plugins import AuthPlugin
|
from flaschengeist.plugins import AuthPlugin
|
||||||
from flaschengeist.config import config, configure_app
|
from flaschengeist.config import config, configure_app
|
||||||
|
|
|
@ -6,7 +6,7 @@ from importlib.metadata import EntryPoint, entry_points
|
||||||
|
|
||||||
from flaschengeist.database import db
|
from flaschengeist.database import db
|
||||||
from flaschengeist.config import config
|
from flaschengeist.config import config
|
||||||
from flaschengeist.models.user import Permission
|
from flaschengeist.models import Permission
|
||||||
|
|
||||||
|
|
||||||
@click.group()
|
@click.group()
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from flask import send_file
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from flask import send_file
|
||||||
from PIL import Image as PImage
|
from PIL import Image as PImage
|
||||||
|
|
||||||
from werkzeug.exceptions import NotFound, UnprocessableEntity
|
|
||||||
from werkzeug.datastructures import FileStorage
|
|
||||||
from werkzeug.utils import secure_filename
|
from werkzeug.utils import secure_filename
|
||||||
|
from werkzeug.datastructures import FileStorage
|
||||||
|
from werkzeug.exceptions import NotFound, UnprocessableEntity
|
||||||
|
|
||||||
from flaschengeist.models.image import Image
|
from ..models import Image
|
||||||
from flaschengeist.database import db
|
from ..database import db
|
||||||
from flaschengeist.config import config
|
from ..config import config
|
||||||
|
|
||||||
|
|
||||||
def check_mimetype(mime: str):
|
def check_mimetype(mime: str):
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from flaschengeist.utils.hook import Hook
|
from flaschengeist.utils.hook import Hook
|
||||||
from flaschengeist.models.user import User, Role
|
from flaschengeist.models import User, Role
|
||||||
|
|
||||||
|
|
||||||
class Message:
|
class Message:
|
||||||
|
|
|
@ -4,9 +4,16 @@ Used by plugins for setting and notification functionality.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sqlalchemy
|
import sqlalchemy
|
||||||
|
|
||||||
|
from typing import Union
|
||||||
|
from flask import current_app
|
||||||
|
from werkzeug.exceptions import NotFound
|
||||||
|
from importlib.metadata import entry_points
|
||||||
|
|
||||||
|
from .. import logger
|
||||||
from ..database import db
|
from ..database import db
|
||||||
from ..models.setting import _PluginSetting
|
from ..utils import Hook
|
||||||
from ..models.notification import Notification
|
from ..models import Plugin, PluginSetting, Notification
|
||||||
|
|
||||||
|
|
||||||
def get_setting(plugin_id: str, name: str, **kwargs):
|
def get_setting(plugin_id: str, name: str, **kwargs):
|
||||||
|
@ -23,7 +30,7 @@ def get_setting(plugin_id: str, name: str, **kwargs):
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
setting = (
|
setting = (
|
||||||
_PluginSetting.query.filter(_PluginSetting.plugin == plugin_id).filter(_PluginSetting.name == name).one()
|
PluginSetting.query.filter(PluginSetting.plugin == plugin_id).filter(PluginSetting.name == name).one()
|
||||||
)
|
)
|
||||||
return setting.value
|
return setting.value
|
||||||
except sqlalchemy.orm.exc.NoResultFound:
|
except sqlalchemy.orm.exc.NoResultFound:
|
||||||
|
@ -42,8 +49,8 @@ def set_setting(plugin_id: str, name: str, value):
|
||||||
value: Value to be stored
|
value: Value to be stored
|
||||||
"""
|
"""
|
||||||
setting = (
|
setting = (
|
||||||
_PluginSetting.query.filter(_PluginSetting.plugin == plugin_id)
|
PluginSetting.query.filter(PluginSetting.plugin == plugin_id)
|
||||||
.filter(_PluginSetting.name == name)
|
.filter(PluginSetting.name == name)
|
||||||
.one_or_none()
|
.one_or_none()
|
||||||
)
|
)
|
||||||
if setting is not None:
|
if setting is not None:
|
||||||
|
@ -52,7 +59,7 @@ def set_setting(plugin_id: str, name: str, value):
|
||||||
else:
|
else:
|
||||||
setting.value = value
|
setting.value = value
|
||||||
else:
|
else:
|
||||||
db.session.add(_PluginSetting(plugin=plugin_id, name=name, value=value))
|
db.session.add(PluginSetting(plugin=plugin_id, name=name, value=value))
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,10 @@ from typing import Union
|
||||||
from sqlalchemy.exc import IntegrityError
|
from sqlalchemy.exc import IntegrityError
|
||||||
from werkzeug.exceptions import BadRequest, Conflict, NotFound
|
from werkzeug.exceptions import BadRequest, Conflict, NotFound
|
||||||
|
|
||||||
from flaschengeist import logger
|
from .. import logger
|
||||||
from flaschengeist.models.user import Role, Permission
|
from ..models import Role, Permission
|
||||||
from flaschengeist.database import db, case_sensitive
|
from ..database import db, case_sensitive
|
||||||
from flaschengeist.utils.hook import Hook
|
from ..utils.hook import Hook
|
||||||
|
|
||||||
|
|
||||||
def get_all():
|
def get_all():
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
import secrets
|
import secrets
|
||||||
from flaschengeist.models.session import Session
|
|
||||||
from flaschengeist.database import db
|
|
||||||
from flaschengeist import logger
|
|
||||||
from werkzeug.exceptions import Forbidden, Unauthorized
|
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
|
from werkzeug.exceptions import Forbidden, Unauthorized
|
||||||
|
|
||||||
|
from .. import logger
|
||||||
|
from ..models import Session
|
||||||
|
from ..database import db
|
||||||
|
|
||||||
|
|
||||||
lifetime = 1800
|
lifetime = 1800
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import secrets
|
|
||||||
import re
|
import re
|
||||||
|
import secrets
|
||||||
|
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from sqlalchemy import exc
|
from sqlalchemy import exc
|
||||||
from flask import current_app
|
from flask import current_app
|
||||||
|
@ -7,15 +8,15 @@ from datetime import datetime, timedelta, timezone
|
||||||
from flask.helpers import send_file
|
from flask.helpers import send_file
|
||||||
from werkzeug.exceptions import NotFound, BadRequest, Forbidden
|
from werkzeug.exceptions import NotFound, BadRequest, Forbidden
|
||||||
|
|
||||||
from flaschengeist import logger
|
from .. import logger
|
||||||
from flaschengeist.config import config
|
from ..config import config
|
||||||
from flaschengeist.database import db
|
from ..database import db
|
||||||
from flaschengeist.models.notification import Notification
|
from ..models import Notification, User, Role
|
||||||
from flaschengeist.utils.hook import Hook
|
from ..models.user import _PasswordReset
|
||||||
from flaschengeist.utils.datetime import from_iso_format
|
from ..utils.hook import Hook
|
||||||
from flaschengeist.utils.foreign_keys import merge_references
|
from ..utils.datetime import from_iso_format
|
||||||
from flaschengeist.models.user import User, Role, _PasswordReset
|
from ..utils.foreign_keys import merge_references
|
||||||
from flaschengeist.controller import imageController, messageController, sessionController
|
from ..controller import imageController, messageController, sessionController
|
||||||
|
|
||||||
|
|
||||||
def __active_users():
|
def __active_users():
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
import sys
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from sqlalchemy import BigInteger, util
|
||||||
|
from sqlalchemy.dialects import mysql, sqlite
|
||||||
|
from sqlalchemy.types import DateTime, TypeDecorator
|
||||||
|
|
||||||
|
|
||||||
|
class ModelSerializeMixin:
|
||||||
|
"""Mixin class used for models to serialize them automatically
|
||||||
|
Ignores private and protected members as well as members marked as not to publish (name ends with _)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __is_optional(self, param):
|
||||||
|
if sys.version_info < (3, 8):
|
||||||
|
return False
|
||||||
|
|
||||||
|
import typing
|
||||||
|
|
||||||
|
hint = typing.get_type_hints(self.__class__)[param]
|
||||||
|
if (
|
||||||
|
typing.get_origin(hint) is typing.Union
|
||||||
|
and len(typing.get_args(hint)) == 2
|
||||||
|
and typing.get_args(hint)[1] is type(None)
|
||||||
|
):
|
||||||
|
return getattr(self, param) is None
|
||||||
|
|
||||||
|
def serialize(self):
|
||||||
|
"""Serialize class to dict
|
||||||
|
Returns:
|
||||||
|
Dict of all not private or protected annotated member variables.
|
||||||
|
"""
|
||||||
|
d = {
|
||||||
|
param: getattr(self, param)
|
||||||
|
for param in self.__class__.__annotations__
|
||||||
|
if not param.startswith("_") and not param.endswith("_") and not self.__is_optional(param)
|
||||||
|
}
|
||||||
|
if len(d) == 1:
|
||||||
|
key, value = d.popitem()
|
||||||
|
return value
|
||||||
|
return d
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return self.serialize().__str__()
|
||||||
|
|
||||||
|
|
||||||
|
class Serial(TypeDecorator):
|
||||||
|
"""Same as MariaDB Serial used for IDs"""
|
||||||
|
|
||||||
|
cache_ok = True
|
||||||
|
impl = BigInteger().with_variant(mysql.BIGINT(unsigned=True), "mysql").with_variant(sqlite.INTEGER(), "sqlite")
|
||||||
|
|
||||||
|
# https://alembic.sqlalchemy.org/en/latest/autogenerate.html?highlight=custom%20column#affecting-the-rendering-of-types-themselves
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return util.generic_repr(self)
|
||||||
|
|
||||||
|
|
||||||
|
class UtcDateTime(TypeDecorator):
|
||||||
|
"""Almost equivalent to `sqlalchemy.types.DateTime` with
|
||||||
|
``timezone=True`` option, but it differs from that by:
|
||||||
|
|
||||||
|
- Never silently take naive :class:`datetime.datetime`, instead it
|
||||||
|
always raise :exc:`ValueError` unless time zone aware value.
|
||||||
|
- :class:`datetime.datetime` value's :attr:`datetime.datetime.tzinfo`
|
||||||
|
is always converted to UTC.
|
||||||
|
- Unlike SQLAlchemy's built-in :class:`sqlalchemy.types.DateTime`,
|
||||||
|
it never return naive :class:`datetime.datetime`, but time zone
|
||||||
|
aware value, even with SQLite or MySQL.
|
||||||
|
"""
|
||||||
|
|
||||||
|
cache_ok = True
|
||||||
|
impl = DateTime(timezone=True)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def current_utc():
|
||||||
|
return datetime.datetime.now(tz=datetime.timezone.utc)
|
||||||
|
|
||||||
|
def process_bind_param(self, value, dialect):
|
||||||
|
if value is not None:
|
||||||
|
if not isinstance(value, datetime.datetime):
|
||||||
|
raise TypeError("expected datetime.datetime, not " + repr(value))
|
||||||
|
elif value.tzinfo is None:
|
||||||
|
raise ValueError("naive datetime is disallowed")
|
||||||
|
return value.astimezone(datetime.timezone.utc)
|
||||||
|
|
||||||
|
def process_result_value(self, value, dialect):
|
||||||
|
if value is not None:
|
||||||
|
if value.tzinfo is not None:
|
||||||
|
value = value.astimezone(datetime.timezone.utc)
|
||||||
|
value = value.replace(tzinfo=datetime.timezone.utc)
|
||||||
|
return value
|
||||||
|
|
||||||
|
# https://alembic.sqlalchemy.org/en/latest/autogenerate.html?highlight=custom%20column#affecting-the-rendering-of-types-themselves
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return util.generic_repr(self)
|
|
@ -1,95 +1,5 @@
|
||||||
import sys
|
from .session import *
|
||||||
import datetime
|
from .user import *
|
||||||
|
from .plugin import *
|
||||||
from sqlalchemy import BigInteger, util
|
from .notification import *
|
||||||
from sqlalchemy.dialects import mysql, sqlite
|
from .image import *
|
||||||
from sqlalchemy.types import DateTime, TypeDecorator
|
|
||||||
|
|
||||||
|
|
||||||
class ModelSerializeMixin:
|
|
||||||
"""Mixin class used for models to serialize them automatically
|
|
||||||
Ignores private and protected members as well as members marked as not to publish (name ends with _)
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __is_optional(self, param):
|
|
||||||
if sys.version_info < (3, 8):
|
|
||||||
return False
|
|
||||||
|
|
||||||
import typing
|
|
||||||
|
|
||||||
hint = typing.get_type_hints(self.__class__)[param]
|
|
||||||
if (
|
|
||||||
typing.get_origin(hint) is typing.Union
|
|
||||||
and len(typing.get_args(hint)) == 2
|
|
||||||
and typing.get_args(hint)[1] is type(None)
|
|
||||||
):
|
|
||||||
return getattr(self, param) is None
|
|
||||||
|
|
||||||
def serialize(self):
|
|
||||||
"""Serialize class to dict
|
|
||||||
Returns:
|
|
||||||
Dict of all not private or protected annotated member variables.
|
|
||||||
"""
|
|
||||||
d = {
|
|
||||||
param: getattr(self, param)
|
|
||||||
for param in self.__class__.__annotations__
|
|
||||||
if not param.startswith("_") and not param.endswith("_") and not self.__is_optional(param)
|
|
||||||
}
|
|
||||||
if len(d) == 1:
|
|
||||||
key, value = d.popitem()
|
|
||||||
return value
|
|
||||||
return d
|
|
||||||
|
|
||||||
def __str__(self) -> str:
|
|
||||||
return self.serialize().__str__()
|
|
||||||
|
|
||||||
|
|
||||||
class Serial(TypeDecorator):
|
|
||||||
"""Same as MariaDB Serial used for IDs"""
|
|
||||||
|
|
||||||
cache_ok = True
|
|
||||||
impl = BigInteger().with_variant(mysql.BIGINT(unsigned=True), "mysql").with_variant(sqlite.INTEGER(), "sqlite")
|
|
||||||
|
|
||||||
# https://alembic.sqlalchemy.org/en/latest/autogenerate.html?highlight=custom%20column#affecting-the-rendering-of-types-themselves
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return util.generic_repr(self)
|
|
||||||
|
|
||||||
|
|
||||||
class UtcDateTime(TypeDecorator):
|
|
||||||
"""Almost equivalent to `sqlalchemy.types.DateTime` with
|
|
||||||
``timezone=True`` option, but it differs from that by:
|
|
||||||
|
|
||||||
- Never silently take naive :class:`datetime.datetime`, instead it
|
|
||||||
always raise :exc:`ValueError` unless time zone aware value.
|
|
||||||
- :class:`datetime.datetime` value's :attr:`datetime.datetime.tzinfo`
|
|
||||||
is always converted to UTC.
|
|
||||||
- Unlike SQLAlchemy's built-in :class:`sqlalchemy.types.DateTime`,
|
|
||||||
it never return naive :class:`datetime.datetime`, but time zone
|
|
||||||
aware value, even with SQLite or MySQL.
|
|
||||||
"""
|
|
||||||
|
|
||||||
cache_ok = True
|
|
||||||
impl = DateTime(timezone=True)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def current_utc():
|
|
||||||
return datetime.datetime.now(tz=datetime.timezone.utc)
|
|
||||||
|
|
||||||
def process_bind_param(self, value, dialect):
|
|
||||||
if value is not None:
|
|
||||||
if not isinstance(value, datetime.datetime):
|
|
||||||
raise TypeError("expected datetime.datetime, not " + repr(value))
|
|
||||||
elif value.tzinfo is None:
|
|
||||||
raise ValueError("naive datetime is disallowed")
|
|
||||||
return value.astimezone(datetime.timezone.utc)
|
|
||||||
|
|
||||||
def process_result_value(self, value, dialect):
|
|
||||||
if value is not None:
|
|
||||||
if value.tzinfo is not None:
|
|
||||||
value = value.astimezone(datetime.timezone.utc)
|
|
||||||
value = value.replace(tzinfo=datetime.timezone.utc)
|
|
||||||
return value
|
|
||||||
|
|
||||||
# https://alembic.sqlalchemy.org/en/latest/autogenerate.html?highlight=custom%20column#affecting-the-rendering-of-types-themselves
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return util.generic_repr(self)
|
|
|
@ -1,8 +1,8 @@
|
||||||
from sqlalchemy import event
|
from sqlalchemy import event
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from . import ModelSerializeMixin, Serial
|
|
||||||
from ..database import db
|
from ..database import db
|
||||||
|
from ..database.types import ModelSerializeMixin, Serial
|
||||||
|
|
||||||
|
|
||||||
class Image(db.Model, ModelSerializeMixin):
|
class Image(db.Model, ModelSerializeMixin):
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from . import Serial, UtcDateTime, ModelSerializeMixin
|
|
||||||
from ..database import db
|
from ..database import db
|
||||||
from .user import User
|
from ..database.types import Serial, UtcDateTime, ModelSerializeMixin
|
||||||
|
|
||||||
|
|
||||||
class Notification(db.Model, ModelSerializeMixin):
|
class Notification(db.Model, ModelSerializeMixin):
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from ..database import db
|
||||||
|
from ..database.types import Serial
|
||||||
|
|
||||||
|
|
||||||
|
class Plugin(db.Model):
|
||||||
|
__tablename__ = "plugin"
|
||||||
|
id: int = db.Column("id", Serial, primary_key=True)
|
||||||
|
name: str = db.Column(db.String(127), nullable=False)
|
||||||
|
version: str = db.Column(db.String(30), nullable=False)
|
||||||
|
"""The latest installed version"""
|
||||||
|
enabled: bool = db.Column(db.Boolean, default=False)
|
||||||
|
|
||||||
|
settings_ = db.relationship("PluginSetting", cascade="all, delete")
|
||||||
|
permissions_ = db.relationship("Permission", cascade="all, delete")
|
||||||
|
|
||||||
|
|
||||||
|
class PluginSetting(db.Model):
|
||||||
|
__tablename__ = "plugin_setting"
|
||||||
|
id = db.Column("id", Serial, primary_key=True)
|
||||||
|
plugin_id: int = db.Column("plugin", Serial, db.ForeignKey("plugin.id"))
|
||||||
|
name: str = db.Column(db.String(127), nullable=False)
|
||||||
|
value: Any = db.Column(db.PickleType(protocol=4))
|
|
@ -1,5 +1,9 @@
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from secrets import compare_digest
|
from secrets import compare_digest
|
||||||
|
|
||||||
|
from ..database import db
|
||||||
|
from ..database.types import ModelSerializeMixin, UtcDateTime, Serial
|
||||||
|
|
||||||
from flaschengeist import logger
|
from flaschengeist import logger
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,7 +26,7 @@ class Session(db.Model, ModelSerializeMixin):
|
||||||
|
|
||||||
_id = db.Column("id", Serial, primary_key=True)
|
_id = db.Column("id", Serial, primary_key=True)
|
||||||
_user_id = db.Column("user_id", Serial, db.ForeignKey("user.id"))
|
_user_id = db.Column("user_id", Serial, db.ForeignKey("user.id"))
|
||||||
user_: User = db.relationship("User", back_populates="sessions_")
|
user_: "User" = db.relationship("User", back_populates="sessions_")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def userid(self):
|
def userid(self):
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
from __future__ import annotations # TODO: Remove if python requirement is >= 3.10
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
from . import Serial
|
|
||||||
from ..database import db
|
|
||||||
|
|
||||||
|
|
||||||
class _PluginSetting(db.Model):
|
|
||||||
__tablename__ = "plugin_setting"
|
|
||||||
id = db.Column("id", Serial, primary_key=True)
|
|
||||||
plugin: str = db.Column(db.String(127))
|
|
||||||
name: str = db.Column(db.String(127), nullable=False)
|
|
||||||
value: Any = db.Column(db.PickleType(protocol=4))
|
|
|
@ -3,9 +3,7 @@ from datetime import date, datetime
|
||||||
from sqlalchemy.orm.collections import attribute_mapped_collection
|
from sqlalchemy.orm.collections import attribute_mapped_collection
|
||||||
|
|
||||||
from ..database import db
|
from ..database import db
|
||||||
from . import ModelSerializeMixin, UtcDateTime, Serial
|
from ..database.types import ModelSerializeMixin, UtcDateTime, Serial
|
||||||
from .image import Image
|
|
||||||
|
|
||||||
|
|
||||||
association_table = db.Table(
|
association_table = db.Table(
|
||||||
"user_x_role",
|
"user_x_role",
|
||||||
|
@ -19,7 +17,6 @@ role_permission_association_table = db.Table(
|
||||||
db.Column("permission_id", Serial, db.ForeignKey("permission.id")),
|
db.Column("permission_id", Serial, db.ForeignKey("permission.id")),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Permission(db.Model, ModelSerializeMixin):
|
class Permission(db.Model, ModelSerializeMixin):
|
||||||
__tablename__ = "permission"
|
__tablename__ = "permission"
|
||||||
name: str = db.Column(db.String(30), unique=True)
|
name: str = db.Column(db.String(30), unique=True)
|
||||||
|
@ -66,7 +63,7 @@ class User(db.Model, ModelSerializeMixin):
|
||||||
sessions_: list["Session"] = db.relationship(
|
sessions_: list["Session"] = db.relationship(
|
||||||
"Session", back_populates="user_", cascade="all, delete, delete-orphan"
|
"Session", back_populates="user_", cascade="all, delete, delete-orphan"
|
||||||
)
|
)
|
||||||
avatar_: Optional[Image] = db.relationship("Image", cascade="all, delete, delete-orphan", single_parent=True)
|
avatar_: Optional["Image"] = db.relationship("Image", cascade="all, delete, delete-orphan", single_parent=True)
|
||||||
reset_requests_: list["_PasswordReset"] = db.relationship("_PasswordReset", cascade="all, delete, delete-orphan")
|
reset_requests_: list["_PasswordReset"] = db.relationship("_PasswordReset", cascade="all, delete, delete-orphan")
|
||||||
|
|
||||||
# Private stuff for internal use
|
# Private stuff for internal use
|
||||||
|
|
|
@ -9,7 +9,8 @@ from importlib.metadata import Distribution, EntryPoint
|
||||||
from werkzeug.exceptions import MethodNotAllowed, NotFound
|
from werkzeug.exceptions import MethodNotAllowed, NotFound
|
||||||
from werkzeug.datastructures import FileStorage
|
from werkzeug.datastructures import FileStorage
|
||||||
|
|
||||||
from flaschengeist.models.user import _Avatar, User
|
from flaschengeist.models import User
|
||||||
|
from flaschengeist.models.user import _Avatar
|
||||||
from flaschengeist.utils.hook import HookBefore, HookAfter
|
from flaschengeist.utils.hook import HookBefore, HookAfter
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
@ -19,7 +20,7 @@ __all__ = [
|
||||||
"before_role_updated",
|
"before_role_updated",
|
||||||
"before_update_user",
|
"before_update_user",
|
||||||
"after_role_updated",
|
"after_role_updated",
|
||||||
"Plugin",
|
"BasePlugin",
|
||||||
"AuthPlugin",
|
"AuthPlugin",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -70,7 +71,7 @@ Passed args:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Plugin:
|
class BasePlugin:
|
||||||
"""Base class for all Plugins
|
"""Base class for all Plugins
|
||||||
|
|
||||||
All plugins must derived from this class.
|
All plugins must derived from this class.
|
||||||
|
@ -193,10 +194,10 @@ class Plugin:
|
||||||
return {"version": self.version, "permissions": self.permissions}
|
return {"version": self.version, "permissions": self.permissions}
|
||||||
|
|
||||||
|
|
||||||
class AuthPlugin(Plugin):
|
class AuthPlugin(BasePlugin):
|
||||||
"""Base class for all authentification plugins
|
"""Base class for all authentification plugins
|
||||||
|
|
||||||
See also `Plugin`
|
See also `BasePlugin`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def login(self, user, pw):
|
def login(self, user, pw):
|
||||||
|
|
|
@ -6,13 +6,13 @@ from flask import Blueprint, request, jsonify
|
||||||
from werkzeug.exceptions import Forbidden, BadRequest, Unauthorized
|
from werkzeug.exceptions import Forbidden, BadRequest, Unauthorized
|
||||||
|
|
||||||
from flaschengeist import logger
|
from flaschengeist import logger
|
||||||
from flaschengeist.plugins import Plugin
|
from flaschengeist.plugins import BasePlugin
|
||||||
from flaschengeist.utils.HTTP import no_content, created
|
from flaschengeist.utils.HTTP import no_content, created
|
||||||
from flaschengeist.utils.decorators import login_required
|
from flaschengeist.utils.decorators import login_required
|
||||||
from flaschengeist.controller import sessionController, userController
|
from flaschengeist.controller import sessionController, userController
|
||||||
|
|
||||||
|
|
||||||
class AuthRoutePlugin(Plugin):
|
class AuthRoutePlugin(BasePlugin):
|
||||||
blueprint = Blueprint("auth", __name__)
|
blueprint = Blueprint("auth", __name__)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,8 @@ from werkzeug.datastructures import FileStorage
|
||||||
|
|
||||||
from flaschengeist import logger
|
from flaschengeist import logger
|
||||||
from flaschengeist.controller import userController
|
from flaschengeist.controller import userController
|
||||||
from flaschengeist.models.user import User, Role, _Avatar
|
from flaschengeist.models import User, Role
|
||||||
|
from flaschengeist.models.user import _Avatar
|
||||||
from flaschengeist.plugins import AuthPlugin, before_role_updated
|
from flaschengeist.plugins import AuthPlugin, before_role_updated
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import hashlib
|
||||||
import binascii
|
import binascii
|
||||||
from werkzeug.exceptions import BadRequest
|
from werkzeug.exceptions import BadRequest
|
||||||
from flaschengeist.plugins import AuthPlugin, plugins_installed
|
from flaschengeist.plugins import AuthPlugin, plugins_installed
|
||||||
from flaschengeist.models.user import User, Role, Permission
|
from flaschengeist.models import User, Role, Permission
|
||||||
from flaschengeist.database import db
|
from flaschengeist.database import db
|
||||||
from flaschengeist import logger
|
from flaschengeist import logger
|
||||||
|
|
||||||
|
|
|
@ -3,15 +3,14 @@ from email.mime.text import MIMEText
|
||||||
from email.mime.multipart import MIMEMultipart
|
from email.mime.multipart import MIMEMultipart
|
||||||
|
|
||||||
from flaschengeist import logger
|
from flaschengeist import logger
|
||||||
from flaschengeist.models.user import User
|
from flaschengeist.models import User
|
||||||
|
from flaschengeist.plugins import BasePlugin
|
||||||
from flaschengeist.utils.hook import HookAfter
|
from flaschengeist.utils.hook import HookAfter
|
||||||
from flaschengeist.controller import userController
|
from flaschengeist.controller import userController
|
||||||
from flaschengeist.controller.messageController import Message
|
from flaschengeist.controller.messageController import Message
|
||||||
|
|
||||||
from . import Plugin
|
|
||||||
|
|
||||||
|
class MailMessagePlugin(BasePlugin):
|
||||||
class MailMessagePlugin(Plugin):
|
|
||||||
def __init__(self, entry_point, config):
|
def __init__(self, entry_point, config):
|
||||||
super().__init__(entry_point, config)
|
super().__init__(entry_point, config)
|
||||||
self.server = config["SERVER"]
|
self.server = config["SERVER"]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from flaschengeist.database import db
|
from flaschengeist.database import db
|
||||||
from flaschengeist.models import ModelSerializeMixin, Serial
|
from flaschengeist.database.types import ModelSerializeMixin, Serial
|
||||||
from flaschengeist.models.image import Image
|
from flaschengeist.models import Image
|
||||||
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
|
|
@ -5,17 +5,16 @@ Provides routes used to configure roles and permissions of users / roles.
|
||||||
|
|
||||||
from werkzeug.exceptions import BadRequest
|
from werkzeug.exceptions import BadRequest
|
||||||
from flask import Blueprint, request, jsonify
|
from flask import Blueprint, request, jsonify
|
||||||
from http.client import NO_CONTENT
|
|
||||||
|
|
||||||
from flaschengeist.plugins import Plugin
|
from flaschengeist.plugins import BasePlugin
|
||||||
from flaschengeist.utils.decorators import login_required
|
|
||||||
from flaschengeist.controller import roleController
|
from flaschengeist.controller import roleController
|
||||||
from flaschengeist.utils.HTTP import created, no_content
|
from flaschengeist.utils.HTTP import created, no_content
|
||||||
|
from flaschengeist.utils.decorators import login_required
|
||||||
|
|
||||||
from . import permissions
|
from . import permissions
|
||||||
|
|
||||||
|
|
||||||
class RolesPlugin(Plugin):
|
class RolesPlugin(BasePlugin):
|
||||||
blueprint = Blueprint("roles", __name__)
|
blueprint = Blueprint("roles", __name__)
|
||||||
permissions = permissions.permissions
|
permissions = permissions.permissions
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,9 @@ from flask import Blueprint
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
from flaschengeist import logger
|
from flaschengeist import logger
|
||||||
|
from flaschengeist.plugins import BasePlugin
|
||||||
from flaschengeist.utils.HTTP import no_content
|
from flaschengeist.utils.HTTP import no_content
|
||||||
|
|
||||||
from . import Plugin
|
|
||||||
|
|
||||||
|
|
||||||
class __Task:
|
class __Task:
|
||||||
def __init__(self, function, **kwags):
|
def __init__(self, function, **kwags):
|
||||||
|
@ -38,7 +37,7 @@ def scheduled(id: str, replace=False, **kwargs):
|
||||||
return real_decorator
|
return real_decorator
|
||||||
|
|
||||||
|
|
||||||
class SchedulerPlugin(Plugin):
|
class SchedulerPlugin(BasePlugin):
|
||||||
def __init__(self, entry_point, config=None):
|
def __init__(self, entry_point, config=None):
|
||||||
super().__init__(entry_point, config)
|
super().__init__(entry_point, config)
|
||||||
self.blueprint = Blueprint(self.name, __name__)
|
self.blueprint = Blueprint(self.name, __name__)
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
|
|
||||||
Provides routes used to manage users
|
Provides routes used to manage users
|
||||||
"""
|
"""
|
||||||
from http.client import NO_CONTENT, CREATED
|
from http.client import CREATED
|
||||||
from flask import Blueprint, request, jsonify, make_response
|
from flask import Blueprint, request, jsonify, make_response
|
||||||
from werkzeug.exceptions import BadRequest, Forbidden, MethodNotAllowed, NotFound
|
from werkzeug.exceptions import BadRequest, Forbidden, MethodNotAllowed
|
||||||
|
|
||||||
from . import permissions
|
from . import permissions
|
||||||
from flaschengeist import logger
|
from flaschengeist import logger
|
||||||
from flaschengeist.config import config
|
from flaschengeist.config import config
|
||||||
from flaschengeist.plugins import Plugin
|
from flaschengeist.plugins import BasePlugin
|
||||||
from flaschengeist.models.user import User
|
from flaschengeist.models.user import User
|
||||||
from flaschengeist.utils.decorators import login_required, extract_session, headers
|
from flaschengeist.utils.decorators import login_required, extract_session, headers
|
||||||
from flaschengeist.controller import userController
|
from flaschengeist.controller import userController
|
||||||
|
@ -17,7 +17,7 @@ from flaschengeist.utils.HTTP import created, no_content
|
||||||
from flaschengeist.utils.datetime import from_iso_format
|
from flaschengeist.utils.datetime import from_iso_format
|
||||||
|
|
||||||
|
|
||||||
class UsersPlugin(Plugin):
|
class UsersPlugin(BasePlugin):
|
||||||
blueprint = Blueprint("users", __name__)
|
blueprint = Blueprint("users", __name__)
|
||||||
permissions = permissions.permissions
|
permissions = permissions.permissions
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue