diff --git a/app/admin/admin.go b/app/admin/admin.go index 61b9f36..0c1eb21 100644 --- a/app/admin/admin.go +++ b/app/admin/admin.go @@ -275,6 +275,10 @@ func (h *Handler) jwtMiddleware(next http.Handler) http.Handler { type createTenantRequest struct { Name string `json:"name"` MaxQueues int `json:"max_queues"` + // BYOC: необязательные произвольные креды. Если задано одно поле — + // оба обязательны. + AccessKey string `json:"access_key,omitempty"` + SecretKey string `json:"secret_key,omitempty"` } // tenantCreateResponse — ответ с secret_key (показывается ТОЛЬКО при создании) @@ -317,7 +321,23 @@ func (h *Handler) createTenant(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(map[string]string{"error": "name is required"}) return } - t, err := h.store.Create(req.Name, req.MaxQueues) + + // BYOC: либо оба поля кредов заданы, либо ни одного. + var t *tenant.Tenant + var err error + if req.AccessKey != "" || req.SecretKey != "" { + if req.AccessKey == "" || req.SecretKey == "" { + jsonErr(w, http.StatusBadRequest, "both access_key and secret_key are 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.CreateFixedAutoID(req.Name, req.MaxQueues, req.AccessKey, req.SecretKey) + } else { + t, err = h.store.Create(req.Name, req.MaxQueues) + } if err != nil { log.Errorf("admin: failed to create tenant: %v", err) w.Header().Set("Content-Type", "application/json") diff --git a/app/tenant/tenant_store.go b/app/tenant/tenant_store.go index e8f3d9b..70a921b 100644 --- a/app/tenant/tenant_store.go +++ b/app/tenant/tenant_store.go @@ -103,6 +103,17 @@ func (s *TenantStore) Create(name string, maxQueues int) (*Tenant, error) { // CreateFixed — создаёт тенанта с заранее известными credentials (для seed/demo). // Используется только при инициализации демо-данных; не вызывается из user-facing API. +// CreateFixedAutoID — создаёт тенанта с заранее известными креды (BYOC), +// но СЛУЧАЙНЫМ tenantID (генерируется через crypto/rand). Используется Admin API +// при задании произвольных access_key/secret_key без явного tenantID. +func (s *TenantStore) CreateFixedAutoID(name string, maxQueues int, accessKey, secretKey string) (*Tenant, error) { + id, err := generateTenantID() + if err != nil { + return nil, fmt.Errorf("generate tenant id: %w", err) + } + return s.CreateFixed(name, maxQueues, id, accessKey, secretKey) +} + func (s *TenantStore) CreateFixed(name string, maxQueues int, tenantID, accessKey, secretKey string) (*Tenant, error) { s.mu.Lock() if _, exists := s.byID[tenantID]; exists {