126 lines
4.2 KiB
Go
126 lines
4.2 KiB
Go
package dnspublish
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/x509"
|
|
"encoding/json"
|
|
"encoding/pem"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// fakeGCloudServiceAccountJSON builds a syntactically valid (but not
|
|
// Google-registered) service account key whose token_uri points at tokenURL — enough
|
|
// for golang.org/x/oauth2/google.JWTConfigFromJSON to parse and for its JWT-signing
|
|
// flow to run end-to-end against a fake local token endpoint, with no real network or
|
|
// real Google credentials involved.
|
|
func fakeGCloudServiceAccountJSON(t *testing.T, tokenURL string) string {
|
|
t.Helper()
|
|
priv, err := rsa.GenerateKey(rand.Reader, 2048)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
der, err := x509.MarshalPKCS8PrivateKey(priv)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: der})
|
|
body, err := json.Marshal(map[string]string{
|
|
"type": "service_account",
|
|
"project_id": "test-project",
|
|
"private_key_id": "kid1",
|
|
"private_key": string(keyPEM),
|
|
"client_email": "test@test-project.iam.gserviceaccount.com",
|
|
"token_uri": tokenURL,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return string(body)
|
|
}
|
|
|
|
func TestSetTXTRecordGCloudCreatesWhenNoneExists(t *testing.T) {
|
|
var gotChangePath string
|
|
var gotChangeBody map[string]any
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.Write([]byte(`{"access_token":"fake-token","token_type":"Bearer","expires_in":3600}`))
|
|
})
|
|
mux.HandleFunc("/dns/v1/projects/test-project/managedZones", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Write([]byte(`{"managedZones":[{"name":"my-zone","dnsName":"example.com."}]}`))
|
|
})
|
|
mux.HandleFunc("/dns/v1/projects/test-project/managedZones/my-zone/rrsets", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Write([]byte(`{"rrsets":[]}`))
|
|
})
|
|
mux.HandleFunc("/dns/v1/projects/test-project/managedZones/my-zone/changes", func(w http.ResponseWriter, r *http.Request) {
|
|
gotChangePath = r.URL.Path
|
|
body, _ := io.ReadAll(r.Body)
|
|
json.Unmarshal(body, &gotChangeBody)
|
|
w.Write([]byte(`{"id":"1","status":"pending"}`))
|
|
})
|
|
srv := httptest.NewServer(mux)
|
|
defer srv.Close()
|
|
|
|
origGCloud := GCloudAPIBase
|
|
GCloudAPIBase = srv.URL
|
|
defer func() { GCloudAPIBase = origGCloud }()
|
|
|
|
creds := Credentials{
|
|
Provider: "gcloud", ZoneName: "example.com", GCloudProject: "test-project",
|
|
GCloudServiceAccountJSON: fakeGCloudServiceAccountJSON(t, srv.URL+"/token"),
|
|
}
|
|
if err := SetTXTRecord(creds, "selector._domainkey.example.com", `"v=DKIM1; k=rsa; p=abc"`); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if gotChangePath == "" {
|
|
t.Fatal("expected a Changes.Create request, got none")
|
|
}
|
|
additions, _ := gotChangeBody["additions"].([]any)
|
|
if len(additions) != 1 {
|
|
t.Fatalf("expected exactly one addition, got %v", gotChangeBody)
|
|
}
|
|
added := additions[0].(map[string]any)
|
|
if added["name"] != "selector._domainkey.example.com." || fmt.Sprint(added["rrdatas"]) != "[\"v=DKIM1; k=rsa; p=abc\"]" {
|
|
t.Fatalf("unexpected addition: %+v", added)
|
|
}
|
|
if deletions, ok := gotChangeBody["deletions"]; ok && deletions != nil {
|
|
if arr, ok := deletions.([]any); ok && len(arr) != 0 {
|
|
t.Fatalf("expected no deletions when no record existed, got %v", deletions)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSetTXTRecordGCloudNoZoneFound(t *testing.T) {
|
|
mux := http.NewServeMux()
|
|
mux.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Write([]byte(`{"access_token":"fake-token","token_type":"Bearer","expires_in":3600}`))
|
|
})
|
|
mux.HandleFunc("/dns/v1/projects/test-project/managedZones", func(w http.ResponseWriter, r *http.Request) {
|
|
w.Write([]byte(`{"managedZones":[]}`))
|
|
})
|
|
srv := httptest.NewServer(mux)
|
|
defer srv.Close()
|
|
|
|
orig := GCloudAPIBase
|
|
GCloudAPIBase = srv.URL
|
|
defer func() { GCloudAPIBase = orig }()
|
|
|
|
creds := Credentials{
|
|
Provider: "gcloud", ZoneName: "nope.example", GCloudProject: "test-project",
|
|
GCloudServiceAccountJSON: fakeGCloudServiceAccountJSON(t, srv.URL+"/token"),
|
|
}
|
|
err := SetTXTRecord(creds, "selector._domainkey.nope.example", `"v=DKIM1"`)
|
|
if err == nil {
|
|
t.Fatal("expected an error when no managed zone matches")
|
|
}
|
|
if !strings.Contains(err.Error(), "no managed zone found") {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
}
|