Commit 2f3a60f2e9
Verified · cmc ci/build: success
Layout: unified · split
e2e/accounts_test.go +21
| @@ -217,3 +217,24 @@ func TestViewOnlyHasNoLoginOnTheWire(t *testing.T) { | |||
| 217 | t.Fatalf("web login in view_only: exit %d, %s", code, errOut) | 217 | t.Fatalf("web login in view_only: exit %d, %s", code, errOut) |
| 218 | } | 218 | } |
| 219 | } | 219 | } |
| 220 | |||
| 221 | // TestTitleIsNotAHostname pins the split between the instance's display name | ||
| 222 | // and its hostname: the login page prints a command to paste into a terminal, | ||
| 223 | // so it must name the host even when the operator has set a display title. | ||
| 224 | func TestTitleIsNotAHostname(t *testing.T) { | ||
| 225 | inst := startInstanceWith(t, "[web]\nmode = \"accounts\"\ntitle = \"GitBay\"\n") | ||
| 226 | |||
| 227 | status, body := inst.get(t, "/login") | ||
| 228 | if status != 200 { | ||
| 229 | t.Fatalf("/login = %d", status) | ||
| 230 | } | ||
| 231 | if strings.Contains(body, "ssh git@GitBay") { | ||
| 232 | t.Fatal("login page tells you to ssh to the display title") | ||
| 233 | } | ||
| 234 | if !strings.Contains(body, "ssh git@gitbay.test web login") { | ||
| 235 | t.Fatalf("login page does not name the host:\n%s", body) | ||
| 236 | } | ||
| 237 | if !strings.Contains(body, "GitBay") { | ||
| 238 | t.Fatal("login page dropped the display title entirely") | ||
| 239 | } | ||
| 240 | } | ||
internal/httpd/accounts.go +4 −4
| @@ -66,7 +66,7 @@ func (s *Server) login(w http.ResponseWriter, r *http.Request) { | |||
| 66 | s.render(w, "login.html", struct { | 66 | s.render(w, "login.html", struct { |
| 67 | basePage | 67 | basePage |
| 68 | Error string | 68 | Error string |
| 69 | }{basePage{Site: s.siteName()}, ""}) | 69 | }{basePage{Site: s.siteName(), Host: s.cfg.SiteHost()}, ""}) |
| 70 | return | 70 | return |
| 71 | } | 71 | } |
| 72 | userID, err := s.st.ConsumeLoginToken(store.HashToken(token)) | 72 | userID, err := s.st.ConsumeLoginToken(store.HashToken(token)) |
| @@ -74,7 +74,7 @@ func (s *Server) login(w http.ResponseWriter, r *http.Request) { | |||
| 74 | s.render(w, "login.html", struct { | 74 | s.render(w, "login.html", struct { |
| 75 | basePage | 75 | basePage |
| 76 | Error string | 76 | Error string |
| 77 | }{basePage{Site: s.siteName()}, | 77 | }{basePage{Site: s.siteName(), Host: s.cfg.SiteHost()}, |
| 78 | "that login link is invalid, expired, or already used — mint a new one"}) | 78 | "that login link is invalid, expired, or already used — mint a new one"}) |
| 79 | return | 79 | return |
| 80 | } | 80 | } |
| @@ -226,7 +226,7 @@ func (s *Server) renderSignup(w http.ResponseWriter, errMsg, username string) { | |||
| 226 | Mode string // open | invite | 226 | Mode string // open | invite |
| 227 | Error string | 227 | Error string |
| 228 | Username string | 228 | Username string |
| 229 | }{basePage{Site: s.siteName()}, s.cfg.SiteHost(), s.cfg.Registration.Mode, errMsg, username}) | 229 | }{basePage{Site: s.siteName(), Host: s.cfg.SiteHost()}, s.cfg.SiteHost(), s.cfg.Registration.Mode, errMsg, username}) |
| 230 | } | 230 | } |
| 231 | 231 | ||
| 232 | func (s *Server) signupSubmit(w http.ResponseWriter, r *http.Request) { | 232 | func (s *Server) signupSubmit(w http.ResponseWriter, r *http.Request) { |
| @@ -248,7 +248,7 @@ func (s *Server) signupSubmit(w http.ResponseWriter, r *http.Request) { | |||
| 248 | Username string | 248 | Username string |
| 249 | Message string | 249 | Message string |
| 250 | Host string | 250 | Host string |
| 251 | }{basePage{Site: s.siteName()}, username, msg, s.cfg.SiteHost()}) | 251 | }{basePage{Site: s.siteName(), Host: s.cfg.SiteHost()}, username, msg, s.cfg.SiteHost()}) |
| 252 | } | 252 | } |
| 253 | 253 | ||
| 254 | // issueCreateForm renders the new-issue form, prefilled from the repo's | 254 | // issueCreateForm renders the new-issue form, prefilled from the repo's |
internal/httpd/page.go +5 −2
| @@ -36,7 +36,10 @@ func (r rail) Empty() bool { return len(r.Pinned) == 0 && len(r.Reviews) == 0 } | |||
| 36 | // basePage is what the layout needs on every page, repo or not. Page | 36 | // basePage is what the layout needs on every page, repo or not. Page |
| 37 | // structs embed it so the rail and the site name are always in scope. | 37 | // structs embed it so the rail and the site name are always in scope. |
| 38 | type basePage struct { | 38 | type basePage struct { |
| 39 | // Site is the instance's display name; Host is the name a command or | ||
| 40 | // URL must use. Anything copy-pasteable takes Host. | ||
| 39 | Site string | 41 | Site string |
| 42 | Host string | ||
| 40 | Viewer string | 43 | Viewer string |
| 41 | Rail rail | 44 | Rail rail |
| 42 | } | 45 | } |
| @@ -45,7 +48,7 @@ type basePage struct { | |||
| 45 | // resolved a viewer. | 48 | // resolved a viewer. |
| 46 | func (s *Server) base(r *http.Request) basePage { | 49 | func (s *Server) base(r *http.Request) basePage { |
| 47 | if s.cfg.Web.Mode != "accounts" { | 50 | if s.cfg.Web.Mode != "accounts" { |
| 48 | return basePage{Site: s.siteName()} | 51 | return basePage{Site: s.siteName(), Host: s.cfg.SiteHost()} |
| 49 | } | 52 | } |
| 50 | return s.baseFor(s.viewer(r)) | 53 | return s.baseFor(s.viewer(r)) |
| 51 | } | 54 | } |
| @@ -53,7 +56,7 @@ func (s *Server) base(r *http.Request) basePage { | |||
| 53 | // baseFor is base for a handler that already holds the viewer, so the | 56 | // baseFor is base for a handler that already holds the viewer, so the |
| 54 | // session lookup is not repeated. | 57 | // session lookup is not repeated. |
| 55 | func (s *Server) baseFor(viewer store.User) basePage { | 58 | func (s *Server) baseFor(viewer store.User) basePage { |
| 56 | b := basePage{Site: s.siteName()} | 59 | b := basePage{Site: s.siteName(), Host: s.cfg.SiteHost()} |
| 57 | if viewer.ID == 0 { | 60 | if viewer.ID == 0 { |
| 58 | return b | 61 | return b |
| 59 | } | 62 | } |
internal/web/templates/login.html +1 −1
| @@ -4,6 +4,6 @@ | |||
| 4 | {{if .Error}}<p class="error" role="alert">{{.Error}}</p>{{end}} | 4 | {{if .Error}}<p class="error" role="alert">{{.Error}}</p>{{end}} |
| 5 | <p>Browser sessions are minted over SSH — there is no password. From a machine | 5 | <p>Browser sessions are minted over SSH — there is no password. From a machine |
| 6 | with your registered key:</p> | 6 | with your registered key:</p> |
| 7 | <pre class="message">ssh git@{{.Site}} web login</pre> | 7 | <pre class="message">ssh git@{{.Host}} web login</pre> |
| 8 | <p>then open the printed URL within five minutes.</p> | 8 | <p>then open the printed URL within five minutes.</p> |
| 9 | {{end}} | 9 | {{end}} |