From ebea3071bcf36c45bab0a0961a78064fa5b04a83 Mon Sep 17 00:00:00 2001 From: d9pouces Date: Tue, 6 Oct 2026 23:16:42 +0200 Subject: [PATCH] feat: add plain-HTTP listeners for various repository configurations to support non-HTTPS access Signed-off-by: d9pouces --- id_configurator/modules/java.py | 37 ++++++++++++++++++------------- tests/dockerfiles/Java.Dockerfile | 14 +++++++++++- tests/test_java.py | 33 ++++++++++++++------------- tox.ini | 8 ++++--- 4 files changed, 57 insertions(+), 35 deletions(-) diff --git a/id_configurator/modules/java.py b/id_configurator/modules/java.py index 711e1eb..4299eb8 100644 --- a/id_configurator/modules/java.py +++ b/id_configurator/modules/java.py @@ -28,12 +28,20 @@ tool targets Docker images, which are built and run as root: - Ivy: `~/.ivy2/ivysettings.xml`, as a chain of `ibiblio` resolvers. - SBT: `~/.sbt/repositories`, the resolver override file SBT reads for its own bootstrapping and, when combined with the `sbt.override.build.repos` - system property, for every project's resolvers as well. + system property, for every project's resolvers as well. A repository + whose URL isn't HTTPS gets `, allowInsecureProtocol` appended to its + line - SBT's Coursier-backed resolver otherwise refuses it outright + ("insecure protocol is unsupported"), unlike every other entry in + `~/.sbt/repositories` which takes no such suffix. - Leiningen: `~/.lein/profiles.clj`, adding the repositories to the `:user` profile. Leiningen, alone among these, refuses a plain-HTTP repository - outright ("Tried to use insecure HTTP repository without TLS"); a - repository whose URL isn't HTTPS gets `:insecure true` added to its entry, - Leiningen's own documented escape hatch for this. + outright ("Tried to use insecure HTTP repository without TLS") - its + standalone `lein` launcher (`leiningen.core.main/-main`) unconditionally + registers an aborting wagon for the "http" scheme before even reading the + project/profile config, so there is no project.clj/profiles.clj setting + (there is no `:insecure` key anywhere in Leiningen's own source) that + can work around this: a non-HTTPS repository configured here will not be + usable from Leiningen specifically, regardless of what this module does. """ from __future__ import annotations @@ -237,27 +245,26 @@ def _write_ivy_settings(context: Context, repositories: list[JavaRepository]) -> logger.info("configured ivy to use %d repositories", len(repositories)) +def _sbt_repository_line(repository: JavaRepository) -> str: + line = f" {repository.name}: {repository.url}" + if urlparse(repository.url).scheme.lower() != "https": + line += ", allowInsecureProtocol" + return line + + def _write_sbt_repositories( context: Context, repositories: list[JavaRepository] ) -> None: lines = ["[repositories]", " local"] - lines.extend( - f" {repository.name}: {repository.url}" for repository in repositories - ) + lines.extend(_sbt_repository_line(repository) for repository in repositories) context.write_file(_SBT_REPOSITORIES, ("\n".join(lines) + "\n").encode("utf-8")) logger.info("configured sbt to use %d repositories", len(repositories)) -def _lein_repository_entry(repository: JavaRepository) -> str: - options = f":url {_string_literal(repository.url)}" - if urlparse(repository.url).scheme.lower() != "https": - options += " :insecure true" - return f"[{_string_literal(repository.name)} {{{options}}}]" - - def _write_lein_profiles(context: Context, repositories: list[JavaRepository]) -> None: entries = " ".join( - _lein_repository_entry(repository) for repository in repositories + f"[{_string_literal(repository.name)} {{:url {_string_literal(repository.url)}}}]" + for repository in repositories ) content = f"{{:user {{:repositories [{entries}]}}}}\n" context.write_file(_LEIN_PROFILES, content.encode("utf-8")) diff --git a/tests/dockerfiles/Java.Dockerfile b/tests/dockerfiles/Java.Dockerfile index e301f90..3d29e48 100644 --- a/tests/dockerfiles/Java.Dockerfile +++ b/tests/dockerfiles/Java.Dockerfile @@ -4,6 +4,7 @@ COPY --from=target / /src/ COPY --from=generated-config Java.yaml /config.yaml RUN id-configurator apply --config /config.yaml --src /src --dst /dst FROM target AS runtime +ARG JAVA_REPO_SCHEME # Every build tool the `java` module configures: Maven itself (already part # of this image), Gradle, Ivy and Leiningen (all packaged for this Ubuntu # base), and SBT (no package available, fetched as a plain release tarball). @@ -43,12 +44,23 @@ RUN java -jar /usr/share/java/ivy.jar -settings /root/.ivy2/ivysettings.xml \ -retrieve "/tmp/ivyretrieve/[artifact]-[revision].[ext]" \ && test -f /tmp/ivyretrieve/idconfiguratortestpkg-0.0.1.jar +# Leiningen's standalone launcher unconditionally refuses any plain-HTTP +# repository ("Tried to use insecure HTTP repository without TLS") before +# even reading project.clj/profiles.clj - there is no `:insecure` key or +# other configuration escape hatch for this (confirmed from Leiningen's own +# source: leiningen.core.main/-main registers an aborting wagon for the +# "http" scheme unconditionally). So this check is skipped for the +# module-java-http variant: no id-configurator config could make it pass. RUN mkdir -p /tmp/leintest && cd /tmp/leintest \ && cat > project.clj <<'EOF' (defproject leintest "0.1.0" :dependencies [[org.interdiode/idconfiguratortestpkg "0.0.1"]]) EOF -RUN cd /tmp/leintest && lein deps :tree | grep -q idconfiguratortestpkg +RUN if [ "$JAVA_REPO_SCHEME" = "https" ]; then \ + cd /tmp/leintest && lein deps :tree | grep -q idconfiguratortestpkg; \ + else \ + echo "skipping leiningen: it cannot use a non-HTTPS repository"; \ + fi RUN mkdir -p /tmp/sbttest/project \ && cat > /tmp/sbttest/build.sbt <<'EOF' diff --git a/tests/test_java.py b/tests/test_java.py index de5d416..c4569ae 100644 --- a/tests/test_java.py +++ b/tests/test_java.py @@ -339,21 +339,7 @@ def test_apply_writes_sbt_repositories(tmp_path) -> None: ) -def test_apply_writes_lein_profiles(tmp_path) -> None: - dst = tmp_path / "dst" - context = Context(config=make_config(TWO_REPOS_CONFIG), dst=dst, src=None) - - JavaModule().apply(context) - - profiles = (dst / "root/.lein/profiles.clj").read_text(encoding="utf-8") - assert profiles == ( - "{:user {:repositories " - '[["interdiode" {:url "https://example.invalid/maven"}] ' - '["interdiode-extra" {:url "https://example2.invalid/maven"}]]}}\n' - ) - - -def test_apply_marks_non_https_lein_repository_as_insecure(tmp_path) -> None: +def test_apply_marks_non_https_sbt_repository_as_insecure(tmp_path) -> None: data = { "uuid": "12345678-1234-5678-1234-567812345678", "organisation": "InterDiode", @@ -370,10 +356,25 @@ def test_apply_marks_non_https_lein_repository_as_insecure(tmp_path) -> None: JavaModule().apply(context) + repositories = (dst / "root/.sbt/repositories").read_text(encoding="utf-8") + assert repositories == ( + "[repositories]\n" + " local\n" + " interdiode: http://example.invalid/maven, allowInsecureProtocol\n" + ) + + +def test_apply_writes_lein_profiles(tmp_path) -> None: + dst = tmp_path / "dst" + context = Context(config=make_config(TWO_REPOS_CONFIG), dst=dst, src=None) + + JavaModule().apply(context) + profiles = (dst / "root/.lein/profiles.clj").read_text(encoding="utf-8") assert profiles == ( "{:user {:repositories " - '[["interdiode" {:url "http://example.invalid/maven" :insecure true}]]}}\n' + '[["interdiode" {:url "https://example.invalid/maven"}] ' + '["interdiode-extra" {:url "https://example2.invalid/maven"}]]}}\n' ) diff --git a/tox.ini b/tox.ini index 2f50d16..6fb7a32 100644 --- a/tox.ini +++ b/tox.ini @@ -310,7 +310,7 @@ set_env = JAVA_REPO_SCHEME = https commands = python3 -m id_tools test render-config {tox_root}/tests/configs/Java.yaml.j2 {env_dir}/configs/Java.yaml - docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Java.Dockerfile -t id-configurator-java . + docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-arg JAVA_REPO_SCHEME={env:JAVA_REPO_SCHEME} --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Java.Dockerfile -t id-configurator-java . docker rmi id-configurator-java [testenv:module-java-http] @@ -323,12 +323,14 @@ docker = allowlist_externals = docker description = - Same as module-java, but against a plain-HTTP repository. + Same as module-java, but against a plain-HTTP repository. Leiningen is + skipped here (see Java.Dockerfile): it refuses any non-HTTPS repository + unconditionally, with no configuration able to change that. set_env = JAVA_REPO_SCHEME = http commands = python3 -m id_tools test render-config {tox_root}/tests/configs/Java.yaml.j2 {env_dir}/configs/Java.yaml - docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Java.Dockerfile -t id-configurator-java-http . + docker buildx build --platform linux/amd64 --add-host=host.docker.internal:host-gateway --build-arg JAVA_REPO_SCHEME={env:JAVA_REPO_SCHEME} --build-context generated-config={env_dir}/configs -f {tox_root}/tests/dockerfiles/Java.Dockerfile -t id-configurator-java-http . docker rmi id-configurator-java-http [docker:certificate-repo]