P2.1: BYOC Admin API — произвольные креды при создании тенанта
POST /admin/tenants теперь принимает опциональные access_key/secret_key. Если задано любое из полей — оба обязательны (мин. длина 8/16). С произвольными кредами tenantID генерируется случайно (CreateFixedAutoID). Без кредов — поведение прежнее (store.Create).
This commit is contained in:
+21
-1
@@ -275,6 +275,10 @@ func (h *Handler) jwtMiddleware(next http.Handler) http.Handler {
|
|||||||
type createTenantRequest struct {
|
type createTenantRequest struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
MaxQueues int `json:"max_queues"`
|
MaxQueues int `json:"max_queues"`
|
||||||
|
// BYOC: необязательные произвольные креды. Если задано одно поле —
|
||||||
|
// оба обязательны.
|
||||||
|
AccessKey string `json:"access_key,omitempty"`
|
||||||
|
SecretKey string `json:"secret_key,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// tenantCreateResponse — ответ с secret_key (показывается ТОЛЬКО при создании)
|
// 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"})
|
json.NewEncoder(w).Encode(map[string]string{"error": "name is required"})
|
||||||
return
|
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 {
|
if err != nil {
|
||||||
log.Errorf("admin: failed to create tenant: %v", err)
|
log.Errorf("admin: failed to create tenant: %v", err)
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|||||||
@@ -103,6 +103,17 @@ func (s *TenantStore) Create(name string, maxQueues int) (*Tenant, error) {
|
|||||||
|
|
||||||
// CreateFixed — создаёт тенанта с заранее известными credentials (для seed/demo).
|
// CreateFixed — создаёт тенанта с заранее известными credentials (для seed/demo).
|
||||||
// Используется только при инициализации демо-данных; не вызывается из user-facing API.
|
// Используется только при инициализации демо-данных; не вызывается из 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) {
|
func (s *TenantStore) CreateFixed(name string, maxQueues int, tenantID, accessKey, secretKey string) (*Tenant, error) {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
if _, exists := s.byID[tenantID]; exists {
|
if _, exists := s.byID[tenantID]; exists {
|
||||||
|
|||||||
Reference in New Issue
Block a user