Skip to content

Commit d3cf5f5

Browse files
authored
Merge pull request #15 from jlab/tornado_FetchFileFromCentralHandler_delete
add ability to delete files/dirs through API, but only in qiita test …
2 parents c446bf9 + 50a1788 commit d3cf5f5

File tree

3 files changed

+124
-2
lines changed

3 files changed

+124
-2
lines changed
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
from .file_transfer_handlers import (FetchFileFromCentralHandler,
2-
PushFileToCentralHandler)
2+
PushFileToCentralHandler,
3+
DeleteFileFromCentralHandler)
4+
from qiita_core.util import is_test_environment
35

46
__all__ = ['FetchFileFromCentralHandler']
57

68
ENDPOINTS = [
79
(r"/cloud/fetch_file_from_central/(.*)", FetchFileFromCentralHandler),
810
(r"/cloud/push_file_to_central/", PushFileToCentralHandler)
911
]
12+
13+
if is_test_environment():
14+
ENDPOINTS.append(
15+
(r"/cloud/delete_file_from_central/(.*)",
16+
DeleteFileFromCentralHandler))

qiita_pet/handlers/cloud_handlers/file_transfer_handlers.py

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from tornado.gen import coroutine
66
import zipfile
77
from io import BytesIO
8+
from shutil import rmtree
89

910
from qiita_core.util import execute_as_transaction, is_test_environment
1011
from qiita_db.handlers.oauth2 import authenticate_oauth
@@ -104,7 +105,7 @@ def get(self, requested_filepath):
104105

105106
filename_directory = "qiita-main-data.zip"
106107
if os.path.isdir(filepath):
107-
# Test if this directory is manages by Qiita's DB as directory
108+
# Test if this directory is managed by Qiita's DB as directory
108109
# Thus we can prevent that a lazy client simply downloads the whole
109110
# basa_data_directory
110111
if not is_directory(filepath):
@@ -256,3 +257,45 @@ def post(self):
256257
'\n'.join(map(lambda x: ' - %s' % x, objs))))
257258

258259
self.finish()
260+
261+
262+
class DeleteFileFromCentralHandler(RequestHandler):
263+
# Note: this function is NOT available in productive instances!
264+
@authenticate_oauth
265+
@coroutine
266+
@execute_as_transaction
267+
def get(self, requested_filepath):
268+
if not is_test_environment():
269+
raise HTTPError(403, reason=(
270+
"You cannot delete files through this API endpoint, when "
271+
"Qiita is not in test-mode!"))
272+
273+
# ensure we have an absolute path, i.e. starting at /
274+
filepath = os.path.join(os.path.sep, requested_filepath)
275+
# use a canonic version of the filepath
276+
filepath = os.path.abspath(filepath)
277+
278+
# canonic version of base_data_dir
279+
basedatadir = os.path.abspath(qiita_config.base_data_dir)
280+
281+
if not filepath.startswith(basedatadir):
282+
# attempt to access files outside of the BASE_DATA_DIR
283+
raise HTTPError(403, reason=(
284+
"You cannot delete file '%s', which is outside of "
285+
"the BASE_DATA_DIR of Qiita!" % filepath))
286+
287+
if not os.path.exists(filepath):
288+
raise HTTPError(403, reason=(
289+
"The requested file %s is not present "
290+
"in Qiita's BASE_DATA_DIR!" % filepath))
291+
292+
if os.path.isdir(filepath):
293+
rmtree(filepath)
294+
self.write("Deleted directory %s from BASE_DATA_DIR of QIita" %
295+
filepath)
296+
else:
297+
os.remove(filepath)
298+
self.write("Deleted file %s from BASE_DATA_DIR of Qiita" %
299+
filepath)
300+
301+
self.finish()

qiita_pet/handlers/cloud_handlers/tests/test_file_transfer_handlers.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,5 +263,77 @@ def test_is_directory(self):
263263
self.assertTrue(obs)
264264

265265

266+
class DeleteFileFromCentralHandlerTests(OauthTestingBase):
267+
def setUp(self):
268+
super(DeleteFileFromCentralHandlerTests, self).setUp()
269+
self.endpoint = '/cloud/delete_file_from_central/'
270+
self.base_data_dir = qdb.util.get_db_files_base_dir()
271+
self._clean_up_files = []
272+
273+
def tearDown(self):
274+
for fp in self._clean_up_files:
275+
if exists(fp):
276+
if isdir(fp):
277+
rmtree(fp)
278+
else:
279+
remove(fp)
280+
281+
def test_post(self):
282+
# check if error is raised when NOT providing a filepath
283+
obs = self.get_authed(self.endpoint)
284+
self.assertEqual(obs.status_code, 403)
285+
self.assertIn("You cannot delete file '/', which", obs.reason)
286+
287+
# check if error is raised when deleting something in productive mode
288+
# we need to let qiita thinks for this test, to NOT be in test mode
289+
with TRN:
290+
TRN.add("UPDATE settings SET test = False")
291+
TRN.execute()
292+
obs = self.get_authed(self.endpoint)
293+
with TRN:
294+
TRN.add("UPDATE settings SET test = True")
295+
TRN.execute()
296+
self.assertEqual(obs.status_code, 403)
297+
self.assertEqual("You cannot delete files through this API endpoint"
298+
", when Qiita is not in test-mode!", obs.reason)
299+
300+
# check if error is raised when deleting existing file outside of base
301+
# dir
302+
obs = self.get_authed(self.endpoint + 'home')
303+
self.assertEqual(obs.status_code, 403)
304+
self.assertIn("You cannot delete file '/home', which", obs.reason)
305+
306+
# check if a file can be deleted
307+
# step 1: create file
308+
fp_file = join(self.base_data_dir, 'deleteme')
309+
with open(fp_file, 'w') as f:
310+
f.write("this file shall be deleted")
311+
self._clean_up_files.append(fp_file)
312+
# step 2: ensure file exists
313+
self.assertTrue(exists(fp_file))
314+
# step 3: delete file via API
315+
obs = self.get_authed(self.endpoint + fp_file)
316+
self.assertEqual(obs.status_code, 200)
317+
self.assertIn("Deleted file %s from BASE_DATA_DIR" % fp_file,
318+
str(obs.content))
319+
# step 4: ensure file does not exist anymore
320+
self.assertFalse(exists(fp_file))
321+
322+
# check if a directory can be deleted
323+
# step 1: create directory
324+
fp_dir = join(self.base_data_dir, 'deletemeDir')
325+
makedirs(fp_dir)
326+
self._clean_up_files.append(fp_dir)
327+
# step 2: ensure file exists
328+
self.assertTrue(exists(fp_dir))
329+
# step 3: delete file via API
330+
obs = self.get_authed(self.endpoint + fp_dir)
331+
self.assertEqual(obs.status_code, 200)
332+
self.assertIn("Deleted directory %s from BASE_DATA_DIR" % fp_dir,
333+
str(obs.content))
334+
# step 4: ensure file does not exist anymore
335+
self.assertFalse(exists(fp_dir))
336+
337+
266338
if __name__ == "__main__":
267339
main()

0 commit comments

Comments
 (0)