Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53b3c49612 |
@@ -42,14 +42,12 @@ export const BOOLEAN_SETTING_KEYS = new Set<KnownSettingKey>(['runtime_logs_auto
|
||||
|
||||
export const settingsKeys = {
|
||||
all: ['settings'] as const,
|
||||
tenant: (tenantId: string) => [...settingsKeys.all, tenantId] as const,
|
||||
}
|
||||
|
||||
export function settingsQueryOptions(tenantId?: string | null) {
|
||||
export function settingsQueryOptions() {
|
||||
return queryOptions<AppSettings>({
|
||||
queryKey: settingsKeys.tenant(tenantId ?? ''),
|
||||
queryKey: settingsKeys.all,
|
||||
queryFn: () => apiJSON<AppSettings>('/v1/settings'),
|
||||
enabled: Boolean(tenantId),
|
||||
staleTime: 30_000,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -39,7 +39,6 @@ import {
|
||||
settingsQueryOptions,
|
||||
type BirdSettingKey,
|
||||
} from '@/queries/settings'
|
||||
import { authSessionQueryOptions } from '@/queries/auth'
|
||||
import { apiMutate } from '@/lib/api-client'
|
||||
|
||||
export const Route = createFileRoute('/_auth/tenant-settings')({
|
||||
@@ -72,9 +71,7 @@ const BIRD_LABELS: Record<BirdSettingKey, string> = {
|
||||
|
||||
function TenantSettingsComponent() {
|
||||
const search = useSearch({ from: '/_auth/tenant-settings' })
|
||||
const sessionQ = useQuery(authSessionQueryOptions())
|
||||
const tenantId = sessionQ.data?.tenant_id ?? null
|
||||
const settingsQ = useQuery(settingsQueryOptions(tenantId))
|
||||
const settingsQ = useQuery(settingsQueryOptions())
|
||||
const qc = useQueryClient()
|
||||
|
||||
const partitioned = settingsQ.data ? partitionSettings(settingsQ.data) : null
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ opkey|01ARZ3NDEKTSV4RRFFQ69G5FAV|operator,nodekey|01ARZ3NDEKTSV4RRFFQ69G5FAV|nod
|
||||
|
||||
Если в store доступен демо-tenant (`DemoIDs`, обычно `EVOBGP_SEED_DEMO` не равен `0`), заголовок **`Authorization: Bearer dev`** даёт роль **`operator`** для этого tenant. **Не зависит** от `EVOBGP_DEV_INSECURE`.
|
||||
|
||||
Если токен `dev` также задан в `EVOBGP_API_KEYS` или таблице `api_key`, **приоритет у явной записи** (production tenant), а не у demo-shortcut.
|
||||
Без demo-tenant токен `dev` может быть задан в `EVOBGP_API_KEYS` (break-glass).
|
||||
|
||||
**Запрещено** в продакшене: не оставляйте demo-seed с известным токеном `dev` на боевых данных. Переменная `EVOBGP_DEV_INSECURE` в текущей версии **не влияет** на аутентификацию (оставлена в compose для совместимости; не включайте в production — см. SEC-02 в инженерных правилах).
|
||||
|
||||
|
||||
@@ -81,15 +81,16 @@ func authFromKeyRecord(raw string, rec apiKeyRecord) Auth {
|
||||
}
|
||||
|
||||
// resolveAuth maps a bearer token to tenant identity.
|
||||
// For the literal token "dev", env/DB keys take precedence over the demo shortcut (devAuth).
|
||||
// For the literal token "dev", the demo shortcut (devAuth) takes precedence when demo-seed
|
||||
// is available; env/DB mapping is used only when demo tenant is absent.
|
||||
func (s *Server) resolveAuth(raw string) (Auth, bool) {
|
||||
if raw == "dev" {
|
||||
if rec, ok := s.keyResolver.Lookup(raw); ok {
|
||||
return authFromKeyRecord(raw, rec), true
|
||||
}
|
||||
if a, ok := s.devAuth(); ok {
|
||||
return a, true
|
||||
}
|
||||
if rec, ok := s.keyResolver.Lookup(raw); ok {
|
||||
return authFromKeyRecord(raw, rec), true
|
||||
}
|
||||
return Auth{}, false
|
||||
}
|
||||
rec, ok := s.keyResolver.Lookup(raw)
|
||||
|
||||
@@ -31,7 +31,7 @@ func TestBearerDevGetSettings(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBearerDevPrefersEnvAPIKeyOverDemoTenant(t *testing.T) {
|
||||
func TestBearerDevPrefersDemoTenantOverEnvKey(t *testing.T) {
|
||||
srv, err := New(Options{SeedDemo: true, BundleSeedHex: testBundleSeed})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -61,7 +61,41 @@ func TestBearerDevPrefersEnvAPIKeyOverDemoTenant(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, _ := body["tenant_id"].(string)
|
||||
if got != demoTenant {
|
||||
t.Fatalf("tenant_id=%q want demo tenant %q (env=%q)", got, demoTenant, otherTenant)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBearerDevFallsBackToEnvKeyWithoutDemo(t *testing.T) {
|
||||
srv, err := New(Options{SeedDemo: false, BundleSeedHex: testBundleSeed})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer srv.Close()
|
||||
|
||||
otherTenant := "00000000-0000-4000-8000-000000000001"
|
||||
mustSetTestAPIKeys(t, srv, "dev|"+otherTenant+"|operator")
|
||||
|
||||
ts := httptest.NewServer(srv.Handler())
|
||||
defer ts.Close()
|
||||
|
||||
req, _ := http.NewRequest(http.MethodGet, ts.URL+"/v1/auth/session", nil)
|
||||
req.Header.Set("Authorization", "Bearer dev")
|
||||
resp, err := ts.Client().Do(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
b, _ := io.ReadAll(resp.Body)
|
||||
t.Fatalf("session status=%d body=%s", resp.StatusCode, b)
|
||||
}
|
||||
var body map[string]any
|
||||
if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, _ := body["tenant_id"].(string)
|
||||
if got != otherTenant {
|
||||
t.Fatalf("tenant_id=%q want env key tenant %q (demo=%q)", got, otherTenant, demoTenant)
|
||||
t.Fatalf("tenant_id=%q want env key tenant %q", got, otherTenant)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user