From f438aa643306190b57a42b4fb96098588ca5d582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=B3=D0=BE=D1=80=D1=8C=20=D0=9D=D0=B0=D0=B4=D0=B5?= =?UTF-8?q?=D0=B8=D0=BD?= Date: Fri, 5 Dec 2025 16:18:45 +0300 Subject: [PATCH] test: add my_requests.html and IP restriction test scripts --- .../templates/requests_app/my_requests.html | 54 ++++++ backend/test_ip_restrictions.py | 171 ++++++++++++++++++ backend/test_ip_simple.py | 167 +++++++++++++++++ 3 files changed, 392 insertions(+) create mode 100644 backend/templates/requests_app/my_requests.html create mode 100644 backend/test_ip_restrictions.py create mode 100644 backend/test_ip_simple.py diff --git a/backend/templates/requests_app/my_requests.html b/backend/templates/requests_app/my_requests.html new file mode 100644 index 00000000..bf3e67f4 --- /dev/null +++ b/backend/templates/requests_app/my_requests.html @@ -0,0 +1,54 @@ +{% extends 'base.html' %} +{% load static django_bootstrap5 %} + +{% block title %}Мои заявления{% endblock %} + +{% block extra_css %} + + + + +{% endblock %} + +{% block content %} +
+
+
+ + {# — Шапка страницы — #} + {% include "includes/components/page_header.html" with icon="bi-file-earmark-text" title="Мои заявления" search_id="requestFilter" search_placeholder="Поиск по типу или комментарию" create_url="requests_app:request_create" create_text="Подать новое заявление" %} + +
+
+ {# Список загружается через JavaScript из requestListHandler.js #} +
+
+ Загрузка... +
+
+
+
+ +
+
+
+{% endblock %} + +{% block extra_js %} + +{% endblock %} diff --git a/backend/test_ip_restrictions.py b/backend/test_ip_restrictions.py new file mode 100644 index 00000000..8b190a0a --- /dev/null +++ b/backend/test_ip_restrictions.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python +""" +Тестирование IP-ограничений для регистрации. + +Запуск: + python backend/test_ip_restrictions.py +""" +import os +import sys +import django + +# Настройка Django +sys.path.insert(0, os.path.join(os.path.dirname(__file__))) +os.environ.setdefault("DJANGO_SECRET_KEY", "test-key") +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "eusrr_backend.settings") +django.setup() + +from common.ip_restrictions import ( + get_client_ip, + is_local_ip, + is_ip_allowed, +) + + +def test_is_local_ip(): + """Тест проверки локальных IP.""" + print("\n=== Тест: is_local_ip ===") + + test_cases = [ + ('127.0.0.1', True, 'localhost IPv4'), + ('127.0.0.100', True, 'localhost range'), + ('192.168.1.1', True, 'private class C'), + ('192.168.100.50', True, 'private class C'), + ('10.0.0.1', True, 'private class A'), + ('172.16.0.1', True, 'private class B'), + ('172.31.255.255', True, 'private class B edge'), + ('8.8.8.8', False, 'public Google DNS'), + ('203.0.113.50', False, 'public TEST-NET'), + ('::1', True, 'localhost IPv6'), + ('invalid-ip', False, 'invalid IP'), + ] + + passed = 0 + failed = 0 + + for ip, expected, description in test_cases: + result = is_local_ip(ip) + status = "✅" if result == expected else "❌" + + if result == expected: + passed += 1 + else: + failed += 1 + + print(f"{status} {ip:20} -> {result:5} (expected: {expected:5}) # {description}") + + print(f"\nПройдено: {passed}/{passed + failed}") + return failed == 0 + + +def test_is_ip_allowed(): + """Тест проверки разрешенных IP с кастомными настройками.""" + print("\n=== Тест: is_ip_allowed ===") + + # Тест 1: Разрешить все + print("\n1. Разрешить все IP ['*']:") + result = is_ip_allowed('203.0.113.50', ['*']) + print(f" {'✅' if result else '❌'} 203.0.113.50 -> {result} (expected: True)") + + # Тест 2: Разрешить конкретную сеть + print("\n2. Разрешить сеть 192.168.1.0/24:") + allowed_net = ['192.168.1.0/24'] + tests = [ + ('192.168.1.1', True), + ('192.168.1.100', True), + ('192.168.1.255', True), + ('192.168.2.1', False), + ('10.0.0.1', False), + ] + + for ip, expected in tests: + result = is_ip_allowed(ip, allowed_net) + status = "✅" if result == expected else "❌" + print(f" {status} {ip:20} -> {result:5} (expected: {expected:5})") + + # Тест 3: Разрешить конкретные IP + print("\n3. Разрешить конкретные IP ['192.168.1.100', '10.0.0.5']:") + allowed_ips = ['192.168.1.100', '10.0.0.5'] + tests = [ + ('192.168.1.100', True), + ('10.0.0.5', True), + ('192.168.1.101', False), + ('10.0.0.6', False), + ] + + for ip, expected in tests: + result = is_ip_allowed(ip, allowed_ips) + status = "✅" if result == expected else "❌" + print(f" {status} {ip:20} -> {result:5} (expected: {expected:5})") + + # Тест 4: По умолчанию (только локальные) + print("\n4. По умолчанию (None = только локальные IP):") + tests = [ + ('127.0.0.1', True), + ('192.168.1.1', True), + ('10.0.0.1', True), + ('8.8.8.8', False), + ('203.0.113.50', False), + ] + + for ip, expected in tests: + result = is_ip_allowed(ip, None) + status = "✅" if result == expected else "❌" + print(f" {status} {ip:20} -> {result:5} (expected: {expected:5})") + + +def test_get_client_ip(): + """Тест получения IP из request.""" + print("\n=== Тест: get_client_ip ===") + + from django.test import RequestFactory + + factory = RequestFactory() + + # Тест 1: Обычный запрос + print("\n1. Обычный запрос (без прокси):") + request = factory.get('/') + ip = get_client_ip(request) + print(f" IP: {ip}") + + # Тест 2: С заголовком X-Forwarded-For + print("\n2. Запрос через прокси (X-Forwarded-For):") + request = factory.get('/', HTTP_X_FORWARDED_FOR='203.0.113.50, 192.168.1.1') + ip = get_client_ip(request) + expected = '203.0.113.50' + status = "✅" if ip == expected else "❌" + print(f" {status} IP: {ip} (expected: {expected})") + + # Тест 3: С одним IP в X-Forwarded-For + print("\n3. Запрос через прокси (один IP):") + request = factory.get('/', HTTP_X_FORWARDED_FOR='10.0.0.5') + ip = get_client_ip(request) + expected = '10.0.0.5' + status = "✅" if ip == expected else "❌" + print(f" {status} IP: {ip} (expected: {expected})") + + +def main(): + """Запуск всех тестов.""" + print("=" * 60) + print("Тестирование модуля IP-ограничений") + print("=" * 60) + + try: + test_is_local_ip() + test_is_ip_allowed() + test_get_client_ip() + + print("\n" + "=" * 60) + print("✅ Все тесты выполнены") + print("=" * 60) + + except Exception as e: + print(f"\n❌ Ошибка: {e}") + import traceback + traceback.print_exc() + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/backend/test_ip_simple.py b/backend/test_ip_simple.py new file mode 100644 index 00000000..4691c53a --- /dev/null +++ b/backend/test_ip_simple.py @@ -0,0 +1,167 @@ +#!/usr/bin/env python +""" +Простой тест IP-ограничений (без Django). +""" +import ipaddress + + +def is_local_ip(ip_address: str) -> bool: + """Проверяет, является ли IP-адрес локальным.""" + try: + ip = ipaddress.ip_address(ip_address) + return ip.is_private or ip.is_loopback or ip.is_link_local + except ValueError: + return False + + +def is_ip_allowed(ip_address: str, allowed_networks=None): + """Проверяет, разрешен ли доступ с данного IP-адреса.""" + if allowed_networks is None: + return is_local_ip(ip_address) + + if not allowed_networks: + return False + + if allowed_networks == ['*'] or '*' in allowed_networks: + return True + + try: + ip = ipaddress.ip_address(ip_address) + + for network_str in allowed_networks: + if '/' in network_str: + network = ipaddress.ip_network(network_str, strict=False) + if ip in network: + return True + else: + allowed_ip = ipaddress.ip_address(network_str) + if ip == allowed_ip: + return True + + return False + except ValueError: + return False + + +def test_is_local_ip(): + """Тест проверки локальных IP.""" + print("\n=== Тест: is_local_ip ===") + + test_cases = [ + ('127.0.0.1', True, 'localhost IPv4'), + ('127.0.0.100', True, 'localhost range'), + ('192.168.1.1', True, 'private class C'), + ('192.168.100.50', True, 'private class C'), + ('10.0.0.1', True, 'private class A'), + ('172.16.0.1', True, 'private class B'), + ('172.31.255.255', True, 'private class B edge'), + ('8.8.8.8', False, 'public Google DNS'), + ('203.0.113.50', False, 'public TEST-NET'), + ('::1', True, 'localhost IPv6'), + ('invalid-ip', False, 'invalid IP'), + ] + + passed = 0 + failed = 0 + + for ip, expected, description in test_cases: + result = is_local_ip(ip) + status = "✅" if result == expected else "❌" + + if result == expected: + passed += 1 + else: + failed += 1 + + print(f"{status} {ip:20} -> {str(result):5} " + f"(expected: {str(expected):5}) # {description}") + + print(f"\nПройдено: {passed}/{passed + failed}") + return failed == 0 + + +def test_is_ip_allowed(): + """Тест проверки разрешенных IP с кастомными настройками.""" + print("\n=== Тест: is_ip_allowed ===") + + # Тест 1: Разрешить все + print("\n1. Разрешить все IP ['*']:") + result = is_ip_allowed('203.0.113.50', ['*']) + status = '✅' if result else '❌' + print(f" {status} 203.0.113.50 -> {result} (expected: True)") + + # Тест 2: Разрешить конкретную сеть + print("\n2. Разрешить сеть 192.168.1.0/24:") + allowed_net = ['192.168.1.0/24'] + tests = [ + ('192.168.1.1', True), + ('192.168.1.100', True), + ('192.168.1.255', True), + ('192.168.2.1', False), + ('10.0.0.1', False), + ] + + for ip, expected in tests: + result = is_ip_allowed(ip, allowed_net) + status = "✅" if result == expected else "❌" + print(f" {status} {ip:20} -> {str(result):5} " + f"(expected: {str(expected):5})") + + # Тест 3: Разрешить конкретные IP + print("\n3. Разрешить IP ['192.168.1.100', '10.0.0.5']:") + allowed_ips = ['192.168.1.100', '10.0.0.5'] + tests = [ + ('192.168.1.100', True), + ('10.0.0.5', True), + ('192.168.1.101', False), + ('10.0.0.6', False), + ] + + for ip, expected in tests: + result = is_ip_allowed(ip, allowed_ips) + status = "✅" if result == expected else "❌" + print(f" {status} {ip:20} -> {str(result):5} " + f"(expected: {str(expected):5})") + + # Тест 4: По умолчанию (только локальные) + print("\n4. По умолчанию (None = только локальные IP):") + tests = [ + ('127.0.0.1', True), + ('192.168.1.1', True), + ('10.0.0.1', True), + ('8.8.8.8', False), + ('203.0.113.50', False), + ] + + for ip, expected in tests: + result = is_ip_allowed(ip, None) + status = "✅" if result == expected else "❌" + print(f" {status} {ip:20} -> {str(result):5} " + f"(expected: {str(expected):5})") + + +def main(): + """Запуск всех тестов.""" + print("=" * 60) + print("Тестирование модуля IP-ограничений") + print("=" * 60) + + try: + test_is_local_ip() + test_is_ip_allowed() + + print("\n" + "=" * 60) + print("✅ Все тесты выполнены успешно") + print("=" * 60) + + except Exception as e: + print(f"\n❌ Ошибка: {e}") + import traceback + traceback.print_exc() + return 1 + + return 0 + + +if __name__ == "__main__": + exit(main())