package console import ( "context" "net/http" "strings" "testing" "time" "github.com/carlosframework/platform/internal/domains" "github.com/carlosframework/platform/internal/edgesfile" "github.com/carlosframework/platform/internal/registry" "github.com/carlosframework/platform/internal/status" ) func TestDirectAppsDomainHost(t *testing.T) { cases := []struct { name string appsDomain string host string want bool }{ {"no apps domain configured", "", "widget.apps.test", false}, {"the apex itself is not a one-label alias", "apps.test", "apps.test", false}, {"one label under the apps domain", "apps.test", "widget.apps.test", true}, {"a nested name is refused", "apps.test", "a.b.apps.test", false}, {"a host unrelated to the apps domain", "apps.test", "widget.other.test", false}, {"a suffix match without the separating dot", "apps.test", "notapps.test", false}, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { s := &Server{cfg: Config{AppsDomain: c.appsDomain, ReservePlatformDomains: true}} if got := s.directAppsDomainHost(c.host); got != c.want { t.Errorf("directAppsDomainHost(%q) with AppsDomain=%q = %v, want %v", c.host, c.appsDomain, got, c.want) } }) } } func TestDomainHostDeniedPolicyMatrix(t *testing.T) { const appsDomain = "apps.test" const alias = "widget." + appsDomain base := func() Config { return Config{ AppsDomain: appsDomain, ReservePlatformDomains: true, Origin: "https://origin." + appsDomain, DenyDomains: []string{"blocked." + appsDomain}, RedirectHosts: []string{"retired." + appsDomain}, } } cases := []struct { name string mutate func(*Config) host string want bool }{ {"flag off denies a one-label alias, unchanged from before the flag existed", func(c *Config) {}, alias, true}, {"flag on allows an ordinary one-label alias", func(c *Config) { c.AppsDomainAliasAccounts = []string{"bab"} }, alias, false}, {"flag on still denies the apex", func(c *Config) { c.AppsDomainAliasAccounts = []string{"bab"} }, appsDomain, true}, {"flag on still denies a nested name", func(c *Config) { c.AppsDomainAliasAccounts = []string{"bab"} }, "a.b." + appsDomain, true}, {"flag on still denies an explicit DenyDomains entry", func(c *Config) { c.AppsDomainAliasAccounts = []string{"bab"} }, "blocked." + appsDomain, true}, {"flag on still denies the console's own origin host", func(c *Config) { c.AppsDomainAliasAccounts = []string{"bab"} }, "origin." + appsDomain, true}, {"flag on still denies a retired redirect host", func(c *Config) { c.AppsDomainAliasAccounts = []string{"bab"} }, "retired." + appsDomain, true}, {"flag on with no apps domain configured denies nothing new", func(c *Config) { c.AppsDomainAliasAccounts = []string{"bab"}; c.AppsDomain = "" }, "widget.example.com", false}, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { cfg := base() c.mutate(&cfg) s := &Server{cfg: cfg} if got := s.domainHostDenied(c.host, "bab"); got != c.want { t.Errorf("domainHostDenied(%q) = %v, want %v (cfg=%+v)", c.host, got, c.want, cfg) } }) } } func TestAPIDomainAttachDeniesAppsDomainAliasByDefault(t *testing.T) { fx, member := instanceFixture(t, "code-dom-alias-default") fx.srv.cfg.AppsDomain = "oncarlos.test" fx.srv.cfg.ReservePlatformDomains = true code, _, out := rawReq(t, http.MethodPost, domainsURL(fx, "bab", "titogo"), member, attachBody("widget.oncarlos.test", "go.tito.io")) if code != http.StatusBadRequest { t.Fatalf("attach of an apps-domain alias with the flag unset = %d %s, want 400", code, out) } claimAbsent(t, fx, "widget.oncarlos.test") } func TestAPIDomainAttachAllowsAppsDomainAlias(t *testing.T) { fx, member := instanceFixture(t, "code-dom-alias-allow") fx.srv.cfg.AppsDomain = "oncarlos.test" fx.srv.cfg.ReservePlatformDomains = true fx.srv.cfg.AppsDomainAliasAccounts = []string{fx.sqid} if got := attachDomain(t, fx, member, "widget.oncarlos.test", "go.tito.io"); !got.Created { t.Fatalf("attach of a one-label apps-domain alias = %+v, want created true", got) } for _, host := range []string{ "a.widget.oncarlos.test", // nested "oncarlos.test", // the apex itself } { code, _, out := rawReq(t, http.MethodPost, domainsURL(fx, "bab", "titogo"), member, attachBody(host, "go.tito.io")) if code != http.StatusBadRequest { t.Errorf("attach %q with aliases allowed = %d %s, want 400", host, code, out) } claimAbsent(t, fx, host) } } func TestAPIDomainAttachAppsDomainAliasRejectsWildcardAndCatchall(t *testing.T) { fx, member := instanceFixture(t, "code-dom-alias-wild") fx.srv.cfg.AppsDomain = "oncarlos.test" fx.srv.cfg.ReservePlatformDomains = true fx.srv.cfg.AppsDomainAliasAccounts = []string{fx.sqid} const host = "widget.oncarlos.test" for name, body := range map[string]string{ "wildcard": `{"host":"` + host + `","route":"go.tito.io","wildcard":true}`, "catchall": `{"host":"` + host + `","route":"go.tito.io","catchall":true}`, } { code, _, out := rawReq(t, http.MethodPost, domainsURL(fx, "bab", "titogo"), member, body) if code != http.StatusBadRequest { t.Errorf("%s flag on an apps-domain alias = %d %s, want 400", name, code, out) } claimAbsent(t, fx, host) } if got := attachDomain(t, fx, member, host, "go.tito.io"); !got.Created { t.Fatalf("plain attach of the same host with neither flag set = %+v, want created true", got) } } func TestAPIDomainAttachAppsDomainAliasStillRefusesForeignClaim(t *testing.T) { fx, member := instanceFixture(t, "code-dom-alias-conflict") fx.srv.cfg.AppsDomain = "oncarlos.test" fx.srv.cfg.ReservePlatformDomains = true fx.srv.cfg.AppsDomainAliasAccounts = []string{fx.sqid} const host = "widget.oncarlos.test" if _, _, err := domains.AttachClaim(context.Background(), fx.rel, domains.Claim{ Host: host, Account: "cdef", App: "widgets", Route: "widgets.example.net", CreatedAt: time.Now().UTC().Format(time.RFC3339), }); err != nil { t.Fatal(err) } code, _, out := rawReq(t, http.MethodPost, domainsURL(fx, "bab", "titogo"), member, attachBody(host, "go.tito.io")) if code != http.StatusConflict { t.Fatalf("attach of an apps-domain alias another account holds = %d %s, want 409", code, out) } if got := strings.TrimSpace(out); got != `{"error":"hostname unavailable"}` { t.Fatalf("409 body = %s, want the neutral refusal", got) } } func TestAPIDomainAttachAppsDomainAliasStillRefusesDeploymentRoute(t *testing.T) { ctx := context.Background() fx, member := instanceFixture(t, "code-dom-alias-route") fx.srv.cfg.AppsDomain = "oncarlos.test" fx.srv.cfg.ReservePlatformDomains = true fx.srv.cfg.AppsDomainAliasAccounts = []string{fx.sqid} const host = "widget.oncarlos.test" if _, err := fx.rel.Put(ctx, edgesfile.Key("annie"), []byte(`{"v":1,"box":"annie","eip":"198.51.100.9","hosts":{"`+host+`":{"role":"home"}}}`), ""); err != nil { t.Fatal(err) } if err := status.WriteBox(ctx, fx.rel, status.Box{Label: "annie", GeneratedAt: time.Now()}); err != nil { t.Fatal(err) } code, _, out := rawReq(t, http.MethodPost, domainsURL(fx, "bab", "titogo"), member, attachBody(host, "two.go.tito.io")) if code != http.StatusConflict { t.Fatalf("attach of an apps-domain alias a live box already serves = %d %s, want 409", code, out) } claimAbsent(t, fx, host) } func TestAttachDomainCoreAppsDomainAlias(t *testing.T) { fx, acct := attachFixture(t) fx.srv.cfg.ReservePlatformDomains = true fx.srv.cfg.Registry = attachRegistry(t, registry.Route{Host: "story." + fx.sqid + ".oncarlos.com", Kind: "static", App: "story", Account: fx.sqid, Channel: "canary/live", Status: "ready"}, ) const alias = "widget.oncarlos.com" if _, ref := fx.srv.attachDomain(context.Background(), acct, "story", alias, "", "", "paul@keymail.dev"); ref == nil || ref.Status != http.StatusUnprocessableEntity { t.Fatalf("attach of an apps-domain alias with the flag unset = %+v, want 422", ref) } claimAbsent(t, fx, alias) fx.srv.cfg.AppsDomainAliasAccounts = []string{fx.sqid} res, ref := fx.srv.attachDomain(context.Background(), acct, "story", alias, "", "", "paul@keymail.dev") if ref != nil { t.Fatalf("attach of an apps-domain alias with the flag set = refused %+v", ref) } want := "story." + fx.sqid + ".oncarlos.com" if res.Claim.Route != want { t.Fatalf("claim route = %q, want the ready route %q it aliases", res.Claim.Route, want) } if _, ref := fx.srv.attachDomain(context.Background(), acct, "story", "a.b.oncarlos.com", "", "", "paul@keymail.dev"); ref == nil || ref.Status != http.StatusUnprocessableEntity { t.Fatalf("attach of a nested apps-domain name with the flag set = %+v, want still 422", ref) } claimAbsent(t, fx, "a.b.oncarlos.com") } func TestAttachDomainCoreAppsDomainAliasStillRefusesHeldHost(t *testing.T) { fx, acct := attachFixture(t) fx.srv.cfg.ReservePlatformDomains = true fx.srv.cfg.AppsDomainAliasAccounts = []string{fx.sqid} fx.srv.cfg.Registry = attachRegistry(t, registry.Route{Host: "story." + fx.sqid + ".oncarlos.com", Kind: "static", App: "story", Account: fx.sqid, Channel: "canary/live", Status: "ready"}, ) const alias = "widget.oncarlos.com" seedClaim(t, fx, domains.Claim{Schema: 1, Host: alias, Account: "zzz", App: "rival"}) _, ref := fx.srv.attachDomain(context.Background(), acct, "story", alias, "", "", "paul@keymail.dev") if ref == nil || ref.Status != http.StatusConflict || ref.Msg != "hostname unavailable" { t.Fatalf("refusal = %+v, want exactly 409 hostname unavailable even though aliases are allowed", ref) } } func TestAppsDomainAliasAccountAuthority(t *testing.T) { s := &Server{cfg: Config{ AppsDomain: "platform.example.test", AppsDomainAliasAccounts: []string{"bap"}, ReservePlatformDomains: true, Origin: "https://console.platform.example.test", DenyDomains: []string{"example.test", "platform.example.test", "blocked.platform.example.test"}, }} for _, tc := range []struct { host, account string denied bool }{ {"pullup.platform.example.test", "bap", false}, {"pullup.platform.example.test", "bab", true}, {"pullup.platform.example.test", "", true}, {"bap.platform.example.test", "bap", true}, {"bab.platform.example.test", "bap", true}, {"edge.platform.example.test", "bap", true}, {"console.platform.example.test", "bap", true}, {"blocked.platform.example.test", "bap", true}, {"x.bap.platform.example.test", "bap", true}, {"platform.example.test", "bap", true}, {"outside.example.test", "bap", true}, } { if got := s.domainHostDenied(tc.host, tc.account); got != tc.denied { t.Errorf("%s/%s: denied=%v, want %v", tc.account, tc.host, got, tc.denied) } } s.cfg.SteeringHost = "steering.platform.example.test." if !s.domainHostDenied("steering.platform.example.test", "bap") { t.Fatal("steering host allowed") } } func TestAppsDomainAliasCannotMintFirstRoute(t *testing.T) { fx, acct := attachFixture(t) fx.srv.cfg.ReservePlatformDomains = true fx.srv.cfg.AppsDomainAliasAccounts = []string{fx.sqid} fx.srv.cfg.Registry = attachRegistry(t) for _, kind := range []string{"", "static"} { _, ref := fx.srv.attachDomain(context.Background(), acct, "story", "pullup.oncarlos.com", kind, "", "actor") if ref == nil || ref.Status != http.StatusUnprocessableEntity { t.Fatalf("kind %q: refusal=%v", kind, ref) } claimAbsent(t, fx, "pullup.oncarlos.com") } }