##// END OF EJS Templates
test /files/ gives 403 on hidden files
MinRK -
Show More
@@ -0,0 +1,52 b''
1 # coding: utf-8
2 """Test the /files/ handler."""
3
4 import io
5 import os
6 from unicodedata import normalize
7
8 pjoin = os.path.join
9
10 import requests
11
12 from IPython.html.utils import url_path_join
13 from .launchnotebook import NotebookTestBase
14 from IPython.utils import py3compat
15
16 class FilesTest(NotebookTestBase):
17 def test_hidden_files(self):
18 not_hidden = [
19 u'Γ₯ b',
20 pjoin(u'Γ₯ b/Γ§. d')
21 ]
22 hidden = [
23 u'.Γ₯ b',
24 pjoin(u'Γ₯ b/.Γ§ d')
25 ]
26 dirs = not_hidden + hidden
27
28 nbdir = self.notebook_dir.name
29 for d in dirs:
30 path = pjoin(nbdir, d.replace('/', os.path.sep))
31 if not os.path.exists(path):
32 os.mkdir(path)
33 with io.open(pjoin(path, 'foo'), 'w', encoding='utf8') as f:
34 f.write(path)
35 with io.open(pjoin(path, '.foo'), 'w', encoding='utf8') as f:
36 f.write(path + '.foo')
37 url = self.base_url()
38
39 for d in not_hidden:
40 path = pjoin(nbdir, d.replace('/', os.path.sep))
41 r = requests.get(url_path_join(url, 'files', d, 'foo'))
42 r.raise_for_status()
43 reply = py3compat.cast_unicode(r.content)
44 self.assertEqual(normalize('NFC', path), normalize('NFC', reply))
45 r = requests.get(url_path_join(url, 'files', d, '.foo'))
46 self.assertEqual(r.status_code, 403)
47
48 for d in hidden:
49 path = pjoin(nbdir, d.replace('/', os.path.sep))
50 for foo in ('foo', '.foo'):
51 r = requests.get(url_path_join(url, 'files', d, foo))
52 self.assertEqual(r.status_code, 403)
@@ -1,312 +1,314 b''
1 1 # coding: utf-8
2 2 """Test the notebooks webservice API."""
3 3
4 4 import io
5 5 import json
6 6 import os
7 7 import shutil
8 8 from unicodedata import normalize
9 9
10 10 pjoin = os.path.join
11 11
12 12 import requests
13 13
14 14 from IPython.html.utils import url_path_join, url_escape
15 15 from IPython.html.tests.launchnotebook import NotebookTestBase, assert_http_error
16 16 from IPython.nbformat import current
17 17 from IPython.nbformat.current import (new_notebook, write, read, new_worksheet,
18 18 new_heading_cell, to_notebook_json)
19 19 from IPython.nbformat import v2
20 20 from IPython.utils import py3compat
21 21 from IPython.utils.data import uniq_stable
22 22
23 23
24 24 class NBAPI(object):
25 25 """Wrapper for notebook API calls."""
26 26 def __init__(self, base_url):
27 27 self.base_url = base_url
28 28
29 29 def _req(self, verb, path, body=None):
30 30 response = requests.request(verb,
31 31 url_path_join(self.base_url, 'api/notebooks', path),
32 32 data=body,
33 33 )
34 34 response.raise_for_status()
35 35 return response
36 36
37 37 def list(self, path='/'):
38 38 return self._req('GET', path)
39 39
40 40 def read(self, name, path='/'):
41 41 return self._req('GET', url_path_join(path, name))
42 42
43 43 def create_untitled(self, path='/'):
44 44 return self._req('POST', path)
45 45
46 46 def upload_untitled(self, body, path='/'):
47 47 return self._req('POST', path, body)
48 48
49 49 def copy_untitled(self, copy_from, path='/'):
50 50 body = json.dumps({'copy_from':copy_from})
51 51 return self._req('POST', path, body)
52 52
53 53 def create(self, name, path='/'):
54 54 return self._req('PUT', url_path_join(path, name))
55 55
56 56 def upload(self, name, body, path='/'):
57 57 return self._req('PUT', url_path_join(path, name), body)
58 58
59 59 def copy(self, copy_from, copy_to, path='/'):
60 60 body = json.dumps({'copy_from':copy_from})
61 61 return self._req('PUT', url_path_join(path, copy_to), body)
62 62
63 63 def save(self, name, body, path='/'):
64 64 return self._req('PUT', url_path_join(path, name), body)
65 65
66 66 def delete(self, name, path='/'):
67 67 return self._req('DELETE', url_path_join(path, name))
68 68
69 69 def rename(self, name, path, new_name):
70 70 body = json.dumps({'name': new_name})
71 71 return self._req('PATCH', url_path_join(path, name), body)
72 72
73 73 def get_checkpoints(self, name, path):
74 74 return self._req('GET', url_path_join(path, name, 'checkpoints'))
75 75
76 76 def new_checkpoint(self, name, path):
77 77 return self._req('POST', url_path_join(path, name, 'checkpoints'))
78 78
79 79 def restore_checkpoint(self, name, path, checkpoint_id):
80 80 return self._req('POST', url_path_join(path, name, 'checkpoints', checkpoint_id))
81 81
82 82 def delete_checkpoint(self, name, path, checkpoint_id):
83 83 return self._req('DELETE', url_path_join(path, name, 'checkpoints', checkpoint_id))
84 84
85 85 class APITest(NotebookTestBase):
86 86 """Test the kernels web service API"""
87 87 dirs_nbs = [('', 'inroot'),
88 88 ('Directory with spaces in', 'inspace'),
89 89 (u'unicodΓ©', 'innonascii'),
90 90 ('foo', 'a'),
91 91 ('foo', 'b'),
92 92 ('foo', 'name with spaces'),
93 93 ('foo', u'unicodΓ©'),
94 94 ('foo/bar', 'baz'),
95 95 (u'Γ₯ b', u'Γ§ d')
96 96 ]
97 97
98 98 dirs = uniq_stable([d for (d,n) in dirs_nbs])
99 99 del dirs[0] # remove ''
100 100
101 101 def setUp(self):
102 102 nbdir = self.notebook_dir.name
103 103
104 104 for d in self.dirs:
105 d.replace('/', os.path.sep)
105 106 if not os.path.isdir(pjoin(nbdir, d)):
106 107 os.mkdir(pjoin(nbdir, d))
107 108
108 109 for d, name in self.dirs_nbs:
110 d = d.replace('/', os.path.sep)
109 111 with io.open(pjoin(nbdir, d, '%s.ipynb' % name), 'w') as f:
110 112 nb = new_notebook(name=name)
111 113 write(nb, f, format='ipynb')
112 114
113 115 self.nb_api = NBAPI(self.base_url())
114 116
115 117 def tearDown(self):
116 118 nbdir = self.notebook_dir.name
117 119
118 120 for dname in ['foo', 'Directory with spaces in', u'unicodΓ©', u'Γ₯ b']:
119 121 shutil.rmtree(pjoin(nbdir, dname), ignore_errors=True)
120 122
121 123 if os.path.isfile(pjoin(nbdir, 'inroot.ipynb')):
122 124 os.unlink(pjoin(nbdir, 'inroot.ipynb'))
123 125
124 126 def test_list_notebooks(self):
125 127 nbs = self.nb_api.list().json()
126 128 self.assertEqual(len(nbs), 1)
127 129 self.assertEqual(nbs[0]['name'], 'inroot.ipynb')
128 130
129 131 nbs = self.nb_api.list('/Directory with spaces in/').json()
130 132 self.assertEqual(len(nbs), 1)
131 133 self.assertEqual(nbs[0]['name'], 'inspace.ipynb')
132 134
133 135 nbs = self.nb_api.list(u'/unicodΓ©/').json()
134 136 self.assertEqual(len(nbs), 1)
135 137 self.assertEqual(nbs[0]['name'], 'innonascii.ipynb')
136 138 self.assertEqual(nbs[0]['path'], u'unicodΓ©')
137 139
138 140 nbs = self.nb_api.list('/foo/bar/').json()
139 141 self.assertEqual(len(nbs), 1)
140 142 self.assertEqual(nbs[0]['name'], 'baz.ipynb')
141 143 self.assertEqual(nbs[0]['path'], 'foo/bar')
142 144
143 145 nbs = self.nb_api.list('foo').json()
144 146 self.assertEqual(len(nbs), 4)
145 147 nbnames = { normalize('NFC', n['name']) for n in nbs }
146 148 expected = [ u'a.ipynb', u'b.ipynb', u'name with spaces.ipynb', u'unicodΓ©.ipynb']
147 149 expected = { normalize('NFC', name) for name in expected }
148 150 self.assertEqual(nbnames, expected)
149 151
150 152 def test_list_nonexistant_dir(self):
151 153 with assert_http_error(404):
152 154 self.nb_api.list('nonexistant')
153 155
154 156 def test_get_contents(self):
155 157 for d, name in self.dirs_nbs:
156 158 nb = self.nb_api.read('%s.ipynb' % name, d+'/').json()
157 159 self.assertEqual(nb['name'], u'%s.ipynb' % name)
158 160 self.assertIn('content', nb)
159 161 self.assertIn('metadata', nb['content'])
160 162 self.assertIsInstance(nb['content']['metadata'], dict)
161 163
162 164 # Name that doesn't exist - should be a 404
163 165 with assert_http_error(404):
164 166 self.nb_api.read('q.ipynb', 'foo')
165 167
166 168 def _check_nb_created(self, resp, name, path):
167 169 self.assertEqual(resp.status_code, 201)
168 170 location_header = py3compat.str_to_unicode(resp.headers['Location'])
169 171 self.assertEqual(location_header, url_escape(url_path_join(u'/api/notebooks', path, name)))
170 172 self.assertEqual(resp.json()['name'], name)
171 173 assert os.path.isfile(pjoin(self.notebook_dir.name, path, name))
172 174
173 175 def test_create_untitled(self):
174 176 resp = self.nb_api.create_untitled(path=u'Γ₯ b')
175 177 self._check_nb_created(resp, 'Untitled0.ipynb', u'Γ₯ b')
176 178
177 179 # Second time
178 180 resp = self.nb_api.create_untitled(path=u'Γ₯ b')
179 181 self._check_nb_created(resp, 'Untitled1.ipynb', u'Γ₯ b')
180 182
181 183 # And two directories down
182 184 resp = self.nb_api.create_untitled(path='foo/bar')
183 185 self._check_nb_created(resp, 'Untitled0.ipynb', pjoin('foo', 'bar'))
184 186
185 187 def test_upload_untitled(self):
186 188 nb = new_notebook(name='Upload test')
187 189 nbmodel = {'content': nb}
188 190 resp = self.nb_api.upload_untitled(path=u'Γ₯ b',
189 191 body=json.dumps(nbmodel))
190 192 self._check_nb_created(resp, 'Untitled0.ipynb', u'Γ₯ b')
191 193
192 194 def test_upload(self):
193 195 nb = new_notebook(name=u'ignored')
194 196 nbmodel = {'content': nb}
195 197 resp = self.nb_api.upload(u'Upload tΓ©st.ipynb', path=u'Γ₯ b',
196 198 body=json.dumps(nbmodel))
197 199 self._check_nb_created(resp, u'Upload tΓ©st.ipynb', u'Γ₯ b')
198 200
199 201 def test_upload_v2(self):
200 202 nb = v2.new_notebook()
201 203 ws = v2.new_worksheet()
202 204 nb.worksheets.append(ws)
203 205 ws.cells.append(v2.new_code_cell(input='print("hi")'))
204 206 nbmodel = {'content': nb}
205 207 resp = self.nb_api.upload(u'Upload tΓ©st.ipynb', path=u'Γ₯ b',
206 208 body=json.dumps(nbmodel))
207 209 self._check_nb_created(resp, u'Upload tΓ©st.ipynb', u'Γ₯ b')
208 210 resp = self.nb_api.read(u'Upload tΓ©st.ipynb', u'Γ₯ b')
209 211 data = resp.json()
210 212 self.assertEqual(data['content']['nbformat'], current.nbformat)
211 213 self.assertEqual(data['content']['orig_nbformat'], 2)
212 214
213 215 def test_copy_untitled(self):
214 216 resp = self.nb_api.copy_untitled(u'Γ§ d.ipynb', path=u'Γ₯ b')
215 217 self._check_nb_created(resp, u'Γ§ d-Copy0.ipynb', u'Γ₯ b')
216 218
217 219 def test_copy(self):
218 220 resp = self.nb_api.copy(u'Γ§ d.ipynb', u'cΓΈpy.ipynb', path=u'Γ₯ b')
219 221 self._check_nb_created(resp, u'cΓΈpy.ipynb', u'Γ₯ b')
220 222
221 223 def test_delete(self):
222 224 for d, name in self.dirs_nbs:
223 225 resp = self.nb_api.delete('%s.ipynb' % name, d)
224 226 self.assertEqual(resp.status_code, 204)
225 227
226 228 for d in self.dirs + ['/']:
227 229 nbs = self.nb_api.list(d).json()
228 230 self.assertEqual(len(nbs), 0)
229 231
230 232 def test_rename(self):
231 233 resp = self.nb_api.rename('a.ipynb', 'foo', 'z.ipynb')
232 234 self.assertEqual(resp.headers['Location'].split('/')[-1], 'z.ipynb')
233 235 self.assertEqual(resp.json()['name'], 'z.ipynb')
234 236 assert os.path.isfile(pjoin(self.notebook_dir.name, 'foo', 'z.ipynb'))
235 237
236 238 nbs = self.nb_api.list('foo').json()
237 239 nbnames = set(n['name'] for n in nbs)
238 240 self.assertIn('z.ipynb', nbnames)
239 241 self.assertNotIn('a.ipynb', nbnames)
240 242
241 243 def test_save(self):
242 244 resp = self.nb_api.read('a.ipynb', 'foo')
243 245 nbcontent = json.loads(resp.text)['content']
244 246 nb = to_notebook_json(nbcontent)
245 247 ws = new_worksheet()
246 248 nb.worksheets = [ws]
247 249 ws.cells.append(new_heading_cell(u'Created by test Β³'))
248 250
249 251 nbmodel= {'name': 'a.ipynb', 'path':'foo', 'content': nb}
250 252 resp = self.nb_api.save('a.ipynb', path='foo', body=json.dumps(nbmodel))
251 253
252 254 nbfile = pjoin(self.notebook_dir.name, 'foo', 'a.ipynb')
253 255 with io.open(nbfile, 'r', encoding='utf-8') as f:
254 256 newnb = read(f, format='ipynb')
255 257 self.assertEqual(newnb.worksheets[0].cells[0].source,
256 258 u'Created by test Β³')
257 259 nbcontent = self.nb_api.read('a.ipynb', 'foo').json()['content']
258 260 newnb = to_notebook_json(nbcontent)
259 261 self.assertEqual(newnb.worksheets[0].cells[0].source,
260 262 u'Created by test Β³')
261 263
262 264 # Save and rename
263 265 nbmodel= {'name': 'a2.ipynb', 'path':'foo/bar', 'content': nb}
264 266 resp = self.nb_api.save('a.ipynb', path='foo', body=json.dumps(nbmodel))
265 267 saved = resp.json()
266 268 self.assertEqual(saved['name'], 'a2.ipynb')
267 269 self.assertEqual(saved['path'], 'foo/bar')
268 270 assert os.path.isfile(pjoin(self.notebook_dir.name,'foo','bar','a2.ipynb'))
269 271 assert not os.path.isfile(pjoin(self.notebook_dir.name, 'foo', 'a.ipynb'))
270 272 with assert_http_error(404):
271 273 self.nb_api.read('a.ipynb', 'foo')
272 274
273 275 def test_checkpoints(self):
274 276 resp = self.nb_api.read('a.ipynb', 'foo')
275 277 r = self.nb_api.new_checkpoint('a.ipynb', 'foo')
276 278 self.assertEqual(r.status_code, 201)
277 279 cp1 = r.json()
278 280 self.assertEqual(set(cp1), {'id', 'last_modified'})
279 281 self.assertEqual(r.headers['Location'].split('/')[-1], cp1['id'])
280 282
281 283 # Modify it
282 284 nbcontent = json.loads(resp.text)['content']
283 285 nb = to_notebook_json(nbcontent)
284 286 ws = new_worksheet()
285 287 nb.worksheets = [ws]
286 288 hcell = new_heading_cell('Created by test')
287 289 ws.cells.append(hcell)
288 290 # Save
289 291 nbmodel= {'name': 'a.ipynb', 'path':'foo', 'content': nb}
290 292 resp = self.nb_api.save('a.ipynb', path='foo', body=json.dumps(nbmodel))
291 293
292 294 # List checkpoints
293 295 cps = self.nb_api.get_checkpoints('a.ipynb', 'foo').json()
294 296 self.assertEqual(cps, [cp1])
295 297
296 298 nbcontent = self.nb_api.read('a.ipynb', 'foo').json()['content']
297 299 nb = to_notebook_json(nbcontent)
298 300 self.assertEqual(nb.worksheets[0].cells[0].source, 'Created by test')
299 301
300 302 # Restore cp1
301 303 r = self.nb_api.restore_checkpoint('a.ipynb', 'foo', cp1['id'])
302 304 self.assertEqual(r.status_code, 204)
303 305 nbcontent = self.nb_api.read('a.ipynb', 'foo').json()['content']
304 306 nb = to_notebook_json(nbcontent)
305 307 self.assertEqual(nb.worksheets, [])
306 308
307 309 # Delete cp1
308 310 r = self.nb_api.delete_checkpoint('a.ipynb', 'foo', cp1['id'])
309 311 self.assertEqual(r.status_code, 204)
310 312 cps = self.nb_api.get_checkpoints('a.ipynb', 'foo').json()
311 313 self.assertEqual(cps, [])
312
314
General Comments 0
You need to be logged in to leave comments. Login now