Skip to content

Commit

Permalink
pubkey added (#36)
Browse files Browse the repository at this point in the history
Co-authored-by: Thomas Zajac <[email protected]>
  • Loading branch information
Moritz Hahn and mephenor authored Dec 21, 2022
1 parent 0f5f7d3 commit fd9983e
Show file tree
Hide file tree
Showing 10 changed files with 65 additions and 13 deletions.
2 changes: 1 addition & 1 deletion ghga_connector/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@
CLI - Client to perform up- and download operations to and from a local ghga instance
"""

__version__ = "0.2.1"
__version__ = "0.2.2"
7 changes: 7 additions & 0 deletions ghga_connector/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,23 @@ def upload( # noqa C901
*,
file_id: str = typer.Option(..., help="The id if the file to upload"),
file_path: Path = typer.Option(..., help="The path to the file to upload"),
pubkey_path: Path = typer.Argument(
"./key.pub",
help="The path to a public key from the key pair that was used to encrypt the "
+ "crypt4gh envelope. Defaults to the file key.pub in the current folder.",
),
):
"""
Command to upload a file
"""
core.RequestsSession.configure(config.max_retries)

core.upload(
api_url=config.upload_api,
file_id=file_id,
file_path=file_path,
message_display=CLIMessageDisplay(),
pubkey_path=pubkey_path,
)


Expand Down
28 changes: 23 additions & 5 deletions ghga_connector/core/api_calls.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,14 @@
Contains calls to the GHGA storage API
"""

import base64
import json
from enum import Enum
from pathlib import Path
from time import sleep
from typing import Dict, Iterator, Tuple, Union

import crypt4gh.keys
import requests
from requests.structures import CaseInsensitiveDict

Expand Down Expand Up @@ -51,7 +54,12 @@ class UploadStatus(str, Enum):
UPLOADED = "uploaded"


def initiate_multipart_upload(*, api_url: str, file_id: str) -> Tuple[str, int]:
def initiate_multipart_upload(
*,
api_url: str,
file_id: str,
pubkey_path: Path,
) -> Tuple[str, int]:
"""
Perform a RESTful API call to initiate a multipart upload
Returns an upload id and a part size
Expand All @@ -60,7 +68,9 @@ def initiate_multipart_upload(*, api_url: str, file_id: str) -> Tuple[str, int]:
# build url and headers
url = f"{api_url}/uploads"
headers = {"Accept": "application/json", "Content-Type": "application/json"}
post_data = {"file_id": file_id}
public_key = base64.b64encode(crypt4gh.keys.get_public_key(pubkey_path)).decode()

post_data = {"file_id": file_id, "public_key": public_key}
serialized_data = json.dumps(post_data)

# Make function call to get upload url
Expand Down Expand Up @@ -344,12 +354,18 @@ def download_api_call(
return download_url, file_size, NO_RETRY_TIME


def start_multipart_upload(*, api_url: str, file_id: str) -> Tuple[str, int]:
def start_multipart_upload(
*, api_url: str, file_id: str, pubkey_path: Path
) -> Tuple[str, int]:
"""Try to initiate a multipart upload. If it fails, try to cancel the current upload
can and then try to initiate a multipart upload again."""

try:
multipart_upload = initiate_multipart_upload(api_url=api_url, file_id=file_id)
multipart_upload = initiate_multipart_upload(
api_url=api_url,
file_id=file_id,
pubkey_path=pubkey_path,
)
return multipart_upload
except exceptions.NoUploadPossibleError as error:
file_metadata = get_file_metadata(api_url=api_url, file_id=file_id)
Expand All @@ -363,7 +379,9 @@ def start_multipart_upload(*, api_url: str, file_id: str) -> Tuple[str, int]:
upload_status=UploadStatus.CANCELLED,
)

multipart_upload = initiate_multipart_upload(api_url=api_url, file_id=file_id)
multipart_upload = initiate_multipart_upload(
api_url=api_url, file_id=file_id, pubkey_path=pubkey_path
)

except Exception as error:
raise error
Expand Down
10 changes: 9 additions & 1 deletion ghga_connector/core/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,21 @@ def __init__(self, *, output_file: str):


class FileDoesNotExistError(RuntimeError):
"""Thrown, when the specified file already exists."""
"""Thrown, when the specified file does not exist."""

def __init__(self, *, file_path: Path):
message = f"The file {file_path} does not exist."
super().__init__(message)


class PubKeyFileDoesNotExistError(RuntimeError):
"""Thrown, when the specified public key file already exists."""

def __init__(self, *, pubkey_path: Path):
message = f"The public key file {pubkey_path} does not exist."
super().__init__(message)


class ApiNotReachableError(RuntimeError):
"""Thrown, when the api is not reachable."""

Expand Down
11 changes: 9 additions & 2 deletions ghga_connector/core/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,22 @@ def check_url(api_url, *, wait_time=1000) -> bool:
return True


def upload( # noqa C901, pylint: disable=too-many-statements
def upload( # noqa C901, pylint: disable=too-many-statements,too-many-branches
*,
api_url: str,
file_id: str,
file_path: Path,
message_display: AbstractMessageDisplay,
pubkey_path: Path,
) -> None:
"""
Core command to upload a file. Can be called by CLI, GUI, etc.
"""

if not os.path.isfile(pubkey_path):
message_display.failure(f"The file {pubkey_path} does not exist.")
raise exceptions.PubKeyFileDoesNotExistError(pubkey_path=pubkey_path)

if not os.path.isfile(file_path):
message_display.failure(f"The file {file_path} does not exist.")
raise exceptions.FileDoesNotExistError(file_path=file_path)
Expand All @@ -70,7 +75,9 @@ def upload( # noqa C901, pylint: disable=too-many-statements
raise exceptions.ApiNotReachableError(api_url=api_url)

try:
upload_id, part_size = start_multipart_upload(api_url=api_url, file_id=file_id)
upload_id, part_size = start_multipart_upload(
api_url=api_url, file_id=file_id, pubkey_path=pubkey_path
)
except exceptions.NoUploadPossibleError as error:
message_display.failure(
f"This user can't start a multipart upload for the file_id '{file_id}'"
Expand Down
2 changes: 1 addition & 1 deletion scripts/license_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
]

# exclude file by file ending from license header check:
EXCLUDE_ENDINGS = ["json", "pyc", "yaml", "yml", "md", "html", "xml"]
EXCLUDE_ENDINGS = ["json", "pub", "pyc", "sec", "yaml", "yml", "md", "html", "xml"]

# exclude any files with names that match any of the following regex:
EXCLUDE_PATTERN = [r".*\.egg-info.*", r".*__cache__.*", r".*\.git.*"]
Expand Down
7 changes: 4 additions & 3 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@ zip_safe = False
include_package_data = True
packages = find:
install_requires =
ghga-service-chassis-lib[s3]==0.15.1
httpyexpect==0.2.2
ghga-service-chassis-lib[s3]==0.16.1
httpyexpect==0.2.4
typer==0.4.1
crypt4gh==1.6

python_requires = >= 3.9.10

Expand All @@ -47,7 +48,7 @@ console_scripts =

[options.extras_require]
dev =
ghga-service-chassis-lib[dev]==0.15.1
ghga-service-chassis-lib[dev]==0.16.1
fastapi
uvicorn

Expand Down
3 changes: 3 additions & 0 deletions tests/fixtures/keypair/key.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-----BEGIN CRYPT4GH PUBLIC KEY-----
P1oBHfr9AA37Kg8WW79RKKpDZlp77KoFx+n+3sLzPBY=
-----END CRYPT4GH PUBLIC KEY-----
3 changes: 3 additions & 0 deletions tests/fixtures/keypair/key.sec
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-----BEGIN CRYPT4GH PRIVATE KEY-----
YzRnaC12MQAEbm9uZQAEbm9uZQAgvT/vZ90UMmestkwn/7GDRU4uCxzz91KvP017CjYyY0Q=
-----END CRYPT4GH PRIVATE KEY-----
5 changes: 5 additions & 0 deletions tests/integration/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
from tests.fixtures.config import get_test_config
from tests.fixtures.mock_api.testcontainer import MockAPIContainer
from tests.fixtures.s3 import S3Fixture, get_big_s3_object, s3_fixture # noqa: F401
from tests.fixtures.utils import BASE_DIR

PUBLIC_KEY_FILE = BASE_DIR / "keypair/key.pub"


@pytest.mark.parametrize(
Expand Down Expand Up @@ -184,6 +187,7 @@ def test_upload(
upload(
file_id=uploadable_file.file_id,
file_path=uploadable_file.file_path.resolve(),
pubkey_path=Path(PUBLIC_KEY_FILE),
)

s3_fixture.storage.complete_multipart_upload(
Expand Down Expand Up @@ -259,6 +263,7 @@ def test_multipart_upload(
upload(
file_id=file_id,
file_path=Path(file.name),
pubkey_path=Path(PUBLIC_KEY_FILE),
)

# confirm upload
Expand Down

0 comments on commit fd9983e

Please sign in to comment.