Skip to content
Snippets Groups Projects
sso_test.go 4.47 KiB
Newer Older
ale's avatar
ale committed
package sso

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
ale's avatar
ale committed
	"testing"
	"time"

	"golang.org/x/crypto/ed25519"
)

func TestEd25519(t *testing.T) {
ale's avatar
ale committed
	pub, priv, err := ed25519.GenerateKey(nil)
ale's avatar
ale committed
	if err != nil {
		t.Fatal(err)
	}

	signer := &ssoSigner{key: priv}
	validator := &ssoValidator{
		publicKey: pub,
		domain:    "domain",
	}

	tkt := NewTicket("user", "service", "domain", "nonce", nil, 300*time.Second)
	signed, err := signer.Sign(tkt)
	if err != nil {
		t.Fatal("Sign():", err)
	}
	if signed == "" {
		t.Fatal("signed is nil")
	}

	tkt2, err := validator.Validate(signed, "nonce", "service", nil)
	if err != nil {
		t.Fatal("Validate():", err)
	}
	if tkt.User != tkt2.User || tkt.Service != tkt2.Service || tkt.Domain != tkt2.Domain || tkt.Nonce != tkt2.Nonce || tkt.Expires != tkt2.Expires {
		t.Fatalf("decoded ticket differs: orig=%v, decoded=%v", tkt, tkt2)
	}
}

var (
	legacyTicket    = "yIo0k9TRhTT9F1C89nMbTDqJH905aMbN_O72ke2mWlss3vQAWffQWMRHoc0auYBGEex01fNFVRREEJALcZ15BzR8dXNlcnxzZXJ2aWNlfGRvbWFpbnx8MTUwODA3MjQyNXw"
	legacyPublicKey = []byte{47, 234, 144, 101, 76, 245, 1, 73, 155, 115, 89, 105, 165, 252, 49, 114, 48, 166, 231, 130, 82, 123, 147, 179, 50, 50, 34, 198, 219, 251, 151, 17}
)

// TestLegacy verifies that tickets signed by the legacy C
// implementation can be verified correctly by the Go code.
ale's avatar
ale committed
func TestLegacy(t *testing.T) {
	validator := &ssoValidator{publicKey: legacyPublicKey}
	tkt, err := validator.parse(legacyTicket)
	if err != nil {
		t.Fatal("Parse():", err)
	}
	if tkt.User != "user" || tkt.Service != "service" || tkt.Domain != "domain" {
		t.Fatalf("decoded bad values: %+v", tkt)
	}
}

// Create a ssoSigner and sign a valid ticket.
func makeTestSigner(t *testing.T) (string, string, func()) {
	dir, err := ioutil.TempDir("", "")
	if err != nil {
		t.Fatal(err)
	}

	pub, priv, err := ed25519.GenerateKey(nil)
	if err != nil {
		t.Fatal(err)
	}
	pubPath := filepath.Join(dir, "public.key")
	ioutil.WriteFile(pubPath, pub, 0600)
	//ioutil.WriteFile(filepath.Join(dir, "secret.key"), priv, 0600)

	tkt := NewTicket("user", "service/", "domain", "nonce", nil, 300*time.Second)
	signer := &ssoSigner{key: priv}
	signed, err := signer.Sign(tkt)
	if err != nil {
		t.Fatal("Sign():", err)
	}

	return dir, signed, func() {
		os.RemoveAll(dir)
	}
}

// TestLegacyIntegration verifies that tickets signed by the Go code
// can be verified successfully by the legacy C implementation.
//
// To run this test, set the SSO_SRCDIR environment variable and point
// it at the root of the ai/sso source package repository.
func TestLegacyIntegration(t *testing.T) {
	ssoSrcDir := os.Getenv("SSO_SRCDIR")
	if ssoSrcDir == "" {
		t.Skip("SSO_SRCDIR not set")
	}
	ssotool := filepath.Join(ssoSrcDir, "src/sso/ssotool")
	if _, err := os.Stat(ssotool); os.IsNotExist(err) {
		t.Skip("ssotool not installed")
	}

	dir, signed, cleanup := makeTestSigner(t)
	defer cleanup()
	pubPath := filepath.Join(dir, "public.key")

	cmd := exec.Command(ssotool, "--public-key", pubPath, "--service", "service/", "--domain", "domain", "--nonce", "nonce", "--verify", signed)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	err := cmd.Run()
	if err != nil {
		t.Fatalf("ssotool validation failed: %v", err)
	}
}

// TestLegacyIntegration verifies that tickets signed by the Go code
// can be verified successfully by the legacy Python implementation.
//
// To run this test, set the SSO_SRCDIR environment variable and point
// it at the root of the ai/sso source package repository.
func TestLegacyIntegration_Python(t *testing.T) {
	ssoSrcDir := os.Getenv("SSO_SRCDIR")
	if ssoSrcDir == "" {
		t.Skip("SSO_SRCDIR not set")
	}
	pythonPath := fmt.Sprintf(
		"%s:%s",
		filepath.Join(ssoSrcDir, "src/python"),
		filepath.Join(ssoSrcDir, "src/python/sso"),
	)
	libPath := filepath.Join(ssoSrcDir, "src/sso/.libs")

	dir, signed, cleanup := makeTestSigner(t)
	defer cleanup()

	pubPath := filepath.Join(dir, "public.key")
	scriptPath := filepath.Join(dir, "test.py")
	ioutil.WriteFile(scriptPath, []byte(`
import sso
import sys
with open(sys.argv[1]) as fd:
    pubkey = fd.read()
verifier = sso.Verifier(pubkey, "service/", "domain", None)
tkt = verifier.verify(sys.argv[2].encode(), "nonce")
sys.exit(0 if (tkt.user() == "user") else 1)
`), 0600)

	cmd := exec.Command("/bin/sh", "-c", fmt.Sprintf("env PYTHONPATH=%s LD_LIBRARY_PATH=%s python %s %s %s", pythonPath, libPath, scriptPath, pubPath, signed))
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	err := cmd.Run()
	if err != nil {
		t.Fatalf("python validation failed: %v", err)
	}
}