182 lines
5.8 KiB
Go
182 lines
5.8 KiB
Go
|
|
package golooseauth
|
||
|
|
|
||
|
|
import (
|
||
|
|
"net/http"
|
||
|
|
"net/http/httptest"
|
||
|
|
"strings"
|
||
|
|
"testing"
|
||
|
|
|
||
|
|
"github.com/danyel/go-guess/backend/internal/config"
|
||
|
|
)
|
||
|
|
|
||
|
|
func tenants() map[string]config.GoLooseTenant {
|
||
|
|
return map[string]config.GoLooseTenant{
|
||
|
|
"nmbs": {ClientID: "glc_nmbs", ClientSecret: "secret"},
|
||
|
|
"ypto": {ClientID: "glc_ypto", ClientSecret: "secret"},
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestNewUsesDevelopmentDomainsByDefault(t *testing.T) {
|
||
|
|
registry, err := New(config.GoLooseConfig{AuthDomain: config.DefaultAuthDomain, AppDomain: config.DefaultAppDomain, Tenants: tenants()})
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
if registry.AppDomain() != "guess-dev.urpi.be" {
|
||
|
|
t.Fatalf("app domain = %q", registry.AppDomain())
|
||
|
|
}
|
||
|
|
location := loginRedirect(t, registry, "nmbs.guess-dev.urpi.be")
|
||
|
|
if !strings.HasPrefix(location, "https://nmbs.auth-dev.urpi.be/connect/authorize?") {
|
||
|
|
t.Fatalf("development login redirect = %q", location)
|
||
|
|
}
|
||
|
|
if !strings.Contains(location, "redirect_uri=https%3A%2F%2Fnmbs.guess-dev.urpi.be%2Fapi%2Fauth%2Fcallback") {
|
||
|
|
t.Fatalf("development callback missing from %q", location)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestNewUsesProductionDomains(t *testing.T) {
|
||
|
|
registry, err := New(config.GoLooseConfig{
|
||
|
|
AuthDomain: config.DefaultProdAuthDomain,
|
||
|
|
AppDomain: config.DefaultProdAppDomain,
|
||
|
|
Tenants: tenants(),
|
||
|
|
})
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
location := loginRedirect(t, registry, "nmbs.guess.urpi.be")
|
||
|
|
if !strings.HasPrefix(location, "https://nmbs.auth.urpi.be/connect/authorize?") {
|
||
|
|
t.Fatalf("production login redirect = %q", location)
|
||
|
|
}
|
||
|
|
if !strings.Contains(location, "redirect_uri=https%3A%2F%2Fnmbs.guess.urpi.be%2Fapi%2Fauth%2Fcallback") {
|
||
|
|
t.Fatalf("production callback missing from %q", location)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestNewRequiresAnAuthDomainWithCredentials(t *testing.T) {
|
||
|
|
if _, err := New(config.GoLooseConfig{Tenants: tenants()}); err == nil {
|
||
|
|
t.Fatal("expected an error when GO_LOOSE_AUTH_DOMAIN is empty")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestNewNormalizesConfiguredDomains(t *testing.T) {
|
||
|
|
registry, err := New(config.GoLooseConfig{
|
||
|
|
AuthDomain: " .Auth-Dev.Urpi.Be. ",
|
||
|
|
AppDomain: ".Guess-Dev.Urpi.Be.",
|
||
|
|
Tenants: map[string]config.GoLooseTenant{"NMBS": {ClientID: "glc_nmbs", ClientSecret: "secret"}},
|
||
|
|
})
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
if registry.AppDomain() != "guess-dev.urpi.be" {
|
||
|
|
t.Fatalf("app domain = %q", registry.AppDomain())
|
||
|
|
}
|
||
|
|
if location := loginRedirect(t, registry, "nmbs.guess-dev.urpi.be"); !strings.HasPrefix(location, "https://nmbs.auth-dev.urpi.be/connect/authorize?") {
|
||
|
|
t.Fatalf("normalized login redirect = %q", location)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestNewRejectsMultiLabelTenantSlug(t *testing.T) {
|
||
|
|
_, err := New(config.GoLooseConfig{
|
||
|
|
AuthDomain: config.DefaultAuthDomain,
|
||
|
|
AppDomain: config.DefaultAppDomain,
|
||
|
|
Tenants: map[string]config.GoLooseTenant{"nmbs.eu": {ClientID: "id", ClientSecret: "secret"}},
|
||
|
|
})
|
||
|
|
if err == nil {
|
||
|
|
t.Fatal("expected an error for a tenant slug containing a dot")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestAppDomainFallsBackToTheDevelopmentDomain(t *testing.T) {
|
||
|
|
var missing *Registry
|
||
|
|
if missing.AppDomain() != config.DefaultAppDomain {
|
||
|
|
t.Fatalf("nil registry app domain = %q", missing.AppDomain())
|
||
|
|
}
|
||
|
|
empty, err := New(config.GoLooseConfig{})
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
if empty.AppDomain() != config.DefaultAppDomain {
|
||
|
|
t.Fatalf("empty registry app domain = %q", empty.AppDomain())
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestForHostResolvesTheTenantOfTheHost(t *testing.T) {
|
||
|
|
registry, err := New(config.GoLooseConfig{
|
||
|
|
AuthDomain: config.DefaultAuthDomain,
|
||
|
|
AppDomain: config.DefaultAppDomain,
|
||
|
|
Tenants: tenants(),
|
||
|
|
})
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
for _, tc := range []struct {
|
||
|
|
host string
|
||
|
|
slug string
|
||
|
|
ok bool
|
||
|
|
}{
|
||
|
|
{host: "nmbs.guess-dev.urpi.be", slug: "nmbs", ok: true},
|
||
|
|
{host: "ypto.guess-dev.urpi.be", slug: "ypto", ok: true},
|
||
|
|
{host: "NMBS.Guess-Dev.Urpi.Be:8443", slug: "nmbs", ok: true},
|
||
|
|
// The apex host identifies no tenant.
|
||
|
|
{host: "guess-dev.urpi.be", slug: "", ok: false},
|
||
|
|
// Another environment's domain has no configured Go Loose client here.
|
||
|
|
{host: "nmbs.guess.urpi.be", slug: "", ok: false},
|
||
|
|
{host: "guess.urpi.be", slug: "", ok: false},
|
||
|
|
{host: "localhost:5173", slug: "", ok: false},
|
||
|
|
{host: "", slug: "", ok: false},
|
||
|
|
} {
|
||
|
|
auth, slug, ok := registry.ForHost(tc.host)
|
||
|
|
if slug != tc.slug || ok != tc.ok {
|
||
|
|
t.Fatalf("ForHost(%q) = %q, %v, want %q, %v", tc.host, slug, ok, tc.slug, tc.ok)
|
||
|
|
}
|
||
|
|
if ok && auth == nil {
|
||
|
|
t.Fatalf("ForHost(%q) returned no client", tc.host)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestHostPrefersTheForwardedHost(t *testing.T) {
|
||
|
|
request := httptest.NewRequest(http.MethodGet, "/api/auth/session", nil)
|
||
|
|
request.Host = "api:8080"
|
||
|
|
if Host(request) != "api:8080" {
|
||
|
|
t.Fatalf("host = %q", Host(request))
|
||
|
|
}
|
||
|
|
request.Header.Set("X-Forwarded-Host", "nmbs.guess-dev.urpi.be")
|
||
|
|
if Host(request) != "nmbs.guess-dev.urpi.be" {
|
||
|
|
t.Fatalf("forwarded host = %q", Host(request))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestTenantFromHost(t *testing.T) {
|
||
|
|
for _, tc := range []struct {
|
||
|
|
host string
|
||
|
|
want string
|
||
|
|
}{
|
||
|
|
{host: "nmbs.guess-dev.urpi.be", want: "nmbs"},
|
||
|
|
{host: "ypto.guess-dev.urpi.be:443", want: "ypto"},
|
||
|
|
{host: "nmbs.guess-dev.urpi.be.", want: "nmbs"},
|
||
|
|
{host: "deep.nmbs.guess-dev.urpi.be", want: ""},
|
||
|
|
{host: "guess-dev.urpi.be", want: ""},
|
||
|
|
{host: "nmbs.evil.test", want: ""},
|
||
|
|
} {
|
||
|
|
if got := TenantFromHost(tc.host, config.DefaultAppDomain); got != tc.want {
|
||
|
|
t.Fatalf("TenantFromHost(%q) = %q, want %q", tc.host, got, tc.want)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func loginRedirect(t *testing.T, registry *Registry, host string) string {
|
||
|
|
t.Helper()
|
||
|
|
auth, _, ok := registry.ForHost(host)
|
||
|
|
if !ok {
|
||
|
|
t.Fatalf("no Go Loose client for %q", host)
|
||
|
|
}
|
||
|
|
request := httptest.NewRequest(http.MethodGet, "/api/auth/login", nil)
|
||
|
|
request.Host = host
|
||
|
|
response := httptest.NewRecorder()
|
||
|
|
auth.LoginHandler(response, request)
|
||
|
|
if response.Code != http.StatusFound {
|
||
|
|
t.Fatalf("login for %q = %d, want 302", host, response.Code)
|
||
|
|
}
|
||
|
|
return response.Header().Get("Location")
|
||
|
|
}
|