Added handler_test.go. (refs #2328)
[arvados.git] / services / keep / src / keep / handler_test.go
1 // Tests for Keep HTTP handlers:
2 //
3 //     GetBlockHandler
4 //     PutBlockHandler
5 //     IndexHandler
6 //
7 // The HTTP handlers are responsible for enforcing permission policy,
8 // so these tests must exercise all possible permission permutations.
9
10 package main
11
12 import (
13         "bytes"
14         "net/http"
15         "net/http/httptest"
16         "regexp"
17         "testing"
18         "time"
19 )
20
21 func TestGetHandler(t *testing.T) {
22         defer teardown()
23
24         // Prepare two test Keep volumes. Our block is stored on the second volume.
25         KeepVM = MakeTestVolumeManager(2)
26         defer func() { KeepVM.Quit() }()
27
28         vols := KeepVM.Volumes()
29         if err := vols[0].Put(TEST_HASH, TEST_BLOCK); err != nil {
30                 t.Error(err)
31         }
32
33         // Set up a REST router for testing the handlers.
34         rest := NewRESTRouter()
35
36         // Test an unsigned GET request.
37         test_url := "http://localhost:25107/" + TEST_HASH
38         req, _ := http.NewRequest("GET", test_url, nil)
39         resp := httptest.NewRecorder()
40         rest.ServeHTTP(resp, req)
41
42         if resp.Code != 200 {
43                 t.Errorf("bad response code: %v", resp)
44         }
45         if bytes.Compare(resp.Body.Bytes(), TEST_BLOCK) != 0 {
46                 t.Errorf("bad response body: %v", resp)
47         }
48
49         // Enable permissions.
50         enforce_permissions = true
51         PermissionSecret = []byte(known_key)
52         permission_ttl = 300
53         expiry := time.Now().Add(time.Duration(permission_ttl) * time.Second)
54
55         // Test GET with a signed locator.
56         test_url = "http://localhost:25107/" + SignLocator(TEST_HASH, known_token, expiry)
57         resp = httptest.NewRecorder()
58         req, _ = http.NewRequest("GET", test_url, nil)
59         req.Header.Set("Authorization", "OAuth "+known_token)
60         rest.ServeHTTP(resp, req)
61
62         if resp.Code != 200 {
63                 t.Errorf("signed request: bad response code: %v", resp)
64         }
65         if bytes.Compare(resp.Body.Bytes(), TEST_BLOCK) != 0 {
66                 t.Errorf("signed request: bad response body: %v", resp)
67         }
68
69         // Test GET with an unsigned locator.
70         test_url = "http://localhost:25107/" + TEST_HASH
71         resp = httptest.NewRecorder()
72         req, _ = http.NewRequest("GET", test_url, nil)
73         req.Header.Set("Authorization", "OAuth "+known_token)
74         rest.ServeHTTP(resp, req)
75
76         if resp.Code != PermissionError.HTTPCode {
77                 t.Errorf("unsigned request: bad response code: %v", resp)
78         }
79
80         // Test GET with a signed locator and an unauthenticated request.
81         test_url = "http://localhost:25107/" + SignLocator(TEST_HASH, known_token, expiry)
82         resp = httptest.NewRecorder()
83         req, _ = http.NewRequest("GET", test_url, nil)
84         rest.ServeHTTP(resp, req)
85
86         if resp.Code != PermissionError.HTTPCode {
87                 t.Errorf("signed locator, unauthenticated request: bad response code: %v", resp)
88         }
89
90         // Test GET with an expired, signed locator.
91         expired_ts := time.Now().Add(-time.Hour)
92         test_url = "http://localhost:25107/" + SignLocator(TEST_HASH, known_token, expired_ts)
93         resp = httptest.NewRecorder()
94         req, _ = http.NewRequest("GET", test_url, nil)
95         req.Header.Set("Authorization", "OAuth "+known_token)
96         rest.ServeHTTP(resp, req)
97
98         if resp.Code != ExpiredError.HTTPCode {
99                 t.Errorf("expired signature: bad response code: %v", resp)
100         }
101 }
102
103 func TestPutHandler(t *testing.T) {
104         defer teardown()
105
106         // Prepare two test Keep volumes.
107         KeepVM = MakeTestVolumeManager(2)
108         defer func() { KeepVM.Quit() }()
109
110         // Set up a REST router for testing the handlers.
111         rest := NewRESTRouter()
112
113         // Execute a PUT request.
114         test_url := "http://localhost:25107/" + TEST_HASH
115         test_body := bytes.NewReader(TEST_BLOCK)
116         req, _ := http.NewRequest("PUT", test_url, test_body)
117         resp := httptest.NewRecorder()
118         rest.ServeHTTP(resp, req)
119
120         if resp.Code != 200 {
121                 t.Errorf("bad response code: %v", resp)
122         }
123         if resp.Body.String() != TEST_HASH {
124                 t.Errorf("bad response body: %v", resp)
125         }
126
127         // Add a permission key.
128         // When a permission key is available, the locator returned
129         // from a PUT request will be signed.
130         PermissionSecret = []byte(known_key)
131
132         // An authenticated PUT request returns a signed locator.
133         test_url = "http://localhost:25107/" + TEST_HASH
134         test_body = bytes.NewReader(TEST_BLOCK)
135         req, _ = http.NewRequest("PUT", test_url, test_body)
136         req.Header.Set("Authorization", "OAuth "+known_token)
137         resp = httptest.NewRecorder()
138         rest.ServeHTTP(resp, req)
139
140         if resp.Code != 200 {
141                 t.Errorf("bad response code: %v", resp)
142         }
143         if !VerifySignature(resp.Body.String(), known_token) {
144                 t.Errorf("bad response body: %v", resp)
145         }
146
147         // An unauthenticated PUT request returns an unsigned locator
148         // even when a permission key is available.
149         test_url = "http://localhost:25107/" + TEST_HASH
150         test_body = bytes.NewReader(TEST_BLOCK)
151         req, _ = http.NewRequest("PUT", test_url, test_body)
152         resp = httptest.NewRecorder()
153         rest.ServeHTTP(resp, req)
154
155         if resp.Code != 200 {
156                 t.Errorf("bad response code: %v", resp)
157         }
158         if resp.Body.String() != TEST_HASH {
159                 t.Errorf("bad response body: %v", resp)
160         }
161 }
162
163 func TestIndexHandler(t *testing.T) {
164         defer teardown()
165
166         // Set up Keep volumes and populate them.
167         // Include multiple blocks on different volumes, and
168         // some metadata files.
169         KeepVM = MakeTestVolumeManager(2)
170         defer func() { KeepVM.Quit() }()
171
172         vols := KeepVM.Volumes()
173         vols[0].Put(TEST_HASH, TEST_BLOCK)
174         vols[1].Put(TEST_HASH_2, TEST_BLOCK_2)
175
176         // Set up a REST router for testing the handlers.
177         rest := NewRESTRouter()
178
179         // Requests for /index with a prefix are okay even if unauthenticated.
180         test_url := "http://localhost:25107/index/" + TEST_HASH[0:5]
181         req, _ := http.NewRequest("GET", test_url, nil)
182         resp := httptest.NewRecorder()
183         rest.ServeHTTP(resp, req)
184
185         expected := `^` + TEST_HASH + `\+\d+ \d+\n$`
186         match, _ := regexp.MatchString(expected, resp.Body.String())
187         if !match {
188                 t.Errorf("IndexHandler returned:\n%s", resp.Body.String())
189         }
190
191         // Unauthenticated /index requests: fail.
192         test_url = "http://localhost:25107/index"
193         req, _ = http.NewRequest("GET", test_url, nil)
194         resp = httptest.NewRecorder()
195         rest.ServeHTTP(resp, req)
196
197         if resp.Code != PermissionError.HTTPCode {
198                 t.Errorf("unauthenticated /index: %+v", resp)
199         }
200
201         // Authenticated /index requests by a non-superuser: also fail.
202         test_url = "http://localhost:25107/index"
203         req, _ = http.NewRequest("GET", test_url, nil)
204         req.Header.Set("Authorization", "OAuth "+known_token)
205         resp = httptest.NewRecorder()
206         rest.ServeHTTP(resp, req)
207
208         if resp.Code != PermissionError.HTTPCode {
209                 t.Errorf("authenticated /index: %+v", resp)
210         }
211
212         // Even superuser /index requests fail if enforce_permissions is off!
213         enforce_permissions = false
214         data_manager_token = "DATA MANAGER TOKEN"
215         test_url = "http://localhost:25107/index"
216         req, _ = http.NewRequest("GET", test_url, nil)
217         req.Header.Set("Authorization", "OAuth "+data_manager_token)
218         resp = httptest.NewRecorder()
219         rest.ServeHTTP(resp, req)
220
221         if resp.Code != PermissionError.HTTPCode {
222                 t.Errorf("superuser /index (permissions off): %+v", resp)
223         }
224
225         // Superuser /index requests with enforce_permissions set: succeed!
226         enforce_permissions = true
227         data_manager_token = "DATA MANAGER TOKEN"
228         test_url = "http://localhost:25107/index"
229         req, _ = http.NewRequest("GET", test_url, nil)
230         req.Header.Set("Authorization", "OAuth "+data_manager_token)
231         resp = httptest.NewRecorder()
232         rest.ServeHTTP(resp, req)
233
234         if resp.Code != http.StatusOK {
235                 t.Errorf("superuser /index: %+v", resp)
236         }
237         expected = `^` + TEST_HASH + `\+\d+ \d+\n` +
238                 TEST_HASH_2 + `\+\d+ \d+\n$`
239         match, _ = regexp.MatchString(expected, resp.Body.String())
240         if !match {
241                 t.Errorf("superuser /index:\n%s", resp.Body.String())
242         }
243 }