Классы моделей исправлены

This commit is contained in:
2026-07-13 13:30:11 +03:00
parent c5e4c88588
commit f0db1ba9b5
4 changed files with 46 additions and 5 deletions

View File

@@ -10,6 +10,8 @@
import secrets import secrets
from net.xeaf.rack.models import SessionModel
class CryptoManager: class CryptoManager:
""" """
@@ -37,6 +39,10 @@ class CryptoManager:
:return: Сгенерированный токен :return: Сгенерированный токен
""" """
# Ограничение длины токена
if length > SessionModel.MAX_TOKEN_LENGTH:
raise ValueError(f"Token length must be less than {SessionModel.MAX_TOKEN_LENGTH}")
bytes_needed = (length + 1) // 2 bytes_needed = (length + 1) // 2
token = secrets.token_hex(bytes_needed) token = secrets.token_hex(bytes_needed)
return token[:length] return token[:length]

View File

@@ -1,4 +1,4 @@
# Generated by Django 6.0.7 on 2026-07-12 10:42 # Generated by Django 6.0.7 on 2026-07-13 10:28
import django.contrib.auth.models import django.contrib.auth.models
import django.db.models.deletion import django.db.models.deletion
@@ -53,7 +53,7 @@ class Migration(migrations.Migration):
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='rack.mixin.created_at')), ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='rack.mixin.created_at')),
('expires_at', models.DateTimeField(verbose_name='rack.mixin.expires_at')), ('expires_at', models.DateTimeField(verbose_name='rack.mixin.expires_at')),
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, verbose_name='rack.mixin.id')), ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, verbose_name='rack.mixin.id')),
('token', models.CharField(max_length=40, verbose_name='rack.models.session_model.token')), ('token', models.CharField(max_length=255, verbose_name='rack.models.session_model.token')),
('device_id', models.CharField(blank=True, default='WEB', max_length=36, null=True, verbose_name='rack.models.session_model.device_id')), ('device_id', models.CharField(blank=True, default='WEB', max_length=36, null=True, verbose_name='rack.models.session_model.device_id')),
('fcm_token', models.CharField(blank=True, default=None, max_length=256, null=True, verbose_name='rack.models.session_model.fcm_token')), ('fcm_token', models.CharField(blank=True, default=None, max_length=256, null=True, verbose_name='rack.models.session_model.fcm_token')),
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='rack.models.session_model.account')), ('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='rack.models.session_model.account')),

View File

@@ -13,7 +13,6 @@ from django.db import models
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from net.xeaf.rack.core import CoreModel from net.xeaf.rack.core import CoreModel
from net.xeaf.rack.managers import CryptoManager
from net.xeaf.rack.models.mixins import CreatedAtMixin from net.xeaf.rack.models.mixins import CreatedAtMixin
from net.xeaf.rack.models.mixins import ExpiresAtMixin from net.xeaf.rack.models.mixins import ExpiresAtMixin
@@ -23,8 +22,11 @@ class SessionModel(CoreModel, CreatedAtMixin, ExpiresAtMixin):
Модель сессии пользователя Модель сессии пользователя
""" """
MAX_TOKEN_LENGTH = 255
""" Максимальная длина токена сессии """
token = models.CharField( token = models.CharField(
max_length=CryptoManager.DEFAULT_TOKEN_LENGTH, max_length=MAX_TOKEN_LENGTH,
null=False, null=False,
blank=False, blank=False,
verbose_name=_("rack.models.session_model.token"), verbose_name=_("rack.models.session_model.token"),

View File

@@ -7,10 +7,11 @@
""" """
Описание класса CryptoTests Описание класса CryptoTests
""" """
from django.db import models
from django.test import SimpleTestCase from django.test import SimpleTestCase
from net.xeaf.rack.managers import CryptoManager from net.xeaf.rack.managers import CryptoManager
from net.xeaf.rack.models import SessionModel
class CryptoManagerTests(SimpleTestCase): class CryptoManagerTests(SimpleTestCase):
@@ -127,3 +128,35 @@ class CryptoManagerTests(SimpleTestCase):
""" """
self.assertRegex(value, r'^[0-9a-f]+$') self.assertRegex(value, r'^[0-9a-f]+$')
def test_crypto_manager_should_generate_default_length_by_default(self) -> None:
"""
Гарантируем, что генератор без параметров использует базовую длину
"""
generated_token = CryptoManager.gen_token()
self.assertEqual(len(generated_token), CryptoManager.DEFAULT_TOKEN_LENGTH)
def test_session_model_field_must_match_max_token_length_constant(self) -> None:
"""
Защита схемы БД: max_length в миграциях равен MAX_TOKEN_LENGTH
"""
field = SessionModel._meta.get_field("token")
self.assertIsInstance(field, models.CharField)
db_max_length = field.max_length
self.assertIsInstance(db_max_length, int)
self.assertEqual(db_max_length, SessionModel.MAX_TOKEN_LENGTH)
def test_crypto_manager_should_raise_value_error_if_length_exceeds_max(self) -> None:
"""
Проверка Fail-Fast контракта: при превышении лимита СУБД выбрасывается ValueError
"""
excessive_length = SessionModel.MAX_TOKEN_LENGTH + 1
# Система обязана громко упасть с ValueError
with self.assertRaises(ValueError) as context:
CryptoManager.gen_token(length=excessive_length)
self.assertIn(f"Token length must be less than {SessionModel.MAX_TOKEN_LENGTH}", str(context.exception))