5759c1862e
M0-M7 已完成:核心网关(身份/RBAC/TOTP/OIDC/SAML/Provider/配额/路由/内容策略/审计/定价)+ 资源市场(MCP/Skills/数字员工)。 含 22 个 PostgreSQL 迁移、管理端/门户端前端源码、OpenAPI 契约、部署 compose。 Co-Authored-By: Claude <noreply@anthropic.com>
74 lines
2.2 KiB
Go
74 lines
2.2 KiB
Go
package apikey
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"testing"
|
|
)
|
|
|
|
func TestBootstrapCompatibilityUsageCounter(t *testing.T) {
|
|
authenticator := NewAuthenticator(nil, nil, "temporary-bootstrap-key")
|
|
if err := authenticator.Authenticate(context.Background(), "temporary-bootstrap-key"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if authenticator.BootstrapUses() != 1 {
|
|
t.Fatalf("unexpected bootstrap usage count %d", authenticator.BootstrapUses())
|
|
}
|
|
}
|
|
|
|
func TestGenerateAndDigest(t *testing.T) {
|
|
first, prefix, hash, err := Generate()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(first) < 40 || len(prefix) != 16 || prefix != first[:16] {
|
|
t.Fatal("invalid API key format")
|
|
}
|
|
computed, _ := Digest(first)
|
|
if !bytes.Equal(hash, computed) {
|
|
t.Fatal("stored digest differs")
|
|
}
|
|
second, _, _, err := Generate()
|
|
if err != nil || second == first {
|
|
t.Fatal("API keys must be independently random")
|
|
}
|
|
}
|
|
|
|
func TestScopes(t *testing.T) {
|
|
if !HasScope([]string{"gateway:invoke"}, "gateway:invoke") || !HasScope([]string{"*"}, "gateway:invoke") {
|
|
t.Fatal("expected scope is missing")
|
|
}
|
|
if HasScope([]string{"gateway:read"}, "gateway:invoke") {
|
|
t.Fatal("unexpected scope accepted")
|
|
}
|
|
}
|
|
|
|
// TestInvokeScope pins the regression where portal application runtime
|
|
// credentials ("application:run") could never reach the gateway: the scope
|
|
// check only admitted "gateway:invoke", so every hosted application chat
|
|
// returned 401 even though the credential is legitimate.
|
|
func TestInvokeScope(t *testing.T) {
|
|
accept := map[string][]string{
|
|
"gateway key": {"gateway:invoke"},
|
|
"admin wildcard": {"*"},
|
|
"application runtime key": {"application:run"},
|
|
"runtime + read": {"application:run", "gateway:read"},
|
|
}
|
|
for name, scopes := range accept {
|
|
if !invokeScope(scopes) {
|
|
t.Fatalf("invokeScope(%v) = false, want true for %s", scopes, name)
|
|
}
|
|
}
|
|
reject := map[string][]string{
|
|
"read-only": {"gateway:read"},
|
|
"unrelated scope": {"workbench:run"},
|
|
"empty": {},
|
|
"runtime scope missing": {"gateway:read", "workbench:run"},
|
|
}
|
|
for name, scopes := range reject {
|
|
if invokeScope(scopes) {
|
|
t.Fatalf("invokeScope(%v) = true, want false for %s", scopes, name)
|
|
}
|
|
}
|
|
}
|