flask_more_smorest.perms.models

User-related models for Flask-More-Smorest.

This module exports abstract user-related models. Concrete default implementations live in flask_more_smorest.perms.models.defaults and are only loaded when explicitly imported.

Version: 0.10.1

class flask_more_smorest.perms.models.AbstractDomain(**kwargs)[source]

Abstract Domain model for multi-domain support.

This is an abstract base class - it does NOT create a database table. Subclasses must define concrete fields and table configuration.

Domains represent distinct contexts within an application (e.g., organizations, tenants, or projects) where roles can be scoped.

Subclassing example:

from flask_more_smorest.perms import AbstractDomain

class CustomDomain(AbstractDomain):
    __tablename__ = "domain"

    name: Mapped[str] = mapped_column(db.String(255), nullable=False)
    display_name: Mapped[str] = mapped_column(db.String(255), nullable=False)
    active: Mapped[bool] = mapped_column(db.Boolean, default=True, nullable=False)

    # Optional: custom fields
    organization_id: Mapped[str] = mapped_column(db.String(50))
    settings: Mapped[dict] = mapped_column(db.JSON, default={})
name: Mapped[str] = <sqlalchemy.orm.properties.MappedColumn object>
display_name: Mapped[str] = <sqlalchemy.orm.properties.MappedColumn object>
active: Mapped[bool] = <sqlalchemy.orm.properties.MappedColumn object>
class flask_more_smorest.perms.models.AbstractToken(**kwargs)[source]

Abstract Token model for API authentication.

This is an abstract base class - it does NOT create a database table. Subclasses must define concrete fields and table configuration.

Permission checks are delegated to the owning user by default (via UserOwnershipMixin). Override _can_read/_can_write/_can_create to customize.

Subclassing example:

from flask_more_smorest.perms import AbstractToken

class CustomToken(AbstractToken):
    __tablename__ = "token"

    token: Mapped[str] = mapped_column(db.String(1024), nullable=False)
    description: Mapped[str | None] = mapped_column(db.String(64), nullable=True)
    expires_at: Mapped[sa.DateTime | None] = mapped_column(sa.DateTime(), nullable=True)
    revoked: Mapped[bool] = mapped_column(db.Boolean(), nullable=False, default=False)
    revoked_at: Mapped[sa.DateTime | None] = mapped_column(sa.DateTime(), nullable=True)

    # Optional: custom fields
    last_used_at: Mapped[sa.DateTime | None] = mapped_column(sa.DateTime(), nullable=True)
    ip_address: Mapped[str | None] = mapped_column(db.String(45), nullable=True)
token: Mapped[str] = <sqlalchemy.orm.properties.MappedColumn object>
description: Mapped[str | None] = <sqlalchemy.orm.properties.MappedColumn object>
expires_at: Mapped[dt.datetime | None] = <sqlalchemy.orm.properties.MappedColumn object>
revoked: Mapped[bool] = <sqlalchemy.orm.properties.MappedColumn object>
revoked_at: Mapped[dt.datetime | None] = <sqlalchemy.orm.properties.MappedColumn object>
class flask_more_smorest.perms.models.AbstractUser(**kwargs)[source]

Abstract User model with email/password auth, roles, and domain support.

This is an abstract base class - it does NOT create a database table. Subclasses must define concrete fields and table configuration.

Features (all inherited): - Email/password authentication - Roles management via UserRole relationship - Settings management via UserSetting relationship - Token management via Token relationship - Permission checks (_can_read, _can_write, _can_create) - Admin properties (is_admin, is_superadmin) - Role checking (has_role, list_roles)

Subclassing example:

from flask_more_smorest.perms import AbstractUser, init_fms

class CustomUser(AbstractUser):
    # Optional: custom fields only
    bio: Mapped[str | None] = mapped_column(sa.String(500))

    def _can_write(self, user) -> bool:
        return super()._can_write(user)

# Register with the system
init_fms(user=CustomUser)
email: Mapped[str] = <sqlalchemy.orm.properties.MappedColumn object>
password: Mapped[bytes | None] = <sqlalchemy.orm.properties.MappedColumn object>
is_enabled: Mapped[bool] = <sqlalchemy.orm.properties.MappedColumn object>
roles = <_RelationshipDeclared at 0x7a2d88c34eb0; no key>
settings = <_RelationshipDeclared at 0x7a2d88c36b70; no key>
tokens = <_RelationshipDeclared at 0x7a2d887d3ed0; no key>
__init__(**kwargs)[source]

Create new user with optional password hashing.

classmethod get_current_user()[source]

Get the current authenticated user of this User subclass.

This provides zero-boilerplate typed access to the current user. Uses the application’s configured authentication (JWT or custom getter).

Return type:

Optional[TypeVar(UserT, bound= AbstractUser)]

Returns:

Current user instance of this User subclass if authenticated, None otherwise

Example

>>> user = AbstractUser.get_current_user()
>>> user = MyCustomUser.get_current_user()
normalize_email(email)[source]

Normalize email to lowercase for case-insensitive lookups.

Emails are automatically converted to lowercase when set, ensuring: - Case-insensitive login (user@example.com == USER@EXAMPLE.COM) - Prevention of duplicate registrations with different cases - Efficient database queries using the email index - Consistent email storage throughout the application

Parameters:

email (str | None) – Email address to normalize

Return type:

str | None

Returns:

Lowercase email address, or None if email is None

set_password(password)[source]

Set password with secure hashing.

Return type:

None

is_password_correct(password)[source]

Check if provided password matches stored hash.

Return type:

bool

update(commit=True, **kwargs)[source]

Update user with password handling.

Return type:

None

property is_admin: bool

Check if user has admin privileges.

property is_superadmin: bool

Check if user has superadmin privileges.

has_role(role, domain_name=None)[source]

Check if user has specified role, optionally scoped to domain.

Parameters:
  • role (Union[Literal['ADMIN', 'SUPERADMIN'], str, Enum]) – Role to check (string or enum value)

  • domain_name (str | None) – Optional domain name to scope the check

Return type:

bool

Returns:

True if user has the role, False otherwise

Example

>>> user.has_role("ADMIN")
True
>>> user.has_role("ADMIN", domain_name="main")
True
list_roles()[source]

List user roles as strings.

Return type:

list[str]

property num_tokens: int

Get number of tokens for this user.

property domain_ids: set[UUID | str]

Return set of domain IDs the user has roles for.

has_domain_access(domain_id)[source]

Check if user has access to a specific domain.

Users have access to a domain if they have any role associated with that domain, or if they have a wildcard role (*). Superadmins automatically have access.

Parameters:

domain_id (UUID | None) – Domain UUID to check access for, or None for global access

Return type:

bool

Returns:

True if user has access to the domain, False otherwise

Example

>>> user.has_domain_access(domain_id)
True
>>> user.has_domain_access(None)  # Global access check
True
class flask_more_smorest.perms.models.AbstractUserRole(domain_id=None, role=None, **kwargs)[source]

Abstract UserRole model with domain scoping for multi-domain applications.

This is an abstract base class - it does NOT create a database table. Subclasses must define concrete fields and table configuration.

Supports custom role enums by accepting any string/enum value:

from enum import Enum

class CustomRole(str, Enum):
    SUPERADMIN = "SUPERADMIN"
    ADMIN = "ADMIN"
    MANAGER = "MANAGER"
    USER = "USER"

class CustomUserRole(AbstractUserRole):
    __tablename__ = "user_role"

    user_id: Mapped[uuid.UUID] = mapped_column(
        sa.Uuid(as_uuid=True),
        db.ForeignKey("user.id"),
        nullable=False
    )
    domain_id: Mapped[uuid.UUID | None] = mapped_column(
        sa.Uuid(as_uuid=True),
        db.ForeignKey("domain.id"),
        nullable=True,
        default=None,
    )
    _role: Mapped[str] = mapped_column("role", sa.String(50), nullable=False)

# Create roles with custom enum values
role = CustomUserRole(user=user, role=CustomRole.MANAGER)
user_id: Mapped[uuid.UUID] = <sqlalchemy.orm.properties.MappedColumn object>
domain_id: Mapped[uuid.UUID | None] = <sqlalchemy.orm.properties.MappedColumn object>
user = <_RelationshipDeclared at 0x7a2d88c36d50; no key>
domain = <_RelationshipDeclared at 0x7a2d88c37570; no key>
property role: str

Get role as string value.

Returns:

Role name as string

__init__(domain_id=None, role=None, **kwargs)[source]

Initialize role with domain and role handling.

Parameters:
  • domain_id (UUID | str | None) – Domain UUID, None for all domains, or ‘*’ string (converted to None)

  • role (str | Enum | None) – Role value (enum or string)

  • **kwargs (object) – Additional field values

class flask_more_smorest.perms.models.AbstractUserSetting(**kwargs)[source]

Abstract UserSetting model for key-value storage.

This is an abstract base class - it does NOT create a database table. Subclasses must define concrete fields and table configuration.

Permission checks are delegated to the owning user by default (via UserOwnershipMixin). Override _can_read/_can_write/_can_create to customize.

Subclassing example:

from flask_more_smorest.perms import AbstractUserSetting

class CustomUserSetting(AbstractUserSetting):
    __tablename__ = "user_setting"

    key: Mapped[str] = mapped_column(db.String(80), nullable=False)
    value: Mapped[str | None] = mapped_column(db.String(1024), nullable=True)

    __table_args__ = (db.UniqueConstraint("user_id", "key"),)

    # Optional: custom fields
    metadata: Mapped[dict] = mapped_column(db.JSON, default={})
    encrypted: Mapped[bool] = mapped_column(db.Boolean, default=False)
key: Mapped[str] = <sqlalchemy.orm.properties.MappedColumn object>
value: Mapped[str | None] = <sqlalchemy.orm.properties.MappedColumn object>

Modules

abstract_role

Abstract role and domain models for Flask-More-Smorest.

abstract_setting

Abstract UserSetting model for key-value storage.

abstract_token

Abstract Token model for API authentication.

abstract_user

Abstract User model for Flask-More-Smorest authentication system.

base_roles

defaults

Default concrete implementations of user-related models.

role

Role and domain models for Flask-More-Smorest.

setting

UserSetting model for key-value storage.

token

Token model for API authentication.

user

User model for Flask-More-Smorest authentication system.