Собственная система разграничения прав доступа, не использующая встроенные механизмы Django (Permission, Group). Реализована на основе ролевой модели доступа (RBAC) с кастомными связями.
Система построена на трёх ключевых сущностях:
- Ресурс (Resource) — объект системы, к которому требуется доступ
- Действие (Action) — операция, которую можно выполнить над ресурсом
- Роль (Role) — набор прав (связок Ресурс-Действие)
Пользователь получает доступ к ресурсам только через назначенные ему роли.
| id | name |
|---|---|
| 1 | documents |
| 2 | reports |
| 3 | permissions |
| id | name |
|---|---|
| 1 | view |
| 2 | manage |
| id | name |
|---|---|
| 1 | Admin |
| 2 | User |
| id | role_id | resource_id | action_id |
|---|---|---|---|
| 1 | 1 | 1 | 1 |
| 2 | 1 | 2 | 1 |
| 3 | 1 | 3 | 2 |
| 4 | 2 | 1 | 1 |
Расшифровка прав:
- Admin может:
view→documentsview→reportsmanage→permissions
- User может:
view→documents
| id | user_id | role_id |
|---|---|---|
| 1 | 1 | 1 |
| 2 | 2 | 2 |
Расшифровка назначения ролей:
admin@test.com(user_id=1) → рольAdminuser@test.com(user_id=2) → рольUser
| Метод | URL | Требуемое право | Пример доступа |
|---|---|---|---|
| POST | /api/register/ | Нет (AllowAny) | Всегда доступно |
| POST | /api/login/ | Нет (AllowAny) | Всегда доступно |
| POST | /api/logout/ | Аутентификация | Любой авторизованный |
| GET | /api/profile/ | Аутентификация | Любой авторизованный |
| PUT | /api/profile/ | Аутентификация | Только свой профиль |
| DELETE | /api/profile/ | Аутентификация | Только свой профиль |
| GET | /api/documents/ | view на documents |
Admin ✓, User ✓ |
| GET | /api/reports/ | view на reports |
Admin ✓, User ✗ (403) |
| GET | /api/admin/roles/ | manage на permissions |
Admin ✓, User ✗ (403) |
cd test_task_Effective_Mobile
python -m venv .venv
source .venv/bin/activate # для Mac/Linux.venv\Scripts\activate # для Windowspip install -r requirements.txtpython manage.py makemigrations accounts
python manage.py migratepython fill_data.pypython manage.py runservercurl -X POST http://127.0.0.1:8000/api/register/ \
-H "Content-Type: application/json" \
-d '{
"email": "newuser@test.com",
"first_name": "New",
"last_name": "User",
"password": "newpass123",
"password2": "newpass123"
}'{
"id": 3,
"email": "newuser@test.com",
"message": "Регистрация успешна"
}curl -X POST http://127.0.0.1:8000/api/register/ \
-H "Content-Type: application/json" \
-d '{
"email": "test2@test.com",
"first_name": "Test",
"last_name": "User",
"password": "pass1",
"password2": "pass2"
}'{"password": ["Пароли не совпадают"]} (400 Bad Request)
curl -X POST http://127.0.0.1:8000/api/login/ \
-H "Content-Type: application/json" \
-d '{"email": "user@test.com", "password": "user123"}' \
-c cookies_user.txt{
"message": "Вход выполнен",
"email": "user@test.com"
}curl -X POST http://127.0.0.1:8000/api/login/ \
-H "Content-Type: application/json" \
-d '{"email": "admin@test.com", "password": "admin123"}' \
-c cookies_admin.txt{
"message": "Вход выполнен",
"email": "admin@test.com"
}curl -X POST http://127.0.0.1:8000/api/login/ \
-H "Content-Type: application/json" \
-d '{"email": "user@test.com", "password": "wrongpass"}'{"error": "Неверный email или пароль"} (401 Unauthorized)
curl -X GET http://127.0.0.1:8000/api/profile/ \
-b cookies_user.txt{
"id": 2,
"email": "user@test.com",
"first_name": "Simple",
"last_name": "Userov",
"is_active": true
}CSRF=$(grep csrftoken cookies_user.txt | cut -f7)
curl -X PUT http://127.0.0.1:8000/api/profile/ \
-H "Content-Type: application/json" \
-H "X-CSRFToken: $CSRF" \
-b cookies_user.txt \
-d '{"first_name": "Updated", "last_name": "Name"}'{
"id": 2,
"email": "user@test.com",
"first_name": "Updated",
"last_name": "Name",
"is_active": true
}curl -X GET http://127.0.0.1:8000/api/documents/ \
-b cookies_user.txt{
"documents": [
{"id": 1, "name": "Договор аренды.pdf"},
{"id": 2, "name": "Отчёт за март.xlsx"}
]
}curl -X GET http://127.0.0.1:8000/api/reports/ \
-b cookies_user.txt{
"detail": "У вас недостаточно прав для выполнения данного действия."
}curl -X GET http://127.0.0.1:8000/api/documents/{
"detail": "Учетные данные не были предоставлены."
}curl -X GET http://127.0.0.1:8000/api/documents/ \
-b cookies_admin.txtcurl -X GET http://127.0.0.1:8000/api/reports/ \
-b cookies_admin.txt{
"reports": [
{"id": 101, "total": 50000},
{"id": 102, "total": 75000}
]
}curl -X GET http://127.0.0.1:8000/api/admin/roles/ \
-b cookies_admin.txt[
{"id": 1, "name": "Admin"},
{"id": 2, "name": "User"}
]CSRF=$(grep csrftoken cookies_user.txt | cut -f7)
curl -X DELETE http://127.0.0.1:8000/api/profile/ \
-H "Content-Type: application/json" \
-H "X-CSRFToken: $CSRF" \
-b cookies_user.txt{
"message": "Аккаунт удалён"
}curl -X POST http://127.0.0.1:8000/api/login/ \
-H "Content-Type: application/json" \
-d '{"email": "user@test.com", "password": "user123"}'{
"error": "Аккаунт удалён"
}python manage.py shell
python
from accounts.models import User
user = User.objects.get(email='user@test.com')
print(user.is_active) # Должно быть FalseCSRF=$(grep csrftoken cookies_admin.txt | awk '{print $7}')
curl -X POST http://127.0.0.1:8000/api/logout/ \
-H "X-CSRFToken: $CSRF" \
-b cookies_admin.txt
{
"message": "Выход выполнен"
}curl -X GET http://127.0.0.1:8000/api/profile/ \
-b cookies_admin.txt{
"detail":"Учетные данные не были предоставлены."
}