478 lines
17 KiB
Markdown
478 lines
17 KiB
Markdown
# Система ролей и разрешений (RBAC)
|
||
|
||
## Общее описание
|
||
|
||
Система управления доступом на основе ролей (Role-Based Access Control, RBAC) обеспечивает гибкое управление правами пользователей в рамках сообществ платформы.
|
||
|
||
## Архитектура системы
|
||
|
||
### Основные компоненты
|
||
|
||
1. **Community** - сообщество, контекст для ролей
|
||
2. **CommunityAuthor** - связь пользователя с сообществом и его ролями
|
||
3. **Role** - роль пользователя (reader, author, editor, admin)
|
||
4. **Permission** - разрешение на выполнение действия
|
||
5. **RBAC Service** - сервис управления ролями и разрешениями
|
||
|
||
### Модель данных
|
||
|
||
```sql
|
||
-- Основная таблица связи пользователя с сообществом
|
||
CREATE TABLE community_author (
|
||
id INTEGER PRIMARY KEY,
|
||
community_id INTEGER REFERENCES community(id),
|
||
author_id INTEGER REFERENCES author(id),
|
||
roles TEXT, -- CSV строка ролей: "reader,author,editor"
|
||
joined_at INTEGER NOT NULL,
|
||
UNIQUE(community_id, author_id)
|
||
);
|
||
|
||
-- Индексы для производительности
|
||
CREATE INDEX idx_community_author_community ON community_author(community_id);
|
||
CREATE INDEX idx_community_author_author ON community_author(author_id);
|
||
```
|
||
|
||
## Роли в системе
|
||
|
||
### Базовые роли
|
||
|
||
#### 1. `reader` (Читатель)
|
||
- **Обязательная роль для всех пользователей**
|
||
- **Права:**
|
||
- Чтение публикаций
|
||
- Просмотр комментариев
|
||
- Подписка на сообщества
|
||
- Базовая навигация по платформе
|
||
|
||
#### 2. `author` (Автор)
|
||
- **Права:**
|
||
- Все права `reader`
|
||
- Создание публикаций (шаутов)
|
||
- Редактирование своих публикаций
|
||
- Комментирование
|
||
- Создание черновиков
|
||
|
||
#### 3. `artist` (Художник)
|
||
- **Права:**
|
||
- Все права `author`
|
||
- Может быть указан как credited artist
|
||
- Загрузка и управление медиафайлами
|
||
|
||
#### 4. `expert` (Эксперт)
|
||
- **Права:**
|
||
- Все права `author`
|
||
- Добавление доказательств (evidence)
|
||
- Верификация контента
|
||
- Экспертная оценка публикаций
|
||
|
||
#### 5. `editor` (Редактор)
|
||
- **Права:**
|
||
- Все права `expert`
|
||
- Модерация контента
|
||
- Редактирование чужих публикаций
|
||
- Управление тегами и категориями
|
||
- Модерация комментариев
|
||
|
||
#### 6. `admin` (Администратор)
|
||
- **Права:**
|
||
- Все права `editor`
|
||
- Управление пользователями
|
||
- Управление ролями
|
||
- Настройка сообщества
|
||
- Полный доступ к административной панели
|
||
|
||
### Иерархия ролей
|
||
|
||
```
|
||
admin > editor > expert > artist/author > reader
|
||
```
|
||
|
||
Каждая роль автоматически включает права всех ролей ниже по иерархии.
|
||
|
||
## Разрешения (Permissions)
|
||
|
||
### Формат разрешений
|
||
|
||
Разрешения записываются в формате `resource:action`:
|
||
|
||
- `shout:create` - создание публикаций
|
||
- `shout:edit` - редактирование публикаций
|
||
- `shout:delete` - удаление публикаций
|
||
- `comment:create` - создание комментариев
|
||
- `comment:moderate` - модерация комментариев
|
||
- `user:manage` - управление пользователями
|
||
- `community:settings` - настройки сообщества
|
||
|
||
### Категории разрешений
|
||
|
||
#### Контент (Content)
|
||
- `shout:create` - создание шаутов
|
||
- `shout:edit_own` - редактирование своих шаутов
|
||
- `shout:edit_any` - редактирование любых шаутов
|
||
- `shout:delete_own` - удаление своих шаутов
|
||
- `shout:delete_any` - удаление любых шаутов
|
||
- `shout:publish` - публикация шаутов
|
||
- `shout:feature` - продвижение шаутов
|
||
|
||
#### Комментарии (Comments)
|
||
- `comment:create` - создание комментариев
|
||
- `comment:edit_own` - редактирование своих комментариев
|
||
- `comment:edit_any` - редактирование любых комментариев
|
||
- `comment:delete_own` - удаление своих комментариев
|
||
- `comment:delete_any` - удаление любых комментариев
|
||
- `comment:moderate` - модерация комментариев
|
||
|
||
#### Пользователи (Users)
|
||
- `user:view_profile` - просмотр профилей
|
||
- `user:edit_own_profile` - редактирование своего профиля
|
||
- `user:manage_roles` - управление ролями пользователей
|
||
- `user:ban` - блокировка пользователей
|
||
|
||
#### Сообщество (Community)
|
||
- `community:view` - просмотр сообщества
|
||
- `community:settings` - настройки сообщества
|
||
- `community:manage_members` - управление участниками
|
||
- `community:analytics` - просмотр аналитики
|
||
|
||
## Логика работы системы
|
||
|
||
### 1. Регистрация пользователя
|
||
|
||
При регистрации пользователя:
|
||
|
||
```python
|
||
# 1. Создается запись в Author
|
||
user = Author(email=email, name=name, ...)
|
||
|
||
# 2. Создается связь с дефолтным сообществом (ID=1)
|
||
community_author = CommunityAuthor(
|
||
community_id=1,
|
||
author_id=user.id,
|
||
roles="reader,author" # Дефолтные роли
|
||
)
|
||
|
||
# 3. Создается подписка на сообщество
|
||
follower = CommunityFollower(
|
||
community=1,
|
||
follower=user.id
|
||
)
|
||
```
|
||
|
||
### 2. Проверка авторизации
|
||
|
||
При входе в систему проверяется наличие роли `reader`:
|
||
|
||
```python
|
||
def login(email, password):
|
||
# 1. Найти пользователя
|
||
author = Author.get_by_email(email)
|
||
|
||
# 2. Проверить пароль
|
||
if not verify_password(password, author.password):
|
||
return error("Неверный пароль")
|
||
|
||
# 3. Получить роли в дефолтном сообществе
|
||
user_roles = get_user_roles_in_community(author.id, community_id=1)
|
||
|
||
# 4. Проверить наличие роли reader
|
||
if "reader" not in user_roles and author.email not in ADMIN_EMAILS:
|
||
return error("Нет прав для входа. Требуется роль 'reader'.")
|
||
|
||
# 5. Создать сессию
|
||
return create_session(author)
|
||
```
|
||
|
||
### 3. Проверка разрешений
|
||
|
||
При выполнении действий проверяются разрешения:
|
||
|
||
```python
|
||
@login_required
|
||
async def create_shout(info, input):
|
||
user_id = info.context["author"]["id"]
|
||
|
||
# Проверяем разрешение на создание шаутов
|
||
has_permission = await check_user_permission_in_community(
|
||
user_id,
|
||
"shout:create",
|
||
community_id=1
|
||
)
|
||
|
||
if not has_permission:
|
||
raise GraphQLError("Недостаточно прав для создания публикации")
|
||
|
||
# Создаем шаут
|
||
return Shout.create(input)
|
||
```
|
||
|
||
### 4. Управление ролями
|
||
|
||
#### Назначение ролей
|
||
|
||
```python
|
||
# Назначить роль пользователю
|
||
assign_role_to_user(user_id=123, role="editor", community_id=1)
|
||
|
||
# Убрать роль
|
||
remove_role_from_user(user_id=123, role="editor", community_id=1)
|
||
|
||
# Установить все роли
|
||
community.set_user_roles(user_id=123, roles=["reader", "author", "editor"])
|
||
```
|
||
|
||
#### Проверка ролей
|
||
|
||
```python
|
||
# Получить роли пользователя
|
||
roles = get_user_roles_in_community(user_id=123, community_id=1)
|
||
|
||
# Проверить конкретную роль
|
||
has_role = "editor" in roles
|
||
|
||
# Проверить разрешение
|
||
has_permission = await check_user_permission_in_community(
|
||
user_id=123,
|
||
permission="shout:edit_any",
|
||
community_id=1
|
||
)
|
||
```
|
||
|
||
## Конфигурация сообщества
|
||
|
||
### Дефолтные роли
|
||
|
||
Каждое сообщество может настроить свои дефолтные роли для новых пользователей:
|
||
|
||
```python
|
||
# Получить дефолтные роли
|
||
default_roles = community.get_default_roles() # ["reader", "author"]
|
||
|
||
# Установить дефолтные роли
|
||
community.set_default_roles(["reader"]) # Только reader по умолчанию
|
||
```
|
||
|
||
### Доступные роли
|
||
|
||
Сообщество может ограничить список доступных ролей:
|
||
|
||
```python
|
||
# Все роли доступны по умолчанию
|
||
available_roles = ["reader", "author", "artist", "expert", "editor", "admin"]
|
||
|
||
# Ограничить только базовыми ролями
|
||
community.set_available_roles(["reader", "author", "editor"])
|
||
```
|
||
|
||
## Миграция данных
|
||
|
||
### Проблемы существующих пользователей
|
||
|
||
1. **Пользователи без роли `reader`** - не могут войти в систему
|
||
2. **Старая система ролей** - данные в `Author.roles` устарели
|
||
3. **Отсутствие связей `CommunityAuthor`** - новые пользователи без ролей
|
||
|
||
### Решения
|
||
|
||
#### 1. Автоматическое добавление роли `reader`
|
||
|
||
```python
|
||
async def ensure_user_has_reader_role(user_id: int) -> bool:
|
||
"""Убеждается, что у пользователя есть роль 'reader'"""
|
||
existing_roles = get_user_roles_in_community(user_id, community_id=1)
|
||
|
||
if "reader" not in existing_roles:
|
||
success = assign_role_to_user(user_id, "reader", community_id=1)
|
||
if success:
|
||
logger.info(f"Роль 'reader' добавлена пользователю {user_id}")
|
||
return True
|
||
|
||
return True
|
||
```
|
||
|
||
#### 2. Массовое исправление ролей
|
||
|
||
```python
|
||
async def fix_all_users_reader_role() -> dict[str, int]:
|
||
"""Проверяет всех пользователей и добавляет роль 'reader'"""
|
||
stats = {"checked": 0, "fixed": 0, "errors": 0}
|
||
|
||
all_authors = session.query(Author).all()
|
||
|
||
for author in all_authors:
|
||
stats["checked"] += 1
|
||
try:
|
||
await ensure_user_has_reader_role(author.id)
|
||
stats["fixed"] += 1
|
||
except Exception as e:
|
||
logger.error(f"Ошибка для пользователя {author.id}: {e}")
|
||
stats["errors"] += 1
|
||
|
||
return stats
|
||
```
|
||
|
||
#### 3. Миграция из старой системы
|
||
|
||
```python
|
||
def migrate_old_roles_to_community_author():
|
||
"""Переносит роли из старой системы в CommunityAuthor"""
|
||
|
||
# Получаем все старые роли из Author.roles
|
||
old_roles = session.query(AuthorRole).all()
|
||
|
||
for role in old_roles:
|
||
# Создаем запись CommunityAuthor
|
||
ca = CommunityAuthor(
|
||
community_id=role.community,
|
||
author_id=role.author,
|
||
roles=role.role
|
||
)
|
||
session.add(ca)
|
||
|
||
session.commit()
|
||
```
|
||
|
||
## API для работы с ролями
|
||
|
||
### GraphQL мутации
|
||
|
||
```graphql
|
||
# Назначить роль пользователю
|
||
mutation AssignRole($userId: Int!, $role: String!, $communityId: Int) {
|
||
assignRole(userId: $userId, role: $role, communityId: $communityId) {
|
||
success
|
||
message
|
||
}
|
||
}
|
||
|
||
# Убрать роль
|
||
mutation RemoveRole($userId: Int!, $role: String!, $communityId: Int) {
|
||
removeRole(userId: $userId, role: $role, communityId: $communityId) {
|
||
success
|
||
message
|
||
}
|
||
}
|
||
|
||
# Установить все роли пользователя
|
||
mutation SetUserRoles($userId: Int!, $roles: [String!]!, $communityId: Int) {
|
||
setUserRoles(userId: $userId, roles: $roles, communityId: $communityId) {
|
||
success
|
||
message
|
||
}
|
||
}
|
||
```
|
||
|
||
### GraphQL запросы
|
||
|
||
```graphql
|
||
# Получить роли пользователя
|
||
query GetUserRoles($userId: Int!, $communityId: Int) {
|
||
userRoles(userId: $userId, communityId: $communityId) {
|
||
roles
|
||
permissions
|
||
community {
|
||
id
|
||
name
|
||
}
|
||
}
|
||
}
|
||
|
||
# Получить всех участников сообщества с ролями
|
||
query GetCommunityMembers($communityId: Int!) {
|
||
communityMembers(communityId: $communityId) {
|
||
authorId
|
||
roles
|
||
permissions
|
||
joinedAt
|
||
author {
|
||
id
|
||
name
|
||
email
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
## Безопасность
|
||
|
||
### Принципы безопасности
|
||
|
||
1. **Принцип минимальных привилегий** - пользователь получает только необходимые права
|
||
2. **Разделение обязанностей** - разные роли для разных функций
|
||
3. **Аудит действий** - логирование всех изменений ролей
|
||
4. **Проверка на каждом уровне** - валидация разрешений в API и UI
|
||
|
||
### Защита от атак
|
||
|
||
1. **Privilege Escalation** - проверка прав на изменение ролей
|
||
2. **Mass Assignment** - валидация входных данных
|
||
3. **CSRF** - использование токенов для изменения ролей
|
||
4. **XSS** - экранирование данных ролей в UI
|
||
|
||
### Логирование
|
||
|
||
```python
|
||
# Логирование изменений ролей
|
||
logger.info(f"Role {role} assigned to user {user_id} by admin {admin_id}")
|
||
logger.warning(f"Failed login attempt for user without reader role: {user_id}")
|
||
logger.error(f"Permission denied: user {user_id} tried to access {resource}")
|
||
```
|
||
|
||
## Тестирование
|
||
|
||
### Тестовые сценарии
|
||
|
||
1. **Регистрация пользователя** - проверка назначения дефолтных ролей
|
||
2. **Вход в систему** - проверка требования роли `reader`
|
||
3. **Назначение ролей** - проверка прав администратора
|
||
4. **Проверка разрешений** - валидация доступа к ресурсам
|
||
5. **Иерархия ролей** - наследование прав
|
||
|
||
### Пример тестов
|
||
|
||
```python
|
||
def test_user_registration_assigns_default_roles():
|
||
"""Проверяет назначение дефолтных ролей при регистрации"""
|
||
user = create_user(email="test@test.com")
|
||
roles = get_user_roles_in_community(user.id, community_id=1)
|
||
|
||
assert "reader" in roles
|
||
assert "author" in roles
|
||
|
||
def test_login_requires_reader_role():
|
||
"""Проверяет требование роли reader для входа"""
|
||
user = create_user_without_roles(email="test@test.com")
|
||
|
||
result = login(email="test@test.com", password="password")
|
||
|
||
assert result["success"] == False
|
||
assert "reader" in result["error"]
|
||
|
||
def test_role_hierarchy():
|
||
"""Проверяет иерархию ролей"""
|
||
user = create_user(email="admin@test.com")
|
||
assign_role_to_user(user.id, "admin", community_id=1)
|
||
|
||
# Админ должен иметь все права
|
||
assert check_permission(user.id, "shout:create")
|
||
assert check_permission(user.id, "user:manage")
|
||
assert check_permission(user.id, "community:settings")
|
||
```
|
||
|
||
## Производительность
|
||
|
||
### Оптимизации
|
||
|
||
1. **Кеширование ролей** - хранение ролей пользователя в Redis
|
||
2. **Индексы БД** - быстрый поиск по `community_id` и `author_id`
|
||
3. **Batch операции** - массовое назначение ролей
|
||
4. **Ленивая загрузка** - загрузка разрешений по требованию
|
||
|
||
### Мониторинг
|
||
|
||
```python
|
||
# Метрики для Prometheus
|
||
role_checks_total = Counter('rbac_role_checks_total')
|
||
permission_checks_total = Counter('rbac_permission_checks_total')
|
||
role_assignments_total = Counter('rbac_role_assignments_total')
|
||
```
|