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.
15 "github.com/gorilla/mux"
24 // A RequestTester represents the parameters for an HTTP request to
25 // be issued on behalf of a unit test.
26 type RequestTester struct {
33 // Test GetBlockHandler on the following situations:
34 // - permissions off, unauthenticated request, unsigned locator
35 // - permissions on, authenticated request, signed locator
36 // - permissions on, authenticated request, unsigned locator
37 // - permissions on, unauthenticated request, signed locator
38 // - permissions on, authenticated request, expired locator
40 func TestGetHandler(t *testing.T) {
43 // Prepare two test Keep volumes. Our block is stored on the second volume.
44 KeepVM = MakeTestVolumeManager(2)
45 defer func() { KeepVM.Quit() }()
47 vols := KeepVM.Volumes()
48 if err := vols[0].Put(TEST_HASH, TEST_BLOCK); err != nil {
52 // Set up a REST router for testing the handlers.
53 rest := MakeRESTRouter()
55 // Create locators for testing.
56 // Turn on permission settings so we can generate signed locators.
57 enforce_permissions = true
58 PermissionSecret = []byte(known_key)
59 permission_ttl = time.Duration(300) * time.Second
62 unsigned_locator = "http://localhost:25107/" + TEST_HASH
63 valid_timestamp = time.Now().Add(permission_ttl)
64 expired_timestamp = time.Now().Add(-time.Hour)
65 signed_locator = "http://localhost:25107/" + SignLocator(TEST_HASH, known_token, valid_timestamp)
66 expired_locator = "http://localhost:25107/" + SignLocator(TEST_HASH, known_token, expired_timestamp)
70 // Test unauthenticated request with permissions off.
71 enforce_permissions = false
73 // Unauthenticated request, unsigned locator
75 response := IssueRequest(rest,
78 uri: unsigned_locator,
81 "Unauthenticated request, unsigned locator", http.StatusOK, response)
83 "Unauthenticated request, unsigned locator",
86 received_xbs := response.Header().Get("X-Block-Size")
87 expected_xbs := fmt.Sprintf("%d", len(TEST_BLOCK))
88 if received_xbs != expected_xbs {
89 t.Errorf("expected X-Block-Size %s, got %s", expected_xbs, received_xbs)
94 enforce_permissions = true
96 // Authenticated request, signed locator
98 response = IssueRequest(rest, &RequestTester{
101 api_token: known_token,
104 "Authenticated request, signed locator", http.StatusOK, response)
106 "Authenticated request, signed locator", string(TEST_BLOCK), response)
107 received_xbs = response.Header().Get("X-Block-Size")
108 expected_xbs = fmt.Sprintf("%d", len(TEST_BLOCK))
109 if received_xbs != expected_xbs {
110 t.Errorf("expected X-Block-Size %s, got %s", expected_xbs, received_xbs)
113 // Authenticated request, unsigned locator
114 // => PermissionError
115 response = IssueRequest(rest, &RequestTester{
117 uri: unsigned_locator,
118 api_token: known_token,
120 ExpectStatusCode(t, "unsigned locator", PermissionError.HTTPCode, response)
122 // Unauthenticated request, signed locator
123 // => PermissionError
124 response = IssueRequest(rest, &RequestTester{
129 "Unauthenticated request, signed locator",
130 PermissionError.HTTPCode, response)
132 // Authenticated request, expired locator
134 response = IssueRequest(rest, &RequestTester{
136 uri: expired_locator,
137 api_token: known_token,
140 "Authenticated request, expired locator",
141 ExpiredError.HTTPCode, response)
144 // Test PutBlockHandler on the following situations:
146 // - with server key, authenticated request, unsigned locator
147 // - with server key, unauthenticated request, unsigned locator
149 func TestPutHandler(t *testing.T) {
152 // Prepare two test Keep volumes.
153 KeepVM = MakeTestVolumeManager(2)
154 defer func() { KeepVM.Quit() }()
156 // Set up a REST router for testing the handlers.
157 rest := MakeRESTRouter()
162 // Unauthenticated request, no server key
163 // => OK (unsigned response)
164 unsigned_locator := "http://localhost:25107/" + TEST_HASH
165 response := IssueRequest(rest,
168 uri: unsigned_locator,
169 request_body: TEST_BLOCK,
173 "Unauthenticated request, no server key", http.StatusOK, response)
175 "Unauthenticated request, no server key",
176 TEST_HASH_PUT_RESPONSE, response)
178 // ------------------
179 // With a server key.
181 PermissionSecret = []byte(known_key)
182 permission_ttl = time.Duration(300) * time.Second
184 // When a permission key is available, the locator returned
185 // from an authenticated PUT request will be signed.
187 // Authenticated PUT, signed locator
188 // => OK (signed response)
189 response = IssueRequest(rest,
192 uri: unsigned_locator,
193 request_body: TEST_BLOCK,
194 api_token: known_token,
198 "Authenticated PUT, signed locator, with server key",
199 http.StatusOK, response)
200 response_locator := strings.TrimSpace(response.Body.String())
201 if !VerifySignature(response_locator, known_token) {
202 t.Errorf("Authenticated PUT, signed locator, with server key:\n"+
203 "response '%s' does not contain a valid signature",
207 // Unauthenticated PUT, unsigned locator
209 response = IssueRequest(rest,
212 uri: unsigned_locator,
213 request_body: TEST_BLOCK,
217 "Unauthenticated PUT, unsigned locator, with server key",
218 http.StatusOK, response)
220 "Unauthenticated PUT, unsigned locator, with server key",
221 TEST_HASH_PUT_RESPONSE, response)
224 // Test /index requests:
225 // - enforce_permissions off | unauthenticated /index request
226 // - enforce_permissions off | unauthenticated /index/prefix request
227 // - enforce_permissions off | authenticated /index request | non-superuser
228 // - enforce_permissions off | authenticated /index/prefix request | non-superuser
229 // - enforce_permissions off | authenticated /index request | superuser
230 // - enforce_permissions off | authenticated /index/prefix request | superuser
231 // - enforce_permissions on | unauthenticated /index request
232 // - enforce_permissions on | unauthenticated /index/prefix request
233 // - enforce_permissions on | authenticated /index request | non-superuser
234 // - enforce_permissions on | authenticated /index/prefix request | non-superuser
235 // - enforce_permissions on | authenticated /index request | superuser
236 // - enforce_permissions on | authenticated /index/prefix request | superuser
238 // The only /index requests that should succeed are those issued by the
239 // superuser when enforce_permissions = true.
241 func TestIndexHandler(t *testing.T) {
244 // Set up Keep volumes and populate them.
245 // Include multiple blocks on different volumes, and
246 // some metadata files (which should be omitted from index listings)
247 KeepVM = MakeTestVolumeManager(2)
248 defer func() { KeepVM.Quit() }()
250 vols := KeepVM.Volumes()
251 vols[0].Put(TEST_HASH, TEST_BLOCK)
252 vols[1].Put(TEST_HASH_2, TEST_BLOCK_2)
253 vols[0].Put(TEST_HASH+".meta", []byte("metadata"))
254 vols[1].Put(TEST_HASH_2+".meta", []byte("metadata"))
256 // Set up a REST router for testing the handlers.
257 rest := MakeRESTRouter()
259 data_manager_token = "DATA MANAGER TOKEN"
261 unauthenticated_req := &RequestTester{
263 uri: "http://localhost:25107/index",
265 authenticated_req := &RequestTester{
267 uri: "http://localhost:25107/index",
268 api_token: known_token,
270 superuser_req := &RequestTester{
272 uri: "http://localhost:25107/index",
273 api_token: data_manager_token,
275 unauth_prefix_req := &RequestTester{
277 uri: "http://localhost:25107/index/" + TEST_HASH[0:3],
279 auth_prefix_req := &RequestTester{
281 uri: "http://localhost:25107/index/" + TEST_HASH[0:3],
282 api_token: known_token,
284 superuser_prefix_req := &RequestTester{
286 uri: "http://localhost:25107/index/" + TEST_HASH[0:3],
287 api_token: data_manager_token,
290 // ----------------------------
291 // enforce_permissions disabled
292 // All /index requests should fail.
293 enforce_permissions = false
295 // unauthenticated /index request
296 // => PermissionError
297 response := IssueRequest(rest, unauthenticated_req)
299 "enforce_permissions off, unauthenticated request",
300 PermissionError.HTTPCode,
303 // unauthenticated /index/prefix request
304 // => PermissionError
305 response = IssueRequest(rest, unauth_prefix_req)
307 "enforce_permissions off, unauthenticated /index/prefix request",
308 PermissionError.HTTPCode,
311 // authenticated /index request, non-superuser
312 // => PermissionError
313 response = IssueRequest(rest, authenticated_req)
315 "enforce_permissions off, authenticated request, non-superuser",
316 PermissionError.HTTPCode,
319 // authenticated /index/prefix request, non-superuser
320 // => PermissionError
321 response = IssueRequest(rest, auth_prefix_req)
323 "enforce_permissions off, authenticated /index/prefix request, non-superuser",
324 PermissionError.HTTPCode,
327 // authenticated /index request, superuser
328 // => PermissionError
329 response = IssueRequest(rest, superuser_req)
331 "enforce_permissions off, superuser request",
332 PermissionError.HTTPCode,
335 // superuser /index/prefix request
336 // => PermissionError
337 response = IssueRequest(rest, superuser_prefix_req)
339 "enforce_permissions off, superuser /index/prefix request",
340 PermissionError.HTTPCode,
343 // ---------------------------
344 // enforce_permissions enabled
345 // Only the superuser should be allowed to issue /index requests.
346 enforce_permissions = true
348 // unauthenticated /index request
349 // => PermissionError
350 response = IssueRequest(rest, unauthenticated_req)
352 "enforce_permissions on, unauthenticated request",
353 PermissionError.HTTPCode,
356 // unauthenticated /index/prefix request
357 // => PermissionError
358 response = IssueRequest(rest, unauth_prefix_req)
360 "permissions on, unauthenticated /index/prefix request",
361 PermissionError.HTTPCode,
364 // authenticated /index request, non-superuser
365 // => PermissionError
366 response = IssueRequest(rest, authenticated_req)
368 "permissions on, authenticated request, non-superuser",
369 PermissionError.HTTPCode,
372 // authenticated /index/prefix request, non-superuser
373 // => PermissionError
374 response = IssueRequest(rest, auth_prefix_req)
376 "permissions on, authenticated /index/prefix request, non-superuser",
377 PermissionError.HTTPCode,
380 // superuser /index request
382 response = IssueRequest(rest, superuser_req)
384 "permissions on, superuser request",
388 expected := `^` + TEST_HASH + `\+\d+ \d+\n` +
389 TEST_HASH_2 + `\+\d+ \d+\n$`
390 match, _ := regexp.MatchString(expected, response.Body.String())
393 "permissions on, superuser request: expected %s, got:\n%s",
394 expected, response.Body.String())
397 // superuser /index/prefix request
399 response = IssueRequest(rest, superuser_prefix_req)
401 "permissions on, superuser request",
405 expected = `^` + TEST_HASH + `\+\d+ \d+\n$`
406 match, _ = regexp.MatchString(expected, response.Body.String())
409 "permissions on, superuser /index/prefix request: expected %s, got:\n%s",
410 expected, response.Body.String())
414 // ====================
416 // ====================
418 // IssueTestRequest executes an HTTP request described by rt, to a
419 // specified REST router. It returns the HTTP response to the request.
420 func IssueRequest(router *mux.Router, rt *RequestTester) *httptest.ResponseRecorder {
421 response := httptest.NewRecorder()
422 body := bytes.NewReader(rt.request_body)
423 req, _ := http.NewRequest(rt.method, rt.uri, body)
424 if rt.api_token != "" {
425 req.Header.Set("Authorization", "OAuth2 "+rt.api_token)
427 router.ServeHTTP(response, req)
431 // ExpectStatusCode checks whether a response has the specified status code,
432 // and reports a test failure if not.
433 func ExpectStatusCode(
437 response *httptest.ResponseRecorder) {
438 if response.Code != expected_status {
439 t.Errorf("%s: expected status %s, got %+v",
440 testname, expected_status, response)
447 expected_body string,
448 response *httptest.ResponseRecorder) {
449 if response.Body.String() != expected_body {
450 t.Errorf("%s: expected response body '%s', got %+v",
451 testname, expected_body, response)