From 7c3f599684607a563a88b07a45489014d782936c Mon Sep 17 00:00:00 2001 From: Athitheya Gobinathan Date: Tue, 13 Aug 2024 18:06:07 -0400 Subject: [PATCH 01/25] feat: create middleware outline --- crypt4gh_middleware/middleware.py | 51 +++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 crypt4gh_middleware/middleware.py diff --git a/crypt4gh_middleware/middleware.py b/crypt4gh_middleware/middleware.py new file mode 100644 index 0000000..39e9544 --- /dev/null +++ b/crypt4gh_middleware/middleware.py @@ -0,0 +1,51 @@ +"""Crypt4GH middleware.""" + +from pathlib import Path + +import flask + + +class CryptMiddleware: + + def __init__(self): + self.request = None + self.original_input_paths = [] + self.output_dir = Path("/vol/crypt/") + + def _set_original_input_paths(self): + """Retrieve the input file paths.""" + for input_body in self.request.json["inputs"]: + self.original_input_paths.append(input_body["path"]) + + def _change_executor_paths(self): + """Change input file paths in provided executors.""" + for executor_body in self.request.json["executors"]: + for i, path in enumerate(executor_body["command"]): + if path in self.original_input_paths: + executor_body["command"][i] = self.output_dir / path.split("/")[-1] + + def _add_decryption_executor(self): + """Add the decryption executor to the executor list.""" + executor = { + "image": "athitheyag/crypt4gh:1.0", + "command": [ + "python3", + "decrypt.py" + ] + self.original_input_paths + [ + "--output-dir", + self.output_dir + ] + } + self.request.json["executors"].insert(0, executor) + + def apply_middleware(self, request: flask.Request): + self.request = request + self._set_original_input_paths() + self._change_executor_paths() + self._add_decryption_executor() + + return self.request + + + + From 1d983f5fd8cec1ff6df7751b798d659f08ae2e9f Mon Sep 17 00:00:00 2001 From: Athitheya Gobinathan Date: Tue, 13 Aug 2024 18:06:25 -0400 Subject: [PATCH 02/25] refactor: remove whitespace --- crypt4gh_middleware/middleware.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crypt4gh_middleware/middleware.py b/crypt4gh_middleware/middleware.py index 39e9544..cd327ff 100644 --- a/crypt4gh_middleware/middleware.py +++ b/crypt4gh_middleware/middleware.py @@ -45,7 +45,3 @@ def apply_middleware(self, request: flask.Request): self._add_decryption_executor() return self.request - - - - From dec2228c3bde74ed012562201fb20a7d700e9585 Mon Sep 17 00:00:00 2001 From: Athitheya Gobinathan Date: Mon, 19 Aug 2024 09:08:44 -0400 Subject: [PATCH 03/25] feat: check output paths for files --- crypt4gh_middleware/middleware.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/crypt4gh_middleware/middleware.py b/crypt4gh_middleware/middleware.py index cd327ff..2fa0c3b 100644 --- a/crypt4gh_middleware/middleware.py +++ b/crypt4gh_middleware/middleware.py @@ -4,7 +4,6 @@ import flask - class CryptMiddleware: def __init__(self): @@ -22,8 +21,14 @@ def _change_executor_paths(self): for executor_body in self.request.json["executors"]: for i, path in enumerate(executor_body["command"]): if path in self.original_input_paths: - executor_body["command"][i] = self.output_dir / path.split("/")[-1] - + executor_body["command"][i] = (self.output_dir/path.split("/")[-1]).as_posix() + + def _change_output_paths(self): + for output_body in self.request.json["outputs"]: + path = output_body["path"] + if path in self.original_input_paths: + output_body["path"] = (self.output_dir/path.split("/")[-1]).as_posix() + def _add_decryption_executor(self): """Add the decryption executor to the executor list.""" executor = { @@ -33,7 +38,7 @@ def _add_decryption_executor(self): "decrypt.py" ] + self.original_input_paths + [ "--output-dir", - self.output_dir + self.output_dir.as_posix() ] } self.request.json["executors"].insert(0, executor) @@ -42,6 +47,7 @@ def apply_middleware(self, request: flask.Request): self.request = request self._set_original_input_paths() self._change_executor_paths() + self._change_output_paths() self._add_decryption_executor() return self.request From ad10247ffe9b590878308783a17d86f3b94eddaa Mon Sep 17 00:00:00 2001 From: Athitheya Gobinathan Date: Mon, 19 Aug 2024 09:15:42 -0400 Subject: [PATCH 04/25] feat: raise error if /vol/crypt/ is used --- crypt4gh_middleware/middleware.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crypt4gh_middleware/middleware.py b/crypt4gh_middleware/middleware.py index 2fa0c3b..4ad475c 100644 --- a/crypt4gh_middleware/middleware.py +++ b/crypt4gh_middleware/middleware.py @@ -10,10 +10,17 @@ def __init__(self): self.request = None self.original_input_paths = [] self.output_dir = Path("/vol/crypt/") + + def _check_volumes(self): + for volume in self.request.json["volumes"]: + if volume.startswith("/vol/crypt/"): + raise ValueError("/vol/crypt/ is not allowed in volumes.") def _set_original_input_paths(self): """Retrieve the input file paths.""" for input_body in self.request.json["inputs"]: + if input_body["path"].startswith("/vol/crypt/"): + raise ValueError("/vol/crypt/ is not allowed in input path.") self.original_input_paths.append(input_body["path"]) def _change_executor_paths(self): From 71f0fb205df2b4c681a97fc598a704a5d5f1414d Mon Sep 17 00:00:00 2001 From: Athitheya Gobinathan Date: Mon, 19 Aug 2024 09:16:24 -0400 Subject: [PATCH 05/25] feat: check volume in apply middleware --- crypt4gh_middleware/middleware.py | 1 + 1 file changed, 1 insertion(+) diff --git a/crypt4gh_middleware/middleware.py b/crypt4gh_middleware/middleware.py index 4ad475c..1586d97 100644 --- a/crypt4gh_middleware/middleware.py +++ b/crypt4gh_middleware/middleware.py @@ -53,6 +53,7 @@ def _add_decryption_executor(self): def apply_middleware(self, request: flask.Request): self.request = request self._set_original_input_paths() + self._check_volumes() self._change_executor_paths() self._change_output_paths() self._add_decryption_executor() From 387a2cc974a2fa1dd8929a295b364c1d48243810 Mon Sep 17 00:00:00 2001 From: Athitheya Gobinathan Date: Mon, 19 Aug 2024 09:25:16 -0400 Subject: [PATCH 06/25] docs: add docstrings --- crypt4gh_middleware/middleware.py | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/crypt4gh_middleware/middleware.py b/crypt4gh_middleware/middleware.py index 1586d97..40533db 100644 --- a/crypt4gh_middleware/middleware.py +++ b/crypt4gh_middleware/middleware.py @@ -10,32 +10,44 @@ def __init__(self): self.request = None self.original_input_paths = [] self.output_dir = Path("/vol/crypt/") - + def _check_volumes(self): + """Check volumes to ensure none start with /vol/crypt. + + Raises: + ValueError if volumes start with /vol/crypt. + """ for volume in self.request.json["volumes"]: - if volume.startswith("/vol/crypt/"): + if volume.startswith("/vol/crypt"): raise ValueError("/vol/crypt/ is not allowed in volumes.") def _set_original_input_paths(self): - """Retrieve the input file paths.""" + """Retrieve the input file paths. + + Raises: + ValueError if any path starts with /vol/crypt. + """ for input_body in self.request.json["inputs"]: - if input_body["path"].startswith("/vol/crypt/"): + if input_body["path"].startswith("/vol/crypt"): raise ValueError("/vol/crypt/ is not allowed in input path.") self.original_input_paths.append(input_body["path"]) def _change_executor_paths(self): - """Change input file paths in provided executors.""" + """Change original input file paths in executors to the output directory.""" for executor_body in self.request.json["executors"]: for i, path in enumerate(executor_body["command"]): if path in self.original_input_paths: executor_body["command"][i] = (self.output_dir/path.split("/")[-1]).as_posix() - + def _change_output_paths(self): + """Change original output file paths to the output directory if the output path is + the same as an input path. + """ for output_body in self.request.json["outputs"]: path = output_body["path"] if path in self.original_input_paths: output_body["path"] = (self.output_dir/path.split("/")[-1]).as_posix() - + def _add_decryption_executor(self): """Add the decryption executor to the executor list.""" executor = { @@ -49,8 +61,13 @@ def _add_decryption_executor(self): ] } self.request.json["executors"].insert(0, executor) + + def set_output_directory(self, output_dir): + """Set the output directory for decrypt.py.""" + self.output_dir = output_dir def apply_middleware(self, request: flask.Request): + """Apply middleware to request.""" self.request = request self._set_original_input_paths() self._check_volumes() From cc467ee46e5b5d399f6a7c653064cfc7edb6a672 Mon Sep 17 00:00:00 2001 From: Athitheya Gobinathan Date: Mon, 19 Aug 2024 09:27:10 -0400 Subject: [PATCH 07/25] docs: add class docstrings --- crypt4gh_middleware/middleware.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crypt4gh_middleware/middleware.py b/crypt4gh_middleware/middleware.py index 40533db..1c45649 100644 --- a/crypt4gh_middleware/middleware.py +++ b/crypt4gh_middleware/middleware.py @@ -5,6 +5,7 @@ import flask class CryptMiddleware: + """Middleware class to handle Crypt4GH file inputs.""" def __init__(self): self.request = None @@ -47,7 +48,7 @@ def _change_output_paths(self): path = output_body["path"] if path in self.original_input_paths: output_body["path"] = (self.output_dir/path.split("/")[-1]).as_posix() - + def _add_decryption_executor(self): """Add the decryption executor to the executor list.""" executor = { @@ -61,7 +62,7 @@ def _add_decryption_executor(self): ] } self.request.json["executors"].insert(0, executor) - + def set_output_directory(self, output_dir): """Set the output directory for decrypt.py.""" self.output_dir = output_dir From 96fd4d5cfed762e9568cc83a63af1da0beb6b207 Mon Sep 17 00:00:00 2001 From: Athitheya Gobinathan Date: Mon, 19 Aug 2024 09:29:43 -0400 Subject: [PATCH 08/25] docs: clarify docstring --- crypt4gh_middleware/middleware.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crypt4gh_middleware/middleware.py b/crypt4gh_middleware/middleware.py index 1c45649..6071d76 100644 --- a/crypt4gh_middleware/middleware.py +++ b/crypt4gh_middleware/middleware.py @@ -43,6 +43,8 @@ def _change_executor_paths(self): def _change_output_paths(self): """Change original output file paths to the output directory if the output path is the same as an input path. + + Accounts for case where input file is modified in place in a TES request. """ for output_body in self.request.json["outputs"]: path = output_body["path"] From dd8bd0e427c507cf7600c55f5d0f3816a3ea416c Mon Sep 17 00:00:00 2001 From: Athitheya Gobinathan Date: Mon, 19 Aug 2024 09:32:21 -0400 Subject: [PATCH 09/25] refactor: sort methods alphabetically --- crypt4gh_middleware/middleware.py | 72 +++++++++++++++---------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/crypt4gh_middleware/middleware.py b/crypt4gh_middleware/middleware.py index 6071d76..477812d 100644 --- a/crypt4gh_middleware/middleware.py +++ b/crypt4gh_middleware/middleware.py @@ -12,26 +12,19 @@ def __init__(self): self.original_input_paths = [] self.output_dir = Path("/vol/crypt/") - def _check_volumes(self): - """Check volumes to ensure none start with /vol/crypt. - - Raises: - ValueError if volumes start with /vol/crypt. - """ - for volume in self.request.json["volumes"]: - if volume.startswith("/vol/crypt"): - raise ValueError("/vol/crypt/ is not allowed in volumes.") - - def _set_original_input_paths(self): - """Retrieve the input file paths. - - Raises: - ValueError if any path starts with /vol/crypt. - """ - for input_body in self.request.json["inputs"]: - if input_body["path"].startswith("/vol/crypt"): - raise ValueError("/vol/crypt/ is not allowed in input path.") - self.original_input_paths.append(input_body["path"]) + def _add_decryption_executor(self): + """Add the decryption executor to the executor list.""" + executor = { + "image": "athitheyag/crypt4gh:1.0", + "command": [ + "python3", + "decrypt.py" + ] + self.original_input_paths + [ + "--output-dir", + self.output_dir.as_posix() + ] + } + self.request.json["executors"].insert(0, executor) def _change_executor_paths(self): """Change original input file paths in executors to the output directory.""" @@ -51,23 +44,26 @@ def _change_output_paths(self): if path in self.original_input_paths: output_body["path"] = (self.output_dir/path.split("/")[-1]).as_posix() - def _add_decryption_executor(self): - """Add the decryption executor to the executor list.""" - executor = { - "image": "athitheyag/crypt4gh:1.0", - "command": [ - "python3", - "decrypt.py" - ] + self.original_input_paths + [ - "--output-dir", - self.output_dir.as_posix() - ] - } - self.request.json["executors"].insert(0, executor) + def _check_volumes(self): + """Check volumes to ensure none start with /vol/crypt. + + Raises: + ValueError if volumes start with /vol/crypt. + """ + for volume in self.request.json["volumes"]: + if volume.startswith("/vol/crypt"): + raise ValueError("/vol/crypt/ is not allowed in volumes.") - def set_output_directory(self, output_dir): - """Set the output directory for decrypt.py.""" - self.output_dir = output_dir + def _set_original_input_paths(self): + """Retrieve the input file paths. + + Raises: + ValueError if any path starts with /vol/crypt. + """ + for input_body in self.request.json["inputs"]: + if input_body["path"].startswith("/vol/crypt"): + raise ValueError("/vol/crypt/ is not allowed in input path.") + self.original_input_paths.append(input_body["path"]) def apply_middleware(self, request: flask.Request): """Apply middleware to request.""" @@ -79,3 +75,7 @@ def apply_middleware(self, request: flask.Request): self._add_decryption_executor() return self.request + + def set_output_directory(self, output_dir): + """Set the output directory for decrypt.py.""" + self.output_dir = output_dir From 09412b1b8f81830894cc96847cb0f3384c210b7a Mon Sep 17 00:00:00 2001 From: Athitheya Gobinathan Date: Mon, 19 Aug 2024 09:33:07 -0400 Subject: [PATCH 10/25] revert: remove set_output_dir function --- crypt4gh_middleware/middleware.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crypt4gh_middleware/middleware.py b/crypt4gh_middleware/middleware.py index 477812d..5f92d71 100644 --- a/crypt4gh_middleware/middleware.py +++ b/crypt4gh_middleware/middleware.py @@ -75,7 +75,3 @@ def apply_middleware(self, request: flask.Request): self._add_decryption_executor() return self.request - - def set_output_directory(self, output_dir): - """Set the output directory for decrypt.py.""" - self.output_dir = output_dir From 0fa2a0281a919935a94c95b8b4964d8dc9b38ff7 Mon Sep 17 00:00:00 2001 From: Athitheya Gobinathan Date: Mon, 19 Aug 2024 09:35:04 -0400 Subject: [PATCH 11/25] docs: modify docstring --- crypt4gh_middleware/middleware.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypt4gh_middleware/middleware.py b/crypt4gh_middleware/middleware.py index 5f92d71..9cc70ff 100644 --- a/crypt4gh_middleware/middleware.py +++ b/crypt4gh_middleware/middleware.py @@ -55,7 +55,7 @@ def _check_volumes(self): raise ValueError("/vol/crypt/ is not allowed in volumes.") def _set_original_input_paths(self): - """Retrieve the input file paths. + """Retrieve and store the original input file paths. Raises: ValueError if any path starts with /vol/crypt. From 99de44125c90b083f385aba637709eb351854210 Mon Sep 17 00:00:00 2001 From: Athitheya Gobinathan Date: Mon, 19 Aug 2024 09:38:08 -0400 Subject: [PATCH 12/25] ci: add flask to project --- poetry.lock | 198 ++++++++++++++++++++++++++++++++++++++++++++++++- pyproject.toml | 5 +- 2 files changed, 201 insertions(+), 2 deletions(-) diff --git a/poetry.lock b/poetry.lock index 0a55cf9..e61162e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -54,6 +54,17 @@ files = [ tests = ["pytest (>=3.2.1,!=3.3.0)"] typecheck = ["mypy"] +[[package]] +name = "blinker" +version = "1.8.2" +description = "Fast, simple object-to-object and broadcast signaling" +optional = false +python-versions = ">=3.8" +files = [ + {file = "blinker-1.8.2-py3-none-any.whl", hash = "sha256:1779309f71bf239144b9399d06ae925637cf6634cf6bd131104184531bf67c01"}, + {file = "blinker-1.8.2.tar.gz", hash = "sha256:8f77b09d3bf7c795e969e9486f39c2c5e9c39d4ee07424be2bc594ece9642d83"}, +] + [[package]] name = "certifi" version = "2024.6.2" @@ -228,6 +239,20 @@ files = [ {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, ] +[[package]] +name = "click" +version = "8.1.7" +description = "Composable command line interface toolkit" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, + {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + [[package]] name = "colorama" version = "0.4.6" @@ -350,6 +375,29 @@ files = [ [package.extras] test = ["pytest (>=6)"] +[[package]] +name = "flask" +version = "3.0.3" +description = "A simple framework for building complex web applications." +optional = false +python-versions = ">=3.8" +files = [ + {file = "flask-3.0.3-py3-none-any.whl", hash = "sha256:34e815dfaa43340d1d15a5c3a02b8476004037eb4840b34910c6e21679d288f3"}, + {file = "flask-3.0.3.tar.gz", hash = "sha256:ceb27b0af3823ea2737928a4d99d125a06175b8512c445cbd9a9ce200ef76842"}, +] + +[package.dependencies] +blinker = ">=1.6.2" +click = ">=8.1.3" +importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} +itsdangerous = ">=2.1.2" +Jinja2 = ">=3.1.2" +Werkzeug = ">=3.0.0" + +[package.extras] +async = ["asgiref (>=3.2)"] +dotenv = ["python-dotenv"] + [[package]] name = "idna" version = "3.7" @@ -361,6 +409,25 @@ files = [ {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, ] +[[package]] +name = "importlib-metadata" +version = "8.2.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_metadata-8.2.0-py3-none-any.whl", hash = "sha256:11901fa0c2f97919b288679932bb64febaeacf289d18ac84dd68cb2e74213369"}, + {file = "importlib_metadata-8.2.0.tar.gz", hash = "sha256:72e8d4399996132204f9a16dcc751af254a48f8d1b20b9ff0f98d4a8f901e73d"}, +] + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] + [[package]] name = "iniconfig" version = "2.0.0" @@ -386,6 +453,103 @@ files = [ [package.extras] colors = ["colorama (>=0.4.6)"] +[[package]] +name = "itsdangerous" +version = "2.2.0" +description = "Safely pass data to untrusted environments and back." +optional = false +python-versions = ">=3.8" +files = [ + {file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"}, + {file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"}, +] + +[[package]] +name = "jinja2" +version = "3.1.4" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + [[package]] name = "mccabe" version = "0.7.0" @@ -744,7 +908,39 @@ h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] +[[package]] +name = "werkzeug" +version = "3.0.3" +description = "The comprehensive WSGI web application library." +optional = false +python-versions = ">=3.8" +files = [ + {file = "werkzeug-3.0.3-py3-none-any.whl", hash = "sha256:fc9645dc43e03e4d630d23143a04a7f947a9a3b5727cd535fdfe155a17cc48c8"}, + {file = "werkzeug-3.0.3.tar.gz", hash = "sha256:097e5bfda9f0aba8da6b8545146def481d06aa7d3266e7448e2cccf67dd8bd18"}, +] + +[package.dependencies] +MarkupSafe = ">=2.1.1" + +[package.extras] +watchdog = ["watchdog (>=2.3)"] + +[[package]] +name = "zipp" +version = "3.20.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zipp-3.20.0-py3-none-any.whl", hash = "sha256:58da6168be89f0be59beb194da1250516fdaa062ccebd30127ac65d30045e10d"}, + {file = "zipp-3.20.0.tar.gz", hash = "sha256:0145e43d89664cfe1a2e533adc75adafed82fe2da404b4bbb6b026c0157bdb31"}, +] + +[package.extras] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] + [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "eea7c3986781d6dbfbd758ea9e50847114fcceed370086e44a0f4a0423b59204" +content-hash = "e4dddbfe637243978357be2788d32773545e7558baa810a1cd4ebc3e28777240" diff --git a/pyproject.toml b/pyproject.toml index 5e39927..9a1043f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,5 +21,8 @@ ruff = "*" pylint = "*" mypy = "*" +[tool.poetry.group.middleware.dependencies] +flask = "*" + [tool.pylint."MESSAGES CONTROL"] -disable = ["logging-fstring-interpolation", "missing-timeout"] +disable = ["logging-fstring-interpolation", "missing-timeout", "too-few-public-methods"] From 7176d66759cfbe21e9dff64f996310d06d031b19 Mon Sep 17 00:00:00 2001 From: Athitheya Gobinathan Date: Mon, 19 Aug 2024 10:29:01 -0400 Subject: [PATCH 13/25] refactor: use path operations instead of strings --- crypt4gh_middleware/middleware.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crypt4gh_middleware/middleware.py b/crypt4gh_middleware/middleware.py index 9cc70ff..df15d63 100644 --- a/crypt4gh_middleware/middleware.py +++ b/crypt4gh_middleware/middleware.py @@ -21,7 +21,7 @@ def _add_decryption_executor(self): "decrypt.py" ] + self.original_input_paths + [ "--output-dir", - self.output_dir.as_posix() + str(self.output_dir) ] } self.request.json["executors"].insert(0, executor) @@ -31,7 +31,7 @@ def _change_executor_paths(self): for executor_body in self.request.json["executors"]: for i, path in enumerate(executor_body["command"]): if path in self.original_input_paths: - executor_body["command"][i] = (self.output_dir/path.split("/")[-1]).as_posix() + executor_body["command"][i] = str(self.output_dir/Path(path).name) def _change_output_paths(self): """Change original output file paths to the output directory if the output path is @@ -42,7 +42,7 @@ def _change_output_paths(self): for output_body in self.request.json["outputs"]: path = output_body["path"] if path in self.original_input_paths: - output_body["path"] = (self.output_dir/path.split("/")[-1]).as_posix() + output_body["path"] = str(self.output_dir/Path(path).name) def _check_volumes(self): """Check volumes to ensure none start with /vol/crypt. From ff71827f51be059655f860ddf43098c670e2bc58 Mon Sep 17 00:00:00 2001 From: Athitheya Gobinathan Date: Mon, 19 Aug 2024 10:30:12 -0400 Subject: [PATCH 14/25] docs: add type hints --- crypt4gh_middleware/middleware.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crypt4gh_middleware/middleware.py b/crypt4gh_middleware/middleware.py index df15d63..bf4cf9e 100644 --- a/crypt4gh_middleware/middleware.py +++ b/crypt4gh_middleware/middleware.py @@ -12,7 +12,7 @@ def __init__(self): self.original_input_paths = [] self.output_dir = Path("/vol/crypt/") - def _add_decryption_executor(self): + def _add_decryption_executor(self) -> None: """Add the decryption executor to the executor list.""" executor = { "image": "athitheyag/crypt4gh:1.0", @@ -26,14 +26,14 @@ def _add_decryption_executor(self): } self.request.json["executors"].insert(0, executor) - def _change_executor_paths(self): + def _change_executor_paths(self) -> None: """Change original input file paths in executors to the output directory.""" for executor_body in self.request.json["executors"]: for i, path in enumerate(executor_body["command"]): if path in self.original_input_paths: executor_body["command"][i] = str(self.output_dir/Path(path).name) - def _change_output_paths(self): + def _change_output_paths(self) -> None: """Change original output file paths to the output directory if the output path is the same as an input path. @@ -44,7 +44,7 @@ def _change_output_paths(self): if path in self.original_input_paths: output_body["path"] = str(self.output_dir/Path(path).name) - def _check_volumes(self): + def _check_volumes(self) -> None: """Check volumes to ensure none start with /vol/crypt. Raises: @@ -54,7 +54,7 @@ def _check_volumes(self): if volume.startswith("/vol/crypt"): raise ValueError("/vol/crypt/ is not allowed in volumes.") - def _set_original_input_paths(self): + def _set_original_input_paths(self) -> None: """Retrieve and store the original input file paths. Raises: @@ -65,7 +65,7 @@ def _set_original_input_paths(self): raise ValueError("/vol/crypt/ is not allowed in input path.") self.original_input_paths.append(input_body["path"]) - def apply_middleware(self, request: flask.Request): + def apply_middleware(self, request: flask.Request) -> flask.Request: """Apply middleware to request.""" self.request = request self._set_original_input_paths() From d0f4721a20f9183d6993a3337caa0f488173cf00 Mon Sep 17 00:00:00 2001 From: Athitheya Gobinathan Date: Mon, 19 Aug 2024 10:34:09 -0400 Subject: [PATCH 15/25] feat: use custom error class --- crypt4gh_middleware/middleware.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crypt4gh_middleware/middleware.py b/crypt4gh_middleware/middleware.py index bf4cf9e..11567ba 100644 --- a/crypt4gh_middleware/middleware.py +++ b/crypt4gh_middleware/middleware.py @@ -4,6 +4,9 @@ import flask +class PathNotAllowedError(ValueError): + pass + class CryptMiddleware: """Middleware class to handle Crypt4GH file inputs.""" @@ -52,7 +55,7 @@ def _check_volumes(self) -> None: """ for volume in self.request.json["volumes"]: if volume.startswith("/vol/crypt"): - raise ValueError("/vol/crypt/ is not allowed in volumes.") + raise PathNotAllowedError("/vol/crypt/ is not allowed in volumes.") def _set_original_input_paths(self) -> None: """Retrieve and store the original input file paths. @@ -62,7 +65,7 @@ def _set_original_input_paths(self) -> None: """ for input_body in self.request.json["inputs"]: if input_body["path"].startswith("/vol/crypt"): - raise ValueError("/vol/crypt/ is not allowed in input path.") + raise PathNotAllowedError("/vol/crypt/ is not allowed in input path.") self.original_input_paths.append(input_body["path"]) def apply_middleware(self, request: flask.Request) -> flask.Request: From daeaf1bcf6fe8b95a8471548781ada7add0aba0d Mon Sep 17 00:00:00 2001 From: Athitheya Gobinathan Date: Mon, 19 Aug 2024 10:35:10 -0400 Subject: [PATCH 16/25] docs: add docstring to error class --- crypt4gh_middleware/middleware.py | 1 + 1 file changed, 1 insertion(+) diff --git a/crypt4gh_middleware/middleware.py b/crypt4gh_middleware/middleware.py index 11567ba..3aa271a 100644 --- a/crypt4gh_middleware/middleware.py +++ b/crypt4gh_middleware/middleware.py @@ -5,6 +5,7 @@ import flask class PathNotAllowedError(ValueError): + """Error raised when a path is not allowed.""" pass class CryptMiddleware: From 5224fafb9ec3847a931c4f36ae1f85ae634b4612 Mon Sep 17 00:00:00 2001 From: Athitheya Gobinathan Date: Mon, 19 Aug 2024 10:36:43 -0400 Subject: [PATCH 17/25] refactor: remove pass statement --- crypt4gh_middleware/middleware.py | 1 - 1 file changed, 1 deletion(-) diff --git a/crypt4gh_middleware/middleware.py b/crypt4gh_middleware/middleware.py index 3aa271a..f12b6ec 100644 --- a/crypt4gh_middleware/middleware.py +++ b/crypt4gh_middleware/middleware.py @@ -6,7 +6,6 @@ class PathNotAllowedError(ValueError): """Error raised when a path is not allowed.""" - pass class CryptMiddleware: """Middleware class to handle Crypt4GH file inputs.""" From 7eb9f7de2d067ff4e00b0cb4c4a78343ae2b8b93 Mon Sep 17 00:00:00 2001 From: Athitheya Gobinathan Date: Sun, 1 Sep 2024 17:45:11 -0400 Subject: [PATCH 18/25] refactor: take request in as parameter --- crypt4gh_middleware/middleware.py | 47 +++++++++++++++---------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/crypt4gh_middleware/middleware.py b/crypt4gh_middleware/middleware.py index f12b6ec..010b8dc 100644 --- a/crypt4gh_middleware/middleware.py +++ b/crypt4gh_middleware/middleware.py @@ -11,11 +11,9 @@ class CryptMiddleware: """Middleware class to handle Crypt4GH file inputs.""" def __init__(self): - self.request = None self.original_input_paths = [] - self.output_dir = Path("/vol/crypt/") - def _add_decryption_executor(self) -> None: + def _add_decryption_executor(self, request: flask.Request) -> flask.Request: """Add the decryption executor to the executor list.""" executor = { "image": "athitheyag/crypt4gh:1.0", @@ -24,57 +22,58 @@ def _add_decryption_executor(self) -> None: "decrypt.py" ] + self.original_input_paths + [ "--output-dir", - str(self.output_dir) + "/vol/crypt/" ] } - self.request.json["executors"].insert(0, executor) + request.json["executors"].insert(0, executor) + return request - def _change_executor_paths(self) -> None: + def _change_executor_paths(self, request: flask.Request) -> flask.Request: """Change original input file paths in executors to the output directory.""" - for executor_body in self.request.json["executors"]: + for executor_body in request.json["executors"]: for i, path in enumerate(executor_body["command"]): if path in self.original_input_paths: - executor_body["command"][i] = str(self.output_dir/Path(path).name) + executor_body["command"][i] = str(Path("/vol/crypt")/Path(path).name) + return request - def _change_output_paths(self) -> None: + def _change_output_paths(self, request: flask.Request) -> flask.Request: """Change original output file paths to the output directory if the output path is the same as an input path. Accounts for case where input file is modified in place in a TES request. """ - for output_body in self.request.json["outputs"]: + for output_body in request.json["outputs"]: path = output_body["path"] if path in self.original_input_paths: - output_body["path"] = str(self.output_dir/Path(path).name) + output_body["path"] = str(Path("/vol/crypt")/Path(path).name) + return request - def _check_volumes(self) -> None: + def _check_volumes(self, request: flask.Request) -> None: """Check volumes to ensure none start with /vol/crypt. Raises: ValueError if volumes start with /vol/crypt. """ - for volume in self.request.json["volumes"]: + for volume in request.json["volumes"]: if volume.startswith("/vol/crypt"): - raise PathNotAllowedError("/vol/crypt/ is not allowed in volumes.") + raise PathNotAllowedError("/vol/crypt is not allowed in volumes.") - def _set_original_input_paths(self) -> None: + def _set_original_input_paths(self, request: flask.Request) -> None: """Retrieve and store the original input file paths. Raises: ValueError if any path starts with /vol/crypt. """ - for input_body in self.request.json["inputs"]: + for input_body in request.json["inputs"]: if input_body["path"].startswith("/vol/crypt"): raise PathNotAllowedError("/vol/crypt/ is not allowed in input path.") self.original_input_paths.append(input_body["path"]) def apply_middleware(self, request: flask.Request) -> flask.Request: """Apply middleware to request.""" - self.request = request - self._set_original_input_paths() - self._check_volumes() - self._change_executor_paths() - self._change_output_paths() - self._add_decryption_executor() - - return self.request + self._set_original_input_paths(request) + self._check_volumes(request) + request = self._change_executor_paths(request) + request = self._change_output_paths(request) + request = self._add_decryption_executor(request) + return request From 3d716cc15e56e9e04a1e418e0c44d4f81c05374d Mon Sep 17 00:00:00 2001 From: Athitheya Gobinathan Date: Sun, 1 Sep 2024 17:54:53 -0400 Subject: [PATCH 19/25] docs: revise docstrings --- crypt4gh_middleware/middleware.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/crypt4gh_middleware/middleware.py b/crypt4gh_middleware/middleware.py index 010b8dc..e9c352c 100644 --- a/crypt4gh_middleware/middleware.py +++ b/crypt4gh_middleware/middleware.py @@ -36,23 +36,23 @@ def _change_executor_paths(self, request: flask.Request) -> flask.Request: executor_body["command"][i] = str(Path("/vol/crypt")/Path(path).name) return request - def _change_output_paths(self, request: flask.Request) -> flask.Request: - """Change original output file paths to the output directory if the output path is - the same as an input path. + def _check_output_paths(self, request: flask.Request) -> None: + """Check if an input path is present in the output paths. Inplace + modifications are not allowed. - Accounts for case where input file is modified in place in a TES request. + Raises: + PathNotAllowedError if volumes start with /vol/crypt. """ for output_body in request.json["outputs"]: path = output_body["path"] if path in self.original_input_paths: - output_body["path"] = str(Path("/vol/crypt")/Path(path).name) - return request + raise PathNotAllowedError(f"{path} is being modified inplace.") def _check_volumes(self, request: flask.Request) -> None: """Check volumes to ensure none start with /vol/crypt. Raises: - ValueError if volumes start with /vol/crypt. + PathNotAllowedError if volumes start with /vol/crypt. """ for volume in request.json["volumes"]: if volume.startswith("/vol/crypt"): @@ -62,7 +62,7 @@ def _set_original_input_paths(self, request: flask.Request) -> None: """Retrieve and store the original input file paths. Raises: - ValueError if any path starts with /vol/crypt. + PathNotAllowedError if any path starts with /vol/crypt. """ for input_body in request.json["inputs"]: if input_body["path"].startswith("/vol/crypt"): @@ -73,7 +73,7 @@ def apply_middleware(self, request: flask.Request) -> flask.Request: """Apply middleware to request.""" self._set_original_input_paths(request) self._check_volumes(request) + self._check_output_paths(request) request = self._change_executor_paths(request) - request = self._change_output_paths(request) request = self._add_decryption_executor(request) return request From 7bba453cf87154d0e86b14bad34e4a8032c68ab0 Mon Sep 17 00:00:00 2001 From: Athitheya Gobinathan Date: Sun, 1 Sep 2024 18:25:47 -0400 Subject: [PATCH 20/25] test: add empty payload error --- crypt4gh_middleware/middleware.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/crypt4gh_middleware/middleware.py b/crypt4gh_middleware/middleware.py index e9c352c..adc493c 100644 --- a/crypt4gh_middleware/middleware.py +++ b/crypt4gh_middleware/middleware.py @@ -1,11 +1,16 @@ """Crypt4GH middleware.""" - +from functools import wraps from pathlib import Path import flask -class PathNotAllowedError(ValueError): - """Error raised when a path is not allowed.""" +# mypy: disable-error-code="index" + +class PathNotAllowedException(ValueError): + """Raised when a path is not allowed.""" + +class EmptyPayloadException(ValueError): + """Raised when request has no JSON payload.""" class CryptMiddleware: """Middleware class to handle Crypt4GH file inputs.""" @@ -46,7 +51,7 @@ def _check_output_paths(self, request: flask.Request) -> None: for output_body in request.json["outputs"]: path = output_body["path"] if path in self.original_input_paths: - raise PathNotAllowedError(f"{path} is being modified inplace.") + raise PathNotAllowedException(f"{path} is being modified inplace.") def _check_volumes(self, request: flask.Request) -> None: """Check volumes to ensure none start with /vol/crypt. @@ -56,7 +61,7 @@ def _check_volumes(self, request: flask.Request) -> None: """ for volume in request.json["volumes"]: if volume.startswith("/vol/crypt"): - raise PathNotAllowedError("/vol/crypt is not allowed in volumes.") + raise PathNotAllowedException("/vol/crypt is not allowed in volumes.") def _set_original_input_paths(self, request: flask.Request) -> None: """Retrieve and store the original input file paths. @@ -66,11 +71,13 @@ def _set_original_input_paths(self, request: flask.Request) -> None: """ for input_body in request.json["inputs"]: if input_body["path"].startswith("/vol/crypt"): - raise PathNotAllowedError("/vol/crypt/ is not allowed in input path.") + raise PathNotAllowedException("/vol/crypt/ is not allowed in input path.") self.original_input_paths.append(input_body["path"]) def apply_middleware(self, request: flask.Request) -> flask.Request: """Apply middleware to request.""" + if not request.json: + raise EmptyPayloadException("Request JSON has no payload.") self._set_original_input_paths(request) self._check_volumes(request) self._check_output_paths(request) From e26b7e88f79b0ae3fe254e19ba46b7c7d8e70d4c Mon Sep 17 00:00:00 2001 From: Athitheya Gobinathan Date: Sun, 1 Sep 2024 18:30:28 -0400 Subject: [PATCH 21/25] refactor: remove functools --- crypt4gh_middleware/middleware.py | 1 - 1 file changed, 1 deletion(-) diff --git a/crypt4gh_middleware/middleware.py b/crypt4gh_middleware/middleware.py index adc493c..a0e093e 100644 --- a/crypt4gh_middleware/middleware.py +++ b/crypt4gh_middleware/middleware.py @@ -1,5 +1,4 @@ """Crypt4GH middleware.""" -from functools import wraps from pathlib import Path import flask From 2c9aed5258aa3f665416bdd2ff4d13140ffc3b26 Mon Sep 17 00:00:00 2001 From: Athitheya Gobinathan Date: Sun, 1 Sep 2024 19:08:09 -0400 Subject: [PATCH 22/25] feat: add /vol/crypt/ to volumes --- crypt4gh_middleware/middleware.py | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/crypt4gh_middleware/middleware.py b/crypt4gh_middleware/middleware.py index a0e093e..09b383f 100644 --- a/crypt4gh_middleware/middleware.py +++ b/crypt4gh_middleware/middleware.py @@ -32,6 +32,18 @@ def _add_decryption_executor(self, request: flask.Request) -> flask.Request: request.json["executors"].insert(0, executor) return request + def _add_volume(self, request: flask.Request) -> flask.Request: + """Check volumes to ensure none start with /vol/crypt/ and add /vol/crypt/. + + Raises: + PathNotAllowedError if volumes start with /vol/crypt/. + """ + for volume in request.json["volumes"]: + if volume.startswith("/vol/crypt/"): + raise PathNotAllowedException("/vol/crypt/ is not allowed in volumes.") + request.json["volumes"].append("/vol/crypt/") + return request + def _change_executor_paths(self, request: flask.Request) -> flask.Request: """Change original input file paths in executors to the output directory.""" for executor_body in request.json["executors"]: @@ -45,23 +57,13 @@ def _check_output_paths(self, request: flask.Request) -> None: modifications are not allowed. Raises: - PathNotAllowedError if volumes start with /vol/crypt. + PathNotAllowedError if input path is present in output paths. """ for output_body in request.json["outputs"]: path = output_body["path"] if path in self.original_input_paths: raise PathNotAllowedException(f"{path} is being modified inplace.") - def _check_volumes(self, request: flask.Request) -> None: - """Check volumes to ensure none start with /vol/crypt. - - Raises: - PathNotAllowedError if volumes start with /vol/crypt. - """ - for volume in request.json["volumes"]: - if volume.startswith("/vol/crypt"): - raise PathNotAllowedException("/vol/crypt is not allowed in volumes.") - def _set_original_input_paths(self, request: flask.Request) -> None: """Retrieve and store the original input file paths. @@ -69,7 +71,7 @@ def _set_original_input_paths(self, request: flask.Request) -> None: PathNotAllowedError if any path starts with /vol/crypt. """ for input_body in request.json["inputs"]: - if input_body["path"].startswith("/vol/crypt"): + if input_body["path"].startswith("/vol/crypt/"): raise PathNotAllowedException("/vol/crypt/ is not allowed in input path.") self.original_input_paths.append(input_body["path"]) @@ -78,8 +80,8 @@ def apply_middleware(self, request: flask.Request) -> flask.Request: if not request.json: raise EmptyPayloadException("Request JSON has no payload.") self._set_original_input_paths(request) - self._check_volumes(request) self._check_output_paths(request) request = self._change_executor_paths(request) + request = self._add_volume(request) request = self._add_decryption_executor(request) return request From d0391e0c29071e7dd68360177e626f08e241b25f Mon Sep 17 00:00:00 2001 From: Athitheya Gobinathan Date: Sun, 1 Sep 2024 19:09:32 -0400 Subject: [PATCH 23/25] docs: add trailing slash --- crypt4gh_middleware/middleware.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypt4gh_middleware/middleware.py b/crypt4gh_middleware/middleware.py index 09b383f..8581646 100644 --- a/crypt4gh_middleware/middleware.py +++ b/crypt4gh_middleware/middleware.py @@ -68,7 +68,7 @@ def _set_original_input_paths(self, request: flask.Request) -> None: """Retrieve and store the original input file paths. Raises: - PathNotAllowedError if any path starts with /vol/crypt. + PathNotAllowedError if any path starts with /vol/crypt/. """ for input_body in request.json["inputs"]: if input_body["path"].startswith("/vol/crypt/"): From b96a12a1097f37bc70bc79069b1d70ac5483b4ac Mon Sep 17 00:00:00 2001 From: Athitheya Gobinathan Date: Tue, 3 Sep 2024 12:35:16 -0400 Subject: [PATCH 24/25] refactor: use constant volume path --- crypt4gh_middleware/middleware.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/crypt4gh_middleware/middleware.py b/crypt4gh_middleware/middleware.py index 8581646..e217ce4 100644 --- a/crypt4gh_middleware/middleware.py +++ b/crypt4gh_middleware/middleware.py @@ -3,6 +3,7 @@ import flask +VOLUME_PATH = "/vol/crypt/" # mypy: disable-error-code="index" class PathNotAllowedException(ValueError): @@ -26,22 +27,22 @@ def _add_decryption_executor(self, request: flask.Request) -> flask.Request: "decrypt.py" ] + self.original_input_paths + [ "--output-dir", - "/vol/crypt/" + VOLUME_PATH ] } request.json["executors"].insert(0, executor) return request def _add_volume(self, request: flask.Request) -> flask.Request: - """Check volumes to ensure none start with /vol/crypt/ and add /vol/crypt/. + """Check volumes to ensure none start with VOLUME_PATH and add VOLUME_PATH. Raises: - PathNotAllowedError if volumes start with /vol/crypt/. + PathNotAllowedError if volumes start with VOLUME_PATH. """ for volume in request.json["volumes"]: - if volume.startswith("/vol/crypt/"): - raise PathNotAllowedException("/vol/crypt/ is not allowed in volumes.") - request.json["volumes"].append("/vol/crypt/") + if volume.startswith(VOLUME_PATH): + raise PathNotAllowedException(f"{VOLUME_PATH} is not allowed in volumes.") + request.json["volumes"].append(VOLUME_PATH) return request def _change_executor_paths(self, request: flask.Request) -> flask.Request: @@ -49,7 +50,7 @@ def _change_executor_paths(self, request: flask.Request) -> flask.Request: for executor_body in request.json["executors"]: for i, path in enumerate(executor_body["command"]): if path in self.original_input_paths: - executor_body["command"][i] = str(Path("/vol/crypt")/Path(path).name) + executor_body["command"][i] = str(Path(VOLUME_PATH)/Path(path).name) return request def _check_output_paths(self, request: flask.Request) -> None: @@ -68,11 +69,11 @@ def _set_original_input_paths(self, request: flask.Request) -> None: """Retrieve and store the original input file paths. Raises: - PathNotAllowedError if any path starts with /vol/crypt/. + PathNotAllowedError if any path starts with VOLUME_PATH. """ for input_body in request.json["inputs"]: - if input_body["path"].startswith("/vol/crypt/"): - raise PathNotAllowedException("/vol/crypt/ is not allowed in input path.") + if input_body["path"].startswith(VOLUME_PATH): + raise PathNotAllowedException(f"{VOLUME_PATH} is not allowed in input path.") self.original_input_paths.append(input_body["path"]) def apply_middleware(self, request: flask.Request) -> flask.Request: From 7b0cecb15cd2336cecfb67524dacf8f02ecf19d1 Mon Sep 17 00:00:00 2001 From: Athitheya Gobinathan Date: Tue, 3 Sep 2024 16:29:46 -0400 Subject: [PATCH 25/25] feat: randomize volume path --- crypt4gh_middleware/middleware.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crypt4gh_middleware/middleware.py b/crypt4gh_middleware/middleware.py index e217ce4..3a86f4c 100644 --- a/crypt4gh_middleware/middleware.py +++ b/crypt4gh_middleware/middleware.py @@ -1,9 +1,10 @@ """Crypt4GH middleware.""" from pathlib import Path +import uuid import flask -VOLUME_PATH = "/vol/crypt/" +VOLUME_PATH = f"/vol/{str(uuid.uuid4().hex)}" # mypy: disable-error-code="index" class PathNotAllowedException(ValueError):