AI Gateway Go 0.10.0 源码快照 + 旗舰版需求规划报告

M0-M7 已完成:核心网关(身份/RBAC/TOTP/OIDC/SAML/Provider/配额/路由/内容策略/审计/定价)+ 资源市场(MCP/Skills/数字员工)。
含 22 个 PostgreSQL 迁移、管理端/门户端前端源码、OpenAPI 契约、部署 compose。

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
ben
2026-08-12 11:45:54 +08:00
commit 5759c1862e
807 changed files with 114727 additions and 0 deletions
+115
View File
@@ -0,0 +1,115 @@
package identity
import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/base64"
"math/big"
"testing"
"time"
"github.com/beevik/etree"
"github.com/crewjam/saml"
dsig "github.com/russellhaering/goxmldsig"
)
func TestValidateSAMLMetadataRequiresLiveSigningCertificate(t *testing.T) {
now := time.Unix(1_800_000_000, 0)
certificate := makeTestCertificate(t, now.Add(-time.Hour), now.Add(time.Hour))
metadata := testIDPMetadata(certificate)
if err := validateSAMLMetadata(context.Background(), metadata, true, now); err != nil {
t.Fatalf("valid metadata rejected: %v", err)
}
metadata.IDPSSODescriptors[0].KeyDescriptors = nil
if err := validateSAMLMetadata(context.Background(), metadata, true, now); err == nil {
t.Fatal("metadata without signing certificate was accepted")
}
metadata = testIDPMetadata(makeTestCertificate(t, now.Add(-2*time.Hour), now.Add(-time.Hour)))
if err := validateSAMLMetadata(context.Background(), metadata, true, now); err == nil {
t.Fatal("expired signing certificate was accepted")
}
metadata = testIDPMetadata(certificate)
metadata.IDPSSODescriptors[0].SingleSignOnServices = nil
if err := validateSAMLMetadata(context.Background(), metadata, true, now); err == nil {
t.Fatal("metadata without redirect SSO endpoint was accepted")
}
}
func TestSAMLEntityIDAndAttributes(t *testing.T) {
if got, err := validateSAMLEntityID(" urn:example:gateway "); err != nil || got != "urn:example:gateway" {
t.Fatalf("valid URN entity ID rejected: %q %v", got, err)
}
for _, raw := range []string{"", "relative", "https://user:secret@example.com/sp", "urn:example:sp#fragment"} {
if _, err := validateSAMLEntityID(raw); err == nil {
t.Fatalf("invalid entity ID accepted: %q", raw)
}
}
assertion := &saml.Assertion{AttributeStatements: []saml.AttributeStatement{{Attributes: []saml.Attribute{{
FriendlyName: "mail", Name: "urn:oid:0.9.2342.19200300.100.1.3",
Values: []saml.AttributeValue{{Value: " user@example.com "}},
}}}}}
if got := samlAttribute(assertion, "mail"); got != "user@example.com" {
t.Fatalf("unexpected friendly-name attribute %q", got)
}
if got := samlAttribute(assertion, "urn:oid:0.9.2342.19200300.100.1.3"); got != "user@example.com" {
t.Fatalf("unexpected named attribute %q", got)
}
}
func TestSAMLSignatureAlgorithmPolicyRejectsSHA1(t *testing.T) {
document := etree.NewDocument()
if err := document.ReadFromString(`<Response><Signature><SignedInfo><SignatureMethod Algorithm="` + dsig.RSASHA256SignatureMethod + `"/><Reference><DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/></Reference></SignedInfo></Signature></Response>`); err != nil {
t.Fatal(err)
}
if err := validateSAMLSignatureAlgorithms(document.Root()); err != nil {
t.Fatalf("SHA-256 signature policy rejected: %v", err)
}
document.Root().FindElement("./Signature/SignedInfo/SignatureMethod").SelectAttr("Algorithm").Value = dsig.RSASHA1SignatureMethod
if err := validateSAMLSignatureAlgorithms(document.Root()); err == nil {
t.Fatal("SHA-1 signature algorithm was accepted")
}
document.Root().FindElement("./Signature/SignedInfo/SignatureMethod").SelectAttr("Algorithm").Value = dsig.RSASHA256SignatureMethod
document.Root().FindElement("./Signature/SignedInfo/Reference/DigestMethod").SelectAttr("Algorithm").Value = "http://www.w3.org/2000/09/xmldsig#sha1"
if err := validateSAMLSignatureAlgorithms(document.Root()); err == nil {
t.Fatal("SHA-1 digest algorithm was accepted")
}
}
func testIDPMetadata(certificate *x509.Certificate) *saml.EntityDescriptor {
return &saml.EntityDescriptor{
EntityID: "https://idp.example/metadata",
IDPSSODescriptors: []saml.IDPSSODescriptor{{
SSODescriptor: saml.SSODescriptor{RoleDescriptor: saml.RoleDescriptor{KeyDescriptors: []saml.KeyDescriptor{{
Use: "signing", KeyInfo: saml.KeyInfo{X509Data: saml.X509Data{X509Certificates: []saml.X509Certificate{{Data: base64.StdEncoding.EncodeToString(certificate.Raw)}}}},
}}}},
SingleSignOnServices: []saml.Endpoint{{Binding: saml.HTTPRedirectBinding, Location: "http://127.0.0.1:9091/sso"}},
}},
}
}
func makeTestCertificate(t *testing.T, notBefore, notAfter time.Time) *x509.Certificate {
t.Helper()
key, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Fatal(err)
}
template := &x509.Certificate{
SerialNumber: big.NewInt(1), Subject: pkix.Name{CommonName: "SAML test"},
NotBefore: notBefore, NotAfter: notAfter, KeyUsage: x509.KeyUsageDigitalSignature,
}
der, err := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key)
if err != nil {
t.Fatal(err)
}
certificate, err := x509.ParseCertificate(der)
if err != nil {
t.Fatal(err)
}
return certificate
}