package httpd import ( "net/http" "net/http/httptest" "net/url" "strings" "testing" "time" "gitbay.org/gitbay/internal/config" "gitbay.org/gitbay/internal/control" "gitbay.org/gitbay/internal/store" ) // A session signed in longer ago than ReauthWindow cannot mint from the // settings page: the form comes back with the refusal and a sign-in // link, and the sign-in returns to /settings (#297). func TestWebMintNeedsRecentSignIn(t *testing.T) { s, st, u := newTokenTestServer(t) stale := u stale.SignedInAt = time.Now().Add(-control.ReauthWindow - time.Minute) rr := submitAccountForm(t, s, stale, url.Values{"field": {"token-create"}, "name": {"laptop"}, "scope": {"full"}}) if rr.Code != http.StatusSeeOther { t.Fatalf("status %d, body %s", rr.Code, rr.Body.String()) } if list, err := st.ListAPITokens(u.ID); err != nil || len(list) != 0 { t.Fatalf("a stale session minted %+v (%v)", list, err) } req := httptest.NewRequest("GET", "/settings", nil) for _, c := range rr.Result().Cookies() { req.AddCookie(c) } page := httptest.NewRecorder() s.accountPage(page, req, stale) body := page.Body.String() if !strings.Contains(body, control.ReauthRefusal) { t.Fatalf("refusal not shown: %s", body) } if !strings.Contains(body, `Sign in again`) { t.Fatalf("no sign-in link: %s", body) } var next string for _, c := range page.Result().Cookies() { if c.Name == nextCookie { next = c.Value } } if next != url.QueryEscape("/settings") { t.Fatalf("gitbay_next = %q, want /settings", next) } } // An API token has no browser session: minting through the API is not // held to the sign-in window. func TestAPIMintIgnoresTheSignInWindow(t *testing.T) { s, st, u := newTokenTestServer(t) if err := st.CreateAPIToken(u.ID, "ci", store.HashToken("gb_reauthtest"), "full", nil, 0); err != nil { t.Fatal(err) } req := httptest.NewRequest("POST", "/api/v1/cmd", strings.NewReader(`{"argv":["token","create","--name","second","--scope","read"]}`)) req.Header.Set("Authorization", "Bearer gb_reauthtest") rr := httptest.NewRecorder() s.apiCmd(rr, req) if rr.Code != http.StatusOK { t.Fatalf("status %d: %s", rr.Code, rr.Body.String()) } } // A fresh session mints without any refusal. func TestWebMintFreshSessionSucceeds(t *testing.T) { s, st, u := newTokenTestServer(t) rr := submitAccountForm(t, s, u, url.Values{"field": {"token-create"}, "name": {"laptop"}, "scope": {"full"}}) if rr.Code != http.StatusOK { t.Fatalf("status %d, body %s", rr.Code, rr.Body.String()) } if list, err := st.ListAPITokens(u.ID); err != nil || len(list) != 1 { t.Fatalf("token not minted: %+v (%v)", list, err) } } // A stale session posting a grant form (org members add, on the // organization's people page) also sees the refusal and the sign-in // link, and the membership is not created. func TestWebGrantNeedsRecentSignIn(t *testing.T) { st, err := store.Open(":memory:") if err != nil { t.Fatal(err) } defer st.Close() if err := st.MigrateUp(); err != nil { t.Fatal(err) } uid, err := st.CreateUser("alice", false) if err != nil { t.Fatal(err) } if _, err := st.CreateUser("bob", false); err != nil { t.Fatal(err) } fresh := store.User{ID: uid, Username: "alice", SignedInAt: time.Now()} stale := fresh stale.SignedInAt = time.Now().Add(-control.ReauthWindow - time.Minute) cfg := config.Default() cfg.Web.Mode = "accounts" s := New(cfg, st, nil) if _, msg, ok := s.runControl(fresh, []string{"org", "create", "krz"}); !ok { t.Fatalf("org create: %s", msg) } req := httptest.NewRequest("POST", "/krz", strings.NewReader(url.Values{"field": {"member-add"}, "user": {"bob"}}.Encode())) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.SetPathValue("owner", "krz") rr := httptest.NewRecorder() s.orgSubmit(rr, req, stale) if rr.Code != http.StatusSeeOther { t.Fatalf("status %d, body %s", rr.Code, rr.Body.String()) } req2 := httptest.NewRequest("GET", "/krz/-/people", nil) req2.SetPathValue("owner", "krz") for _, c := range rr.Result().Cookies() { req2.AddCookie(c) } req2.AddCookie(sessionCookieFor(t, s, st, uid)) page := httptest.NewRecorder() s.ownerProfile(page, req2) body := page.Body.String() if !strings.Contains(body, control.ReauthRefusal) { t.Fatalf("refusal not shown: %s", body) } if !strings.Contains(body, `Sign in again`) { t.Fatalf("no sign-in link: %s", body) } var next string for _, c := range page.Result().Cookies() { if c.Name == nextCookie { next = c.Value } } if next != url.QueryEscape("/krz/-/people") { t.Fatalf("gitbay_next = %q, want /krz/-/people", next) } org, err := st.OrgByName("krz") if err != nil { t.Fatal(err) } members, _ := st.OrgMembers(org.ID) for _, m := range members { if m.Username == "bob" { t.Fatalf("a stale session added bob to the org") } } }