From e8761783dc17a138c29ddb9ec4221ce4eb28c772 Mon Sep 17 00:00:00 2001 From: c0llab0rat0r <78339685+c0llab0rat0r@users.noreply.github.com> Date: Mon, 14 Jun 2021 11:06:57 -0500 Subject: [PATCH 1/2] Fix bug with httpx usage - ensure that basic auth is set when provided --- ipfshttpclient/http_httpx.py | 2 +- test/functional/conftest.py | 65 ++++++++++++++++++++++++++++++ test/functional/docker-compose.yml | 14 +++++++ test/functional/test_auth.py | 12 ++++++ tools/ipfs/Dockerfile | 7 ++++ tools/ipfs/entrypoint.sh | 15 +++++++ tools/nginx/Dockerfile | 12 ++++++ tools/nginx/default.conf | 16 ++++++++ tools/nginx/entrypoint.sh | 3 ++ tox.ini | 2 + 10 files changed, 147 insertions(+), 1 deletion(-) create mode 100644 test/functional/docker-compose.yml create mode 100644 test/functional/test_auth.py create mode 100644 tools/ipfs/Dockerfile create mode 100755 tools/ipfs/entrypoint.sh create mode 100644 tools/nginx/Dockerfile create mode 100644 tools/nginx/default.conf create mode 100755 tools/nginx/entrypoint.sh diff --git a/ipfshttpclient/http_httpx.py b/ipfshttpclient/http_httpx.py index 76e06f62..20c24f65 100644 --- a/ipfshttpclient/http_httpx.py +++ b/ipfshttpclient/http_httpx.py @@ -179,7 +179,7 @@ def _request( url=path, **map_args_to_httpx( params=params, - auth=auth, + auth=auth or session.auth, # type: ignore[arg-type] headers=headers, timeout=timeout, ), diff --git a/test/functional/conftest.py b/test/functional/conftest.py index 6522113b..62ad934b 100644 --- a/test/functional/conftest.py +++ b/test/functional/conftest.py @@ -1,10 +1,12 @@ # Note that this file is special in that py.test will automatically import this file and gather # its list of fixtures even if it is not directly imported into the corresponding test case. +import os import pathlib import pytest import sys import typing as ty +from multiaddr import Multiaddr import ipfshttpclient @@ -12,11 +14,74 @@ TEST_DIR: pathlib.Path = pathlib.Path(__file__).parent +def _running_in_linux() -> bool: + return sys.platform == 'linux' + + +def _running_in_travis_ci() -> bool: + return '/home/travis/build' in os.getenv('PATH') + + @pytest.fixture(scope='session') def fake_dir() -> pathlib.Path: return TEST_DIR.joinpath('fake_dir') +@pytest.fixture(scope='session') +def docker_compose_file() -> str: + """ + Override the location of the file used by pytest-docker. + + The fixture name must be docker_compose_file and return str. + """ + + if _running_in_travis_ci(): + pytest.skip('Docker hub reports rate limit errors on pulls from Travis CI servers') + elif not _running_in_linux(): + pytest.skip("No IPFS server build for Windows; Travis doesn't support Docker on mac") + + return str(TEST_DIR.joinpath('docker-compose.yml')) + + +@pytest.fixture(scope='session') +def ipfs_service_address(docker_ip, docker_services, ipfs_service_auth) -> Multiaddr: + port = docker_services.port_for('proxy', 80) + address = Multiaddr(f'/ip4/{docker_ip}/tcp/{port}') + + print(f'Will connect to {address}') + + def is_responsive() -> bool: + try: + with ipfshttpclient.connect(address, auth=ipfs_service_auth): + pass + except ipfshttpclient.exceptions.Error: + return False + else: + return True + + docker_services.wait_until_responsive( + timeout=20, # Pulling the docker image is not included in this timeout + pause=0.5, + check=is_responsive + ) + + return address + + +@pytest.fixture(scope='session') +def ipfs_service_auth() -> ty.Tuple[str, str]: + return 'TheUser', 'ThePassword' + + +@pytest.fixture(scope="function") +def ipfs_service_client(ipfs_service_address, ipfs_service_auth): + with ipfshttpclient.connect( + addr=ipfs_service_address, + auth=ipfs_service_auth + ) as client: + yield client + + @pytest.fixture(scope='session') def ipfs_is_available() -> bool: """ diff --git a/test/functional/docker-compose.yml b/test/functional/docker-compose.yml new file mode 100644 index 00000000..64c2fa60 --- /dev/null +++ b/test/functional/docker-compose.yml @@ -0,0 +1,14 @@ +version: '3.8' +services: + ipfs: + build: + dockerfile: ../../tools/ipfs/Dockerfile + context: ../../tools/ipfs + expose: [5001] + + proxy: + depends_on: [ipfs] + build: + dockerfile: ../../tools/nginx/Dockerfile + context: ../../tools/nginx + ports: [80:80] diff --git a/test/functional/test_auth.py b/test/functional/test_auth.py new file mode 100644 index 00000000..4d346a0f --- /dev/null +++ b/test/functional/test_auth.py @@ -0,0 +1,12 @@ + + +def test_basic_auth(ipfs_service_client): + """ + Validate that client can connect to an IPFS api that is secured + behind an HTTP reverse proxy requiring basic authentication. + """ + + response = ipfs_service_client.version() + + # Matches version in test/functional/docker-compose.yml + assert response['Version'] == '0.8.0' diff --git a/tools/ipfs/Dockerfile b/tools/ipfs/Dockerfile new file mode 100644 index 00000000..34b6ed42 --- /dev/null +++ b/tools/ipfs/Dockerfile @@ -0,0 +1,7 @@ +FROM ipfs/go-ipfs:v0.8.0 + +RUN sed -i 's/exec ipfs "$@"//' /usr/local/bin/start_ipfs +ADD entrypoint.sh / + +ENTRYPOINT ["/entrypoint.sh"] +CMD ["ipfs", "daemon", "--migrate=true", "--enable-namesys-pubsub"] diff --git a/tools/ipfs/entrypoint.sh b/tools/ipfs/entrypoint.sh new file mode 100755 index 00000000..52caa3f4 --- /dev/null +++ b/tools/ipfs/entrypoint.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +set -e + +# Only does configuration; doesn't start the daemon +/usr/local/bin/start_ipfs + +echo "Enabling experimental features" + +ipfs config --json Experimental.FilestoreEnabled true + +echo "Enabled experimental features" + +# Start the daemon (unless other args provided) +exec "$@" diff --git a/tools/nginx/Dockerfile b/tools/nginx/Dockerfile new file mode 100644 index 00000000..1e99a5e8 --- /dev/null +++ b/tools/nginx/Dockerfile @@ -0,0 +1,12 @@ +FROM nginx:1.19.10 + +RUN apt-get update -y && apt-get install -y apache2-utils +RUN mkdir /secrets \ + && cd /secrets \ + && htpasswd -cb htpasswd TheUser ThePassword + +ADD entrypoint.sh /usr/local/sbin/entrypoint +ADD default.conf /etc/nginx/conf.d/default.conf + +CMD ["nginx", "-g", "daemon off;"] +ENTRYPOINT ["/usr/local/sbin/entrypoint"] diff --git a/tools/nginx/default.conf b/tools/nginx/default.conf new file mode 100644 index 00000000..b9974710 --- /dev/null +++ b/tools/nginx/default.conf @@ -0,0 +1,16 @@ +server { + listen 80; + + location / { + proxy_read_timeout 60; + proxy_connect_timeout 60; + client_max_body_size 104857600; + + proxy_pass http://ipfs:5001; + proxy_http_version 1.1; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + + auth_basic "Some Realm"; + auth_basic_user_file /secrets/htpasswd; + } +} diff --git a/tools/nginx/entrypoint.sh b/tools/nginx/entrypoint.sh new file mode 100755 index 00000000..5fc44481 --- /dev/null +++ b/tools/nginx/entrypoint.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +exec "$@" diff --git a/tox.ini b/tox.ini index d8b653ac..8242be92 100644 --- a/tox.ini +++ b/tox.ini @@ -13,9 +13,11 @@ isolated_build = true [testenv] deps = + docker-compose ~= 1.29 pytest ~= 6.2 pytest-cov ~= 2.11 pytest-dependency ~= 0.5 + pytest-docker ~= 0.10 pytest-localserver ~= 0.5 pytest-mock ~= 3.5 pytest-ordering ~= 0.6 From e323865fe420b7faa5f89f07fcc236f48bce4876 Mon Sep 17 00:00:00 2001 From: c0llab0rat0r <78339685+c0llab0rat0r@users.noreply.github.com> Date: Mon, 14 Jun 2021 11:23:27 -0500 Subject: [PATCH 2/2] Tickle build by fixing whitespace issue with FAKE_FILE1_HASH --- test/functional/test_files.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/functional/test_files.py b/test/functional/test_files.py index bdcd97db..93169ae2 100644 --- a/test/functional/test_files.py +++ b/test/functional/test_files.py @@ -20,8 +20,11 @@ FAKE_FILE1_PATH = conftest.TEST_DIR / "fake_dir" / "fsdfgh" FAKE_FILE2_PATH = conftest.TEST_DIR / "fake_dir" / "popoiopiu" -FAKE_FILE1_HASH = {"Hash": "QmQcCtMgLVwvMQGu6mvsRYLjwqrZJcYtH4mboM9urWW9vX", - "Name": "fsdfgh", "Size": "16"} +FAKE_FILE1_HASH = { + "Hash": "QmQcCtMgLVwvMQGu6mvsRYLjwqrZJcYtH4mboM9urWW9vX", + "Name": "fsdfgh", "Size": "16" +} + FAKE_FILE1_RAW_LEAVES_HASH = { "Hash": "zb2rhXxZH5PFgCwBAm7xQMoBa6QWqytN8NPvXK7Qc9McDz9zJ", "Name": "fsdfgh", "Size": "8"