1 // Tests for Keep HTTP handlers:
7 // The HTTP handlers are responsible for enforcing permission policy,
8 // so these tests must exercise all possible permission permutations.
14 "github.com/gorilla/mux"
22 // A RequestTester represents the parameters for an HTTP request to
23 // be issued on behalf of a unit test.
24 type RequestTester struct {
31 // Test GetBlockHandler on the following situations:
32 // - permissions off, unauthenticated request, unsigned locator
33 // - permissions on, authenticated request, signed locator
34 // - permissions on, authenticated request, unsigned locator
35 // - permissions on, unauthenticated request, signed locator
36 // - permissions on, authenticated request, expired locator
38 func TestGetHandler(t *testing.T) {
41 // Prepare two test Keep volumes. Our block is stored on the second volume.
42 KeepVM = MakeTestVolumeManager(2)
43 defer func() { KeepVM.Quit() }()
45 vols := KeepVM.Volumes()
46 if err := vols[0].Put(TEST_HASH, TEST_BLOCK); err != nil {
50 // Set up a REST router for testing the handlers.
51 rest := MakeRESTRouter()
53 // Create locators for testing.
54 // Turn on permission settings so we can generate signed locators.
55 enforce_permissions = true
56 PermissionSecret = []byte(known_key)
57 permission_ttl = time.Duration(300) * time.Second
60 unsigned_locator = "http://localhost:25107/" + TEST_HASH
61 valid_timestamp = time.Now().Add(permission_ttl)
62 expired_timestamp = time.Now().Add(-time.Hour)
63 signed_locator = "http://localhost:25107/" + SignLocator(TEST_HASH, known_token, valid_timestamp)
64 expired_locator = "http://localhost:25107/" + SignLocator(TEST_HASH, known_token, expired_timestamp)
68 // Test unauthenticated request with permissions off.
69 enforce_permissions = false
71 // Unauthenticated request, unsigned locator
73 response := IssueRequest(rest,
76 uri: unsigned_locator,
79 "Unauthenticated request, unsigned locator", http.StatusOK, response)
81 "Unauthenticated request, unsigned locator",
87 enforce_permissions = true
89 // Authenticated request, signed locator
91 response = IssueRequest(rest, &RequestTester{
94 api_token: known_token,
97 "Authenticated request, signed locator", http.StatusOK, response)
99 "Authenticated request, signed locator", string(TEST_BLOCK), response)
101 // Authenticated request, unsigned locator
102 // => PermissionError
103 response = IssueRequest(rest, &RequestTester{
105 uri: unsigned_locator,
106 api_token: known_token,
108 ExpectStatusCode(t, "unsigned locator", PermissionError.HTTPCode, response)
110 // Unauthenticated request, signed locator
111 // => PermissionError
112 response = IssueRequest(rest, &RequestTester{
117 "Unauthenticated request, signed locator",
118 PermissionError.HTTPCode, response)
120 // Authenticated request, expired locator
122 response = IssueRequest(rest, &RequestTester{
124 uri: expired_locator,
125 api_token: known_token,
128 "Authenticated request, expired locator",
129 ExpiredError.HTTPCode, response)
132 // Test PutBlockHandler on the following situations:
134 // - with server key, authenticated request, unsigned locator
135 // - with server key, unauthenticated request, unsigned locator
137 func TestPutHandler(t *testing.T) {
140 // Prepare two test Keep volumes.
141 KeepVM = MakeTestVolumeManager(2)
142 defer func() { KeepVM.Quit() }()
144 // Set up a REST router for testing the handlers.
145 rest := MakeRESTRouter()
150 // Unauthenticated request, no server key
151 // => OK (unsigned response)
152 unsigned_locator := "http://localhost:25107/" + TEST_HASH
153 response := IssueRequest(rest,
156 uri: unsigned_locator,
157 request_body: TEST_BLOCK,
161 "Unauthenticated request, no server key", http.StatusOK, response)
162 ExpectBody(t, "Unauthenticated request, no server key", TEST_HASH, response)
164 // ------------------
165 // With a server key.
167 PermissionSecret = []byte(known_key)
168 permission_ttl = time.Duration(300) * time.Second
170 // When a permission key is available, the locator returned
171 // from an authenticated PUT request will be signed.
173 // Authenticated PUT, signed locator
174 // => OK (signed response)
175 response = IssueRequest(rest,
178 uri: unsigned_locator,
179 request_body: TEST_BLOCK,
180 api_token: known_token,
184 "Authenticated PUT, signed locator, with server key",
185 http.StatusOK, response)
186 if !VerifySignature(response.Body.String(), known_token) {
187 t.Errorf("Authenticated PUT, signed locator, with server key:\n"+
188 "response '%s' does not contain a valid signature",
189 response.Body.String())
192 // Unauthenticated PUT, unsigned locator
194 response = IssueRequest(rest,
197 uri: unsigned_locator,
198 request_body: TEST_BLOCK,
202 "Unauthenticated PUT, unsigned locator, with server key",
203 http.StatusOK, response)
205 "Unauthenticated PUT, unsigned locator, with server key",
209 // Test /index requests:
210 // - enforce_permissions off | unauthenticated /index request
211 // - enforce_permissions off | unauthenticated /index/prefix request
212 // - enforce_permissions off | authenticated /index request | non-superuser
213 // - enforce_permissions off | authenticated /index/prefix request | non-superuser
214 // - enforce_permissions off | authenticated /index request | superuser
215 // - enforce_permissions off | authenticated /index/prefix request | superuser
216 // - enforce_permissions on | unauthenticated /index request
217 // - enforce_permissions on | unauthenticated /index/prefix request
218 // - enforce_permissions on | authenticated /index request | non-superuser
219 // - enforce_permissions on | authenticated /index/prefix request | non-superuser
220 // - enforce_permissions on | authenticated /index request | superuser
221 // - enforce_permissions on | authenticated /index/prefix request | superuser
223 // The only /index requests that should succeed are those issued by the
224 // superuser when enforce_permissions = true.
226 func TestIndexHandler(t *testing.T) {
229 // Set up Keep volumes and populate them.
230 // Include multiple blocks on different volumes, and
231 // some metadata files (which should be omitted from index listings)
232 KeepVM = MakeTestVolumeManager(2)
233 defer func() { KeepVM.Quit() }()
235 vols := KeepVM.Volumes()
236 vols[0].Put(TEST_HASH, TEST_BLOCK)
237 vols[1].Put(TEST_HASH_2, TEST_BLOCK_2)
238 vols[0].Put(TEST_HASH+".meta", []byte("metadata"))
239 vols[1].Put(TEST_HASH_2+".meta", []byte("metadata"))
241 // Set up a REST router for testing the handlers.
242 rest := MakeRESTRouter()
244 data_manager_token = "DATA MANAGER TOKEN"
246 unauthenticated_req := &RequestTester{
248 uri: "http://localhost:25107/index",
250 authenticated_req := &RequestTester{
252 uri: "http://localhost:25107/index",
253 api_token: known_token,
255 superuser_req := &RequestTester{
257 uri: "http://localhost:25107/index",
258 api_token: data_manager_token,
260 unauth_prefix_req := &RequestTester{
262 uri: "http://localhost:25107/index/" + TEST_HASH[0:3],
264 auth_prefix_req := &RequestTester{
266 uri: "http://localhost:25107/index/" + TEST_HASH[0:3],
267 api_token: known_token,
269 superuser_prefix_req := &RequestTester{
271 uri: "http://localhost:25107/index/" + TEST_HASH[0:3],
272 api_token: data_manager_token,
275 // ----------------------------
276 // enforce_permissions disabled
277 // All /index requests should fail.
278 enforce_permissions = false
280 // unauthenticated /index request
281 // => PermissionError
282 response := IssueRequest(rest, unauthenticated_req)
284 "enforce_permissions off, unauthenticated request",
285 PermissionError.HTTPCode,
288 // unauthenticated /index/prefix request
289 // => PermissionError
290 response = IssueRequest(rest, unauth_prefix_req)
292 "enforce_permissions off, unauthenticated /index/prefix request",
293 PermissionError.HTTPCode,
296 // authenticated /index request, non-superuser
297 // => PermissionError
298 response = IssueRequest(rest, authenticated_req)
300 "enforce_permissions off, authenticated request, non-superuser",
301 PermissionError.HTTPCode,
304 // authenticated /index/prefix request, non-superuser
305 // => PermissionError
306 response = IssueRequest(rest, auth_prefix_req)
308 "enforce_permissions off, authenticated /index/prefix request, non-superuser",
309 PermissionError.HTTPCode,
312 // authenticated /index request, superuser
313 // => PermissionError
314 response = IssueRequest(rest, superuser_req)
316 "enforce_permissions off, superuser request",
317 PermissionError.HTTPCode,
320 // superuser /index/prefix request
321 // => PermissionError
322 response = IssueRequest(rest, superuser_prefix_req)
324 "enforce_permissions off, superuser /index/prefix request",
325 PermissionError.HTTPCode,
328 // ---------------------------
329 // enforce_permissions enabled
330 // Only the superuser should be allowed to issue /index requests.
331 enforce_permissions = true
333 // unauthenticated /index request
334 // => PermissionError
335 response = IssueRequest(rest, unauthenticated_req)
337 "enforce_permissions on, unauthenticated request",
338 PermissionError.HTTPCode,
341 // unauthenticated /index/prefix request
342 // => PermissionError
343 response = IssueRequest(rest, unauth_prefix_req)
345 "permissions on, unauthenticated /index/prefix request",
346 PermissionError.HTTPCode,
349 // authenticated /index request, non-superuser
350 // => PermissionError
351 response = IssueRequest(rest, authenticated_req)
353 "permissions on, authenticated request, non-superuser",
354 PermissionError.HTTPCode,
357 // authenticated /index/prefix request, non-superuser
358 // => PermissionError
359 response = IssueRequest(rest, auth_prefix_req)
361 "permissions on, authenticated /index/prefix request, non-superuser",
362 PermissionError.HTTPCode,
365 // superuser /index request
367 response = IssueRequest(rest, superuser_req)
369 "permissions on, superuser request",
373 expected := `^` + TEST_HASH + `\+\d+ \d+\n` +
374 TEST_HASH_2 + `\+\d+ \d+\n$`
375 match, _ := regexp.MatchString(expected, response.Body.String())
378 "permissions on, superuser request: expected %s, got:\n%s",
379 expected, response.Body.String())
382 // superuser /index/prefix request
384 response = IssueRequest(rest, superuser_prefix_req)
386 "permissions on, superuser request",
390 expected = `^` + TEST_HASH + `\+\d+ \d+\n$`
391 match, _ = regexp.MatchString(expected, response.Body.String())
394 "permissions on, superuser /index/prefix request: expected %s, got:\n%s",
395 expected, response.Body.String())
399 // ====================
401 // ====================
403 // IssueTestRequest executes an HTTP request described by rt, to a
404 // specified REST router. It returns the HTTP response to the request.
405 func IssueRequest(router *mux.Router, rt *RequestTester) *httptest.ResponseRecorder {
406 response := httptest.NewRecorder()
407 body := bytes.NewReader(rt.request_body)
408 req, _ := http.NewRequest(rt.method, rt.uri, body)
409 if rt.api_token != "" {
410 req.Header.Set("Authorization", "OAuth "+rt.api_token)
412 router.ServeHTTP(response, req)
416 // ExpectStatusCode checks whether a response has the specified status code,
417 // and reports a test failure if not.
418 func ExpectStatusCode(
422 response *httptest.ResponseRecorder) {
423 if response.Code != expected_status {
424 t.Errorf("%s: expected status %s, got %+v",
425 testname, expected_status, response)
432 expected_body string,
433 response *httptest.ResponseRecorder) {
434 if response.Body.String() != expected_body {
435 t.Errorf("%s: expected response body '%s', got %+v",
436 testname, expected_body, response)