1package archesrv
2
3import (
4 "io"
5 "net/http"
6 "strings"
7 "testing"
8)
9
10func TestForgeServer_Login_PageLoads(t *testing.T) {
11 s, ts := newTestServer(t)
12 s.db.CreateUser("admin", "pass", true) //nolint:errcheck
13
14 resp, err := http.Get(ts.URL + "/login")
15 if err != nil {
16 t.Fatalf("GET /login: %v", err)
17 }
18 defer resp.Body.Close()
19 if resp.StatusCode != http.StatusOK {
20 t.Errorf("want 200, got %d", resp.StatusCode)
21 }
22}
23
24func TestForgeServer_Login_WrongPassword(t *testing.T) {
25 s, ts := newTestServer(t)
26 s.db.CreateUser("admin", "correctpass", true) //nolint:errcheck
27
28 jar := newCookieJar()
29 client := &http.Client{
30 Jar: jar,
31 CheckRedirect: func(_ *http.Request, _ []*http.Request) error { return http.ErrUseLastResponse },
32 }
33 resp, err := client.PostForm(ts.URL+"/login", map[string][]string{
34 "username": {"admin"},
35 "password": {"wrongpass"},
36 })
37 if err != nil {
38 t.Fatalf("POST /login: %v", err)
39 }
40 body, _ := io.ReadAll(resp.Body)
41 resp.Body.Close()
42
43 if resp.StatusCode != http.StatusOK {
44 t.Errorf("bad creds: want 200 (error page), got %d", resp.StatusCode)
45 }
46 if !strings.Contains(string(body), "invalid") {
47 t.Error("login error page should mention 'invalid'")
48 }
49}
50
51func TestForgeServer_Logout_InvalidatesSession(t *testing.T) {
52 s, ts := newTestServer(t)
53 _, client := loginAsAdmin(t, s, ts)
54 setupRepoWithDisk(t, s, "myrepo", "private")
55
56 r1, err := client.Get(ts.URL + "/myrepo/issues")
57 if err != nil {
58 t.Fatalf("GET while logged in: %v", err)
59 }
60 r1.Body.Close()
61 if r1.StatusCode != http.StatusOK {
62 t.Fatalf("expected 200 while logged in, got %d", r1.StatusCode)
63 }
64
65 r2, err := client.Get(ts.URL + "/logout")
66 if err != nil {
67 t.Fatalf("GET /logout: %v", err)
68 }
69 r2.Body.Close()
70
71 r3, err := client.Get(ts.URL + "/myrepo/issues")
72 if err != nil {
73 t.Fatalf("GET after logout: %v", err)
74 }
75 r3.Body.Close()
76 if r3.StatusCode != http.StatusUnauthorized {
77 t.Errorf("after logout: want 401, got %d", r3.StatusCode)
78 }
79}
80
81func TestForgeServer_Register_OpenRegistration(t *testing.T) {
82 s, ts := newTestServerWith(t, func(cfg *Config) { cfg.Auth.Registration = "open" })
83 s.db.CreateUser("admin", "pass", true) //nolint:errcheck
84
85 jar := newCookieJar()
86 client := &http.Client{Jar: jar, CheckRedirect: func(_ *http.Request, _ []*http.Request) error {
87 return http.ErrUseLastResponse
88 }}
89
90 resp, err := client.PostForm(ts.URL+"/register", map[string][]string{
91 "username": {"newuser"},
92 "password": {"newpass"},
93 "confirm": {"newpass"},
94 })
95 if err != nil {
96 t.Fatalf("POST /register: %v", err)
97 }
98 resp.Body.Close()
99 if resp.StatusCode >= 400 {
100 t.Errorf("open registration: want redirect, got %d", resp.StatusCode)
101 }
102
103 u, _, err := s.db.GetUserByName("newuser")
104 if err != nil || u == nil {
105 t.Fatal("new user not found after registration")
106 }
107}
108
109func TestForgeServer_Register_PasswordMismatch(t *testing.T) {
110 s, ts := newTestServerWith(t, func(cfg *Config) { cfg.Auth.Registration = "open" })
111 s.db.CreateUser("admin", "pass", true) //nolint:errcheck
112
113 jar := newCookieJar()
114 client := &http.Client{Jar: jar, CheckRedirect: func(_ *http.Request, _ []*http.Request) error {
115 return http.ErrUseLastResponse
116 }}
117 resp, err := client.PostForm(ts.URL+"/register", map[string][]string{
118 "username": {"alice"},
119 "password": {"abc"},
120 "confirm": {"xyz"},
121 })
122 if err != nil {
123 t.Fatalf("POST /register: %v", err)
124 }
125 body, _ := io.ReadAll(resp.Body)
126 resp.Body.Close()
127
128 if strings.Contains(string(body), "do not match") == false {
129 u, _, _ := s.db.GetUserByName("alice")
130 if u != nil {
131 t.Error("user should not be created when passwords do not match")
132 }
133 }
134}
135
136func TestForgeServer_Register_InviteRequired(t *testing.T) {
137 s, ts := newTestServerWith(t, func(cfg *Config) { cfg.Auth.Registration = "invite" })
138 admin, _ := s.db.CreateUser("admin", "pass", true)
139
140 jar := newCookieJar()
141 client := &http.Client{Jar: jar, CheckRedirect: func(_ *http.Request, _ []*http.Request) error {
142 return http.ErrUseLastResponse
143 }}
144
145 resp, err := client.PostForm(ts.URL+"/register", map[string][]string{
146 "username": {"bob"},
147 "password": {"pass"},
148 "confirm": {"pass"},
149 })
150 if err != nil {
151 t.Fatalf("POST /register no invite: %v", err)
152 }
153 body, _ := io.ReadAll(resp.Body)
154 resp.Body.Close()
155
156 u, _, _ := s.db.GetUserByName("bob")
157 if u != nil {
158 t.Error("user should not be created without invite token")
159 }
160 _ = body
161
162 inv, _ := s.db.CreateInvite(admin.ID)
163 resp2, err := client.PostForm(ts.URL+"/register", map[string][]string{
164 "username": {"carol"},
165 "password": {"carpass"},
166 "confirm": {"carpass"},
167 "invite_token": {inv.Token},
168 })
169 if err != nil {
170 t.Fatalf("POST /register with invite: %v", err)
171 }
172 resp2.Body.Close()
173 if resp2.StatusCode >= 400 {
174 t.Errorf("invite registration: want redirect, got %d", resp2.StatusCode)
175 }
176
177 u2, _, err := s.db.GetUserByName("carol")
178 if err != nil || u2 == nil {
179 t.Fatal("user carol not found after invite registration")
180 }
181
182 got, _ := s.db.GetInvite(inv.Token)
183 if got == nil || got.UsedBy == nil {
184 t.Error("invite should be marked as used after registration")
185 }
186}
187
188func TestForgeServer_Register_DisabledByDefault(t *testing.T) {
189 s, ts := newTestServer(t)
190 s.db.CreateUser("admin", "pass", true) //nolint:errcheck
191
192 resp, err := http.Get(ts.URL + "/register")
193 if err != nil {
194 t.Fatalf("GET /register: %v", err)
195 }
196 resp.Body.Close()
197 if resp.StatusCode != http.StatusNotFound {
198 t.Errorf("want 404 (registration disabled), got %d", resp.StatusCode)
199 }
200}