sso_test.go 4.47 KB
Newer Older
ale's avatar
ale committed
1 2 3
package sso

import (
4 5 6 7 8
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
ale's avatar
ale committed
9 10 11 12 13 14 15
	"testing"
	"time"

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

func TestEd25519(t *testing.T) {
ale's avatar
ale committed
16
	pub, priv, err := ed25519.GenerateKey(nil)
ale's avatar
ale committed
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
	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}
)

50 51
// TestLegacy verifies that tickets signed by the legacy C
// implementation can be verified correctly by the Go code.
ale's avatar
ale committed
52 53 54 55 56 57 58 59 60 61
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)
	}
}
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159

// 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)
	}
}