Skip to content

bmstu-itstech/sso

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

27 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SSO - Система единого входа

Система единого входа (Single Sign-On) для проектов ITS TECH. Предоставляет централизованную аутентификацию и авторизацию через gRPC API с поддержкой JWT токенов.

Содержание

Возможности

🔐 Аутентификация и авторизация

  • Регистрация пользователей - создание новых учетных записей с валидацией данных
  • Вход в систему - аутентификация с получением JWT токена для конкретного приложения
  • Обновление токена - получение нового JWT токена без повторной аутентификации
  • Мультиприложенность - поддержка нескольких приложений с разными секретами для токенов

👤 Управление пользователями

  • Информация о пользователе - получение данных профиля (сам пользователь или администратор)
  • Обновление пароля - изменение пароля пользователем или администратором
  • Удаление пользователя - удаление аккаунта (самостоятельно или администратором)
  • Список пользователей - получение списка всех пользователей (только для администраторов)

🛡️ Административные функции

  • Проверка прав администратора - определение статуса администратора пользователя
  • Административный доступ - расширенные права для управления пользователями
  • Безопасность - многоуровневая система проверки прав доступа

🔍 Утилиты

  • Ping - проверка доступности сервиса
  • Health checks - мониторинг состояния сервиса

Архитектура

Технологический стек

  • Язык: Go 1.25+
  • Протокол: gRPC
  • База данных: PostgreSQL 16
  • Аутентификация: JWT (JSON Web Tokens)
  • Хеширование паролей: bcrypt
  • Конфигурация: Viper (поддержка .env файлов и переменных окружения)
  • Миграции: migrate/migrate
  • Контейнеризация: Docker, Docker Compose

Структура проекта

sso/
├── cmd/sso/           # Точка входа приложения
├── internal/
│   ├── app/           # Инициализация приложения
│   ├── config/        # Конфигурация
│   ├── domain/        # Доменные модели
│   ├── grpc/          # gRPC handlers
│   ├── lib/           # Утилиты (JWT, генерация)
│   ├── logs/          # Логирование
│   ├── repository/    # Работа с БД
│   └── services/      # Бизнес-логика
├── migrations/        # SQL миграции
├── tests/             # Тесты
├── docker-compose.yml # Docker Compose конфигурация
└── Dockerfile         # Docker образ

Требования

Для запуска через Docker Compose

  • Docker 20.10+
  • Docker Compose 2.0+

Для локального запуска

  • Go 1.25+
  • PostgreSQL 16+
  • migrate/migrate (для миграций)

Варианты запуска

1. Docker Compose (рекомендуемый способ)

Самый простой способ запуска всей инфраструктуры одним командой.

Подготовка

  1. Создайте файл .env в корне проекта:
# Application
APP_NAME=sso
ENV=local

# GRPC
GRPC_PORT=8080
GRPC_TIMEOUT=10s

# Database
POSTGRES_HOST=postgres
POSTGRES_PORT=5432
POSTGRES_EXTERNAL_PORT=5436
POSTGRES_DB=myapp_db
POSTGRES_USER=dbuser
POSTGRES_PASSWORD=dbpass123

# JWT
JWT_SECRET=my-secret
JWT_TOKEN_TTL=12h
  1. Запустите все сервисы:
docker-compose up -d

Эта команда автоматически:

  • Создаст сеть для сервисов
  • Запустит PostgreSQL с проверкой здоровья
  • Выполнит миграции базы данных
  • Запустит SSO приложение

Управление сервисами

# Просмотр логов
docker-compose logs -f sso

# Остановка всех сервисов
docker-compose down

# Остановка с удалением данных БД
docker-compose down -v

# Пересборка образа приложения
docker-compose up -d --build sso

# Перезапуск сервиса
docker-compose restart sso

# Просмотр статуса
docker-compose ps

Доступ к сервисам

  • SSO gRPC: localhost:8080 (порт настраивается через GRPC_PORT)
  • PostgreSQL: localhost:5436 (внешний порт настраивается через POSTGRES_EXTERNAL_PORT)

2. Локальный запуск с Docker PostgreSQL

Если вы хотите запустить приложение локально, а базу данных в Docker:

Шаг 1: Запуск PostgreSQL в Docker

docker run --name=sso-db \
  -e POSTGRES_PASSWORD='qwerty' \
  -e POSTGRES_USER='postgres' \
  -e POSTGRES_DB='postgres' \
  -p 5436:5432 \
  -d postgres:16-alpine3.18

Шаг 2: Выполнение миграций

migrate -path ./migrations \
  -database 'postgres://postgres:qwerty@localhost:5436/postgres?sslmode=disable' \
  up

Шаг 3: Настройка .env файла

ENV=local
GRPC_PORT=8080
GRPC_TIMEOUT=10s
POSTGRES_HOST=localhost
POSTGRES_PORT=5436
POSTGRES_EXTERNAL_PORT=5436
POSTGRES_DB=postgres
POSTGRES_USER=postgres
POSTGRES_PASSWORD=qwerty
JWT_SECRET=my-secret
JWT_TOKEN_TTL=12h

Шаг 4: Запуск приложения

go run cmd/sso/main.go

3. Полностью локальный запуск

Для разработки с локальной PostgreSQL:

Требования

  1. Установленная PostgreSQL 16
  2. Созданная база данных
  3. Установленные зависимости Go

Настройка базы данных

-- Создание базы данных
CREATE DATABASE sso_db;

-- Создание пользователя (опционально)
CREATE USER sso_user WITH PASSWORD 'your_password';
GRANT ALL PRIVILEGES ON DATABASE sso_db TO sso_user;

Выполнение миграций

migrate -path ./migrations \
  -database 'postgres://sso_user:your_password@localhost:5432/sso_db?sslmode=disable' \
  up

Конфигурация .env

ENV=local
GRPC_PORT=8080
GRPC_TIMEOUT=10s
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_EXTERNAL_PORT=5432
POSTGRES_DB=sso_db
POSTGRES_USER=sso_user
POSTGRES_PASSWORD=your_password
JWT_SECRET=my-secret
JWT_TOKEN_TTL=12h

Запуск

# Установка зависимостей
go mod download

# Запуск приложения
go run cmd/sso/main.go

4. Production запуск

Для production окружения рекомендуется:

  1. Использовать Docker Compose с production конфигурацией
  2. Настроить переменные окружения через secrets management
  3. Использовать внешнюю PostgreSQL для высокой доступности
  4. Настроить мониторинг и логирование
  5. Использовать load balancer перед gRPC сервером

Пример production .env:

ENV=prod
GRPC_PORT=8080
GRPC_TIMEOUT=10s
POSTGRES_HOST=postgres-prod.example.com
POSTGRES_PORT=5432
POSTGRES_EXTERNAL_PORT=5432
POSTGRES_DB=sso_prod
POSTGRES_USER=sso_prod_user
POSTGRES_PASSWORD=<secure_password_from_secrets>
JWT_SECRET=<secure_secret_from_secrets>
JWT_TOKEN_TTL=1h

API Reference

gRPC Endpoints

Все endpoints доступны через gRPC протокол на порту, указанном в GRPC_PORT.

Ping

Проверка доступности сервиса.

Request:

Empty

Response:

Empty

Пример использования:

_, err := client.Ping(ctx, &emptypb.Empty{})

Register

Регистрация нового пользователя в системе.

Request:

message RegisterRequest {
  string login = 1;       // Уникальный логин пользователя
  string password = 2;    // Пароль пользователя
  string email = 3;       // Email адрес
  string full_name = 4;   // Полное имя пользователя
}

Response:

message RegisterResponse {
  int64 user_id = 1;      // ID созданного пользователя
}

Ошибки:

  • InvalidArgument - некорректные данные запроса
  • Internal - ошибка при создании пользователя

Особенности:

  • Пароль автоматически хешируется с помощью bcrypt
  • Логин должен быть уникальным
  • Email должен быть валидным форматом

Login

Аутентификация пользователя и получение JWT токена.

Request:

message LoginRequest {
  int32 app_id = 1;       // ID приложения (0 для SSO, >0 для других приложений)
  string login = 2;       // Логин пользователя
  string password = 3;    // Пароль пользователя
}

Response:

message LoginResponse {
  string token = 1;       // JWT токен для приложения
}

Ошибки:

  • NotFound - пользователь или приложение не найдены
  • Internal - неверный логин или пароль

Особенности:

  • Токен для SSO (app_id=0) содержит только uid и exp
  • Токен для других приложений содержит uid, login, email, app_id, exp
  • Время жизни токена настраивается через JWT_TOKEN_TTL
  • Токен подписывается секретом приложения

UserInfo

Получение информации о пользователе.

Request:

message UserInfoRequest {
  int64 user_id = 1;      // ID пользователя
}

Response:

message User {
  int64 user_id = 1;
  string login = 2;
  string email = 3;
  string full_name = 4;
  bool is_admin = 5;
  google.protobuf.Timestamp create_at = 6;
  google.protobuf.Timestamp update_at = 7;
}

Ошибки:

  • Unauthenticated - отсутствует или невалидный JWT токен
  • PermissionDenied - пользователь не имеет прав на просмотр данного профиля
  • Internal - ошибка при получении информации

Особенности:

  • Пользователь может просмотреть только свой профиль
  • Администратор может просмотреть любой профиль
  • Требуется валидный JWT токен в заголовке Authorization: Bearer <token>

IsAdmin

Проверка прав администратора у пользователя.

Request:

message IsAdminRequest {
  int64 user_id = 1;      // ID пользователя для проверки
}

Response:

message IsAdminResponse {
  bool is_admin = 1;      // Статус администратора
}

Ошибки:

  • Unauthenticated - отсутствует или невалидный JWT токен
  • PermissionDenied - обычный пользователь пытается проверить другого пользователя
  • NotFound - пользователь не найден

Особенности:

  • Пользователь может проверить только свои права
  • Администратор может проверить права любого пользователя
  • Требуется валидный JWT токен

UpdatePassword

Обновление пароля пользователя.

Request:

message UpdatePasswordRequest {
  int64 user_id = 1;           // ID пользователя
  string new_password = 2;     // Новый пароль
}

Response:

message UpdatePasswordResponse {
  string message = 1;          // Сообщение об успехе
}

Ошибки:

  • Unauthenticated - отсутствует или невалидный JWT токен
  • PermissionDenied - обычный пользователь пытается изменить чужой пароль
  • NotFound - пользователь не найден
  • Internal - ошибка при обновлении пароля

Особенности:

  • Пользователь может изменить только свой пароль
  • Администратор может изменить пароль любого пользователя
  • Новый пароль автоматически хешируется
  • После обновления старый пароль становится недействительным

RemoveUser

Удаление пользователя из системы.

Request:

message RemoveUserRequest {
  int64 user_id = 1;           // ID пользователя для удаления
}

Response:

message RemoveUserResponse {
  string message = 1;          // Сообщение об успехе
}

Ошибки:

  • Unauthenticated - отсутствует или невалидный JWT токен
  • PermissionDenied - обычный пользователь пытается удалить другого пользователя
  • NotFound - пользователь не найден (может быть успешно, если пользователь уже удален)

Особенности:

  • Пользователь может удалить только свой аккаунт
  • Администратор может удалить любого пользователя
  • После удаления пользователь не сможет войти в систему
  • Удаление выполняется "мягко" (без удаления из БД, с пометкой удаленным)

UpdateToken

Обновление JWT токена без повторной аутентификации.

Request:

Empty

Response:

message UpdateTokenResponse {
  string token = 1;            // Новый JWT токен
}

Ошибки:

  • Unauthenticated - отсутствует или невалидный JWT токен
  • InvalidArgument - токен не требует обновления или отсутствует
  • Internal - ошибка при создании нового токена

Особенности:

  • Для SSO токенов (app_id=0) - создает новый SSO токен
  • Для токенов приложений - обновляет токен приложения
  • Новый токен имеет тот же срок жизни, что указан в конфигурации
  • Старый токен остается валидным до истечения срока действия

UsersInfo

Получение списка всех пользователей (только для администраторов).

Request:

Empty

Response:

message Users {
  repeated User users = 1;     // Список всех пользователей
}

Ошибки:

  • Unauthenticated - отсутствует или невалидный JWT токен
  • PermissionDenied - запрашивающий пользователь не является администратором

Особенности:

  • Доступно только администраторам
  • Возвращает полный список пользователей системы
  • Включает информацию о статусе администратора для каждого пользователя

Авторизация

Большинство endpoints требуют JWT токен в заголовке запроса:

Authorization: Bearer <jwt_token>

Токен должен быть валидным и не истекшим. Для SSO токенов используется секрет из JWT_SECRET, для токенов приложений - секрет конкретного приложения.

Особенности

1. Мультиприложенность

SSO поддерживает работу с несколькими приложениями одновременно:

  • SSO (app_id = 0): Специальное приложение для управления пользователями. Токены подписываются JWT_SECRET.
  • Другие приложения (app_id > 0): Каждое приложение имеет свой секрет для подписи токенов. Информация хранится в таблице apps.

Пример:

// Логин для SSO
loginResp, _ := client.Login(ctx, &ssov1.LoginRequest{
    AppId: 0,  // SSO
    Login: "user",
    Password: "pass",
})

// Логин для приложения с ID=1
appLoginResp, _ := client.Login(ctx, &ssov1.LoginRequest{
    AppId: 1,  // Другое приложение
    Login: "user",
    Password: "pass",
})

2. Система прав доступа

Обычный пользователь:

  • Может просматривать только свой профиль
  • Может изменять только свой пароль
  • Может удалить только свой аккаунт
  • Может проверить только свои права администратора

Администратор:

  • Может просматривать профиль любого пользователя
  • Может изменять пароль любого пользователя
  • Может удалить любого пользователя
  • Может проверять права любого пользователя
  • Может получить список всех пользователей

Проверка прав:

isAdminResp, _ := client.IsAdmin(ctx, &ssov1.IsAdminRequest{
    UserId: userID,
})
if isAdminResp.IsAdmin {
    // Пользователь является администратором
}

3. Безопасность паролей

  • Все пароли хешируются с помощью bcrypt перед сохранением в базу данных
  • Пароли никогда не передаются в открытом виде после сохранения
  • При обновлении пароля автоматически создается новый хеш

4. JWT Токены

Структура SSO токена (app_id=0):

{
  "uid": "123",
  "exp": 1234567890
}

Структура токена приложения (app_id>0):

{
  "uid": "123",
  "login": "user",
  "email": "user@example.com",
  "app_id": 1,
  "exp": 1234567890
}

Особенности:

  • Токены имеют ограниченный срок жизни (настраивается через JWT_TOKEN_TTL)
  • Токены подписываются секретом приложения (HMAC-SHA256)
  • Истекшие токены отклоняются с ошибкой Unauthenticated

5. Конфигурация через переменные окружения

Система поддерживает приоритет конфигурации:

  1. Переменные окружения (высший приоритет)
  2. Файл .env (низкий приоритет)

Это позволяет легко переопределять настройки для разных окружений.

6. Автоматические миграции

При запуске через Docker Compose миграции выполняются автоматически:

  1. PostgreSQL запускается и проходит health check
  2. Выполняется сервис миграций
  3. После успешных миграций запускается SSO приложение

7. Логирование

Система использует структурированное логирование (slog) с поддержкой разных уровней:

  • local/dev: Debug уровень
  • prod: Info уровень

Логи включают контекстные поля (операция, пользователь, время и т.д.)

8. Health Checks

PostgreSQL имеет встроенный health check, который проверяет готовность базы данных перед запуском зависимых сервисов.

Конфигурация

Переменные окружения

Переменная Описание Значение по умолчанию Обязательная
ENV Окружение (local/dev/prod) local Нет
GRPC_PORT Порт gRPC сервера 8080 Нет
GRPC_TIMEOUT Таймаут gRPC запросов 10s Нет
POSTGRES_HOST Хост PostgreSQL localhost Да
POSTGRES_PORT Внутренний порт PostgreSQL 5432 Да
POSTGRES_EXTERNAL_PORT Внешний порт PostgreSQL 5432 Да
POSTGRES_DB Имя базы данных - Да
POSTGRES_USER Пользователь БД - Да
POSTGRES_PASSWORD Пароль БД - Да
JWT_SECRET Секрет для подписи SSO токенов - Да
JWT_TOKEN_TTL Время жизни токена 12h Нет

Формат .env файла

# Application
ENV=local

# GRPC
GRPC_PORT=8080
GRPC_TIMEOUT=10s

# Database
POSTGRES_HOST=postgres
POSTGRES_PORT=5432
POSTGRES_EXTERNAL_PORT=5436
POSTGRES_DB=myapp_db
POSTGRES_USER=dbuser
POSTGRES_PASSWORD=dbpass123

# JWT
JWT_SECRET=my-secret
JWT_TOKEN_TTL=12h

Тестирование

Проект содержит комплексный набор тестов, покрывающий все основные функции.

Запуск тестов

# Все тесты
go test ./tests/...

# Конкретный тест
go test ./tests/... -run TestRegisterLogin_Login_HappyPath

# С verbose выводом
go test ./tests/... -v

Покрытие тестами

Тесты покрывают следующие сценарии:

Регистрация и вход

  • ✅ Успешная регистрация пользователя
  • ✅ Успешный вход с получением JWT токена
  • ✅ Проверка корректности данных в токене

Информация о пользователе

  • ✅ Пользователь получает свою информацию
  • ✅ Администратор получает свою информацию
  • ✅ Администратор получает информацию другого пользователя
  • ✅ Проверка прав доступа

Управление паролем

  • ✅ Пользователь обновляет свой пароль
  • ✅ Администратор обновляет пароль пользователя
  • ✅ Проверка работы нового пароля

Удаление пользователей

  • ✅ Администратор удаляет пользователя
  • ✅ Пользователь удаляет свой аккаунт
  • ✅ Администратор удаляет несуществующего пользователя

Проверка прав администратора

  • ✅ Проверка обычного пользователя (не админ)
  • ✅ Проверка администратора

Обновление токена

  • ✅ Обновление токена пользователя

Список пользователей

  • ✅ Обычный пользователь не может получить список
  • ✅ Администратор получает список всех пользователей

Тестовые данные

По умолчанию в тестах используется:

  • SSO App ID: 0
  • Test App ID: 1
  • Test App Secret: "test-secret"
  • Admin Login: "admin"
  • Admin Password: "admin_pass"
  • SSO Secret: "my-secret"

Разработка

Структура кода

Проект следует принципам Clean Architecture:

  • cmd/ - точки входа приложения
  • internal/app/ - инициализация и сборка приложения
  • internal/config/ - конфигурация
  • internal/domain/ - доменные модели
  • internal/grpc/ - gRPC handlers и валидация
  • internal/lib/ - утилиты (JWT, генерация)
  • internal/repository/ - слой работы с данными
  • internal/services/ - бизнес-логика
  • migrations/ - SQL миграции
  • tests/ - интеграционные тесты

Добавление новой миграции

# Создание новой миграции
migrate create -ext sql -dir ./migrations -seq add_new_table

# Применить миграции
migrate -path ./migrations \
  -database 'postgres://user:pass@localhost:5432/db?sslmode=disable' \
  up

# Откатить последнюю миграцию
migrate -path ./migrations \
  -database 'postgres://user:pass@localhost:5432/db?sslmode=disable' \
  down

Локальная разработка

  1. Запустите PostgreSQL через Docker Compose:
docker-compose up -d postgres
  1. Выполните миграции:
migrate -path ./migrations \
  -database 'postgres://dbuser:dbpass123@localhost:5436/myapp_db?sslmode=disable' \
  up
  1. Настройте .env для локального запуска

  2. Запустите приложение:

go run cmd/sso/main.go

Troubleshooting

Проблема: Не удается подключиться к базе данных

Решение:

  • Проверьте, что PostgreSQL запущен: docker-compose ps
  • Проверьте логи: docker-compose logs postgres
  • Убедитесь, что переменные окружения настроены правильно
  • В Docker используйте имя сервиса postgres вместо localhost

Проблема: Миграции не применяются

Решение:

  • Проверьте подключение к БД
  • Убедитесь, что миграции выполняются до запуска приложения
  • Проверьте логи миграций: docker-compose logs migrate

Проблема: JWT токен не валиден

Решение:

  • Убедитесь, что используется правильный секрет для приложения
  • Проверьте срок действия токена
  • Убедитесь, что токен правильно передается в заголовке Authorization: Bearer <token>

Проблема: Permission Denied

Решение:

  • Проверьте, что пользователь имеет необходимые права (администратор)
  • Убедитесь, что пользователь пытается получить доступ только к своим данным
  • Проверьте, что JWT токен валиден и принадлежит правильному пользователю

Лицензия

Внутренний проект ITS TECH

Контакты

Для вопросов и предложений обращайтесь к команде разработки ITS TECH.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •