feat: backend core - auth, user, role, permission, device, webhook, monitoring, cache, repository, service, middleware, API handlers
This commit is contained in:
169
internal/auth/providers/provider_crypto_test.go
Normal file
169
internal/auth/providers/provider_crypto_test.go
Normal file
@@ -0,0 +1,169 @@
|
||||
package providers
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func generateRSAKeyForTest(t *testing.T) *rsa.PrivateKey {
|
||||
t.Helper()
|
||||
|
||||
key, err := rsa.GenerateKey(rand.Reader, 1024)
|
||||
if err != nil {
|
||||
t.Fatalf("generate rsa key failed: %v", err)
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
func marshalPKCS8PEMForTest(t *testing.T, key *rsa.PrivateKey) string {
|
||||
t.Helper()
|
||||
|
||||
der, err := x509.MarshalPKCS8PrivateKey(key)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal PKCS#8 failed: %v", err)
|
||||
}
|
||||
|
||||
return string(pem.EncodeToMemory(&pem.Block{
|
||||
Type: "PRIVATE KEY",
|
||||
Bytes: der,
|
||||
}))
|
||||
}
|
||||
|
||||
func TestParseAlipayPrivateKeySupportsRawPKCS8AndPKCS1(t *testing.T) {
|
||||
key := generateRSAKeyForTest(t)
|
||||
|
||||
pkcs8DER, err := x509.MarshalPKCS8PrivateKey(key)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal PKCS#8 failed: %v", err)
|
||||
}
|
||||
|
||||
rawPKCS8 := base64.StdEncoding.EncodeToString(pkcs8DER)
|
||||
parsedPKCS8, err := parseAlipayPrivateKey(rawPKCS8)
|
||||
if err != nil {
|
||||
t.Fatalf("parse raw PKCS#8 key failed: %v", err)
|
||||
}
|
||||
if parsedPKCS8.N.Cmp(key.N) != 0 || parsedPKCS8.D.Cmp(key.D) != 0 {
|
||||
t.Fatal("parsed raw PKCS#8 key does not match original key")
|
||||
}
|
||||
|
||||
pkcs1PEM := string(pem.EncodeToMemory(&pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(key),
|
||||
}))
|
||||
parsedPKCS1, err := parseAlipayPrivateKey(pkcs1PEM)
|
||||
if err != nil {
|
||||
t.Fatalf("parse PKCS#1 key failed: %v", err)
|
||||
}
|
||||
if parsedPKCS1.N.Cmp(key.N) != 0 || parsedPKCS1.D.Cmp(key.D) != 0 {
|
||||
t.Fatal("parsed PKCS#1 key does not match original key")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAlipayPrivateKeyRejectsInvalidPEM(t *testing.T) {
|
||||
if _, err := parseAlipayPrivateKey("not-a-valid-private-key"); err == nil {
|
||||
t.Fatal("expected invalid private key parsing to fail")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlipayProviderSignParamsProducesVerifiableSignature(t *testing.T) {
|
||||
key := generateRSAKeyForTest(t)
|
||||
provider := NewAlipayProvider(
|
||||
"app-id",
|
||||
marshalPKCS8PEMForTest(t, key),
|
||||
"https://admin.example.com/login/oauth/callback",
|
||||
false,
|
||||
)
|
||||
|
||||
params := map[string]string{
|
||||
"method": "alipay.system.oauth.token",
|
||||
"app_id": "app-id",
|
||||
"code": "auth-code",
|
||||
"sign": "should-be-ignored",
|
||||
}
|
||||
|
||||
signature, err := provider.signParams(params)
|
||||
if err != nil {
|
||||
t.Fatalf("signParams failed: %v", err)
|
||||
}
|
||||
if signature == "" {
|
||||
t.Fatal("expected non-empty signature")
|
||||
}
|
||||
|
||||
signatureBytes, err := base64.StdEncoding.DecodeString(signature)
|
||||
if err != nil {
|
||||
t.Fatalf("decode signature failed: %v", err)
|
||||
}
|
||||
|
||||
signContent := "app_id=app-id&code=auth-code&method=alipay.system.oauth.token"
|
||||
hash := sha256.Sum256([]byte(signContent))
|
||||
if err := rsa.VerifyPKCS1v15(&key.PublicKey, crypto.SHA256, hash[:], signatureBytes); err != nil {
|
||||
t.Fatalf("signature verification failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTwitterProviderPKCEHelpersAndAuthURL(t *testing.T) {
|
||||
provider := NewTwitterProvider("twitter-client", "https://admin.example.com/login/oauth/callback")
|
||||
|
||||
verifierA, err := provider.GenerateCodeVerifier()
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateCodeVerifier(first) failed: %v", err)
|
||||
}
|
||||
verifierB, err := provider.GenerateCodeVerifier()
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateCodeVerifier(second) failed: %v", err)
|
||||
}
|
||||
|
||||
if verifierA == "" || verifierB == "" {
|
||||
t.Fatal("expected non-empty code verifiers")
|
||||
}
|
||||
if verifierA == verifierB {
|
||||
t.Fatal("expected code verifiers to differ across calls")
|
||||
}
|
||||
if strings.Contains(verifierA, "=") || strings.Contains(verifierB, "=") {
|
||||
t.Fatal("expected code verifiers to be base64url values without padding")
|
||||
}
|
||||
if provider.GenerateCodeChallenge(verifierA) != verifierA {
|
||||
t.Fatal("expected current code challenge implementation to mirror the verifier")
|
||||
}
|
||||
|
||||
authURL, err := provider.GetAuthURL()
|
||||
if err != nil {
|
||||
t.Fatalf("GetAuthURL failed: %v", err)
|
||||
}
|
||||
if authURL.CodeVerifier == "" || authURL.State == "" {
|
||||
t.Fatal("expected auth url response to include verifier and state")
|
||||
}
|
||||
if authURL.Redirect != provider.RedirectURI {
|
||||
t.Fatalf("expected redirect %q, got %q", provider.RedirectURI, authURL.Redirect)
|
||||
}
|
||||
|
||||
parsed, err := url.Parse(authURL.URL)
|
||||
if err != nil {
|
||||
t.Fatalf("parse auth url failed: %v", err)
|
||||
}
|
||||
query := parsed.Query()
|
||||
|
||||
if query.Get("client_id") != "twitter-client" {
|
||||
t.Fatalf("expected twitter client_id, got %q", query.Get("client_id"))
|
||||
}
|
||||
if query.Get("redirect_uri") != provider.RedirectURI {
|
||||
t.Fatalf("expected redirect_uri %q, got %q", provider.RedirectURI, query.Get("redirect_uri"))
|
||||
}
|
||||
if query.Get("code_challenge") != authURL.CodeVerifier {
|
||||
t.Fatalf("expected code challenge to equal verifier, got %q", query.Get("code_challenge"))
|
||||
}
|
||||
if query.Get("code_challenge_method") != "plain" {
|
||||
t.Fatalf("expected code_challenge_method plain, got %q", query.Get("code_challenge_method"))
|
||||
}
|
||||
if query.Get("state") != authURL.State {
|
||||
t.Fatalf("expected state %q, got %q", authURL.State, query.Get("state"))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user