feat(shared-sqs): fixed demo credentials + BYOC foundation (v0.1.9)
- seed.go: CreateFixed с хардкод credentials для demo-service AccessKey=SSAK-demo-shared-sqs (постоянные, README всегда актуален) - tenant_store: добавлен CreateFixed для задания произвольных credentials - deployment.yaml: v0.1.9 - doc/byoc-credentials.md: полная документация BYOC фичи (TODO для API+UI)
This commit is contained in:
@@ -1,7 +1,12 @@
|
||||
// app/cmd/seed.go
|
||||
// Created: 2026-04-09
|
||||
// Updated: 2026-04-09 — фиксированные credentials для demo-tenant (BYOC)
|
||||
// Автосид демо-данных при старте через SHARED_SQS_SEED_DEMO=true.
|
||||
// Создаёт тенанта demo-service с 5 очередями и демо-сообщениями.
|
||||
//
|
||||
// DEMO CREDENTIALS — только для тестового стенда.
|
||||
// Тенант demo-service изолирован: видит только свои очереди, не имеет доступа к
|
||||
// admin API и к очередям других тенантов. Credentials открыты намеренно — стенд публичный.
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -16,10 +21,21 @@ import (
|
||||
"shared-sqs/app/tenant"
|
||||
)
|
||||
|
||||
// Фиксированные credentials демо-тенанта.
|
||||
// Открыты намеренно — тестовый стенд.
|
||||
// Тенант ограничен 10 очередями и не имеет прав admin API.
|
||||
const (
|
||||
demoTenantID = "t-demo-shared-sqs-ngcloud"
|
||||
demoAccessKey = "SSAK-demo-shared-sqs"
|
||||
demoSecretKey = "demo-secret-key-shared-sqs-ngcloud-2026"
|
||||
demoTenantName = "demo-service"
|
||||
demoMaxQueues = 10
|
||||
)
|
||||
|
||||
// seedDemoData создаёт тенанта demo-service с очередями и сообщениями.
|
||||
// Вызывается при SHARED_SQS_SEED_DEMO=true при старте сервера.
|
||||
func seedDemoData(store *tenant.TenantStore) {
|
||||
t, err := store.Create("demo-service", 10)
|
||||
t, err := store.CreateFixed(demoTenantName, demoMaxQueues, demoTenantID, demoAccessKey, demoSecretKey)
|
||||
if err != nil {
|
||||
log.Warnf("seed: не удалось создать demo-tenant: %v", err)
|
||||
return
|
||||
|
||||
@@ -71,6 +71,31 @@ s.mu.Unlock()
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// CreateFixed — создаёт тенанта с заранее известными credentials (для seed/demo).
|
||||
// Используется только при инициализации демо-данных; не вызывается из user-facing API.
|
||||
func (s *TenantStore) CreateFixed(name string, maxQueues int, tenantID, accessKey, secretKey string) (*Tenant, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if _, exists := s.byID[tenantID]; exists {
|
||||
return nil, fmt.Errorf("tenant with id %s already exists", tenantID)
|
||||
}
|
||||
if _, exists := s.byAccessKey[accessKey]; exists {
|
||||
return nil, fmt.Errorf("tenant with access key %s already exists", accessKey)
|
||||
}
|
||||
t := &Tenant{
|
||||
ID: tenantID,
|
||||
Name: name,
|
||||
AccessKey: accessKey,
|
||||
SecretKey: secretKey,
|
||||
MaxQueues: maxQueues,
|
||||
CreatedAt: time.Now().UTC(),
|
||||
Active: true,
|
||||
}
|
||||
s.byID[t.ID] = t
|
||||
s.byAccessKey[t.AccessKey] = t
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// GetByAccessKey — поиск тенанта по AccessKeyId (используется в auth middleware).
|
||||
func (s *TenantStore) GetByAccessKey(accessKey string) (*Tenant, bool) {
|
||||
s.mu.RLock()
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
# BYOC — Bring Your Own Credentials
|
||||
|
||||
**Дата**: 2026-04-09
|
||||
**Статус**: частично реализовано (backend), UI/API — TODO
|
||||
|
||||
---
|
||||
|
||||
## Суть
|
||||
|
||||
Возможность создать тенанта с **произвольными** Access Key и Secret Key вместо авто-генерируемых.
|
||||
|
||||
Нужно для:
|
||||
- демо-стенда с фиксированными credentials (README всегда актуален)
|
||||
- интеграционных тестов с предсказуемыми значениями
|
||||
- миграции с другого SQS-совместимого сервиса (сохранение существующих ключей)
|
||||
|
||||
---
|
||||
|
||||
## Что уже сделано
|
||||
|
||||
### `app/tenant/tenant_store.go` — `CreateFixed`
|
||||
|
||||
```go
|
||||
func (s *TenantStore) CreateFixed(
|
||||
name string,
|
||||
maxQueues int,
|
||||
tenantID string,
|
||||
accessKey string,
|
||||
secretKey string,
|
||||
) (*Tenant, error)
|
||||
```
|
||||
|
||||
Создаёт тенанта с заранее известными credentials.
|
||||
Проверяет уникальность и `tenantID`, и `accessKey` — конфликт возвращает ошибку.
|
||||
|
||||
### `app/cmd/seed.go` — демо-тенант
|
||||
|
||||
Использует `CreateFixed` при `SHARED_SQS_SEED_DEMO=true`:
|
||||
|
||||
```
|
||||
tenantID = "t-demo-shared-sqs-ngcloud"
|
||||
accessKey = "SSAK-demo-shared-sqs"
|
||||
secretKey = "demo-secret-key-shared-sqs-ngcloud-2026"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Что нужно сделать (TODO)
|
||||
|
||||
### Admin API — `POST /admin/tenants`
|
||||
|
||||
Добавить в `createTenantRequest` два опциональных поля:
|
||||
|
||||
```go
|
||||
// app/admin/admin.go
|
||||
type createTenantRequest struct {
|
||||
Name string `json:"name"`
|
||||
MaxQueues int `json:"max_queues"`
|
||||
AccessKey string `json:"access_key,omitempty"` // TODO: BYOC
|
||||
SecretKey string `json:"secret_key,omitempty"` // TODO: BYOC
|
||||
}
|
||||
```
|
||||
|
||||
Логика в `createTenant` handler:
|
||||
|
||||
```go
|
||||
var t *tenant.Tenant
|
||||
var err error
|
||||
if req.AccessKey != "" || req.SecretKey != "" {
|
||||
// BYOC: оба поля обязательны
|
||||
if req.AccessKey == "" || req.SecretKey == "" {
|
||||
jsonErr(w, http.StatusBadRequest, "both access_key and secret_key required when specifying custom credentials")
|
||||
return
|
||||
}
|
||||
// Минимальная длина — защита от случайно слабых ключей
|
||||
if len(req.AccessKey) < 8 || len(req.SecretKey) < 16 {
|
||||
jsonErr(w, http.StatusBadRequest, "access_key min 8 chars, secret_key min 16 chars")
|
||||
return
|
||||
}
|
||||
t, err = h.store.CreateFixed(req.Name, req.MaxQueues, generateTenantID(), req.AccessKey, req.SecretKey)
|
||||
} else {
|
||||
t, err = h.store.Create(req.Name, req.MaxQueues)
|
||||
}
|
||||
```
|
||||
|
||||
> `generateTenantID()` — уже есть в tenant_store.go, нужно экспортировать или вынести.
|
||||
|
||||
### UI — Web форма создания тенанта
|
||||
|
||||
- Добавить в модальное окно "Создать тенанта" два опциональных поля: Access Key, Secret Key
|
||||
- Показывать только если нажата кнопка "задать свои credentials"
|
||||
- Валидация на клиенте: оба поля заполнены, мин. длина
|
||||
|
||||
---
|
||||
|
||||
## Безопасность
|
||||
|
||||
- BYOC-credentials **не дают доступа к admin API** — admin защищён отдельным Bearer токеном
|
||||
- Тенант видит **только свои очереди** — изоляция по AccessKey в auth middleware
|
||||
- Слабые ключи отклоняются на уровне API (минимальная длина)
|
||||
- Credentials передаются только по HTTPS
|
||||
|
||||
---
|
||||
|
||||
## Демо-credentials (открыты намеренно)
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Access Key** | `SSAK-demo-shared-sqs` |
|
||||
| **Secret Key** | `demo-secret-key-shared-sqs-ngcloud-2026` |
|
||||
| **Tenant ID** | `t-demo-shared-sqs-ngcloud` |
|
||||
| **Лимит очередей** | 10 |
|
||||
|
||||
Эти credentials жёстко прописаны в `app/cmd/seed.go`.
|
||||
Тенант создаётся только если `SHARED_SQS_SEED_DEMO=true` (env var в deployment.yaml).
|
||||
Reference in New Issue
Block a user