fix: валидация JWT перебором всех стендов gateway (токен любого стенда принимается)

This commit is contained in:
“Naeel”
2026-08-14 08:05:16 +04:00
parent de72fe5c8f
commit 166baa9a98
3 changed files with 48 additions and 22 deletions
+1 -3
View File
@@ -64,9 +64,7 @@ type Handler struct {
// NewHandler — создаёт admin handler
func NewHandler(store *tenant.TenantStore, adminToken string) *Handler {
nubesEndpoint := os.Getenv("NUBES_ENDPOINT")
if nubesEndpoint == "" {
nubesEndpoint = "https://lk-api-gateway.ngcloud.ru/api/v1/svc"
}
// Если не задан — пустой: PingNubesAPI переберёт все стенды сам.
return &Handler{store: store, adminToken: adminToken, nubesEndpoint: nubesEndpoint}
}
+46 -17
View File
@@ -74,27 +74,56 @@ func TenantIDFromSub(sub string) string {
return fmt.Sprintf("sless-%x", hash[:8])
}
// PingNubesAPI — проверяет валидность токена запросом к nubes API.
// endpoint — базовый URL (например "https://deck-api-test.ngcloud.ru/api/v1").
// Логика: 401/403 → токен отклонён. Ошибка соединения → API недоступен.
// nubesAPIEndpoints — стенды Nubes API Gateway, по которым валидируется токен.
// Токен принимается, если ХОТЯ БЫ ОДИН стенд его принял.
// Юзер может вводить токен от любого стенда — главное, что токен действительный.
var nubesAPIEndpoints = []string{
"https://lk-api-gateway.ngcloud.ru/api/v1/svc",
"https://lk-api-gateway-test.ngcloud.ru/api/v1/svc",
"https://lk-api-gateway-dev.ngcloud.ru/api/v1/svc",
}
// PingNubesAPI — проверяет валидность токена перебором стендов Nubes API.
// endpoint (если задан через NUBES_ENDPOINT) пробуется первым.
// Логика: 401/403 → токен отклонён ЭТИМ стендом, пробуем следующий.
// Любой другой HTTP статус → токен принят.
func PingNubesAPI(ctx context.Context, endpoint, token string) error {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
if err != nil {
return fmt.Errorf("build nubes ping request: %w", err)
candidates := make([]string, 0, len(nubesAPIEndpoints)+1)
if endpoint != "" {
candidates = append(candidates, endpoint)
}
for _, ep := range nubesAPIEndpoints {
if ep != endpoint {
candidates = append(candidates, ep)
}
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("User-Agent", "Mozilla/5.0")
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("nubes API unreachable at %s: %w", endpoint, err)
}
defer resp.Body.Close()
var lastErr error
for _, ep := range candidates {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, ep, nil)
if err != nil {
lastErr = fmt.Errorf("build nubes ping request: %w", err)
continue
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("User-Agent", "Mozilla/5.0")
if resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden {
return fmt.Errorf("nubes API rejected token (HTTP %d)", resp.StatusCode)
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
if err != nil {
lastErr = fmt.Errorf("nubes API unreachable at %s: %w", ep, err)
continue
}
resp.Body.Close()
if resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden {
lastErr = fmt.Errorf("nubes API rejected token (HTTP %d)", resp.StatusCode)
continue
}
return nil
}
return nil
if lastErr == nil {
lastErr = fmt.Errorf("nubes API rejected token")
}
return lastErr
}