Skip to main content

Bazis Users

PyPI version Python Versions License

An extension package for Bazis that provides advanced capabilities for user management and authentication.

Quick Start

# Install the package
uv add bazis-users

# Create user model
from bazis.contrib.users.models_abstract import UserAbstract
from bazis.core.models_abstract import DtMixin, UuidMixin, JsonApiMixin

class User(UserAbstract, DtMixin, UuidMixin, JsonApiMixin):
    """Custom user model"""
    
    class Meta:
        verbose_name = 'User'
        verbose_name_plural = 'Users'

# Create user route
from bazis.contrib.users.routes import UserRouteSet
from django.apps import apps

class MyUserRouteSet(UserRouteSet):
    model = apps.get_model('myapp.User')

Table of Contents

Description

Bazis Users is an extension package for the Bazis framework that provides a complete toolkit for user management and authentication. The package includes:

  • Abstract user models with JWT authentication support
  • Ready-to-use routes for user operations
  • Services for token and authentication management
  • Mixins for linking models to users
  • Integration with Swagger UI for API testing

This package requires the base bazis package to be installed.

Requirements

  • Python: 3.12+
  • bazis: latest version
  • PostgreSQL: 12+
  • Redis: For caching

Installation

uv add bazis-users

Using pip

pip install bazis-users

Core Components

Abstract Models

UserAbstract

Abstract user model implementing methods required for extended work with Bazis.

Location: bazis.contrib.users.models_abstract.UserAbstract

Methods:

  • get_full_name() - returns user's full name
  • jwt_build() - creates JWT token for the user
  • find_or_create() - finds or creates a user
  • raw_password - works with raw password

Example usage:

from bazis.contrib.users.models_abstract import UserAbstract
from bazis.core.models_abstract import DtMixin, UuidMixin, JsonApiMixin

class User(UserAbstract, DtMixin, UuidMixin, JsonApiMixin):
    """Application user model"""
    
    class Meta:
        verbose_name = 'User'
        verbose_name_plural = 'Users'
    
    def __str__(self):
        return self.get_full_name()

AnonymousUserAbstract

Abstract anonymous user model for handling unauthenticated requests.

Location: bazis.contrib.users.models_abstract.AnonymousUserAbstract

Methods:

  • get_full_name() - returns name for anonymous user

Mixins

UserMixin

Mixin for entity models that should be associated with a user.

Location: bazis.contrib.users.models_abstract.UserMixin

Features:

  • User is stored in context variable CTX_USER_REQUEST
  • Automatically links model to current user from request

Example usage:

from bazis.contrib.users.models_abstract import UserMixin
from bazis.core.models_abstract import DtMixin, UuidMixin, JsonApiMixin
from django.db import models

class Article(UserMixin, DtMixin, UuidMixin, JsonApiMixin):
    """Article linked to a user"""
    title = models.CharField(max_length=255)
    content = models.TextField()
    
    class Meta:
        verbose_name = 'Article'
        verbose_name_plural = 'Articles'

Base Routes

UserRouteBase

Base class for routes associated with users.

Location: bazis.contrib.users.routes_abstract.UserRouteBase

Provides base functionality for creating routes that work with user models.

Built-in Routes

token_auth

Basic token authentication (JWT), applied in Swagger UI.

Location: bazis.contrib.users.service.token_auth, declared on the application by bazis.contrib.users.token (also importable from there, and from bazis.contrib.users.routes until 3.0)

Enables JWT token authentication in Swagger UI. POST BAZIS_OPENAPI_TOKEN_URL is a route of the application: import bazis.contrib.users.token in the router module of the project (registering bazis.contrib.users.router imports it too); bazis_doctor reports a project without it (users.E002).

UserRouteSet

Base route class for working with users.

Location: bazis.contrib.users.routes.UserRouteSet

Provides ready-to-use endpoints for user operations:

  • List users
  • Get specific user information
  • Create user
  • Update user
  • Delete user

Example usage:

from bazis.contrib.users.routes import UserRouteSet
from django.apps import apps

class MyUserRouteSet(UserRouteSet):
    model = apps.get_model('myapp.User')

Services

Services for working with users and tokens.

Location: bazis.contrib.users.service

get_token_data

Extracts data from the JWT token. The token is read from the bazis_auth query parameter, the Authorization: Bearer header or the bazis_auth cookie (the name is BAZIS_AUTH_COOKIE_NAME). It must be signed with SECRET_KEY using BAZIS_JWT_SESSION_ALG (HS256, HS384 or HS512) and contain exp and sub (the username); otherwise the request fails with 401. Without a token the result is {}.

from bazis.contrib.users.service import get_token_data

def my_endpoint(token: dict = Depends(get_token_data)):
    username = token.get('sub')
    # Work with token data

decode_token(token) verifies a token outside of a request and raises jwt.ExpiredSignatureError / jwt.InvalidTokenError; session_token_data(token) is the result of get_token_data for a token ({}, the data, or 401).

get_request_tokens

The tokens of the request without decoding them: RequestTokens with header, param, cookie and token, the one the request presents (the query parameter, else the bearer token, else the cookie). For an endpoint that treats the tokens its own way (bazis-authing reads its store token from them).

from bazis.contrib.users.service import RequestTokens, get_request_tokens

def my_endpoint(tokens: RequestTokens = Depends(get_request_tokens)):
    ...

get_user_from_token

Gets user from token.

from bazis.contrib.users.service import get_user_from_token

def my_endpoint(user = Depends(get_user_from_token)):
    # user - user object or AnonymousUser
    print(user.get_full_name())

get_user_required

Gets user from request (authentication required).

from bazis.contrib.users.service import get_user_required

def my_endpoint(user = Depends(get_user_required)):
    # user - authenticated user
    # Returns 401 if token is invalid
    return {"user": user.get_full_name()}

get_user_optional

Gets user from request (authentication optional).

from bazis.contrib.users.service import get_user_optional

def my_endpoint(user = Depends(get_user_optional)):
    # user - user or AnonymousUser
    if user.is_authenticated:
        return {"user": user.get_full_name()}
    return {"user": "Anonymous"}

Usage

Creating User Model

from bazis.contrib.users.models_abstract import UserAbstract
from bazis.core.models_abstract import DtMixin, UuidMixin, JsonApiMixin
from django.db import models

class User(UserAbstract, DtMixin, UuidMixin, JsonApiMixin):
    """Custom user model"""
    
    # Additional fields
    phone = models.CharField('Phone', max_length=20, null=True, blank=True)
    department = models.CharField('Department', max_length=100, null=True, blank=True)
    
    class Meta:
        verbose_name = 'User'
        verbose_name_plural = 'Users'
    
    def __str__(self):
        return self.get_full_name()

Point Django to the model in the project environment (the package does not default AUTH_USER_MODEL; without it Django keeps auth.User and the system check users.E001 fails at startup):

BS_AUTH_USER_MODEL=myapp.User

Creating User Route

# routes.py
from bazis.contrib.users.routes import UserRouteSet
from bazis.core.schemas.fields import SchemaField, SchemaFields
from django.apps import apps

class MyUserRouteSet(UserRouteSet):
    model = apps.get_model('myapp.User')
    
    # Add additional fields to schema
    fields = {
        None: SchemaFields(
            include={
                'phone': None,
                'department': None,
            },
        ),
    }
# router.py
from bazis.core.routing import BazisRouter
from . import routes

router = BazisRouter(tags=['Users'])
router.register(routes.MyUserRouteSet.as_router())

Using Authentication Services

Required Authentication

Use get_user_required for endpoints that require authentication:

from bazis.contrib.users.service import get_user_required
from fastapi import APIRouter, Depends
from pydantic import BaseModel

router = APIRouter()

class NewPasswordRequest(BaseModel):
    old_password: str
    new_password: str

@router.post('/user/change_password/', response_model=dict)
def change_password(
    data: NewPasswordRequest,
    user: User = Depends(get_user_required)
):
    # user - guaranteed authenticated user
    # Returns 401 if token is invalid
    return user.change_password(data)

Optional Authentication

Use get_user_optional for endpoints available to everyone:

from bazis.contrib.users.service import get_user_optional, get_token_data
from fastapi import Depends

@router.get('/auth/', response_model=AuthResponse)
def auth(
    auth_store: AuthStore = Depends(),
    user: User = Depends(get_user_optional),
    token_data: dict = Depends(get_token_data),
):
    # user can be AnonymousUser
    if user.is_anonymous:
        if user_id := auth_store.user_id:
            user = User.objects.filter(id=user_id).first()
    
    # Logic for working with user or anonymous
    return {"user": user.get_full_name() if not user.is_anonymous else "Anonymous"}

Using get_token_data

Getting data from JWT token without loading the user:

from bazis.contrib.users.service import get_token_data
from fastapi import Depends

@router.get('/token-info/')
def token_info(token_data: dict = Depends(get_token_data)):
    # token_data contains decoded data from JWT
    return {
        "username": token_data.get("sub"),
        "exp": token_data.get("exp"),
    }

Linking Models to Users

from bazis.contrib.users.models_abstract import UserMixin
from bazis.core.models_abstract import DtMixin, UuidMixin, JsonApiMixin
from django.db import models

class Document(UserMixin, DtMixin, UuidMixin, JsonApiMixin):
    """Document automatically linked to user"""
    title = models.CharField(max_length=255)
    content = models.TextField()
    
    class Meta:
        verbose_name = 'Document'
        verbose_name_plural = 'Documents'

API Authentication

Getting Token

After setting up routes, users can obtain JWT tokens through the authentication endpoint.

Example request:

POST /api/v1/auth/login
Content-Type: application/json

{
  "username": "user@example.com",
  "password": "password123"
}

Example response:

{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "bearer"
}

Using Token in Requests

GET /api/v1/protected-resource
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Authentication in Swagger UI

  1. Open /api/swagger/
  2. Click "Authorize" button
  3. Enter token in format: Bearer <your_token>
  4. Click "Authorize"
  5. Now all requests will be executed with authentication

Examples

Complete Application Example with Users

models.py:

from bazis.contrib.users.models_abstract import UserAbstract, UserMixin
from bazis.core.models_abstract import DtMixin, UuidMixin, JsonApiMixin
from django.db import models

class User(UserAbstract, DtMixin, UuidMixin, JsonApiMixin):
    """System user"""
    phone = models.CharField('Phone', max_length=20, null=True, blank=True)
    
    class Meta:
        verbose_name = 'User'
        verbose_name_plural = 'Users'

class Task(UserMixin, DtMixin, UuidMixin, JsonApiMixin):
    """Task linked to user"""
    title = models.CharField('Title', max_length=255)
    description = models.TextField('Description')
    is_completed = models.BooleanField('Completed', default=False)
    assigned_to = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name='tasks',
        verbose_name='Assigned To'
    )
    
    class Meta:
        verbose_name = 'Task'
        verbose_name_plural = 'Tasks'

routes.py:

from bazis.contrib.users.routes import UserRouteSet
from bazis.contrib.users.service import get_user_required
from django.apps import apps
from fastapi import APIRouter, Depends
from pydantic import BaseModel

class MyUserRouteSet(UserRouteSet):
    model = apps.get_model('myapp.User')

# Additional endpoints for working with tasks
router = APIRouter()

class TaskCreateRequest(BaseModel):
    title: str
    description: str

@router.get('/my-tasks/')
def get_my_tasks(user: User = Depends(get_user_required)):
    # Return only current user's tasks
    tasks = Task.objects.filter(assigned_to=user)
    return {"tasks": [{"id": str(t.id), "title": t.title} for t in tasks]}

@router.post('/tasks/')
def create_task(
    data: TaskCreateRequest,
    user: User = Depends(get_user_required)
):
    # Create task for current user
    task = Task.objects.create(
        title=data.title,
        description=data.description,
        assigned_to=user
    )
    return {"id": str(task.id), "title": task.title}

router.py:

from bazis.core.routing import BazisRouter
from . import routes

# Register user routes
user_router = BazisRouter(tags=['Users'])
user_router.register(routes.MyUserRouteSet.as_router())

# Register additional task endpoints
user_router.include_router(routes.router, prefix='/api/v1')

admin.py:

from django.contrib import admin
from bazis.core.admin_abstract import DtAdminMixin
from .models import User, Task

@admin.register(User)
class UserAdmin(DtAdminMixin, admin.ModelAdmin):
    list_display = ('id', 'username', 'email', 'phone', 'is_active')
    search_fields = ('username', 'email', 'phone')
    list_filter = ('is_active', 'is_staff')

@admin.register(Task)
class TaskAdmin(DtAdminMixin, admin.ModelAdmin):
    list_display = ('id', 'title', 'assigned_to', 'is_completed')
    search_fields = ('title', 'description')
    list_filter = ('is_completed',)
    raw_id_fields = ('assigned_to',)

License

Apache License 2.0

See LICENSE file for details.

Support

If you have questions or issues:


Made with ❤️ by the Bazis team

Metadata

Release files for bazis-users 2.10.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for bazis-users 2.10.1
File Size Uploaded Provenance Actions
bazis_users-2.10.1.tar.gz 141.9 kB Without provenance Details for bazis_users-2.10.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for bazis-users 2.10.1
File Interpreter ABI Platform Actions
bazis_users-2.10.1-py3-none-any.whl Python 3 none any Details for bazis_users-2.10.1-py3-none-any.whl

Total release size: 182.4 kB

Release files / bazis_users-2.10.1.tar.gz

Download URL bazis_users-2.10.1.tar.gz
Size 141.9 kB
Tags Source
SHA-256 checksum
How to use checksums
dca12b2520408aea06e6aac3c990d30ba38fed8eb731f90740ad88319444dd82
BLAKE2b-256 checksum
How to use checksums
175f4582508a0ca51d82dced7b8c69e2409dad45aebf07849376ab3f3dc5a21d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.4 {"installer":{"name":"uv","version":"0.13.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / bazis_users-2.10.1-py3-none-any.whl

Download URL bazis_users-2.10.1-py3-none-any.whl
Size 40.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cc9cb5a0fd556440d8afd317b32a6e79ae17e0788387f34be55237b734f4f96a
BLAKE2b-256 checksum
How to use checksums
e37d0c4c37cd2f3007135fe0696cdd2c841a30a2813b2376fbec87bc76be5031
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.13.4 {"installer":{"name":"uv","version":"0.13.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for bazis-users 2.10.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page