diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2a1ee76fd0b6..ea5514e68241 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,7 +47,7 @@ jobs: matrix: include: - name: Tests - command: bin/tests + command: bin/tests --postgresql-host localhost - name: Lint command: bin/lint - name: User Documentation @@ -62,6 +62,14 @@ jobs: command: bin/translations runs-on: ubuntu-latest services: + postgres: + image: ${{ (matrix.name == 'Tests') && 'postgres:14.4' || '' }} + ports: + - 5432:5432 + env: + POSTGRES_HOST_AUTH_METHOD: trust # never do this in production! + # Set health checks to wait until postgres has started + options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 stripe: image: ${{ (matrix.name == 'Tests') && 'stripe/stripe-mock:v0.162.0' || '' }} ports: diff --git a/.python-version b/.python-version index d4b278f0a7da..b6d8b7612f0e 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.11.7 +3.11.8 diff --git a/Dockerfile b/Dockerfile index e4d49faccb3e..f76b92b09cf8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -36,7 +36,7 @@ RUN NODE_ENV=production npm run build # We'll build a light-weight layer along the way with just docs stuff -FROM python:3.11.7-slim-bookworm as docs +FROM python:3.11.8-slim-bookworm as docs # By default, Docker has special steps to avoid keeping APT caches in the layers, which # is good, but in our case, we're going to mount a special cache volume (kept between @@ -105,7 +105,7 @@ USER docs # Now we're going to build our actual application, but not the actual production # image that it gets deployed into. -FROM python:3.11.7-slim-bookworm as build +FROM python:3.11.8-slim-bookworm as build # Define whether we're building a production or a development image. This will # generally be used to control whether or not we install our development and @@ -184,7 +184,7 @@ RUN --mount=type=cache,target=/root/.cache/pip \ # Now we're going to build our actual application image, which will eventually # pull in the static files that were built above. -FROM python:3.11.7-slim-bookworm +FROM python:3.11.8-slim-bookworm # Setup some basic environment variables that are ~never going to change. ENV PYTHONUNBUFFERED 1 @@ -212,7 +212,7 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \ && apt-get update \ && apt-get install --no-install-recommends -y \ libpq5 libxml2 libxslt1.1 libcurl4 \ - $(if [ "$DEVEL" = "yes" ]; then echo 'bash libjpeg62 postgresql-client postgresql build-essential libffi-dev libxml2-dev libxslt-dev libpq-dev libcurl4-openssl-dev libssl-dev vim'; fi) \ + $(if [ "$DEVEL" = "yes" ]; then echo 'bash libjpeg62 postgresql-client build-essential libffi-dev libxml2-dev libxslt-dev libpq-dev libcurl4-openssl-dev libssl-dev vim'; fi) \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* @@ -224,6 +224,3 @@ COPY --from=static /opt/warehouse/src/warehouse/static/dist/ /opt/warehouse/src/ COPY --from=static /opt/warehouse/src/warehouse/admin/static/dist/ /opt/warehouse/src/warehouse/admin/static/dist/ COPY --from=build /opt/warehouse/ /opt/warehouse/ COPY . /opt/warehouse/src/ - -# We cannot run `postgres` as root, so add a user to run the application -USER nobody diff --git a/Makefile b/Makefile index 5f678840076f..fee85f82eed3 100644 --- a/Makefile +++ b/Makefile @@ -61,7 +61,7 @@ debug: .state/docker-build-base docker compose run --rm --service-ports web tests: .state/docker-build-base - docker compose run --rm tests bin/tests $(T) $(TESTARGS) + docker compose run --rm tests bin/tests --postgresql-host db $(T) $(TESTARGS) static_tests: .state/docker-build-static docker compose run --rm static bin/static_tests $(T) $(TESTARGS) diff --git a/bin/lint b/bin/lint index b41163324315..c4edebeb5235 100755 --- a/bin/lint +++ b/bin/lint @@ -13,7 +13,7 @@ set -x # Actually run our tests. find . -name '*.py' -exec python -m pyupgrade --py311-plus {} + python -m flake8 . -python -m black --check *.py warehouse/ tests/ +python -m black --check --diff *.py warehouse/ tests/ python -m isort --check *.py warehouse/ tests/ python -m doc8 --allow-long-titles README.rst CONTRIBUTING.rst docs/ --ignore-path "docs/**/_build/" python -m curlylint ./warehouse/templates ./docs/blog diff --git a/bin/tests b/bin/tests index 7f9876afea55..c762888e1499 100755 --- a/bin/tests +++ b/bin/tests @@ -1,10 +1,42 @@ #!/bin/bash -set -ex +set -e + +# Click requires us to ensure we have a well configured environment to run +# our click commands. So we'll set our environment to ensure our locale is +# correct. +export LC_ALL="${ENCODING:-en_US.UTF-8}" +export LANG="${ENCODING:-en_US.UTF-8}" + +COMMAND_ARGS="$@" + +# Test the postgres connection +while [ $# -gt 0 ]; do + case $1 in + "--postgresql-host") POSTGRES_HOST="$2" + esac + shift +done + +# Test the postgres connection +ATTEMPTS=0 +until [ $ATTEMPTS -eq 5 ] || pg_isready -t 10 -h $POSTGRES_HOST; do + >&2 echo "Postgres is unavailable, sleeping" + sleep $(( ATTEMPTS++ )) +done + +if [ $ATTEMPTS -eq 5 ]; then + >&2 echo "Postgres is unavailable, exiting" + exit 1 +fi + +# Print all the following commands +set -x # Create any dist directories to silence whitenoise warnings. -mkdir -p warehouse/warehouse/admin/static/dist/ -mkdir -p warehouse/warehouse/static/dist/ +mkdir -p warehouse/admin/static/dist/ +mkdir -p warehouse/static/dist/ -python -m coverage run -m pytest --strict-markers "$@" +# Actually run our tests. +python -m coverage run -m pytest --strict-markers $COMMAND_ARGS python -m coverage html --show-contexts python -m coverage report -m --fail-under 100 diff --git a/docker-compose.yml b/docker-compose.yml index 3357bfcde7b9..237ed0aebfdf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -83,6 +83,7 @@ services: - ./warehouse:/opt/warehouse/src/warehouse:z - ./tests:/opt/warehouse/src/tests:z - ./htmlcov:/opt/warehouse/src/htmlcov:z + - ./setup.cfg:/opt/warehouse/src/setup.cfg:z - ./pyproject.toml:/opt/warehouse/src/pyproject.toml:z - packages:/var/opt/warehouse/packages - packages-archive:/var/opt/warehouse/packages-archive @@ -118,6 +119,8 @@ services: pull_policy: never volumes: *base_volumes depends_on: + db: + condition: service_healthy stripe: condition: service_started diff --git a/pyproject.toml b/pyproject.toml index e4138a1206db..264f479e718d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,5 @@ [tool.coverage.run] branch = true -data_file = "dev/.coverage" dynamic_context = "test_function" source = ["warehouse"] omit = [ diff --git a/requirements/deploy.in b/requirements/deploy.in index 1013c0542fd5..bb7f4dd0ee1b 100644 --- a/requirements/deploy.in +++ b/requirements/deploy.in @@ -1,2 +1,2 @@ gunicorn==21.2.0 -ddtrace==2.5.2 +ddtrace==2.6.3 diff --git a/requirements/deploy.txt b/requirements/deploy.txt index 40cc1d48985d..47f9e21ecabe 100644 --- a/requirements/deploy.txt +++ b/requirements/deploy.txt @@ -22,67 +22,67 @@ ddsketch==2.0.4 \ --hash=sha256:3227a270fd686a29d3a7128f9352ccf852314410380fc11384356f1ae2a75938 \ --hash=sha256:32f7314077fec8747d4faebaec2c854b5ffc399c5f552f73fa94024f48d74d64 # via ddtrace -ddtrace==2.5.2 \ - --hash=sha256:066403f0e00a8de09c8187037befe7463d1fab5d8178b62a07c2542792710d14 \ - --hash=sha256:15d78b0cd5d2090c063031d76e933b8b24e043d524a6091a751cf57b0fab025f \ - --hash=sha256:1e59f3958016fcec5eb16abd7979a9ec4d850733e2a03b878b096277fc092784 \ - --hash=sha256:20f1cb3bea1170410d603f9d557918c24d4d8783659c03817daea6352d9f37f9 \ - --hash=sha256:227bb0391d310e0d5a54505c7ab59f9692a5db91dc492373489bc45726980e1d \ - --hash=sha256:30186112f156a564efda5e2018240b55baee7664897ca5fc35c452d032a77185 \ - --hash=sha256:37b4d55a5be59530e6e5761a36d727aee812be69c81b00ee0182eb62be6f3b75 \ - --hash=sha256:38cbcb7b4ff1371480b29228d2b8e570e7d7b386a7632b96f9600135ec3eb9db \ - --hash=sha256:3aa2543c2303ab325af7794f2a8a420133cd9222e70bfbce3869da146fc5e2ba \ - --hash=sha256:3f4eed40d978352c7371804ecb68bbe9e55967bb904bd03b0568554e0b6b92cf \ - --hash=sha256:3ff039635470ba483ed448baaf6337d85a731b17af62fef06dfa811f761f374f \ - --hash=sha256:49aa4e0210862e829e09569de2e2f34ac17c5e246567c5b6662ec21e2a06d938 \ - --hash=sha256:4c54bc474c70151d5a141061b6c20a1efabdf458e4239c790d45fa12a13b8e7d \ - --hash=sha256:4d9e7a9e26c38ae1e368f5d820e78459ff2d39689f40d4a3db185ddb3686c383 \ - --hash=sha256:4df564e620ec7e657fcdb0d5bf1231aa1357bf49b736f0d9e9f6df17a23fc569 \ - --hash=sha256:512d3975b1657c706ca9c84373e5fce323f6fc94bfac33c30876ad8d55e0ea71 \ - --hash=sha256:57606af5380888e2e7cc67b7c4fa5e1bc51d29c48f004b4be0cbe1b319fddc75 \ - --hash=sha256:5aafd86eeea622cd0e8cf6b63632efc67a52a32317d2a376382ef6170d383c9f \ - --hash=sha256:5addeb19eea5ebdc23c493e5635f4c8737795b48ba637117a1895f31b900985f \ - --hash=sha256:5d3f1bc3ce87fbcf2256197178179ef681df720ebbc39b0559bda00247744533 \ - --hash=sha256:62e775ba9d2a2b5f952a6609029e965057bdd852ccd6e53b55c0f82ae83aa542 \ - --hash=sha256:637f16af1c84566bde044798312c67bc5676df949632ab02e740440558f2a598 \ - --hash=sha256:6bdfae9fa03af334820678196a4895450d0b6bd9f1b5119d42ddbd327a55fcce \ - --hash=sha256:6c61e72abec3f2f6b46e53712a32a971de1b6a9be657d5ebeff1334f6146babc \ - --hash=sha256:6d97f990d2322a23e82203cc5a2aa694fb0d42541a44bb120390e6598a63e5f5 \ - --hash=sha256:6e55c4738b58b4452933204305243e19000f6f283af93bf51b63382100cb8f21 \ - --hash=sha256:72d21fe6842a8d80c8765dd699153a2475ae2d49e82e10f9668eadb08b454040 \ - --hash=sha256:7351500241eb24c7d789b371a6860ca2b0e2db1ff9d317089153b562a3a461e1 \ - --hash=sha256:8814321822e4afc95ac86fbc476dc20d78dd4b1d510c02606459df4580093d18 \ - --hash=sha256:8840f0e82d6dca3888bd06e7ab0ca6d39009f3cd3475028d8bc03c939127afc2 \ - --hash=sha256:8f4b67e02ba5c316711719dcfc15e94f47684e7af1785289d016a29a2c664827 \ - --hash=sha256:96a791f03b62ebdb9f3e635a0e93711149123a8fc1c1c152be0d1cdb5d8e6359 \ - --hash=sha256:985738fe875b11f05dfa2b1f21a619d499344eb740f63e01d6eae1fb29eb949b \ - --hash=sha256:9bbd675d73aae6516e02a86cb830778771dafb0e182d5a122270ccd82ee77eed \ - --hash=sha256:9eaca41664dd0c2bd7257fe2e91c7e46718b20591bfaa0b5c01c39b599115f88 \ - --hash=sha256:a270d128c6067f52a76ecbb658fae3f4d3bd4888baa9e6159ff82b6de14c53be \ - --hash=sha256:a2cfc6ee800890556e404b94d13680c83952efa5d3dafa72ef8cb08a8782f874 \ - --hash=sha256:a34ccab0c8991c5fc5252d5cd6e88852cd7f77c8bf838de84e70b4a3bfacaad4 \ - --hash=sha256:a50057085b0972e695bb1ef3042f6cd6a1a3b12111fac4985942f2dbbcf8ac2f \ - --hash=sha256:a6e48caf63506d7ac3df7caa955b6258de91c1a1f55149506ab8ac36143770b9 \ - --hash=sha256:aa2e64f79ada9f2fd5307cd0eba726d8585e47b0282fb9463aaa4b267265e94a \ - --hash=sha256:aa596f2e80c525a2310e605bfa3fa6ba6790b2ae90c02e47ceee0e62ceae17a6 \ - --hash=sha256:ad6c0ae7baff9d00c689834aec0627274d681ed1d2a8ae627348a6191e8d32ec \ - --hash=sha256:b5fb2bbd38dc46ba6a7ea1031c4751b1ca888be5fac8a42049ebc2517707c00d \ - --hash=sha256:b923b099b9a1e50f01ce8bcd4d11e3255a48c71f3e6314dd9a482baed0a87ed6 \ - --hash=sha256:b93d8b536f5fc45a72bb2785051dc729f4d581ef2d69ed10bccae6a7487477b2 \ - --hash=sha256:c361ea11b442b04d8e011528205ed65b926d71d18f38d372270204eabf49b068 \ - --hash=sha256:caa6fb6bcfb3810d8f0882e489e7d2ef4dd3a92b452cfdd8d1fd4703dc496b17 \ - --hash=sha256:cbbcbf24bca8497f1412ec438fbdc94847aef9e86092ffd4f8626bbe6d278d33 \ - --hash=sha256:d24841a9390f3e169edcaf1ca5ac80599062e66dee43a510decb25e779b6f7b4 \ - --hash=sha256:d34f8da809e2783770a6c88396b3653fb12a4196e9b5f16b8c10f37bbf2b7b31 \ - --hash=sha256:dc3f26e04ba7521f6885d871fd6266fedc0a7ccf2637b85579c058927404bad7 \ - --hash=sha256:e93f3f5d3d57beb492b04286c758be65495908bd313df6f56865ad7af222e49e \ - --hash=sha256:ea2740a3d61876cb07b271af444e98cdc8b730497cfcddbc3794c7a7441b8d15 \ - --hash=sha256:ee76beaf87695f2204b0c2c2a3664b39f3483b7a8447b28e5e2bcc899861b3eb \ - --hash=sha256:ee8d0259a004964a8eddb394aa84a5754435d4270cd2041e6559c9e68fa49141 \ - --hash=sha256:f56735eb636d3ab2f7224f261d3a6bd43f884e9901d68407d485ea65f3dc0f46 \ - --hash=sha256:f918538a6adb33696be653d343ee318b16ea977376d9b7214d14fe97c42e9bd9 \ - --hash=sha256:f9dccdc69de364cffc2b892280724c78cb54db151452a0b6d1b4a89b6f060c44 \ - --hash=sha256:ffa4f5779c7000fe5960156bd15339184355b30a661b0955799cae50da5d03a7 +ddtrace==2.6.3 \ + --hash=sha256:04f9cf8220e84b201c95502427587073f67138c3b14bd5228ef78d1903e2ed8e \ + --hash=sha256:09fc377dab883dd3754e45df21b634e7f5893127032fc16982739160278daf68 \ + --hash=sha256:1b8bb148ffe3b1b898abc14e55ce0b547c5fd3c4522ce261c4d15dcb4d68ce6f \ + --hash=sha256:1be3b277b84dff4467ea9b89fea59495660970d69f5bda8c696df4e99af363e8 \ + --hash=sha256:2365bcec468fe502be61e1d5a8b7215330f18e86a3a246fc711ddf002e6ecdf4 \ + --hash=sha256:24f302c04eaf4e4b06ca62702abacbbf9652b7c3a4222e505d5395212c6fd61a \ + --hash=sha256:300502b48fbf8691973a8f61aea95b7b9e101fd5dea9256e06a49e01360da734 \ + --hash=sha256:339bd84b7884d284ec370805098996096082687313f72df11ad1fd871ac1b723 \ + --hash=sha256:36a18604c308b0e26716fa44f43382e9ca8937f4d7b1f36b59816eb3e541f39f \ + --hash=sha256:3b3b1b753e47a394917a094f7595570adbe8d30445c629f53fbc2276890dcd38 \ + --hash=sha256:3b7951cee45be8464add1f53c07a8702d1b3ae21e33b893907fddfa316b51bab \ + --hash=sha256:3fb44c3011b09d367d9e62927a8dec8efdc74065496aeca2d69d92b4368cde5c \ + --hash=sha256:49555672d850ab0c694d368c40139694f4eef1adf2bd9baf71a0a69075c5a5f1 \ + --hash=sha256:4adaf68614ce24af69c4a2665d929682b0e6d576997015b8e827ac400575b7cc \ + --hash=sha256:4c0ead2a0643125d6ef96db14dca0a0769fd6fa371a54c017658d072cffb182c \ + --hash=sha256:51705b1741865d9c963dbad19e4eb0ebb0831c02dead7ec804157bdc9c301f2e \ + --hash=sha256:54b3c417fecc3c497d7a2050d61c32aee1fb3d33c7e103aa882f43ad0a24acdd \ + --hash=sha256:5eb51bed0cef5b2ba3314d03c953616ec976ff74eff6e8d1ba957d34c14a315a \ + --hash=sha256:6853fbd7edaa5ef8cf02c4d2976f643d500e102ea7961da291f9fdae6ec31926 \ + --hash=sha256:698246e2cd18b08ac933faf20735aca1f0c9ffb86571cc53f99338b040005140 \ + --hash=sha256:714f36545422f9811af5f8cf1e05cdffbf29f060afae81c80ee2804eaa861bd6 \ + --hash=sha256:722aad4f5a25e5bcde47f38bd48db7f923a8c7708b3afc0ac16bf0da6a09c1d0 \ + --hash=sha256:74d24fce87056d8340b7a53bd48113ed2885a1c847f72ec602e2dadaee3e3e7e \ + --hash=sha256:75022e62cacbce5ac73e1830b809a43ecf3201839ad03ada3ffac394aae0ca29 \ + --hash=sha256:7b37328bab299f177f1ef121d14588156189adbff48d43e46d46aec875e43dd3 \ + --hash=sha256:7de9edf53b24bf299be41475e91e8d136873a51c7812f1ad581b4510085febea \ + --hash=sha256:82e13ee088d4aeb6906408758b84f90eeb8246c6ed9cd3a410e2ba85c6a0c226 \ + --hash=sha256:85945af14c22991f1040f18d221fd61a5a6224f755a726d553215b2864e58e6e \ + --hash=sha256:86bbb8425984bc02e8401c9a9f16f69bebe7491f45d9d7910897c821591a1be9 \ + --hash=sha256:872e12b3f228ae073bf5c7e2e8441887904467d51b9425650367feb5dbf42488 \ + --hash=sha256:8aac6994fc4f91eb6d5b3d0899c0900cafe413068774e4add27f22825d243bc0 \ + --hash=sha256:8f11f25ae5c89421e03eb00079b8d43aa436eb4c6f205b8313510090db58508b \ + --hash=sha256:90255d30d577e5f76903a233a3b53ea231604c70f3f4cc712ab078a235435bfa \ + --hash=sha256:91262d2fafaccb8147b551f53d67575ca41626f766d7579964026f69ffdcd375 \ + --hash=sha256:920f1248002523395b12228c2ecb207854e4bc06729e607c1cd0b7b0dea1d932 \ + --hash=sha256:96331a04a0a02e1df08757d21a7ad39f5d1bf1e648ec3b3572794847c667345f \ + --hash=sha256:98bb1345508a2c6eb887e59859ef37e4b10f2482a6aa0b8fc2fb47a3cb49e265 \ + --hash=sha256:9d56507551a60d8a2adee57ee0ce7b0ae041386585b7887ba42d63333ab6ff0c \ + --hash=sha256:9da62b9f8b62633e99a543340c9048f8bd4cb91f24033f9f03f5129190c06dd0 \ + --hash=sha256:9e6f197da7456c8c2ce8b0f13056b45d10e2e8aaa7a70037ac2f503884784e45 \ + --hash=sha256:a43a8b16b0ab35aca2719269110b07f674e2de48f83cb0d6f96304da947dcb25 \ + --hash=sha256:b16329070942672cc713dc44d1d2e7130d985b6630359c9cae007aa5dc29fce7 \ + --hash=sha256:b50a39a8f75bf8b376fabee92348889b054d7f53035d812c25d32194269cf3aa \ + --hash=sha256:bab18ff1dcd019f9413dcb709758a2455b3d98633d9579aa808cdb1f0d616c25 \ + --hash=sha256:baeb997ca80af756087c014023f9c71bbd8d1e5f65607243de5735b1dc55671e \ + --hash=sha256:bb932a46e978a32d591d53aabcc9ea5a4bd68206909d81a52c32349f2451d75a \ + --hash=sha256:bfdec3f7496006b1c1845ba4cbbe9110829d17a111a480768271429c0ce632a1 \ + --hash=sha256:c66b31ad1f24f23291bfa84a73e7a0750472f1d7b6ade4008f67c31292526324 \ + --hash=sha256:cb2f6220feb8034dd4ccbc52337b7b2248d784a3da0317dd68a28ecc18517bc4 \ + --hash=sha256:cc633b01f2add69d748aa23c01775e8fd31b72e087db8797c75c5b0c63bff915 \ + --hash=sha256:d0924bbcd7b1d2734e05643e19bb03655dc893a6172cdccadaab2fd8634f902d \ + --hash=sha256:d3a10eff219ac6b8d1b2e202d71579574aa50a76df9a4ce34376210d81e293ef \ + --hash=sha256:d60f426f2589c87fd6628ae1b3761a4b2537b8c64b790915461f2328d2f8f77c \ + --hash=sha256:d932b017321e2f1240edac6224d7d5e845397afbf3306aad79392ac26aaf96ee \ + --hash=sha256:de606220b9f6efdb6b2e22e8915e5bbaeb98078b1b25cfcb6e4d7234be1808d4 \ + --hash=sha256:e6b6ebb34352182e3128726aa81f46977186cf25c54ff40afb00f79cb7b9fe9f \ + --hash=sha256:e7d7f86eb76842e4cab59415c96a17d4c0d64380da9978c7f750077c9229729d \ + --hash=sha256:e9a172cd1932ac0d3dcf49e9dbd8b06256813f76f2400909caaa7e70c671be4d \ + --hash=sha256:f5181fae5bb2bf66fbb11c0239052956f481a24d2be1330d60ab2c4849bafca3 \ + --hash=sha256:fbaa1f20cabb688724e3cae8fc6087986bc7cea459773ca77b58c23b7ba31764 # via -r requirements/deploy.in deprecated==1.2.14 \ --hash=sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c \ @@ -108,18 +108,18 @@ packaging==23.2 \ --hash=sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5 \ --hash=sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7 # via gunicorn -protobuf==4.25.2 \ - --hash=sha256:10894a2885b7175d3984f2be8d9850712c57d5e7587a2410720af8be56cdaf62 \ - --hash=sha256:2db9f8fa64fbdcdc93767d3cf81e0f2aef176284071507e3ede160811502fd3d \ - --hash=sha256:33a1aeef4b1927431d1be780e87b641e322b88d654203a9e9d93f218ee359e61 \ - --hash=sha256:47f3de503fe7c1245f6f03bea7e8d3ec11c6c4a2ea9ef910e3221c8a15516d62 \ - --hash=sha256:5e5c933b4c30a988b52e0b7c02641760a5ba046edc5e43d3b94a74c9fc57c1b3 \ - --hash=sha256:8f62574857ee1de9f770baf04dde4165e30b15ad97ba03ceac65f760ff018ac9 \ - --hash=sha256:a8b7a98d4ce823303145bf3c1a8bdb0f2f4642a414b196f04ad9853ed0c8f830 \ - --hash=sha256:b50c949608682b12efb0b2717f53256f03636af5f60ac0c1d900df6213910fd6 \ - --hash=sha256:d66a769b8d687df9024f2985d5137a337f957a0916cf5464d1513eee96a63ff0 \ - --hash=sha256:fc381d1dd0516343f1440019cedf08a7405f791cd49eef4ae1ea06520bc1c020 \ - --hash=sha256:fe599e175cb347efc8ee524bcd4b902d11f7262c0e569ececcb89995c15f0a5e +protobuf==4.25.3 \ + --hash=sha256:19b270aeaa0099f16d3ca02628546b8baefe2955bbe23224aaf856134eccf1e4 \ + --hash=sha256:209ba4cc916bab46f64e56b85b090607a676f66b473e6b762e6f1d9d591eb2e8 \ + --hash=sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c \ + --hash=sha256:7c8daa26095f82482307bc717364e7c13f4f1c99659be82890dcfc215194554d \ + --hash=sha256:c053062984e61144385022e53678fbded7aea14ebb3e0305ae3592fb219ccfa4 \ + --hash=sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa \ + --hash=sha256:e3c97a1555fd6388f857770ff8b9703083de6bf1f9274a002a332d65fbb56c8c \ + --hash=sha256:e7cb0ae90dd83727f0c0718634ed56837bfeeee29a5f82a7514c03ee1364c019 \ + --hash=sha256:f0700d54bcf45424477e46a9f0944155b46fb0639d69728739c0e47bab83f2b9 \ + --hash=sha256:f1279ab38ecbfae7e456a108c5c0681e4956d5b1090027c1de0f934dfdb4b35c \ + --hash=sha256:f4f118245c4a087776e0a8408be33cf09f6c547442c00395fbfb116fac2f8ac2 # via # ddsketch # ddtrace diff --git a/requirements/docs-blog.txt b/requirements/docs-blog.txt index 1fc1782b630f..fe909766423a 100644 --- a/requirements/docs-blog.txt +++ b/requirements/docs-blog.txt @@ -190,9 +190,9 @@ gitdb==4.0.11 \ --hash=sha256:81a3407ddd2ee8df444cbacea00e2d038e40150acfa3001696fe0dcf1d3adfa4 \ --hash=sha256:bf5421126136d6d0af55bc1e7c1af1c397a34f5b7bd79e776cd3e89785c2b04b # via gitpython -gitpython==3.1.41 \ - --hash=sha256:c36b6634d069b3f719610175020a9aed919421c87552185b085e04fbbdb10b7c \ - --hash=sha256:ed66e624884f76df22c8e16066d567aaa5a37d5b5fa19db2c6df6f7156db9048 +gitpython==3.1.42 \ + --hash=sha256:1bf9cd7c9e7255f77778ea54359e54ac22a72a5b51288c457c881057b7bb9ecd \ + --hash=sha256:2d99869e0fef71a73cbd242528105af1d6c1b108c60dfabd994bf292f76c3ceb # via mkdocs-rss-plugin idna==3.6 \ --hash=sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca \ @@ -286,17 +286,17 @@ mkdocs==1.5.3 \ # -r requirements/docs-blog.in # mkdocs-material # mkdocs-rss-plugin -mkdocs-material==9.5.7 \ - --hash=sha256:0be8ce8bcfebb52bae9b00cf9b851df45b8a92d629afcfd7f2c09b2dfa155ea3 \ - --hash=sha256:16110292575d88a338d2961f3cb665cf12943ff8829e551a9b364f24019e46af +mkdocs-material==9.5.10 \ + --hash=sha256:3c6c46b57d2ee3c8890e6e0406e68b6863cf65768f0f436990a742702d198442 \ + --hash=sha256:6ad626dbb31070ebbaedff813323a16a406629620e04b96458f16e6e9c7008fe # via -r requirements/docs-blog.in mkdocs-material-extensions==1.3.1 \ --hash=sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443 \ --hash=sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31 # via mkdocs-material -mkdocs-rss-plugin==1.12.0 \ - --hash=sha256:81ccf8d328539e59960b08466a7aaefc5937b4747534e624166429e4a274dcca \ - --hash=sha256:e2150df0765ce077bdbf884964fbd04dce78c564e42e5d13c2046e394736965c +mkdocs-rss-plugin==1.12.1 \ + --hash=sha256:5df9bddfdc1465623def1b14c2656c5e8f62fa7c8bd1c0c667e01fc86105d415 \ + --hash=sha256:acfb8eec95f1db389b36770baf99af3b87e38484cc37993194a35aa173eb3fe8 # via -r requirements/docs-blog.in packaging==23.2 \ --hash=sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5 \ @@ -567,34 +567,36 @@ urllib3==1.26.18 \ --hash=sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07 \ --hash=sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0 # via requests -watchdog==3.0.0 \ - --hash=sha256:0e06ab8858a76e1219e68c7573dfeba9dd1c0219476c5a44d5333b01d7e1743a \ - --hash=sha256:13bbbb462ee42ec3c5723e1205be8ced776f05b100e4737518c67c8325cf6100 \ - --hash=sha256:233b5817932685d39a7896b1090353fc8efc1ef99c9c054e46c8002561252fb8 \ - --hash=sha256:25f70b4aa53bd743729c7475d7ec41093a580528b100e9a8c5b5efe8899592fc \ - --hash=sha256:2b57a1e730af3156d13b7fdddfc23dea6487fceca29fc75c5a868beed29177ae \ - --hash=sha256:336adfc6f5cc4e037d52db31194f7581ff744b67382eb6021c868322e32eef41 \ - --hash=sha256:3aa7f6a12e831ddfe78cdd4f8996af9cf334fd6346531b16cec61c3b3c0d8da0 \ - --hash=sha256:3ed7c71a9dccfe838c2f0b6314ed0d9b22e77d268c67e015450a29036a81f60f \ - --hash=sha256:4c9956d27be0bb08fc5f30d9d0179a855436e655f046d288e2bcc11adfae893c \ - --hash=sha256:4d98a320595da7a7c5a18fc48cb633c2e73cda78f93cac2ef42d42bf609a33f9 \ - --hash=sha256:4f94069eb16657d2c6faada4624c39464f65c05606af50bb7902e036e3219be3 \ - --hash=sha256:5113334cf8cf0ac8cd45e1f8309a603291b614191c9add34d33075727a967709 \ - --hash=sha256:51f90f73b4697bac9c9a78394c3acbbd331ccd3655c11be1a15ae6fe289a8c83 \ - --hash=sha256:5d9f3a10e02d7371cd929b5d8f11e87d4bad890212ed3901f9b4d68767bee759 \ - --hash=sha256:7ade88d0d778b1b222adebcc0927428f883db07017618a5e684fd03b83342bd9 \ - --hash=sha256:7c5f84b5194c24dd573fa6472685b2a27cc5a17fe5f7b6fd40345378ca6812e3 \ - --hash=sha256:7e447d172af52ad204d19982739aa2346245cc5ba6f579d16dac4bfec226d2e7 \ - --hash=sha256:8ae9cda41fa114e28faf86cb137d751a17ffd0316d1c34ccf2235e8a84365c7f \ - --hash=sha256:8f3ceecd20d71067c7fd4c9e832d4e22584318983cabc013dbf3f70ea95de346 \ - --hash=sha256:9fac43a7466eb73e64a9940ac9ed6369baa39b3bf221ae23493a9ec4d0022674 \ - --hash=sha256:a70a8dcde91be523c35b2bf96196edc5730edb347e374c7de7cd20c43ed95397 \ - --hash=sha256:adfdeab2da79ea2f76f87eb42a3ab1966a5313e5a69a0213a3cc06ef692b0e96 \ - --hash=sha256:ba07e92756c97e3aca0912b5cbc4e5ad802f4557212788e72a72a47ff376950d \ - --hash=sha256:c07253088265c363d1ddf4b3cdb808d59a0468ecd017770ed716991620b8f77a \ - --hash=sha256:c9d8c8ec7efb887333cf71e328e39cffbf771d8f8f95d308ea4125bf5f90ba64 \ - --hash=sha256:d00e6be486affb5781468457b21a6cbe848c33ef43f9ea4a73b4882e5f188a44 \ - --hash=sha256:d429c2430c93b7903914e4db9a966c7f2b068dd2ebdd2fa9b9ce094c7d459f33 +watchdog==4.0.0 \ + --hash=sha256:11e12fafb13372e18ca1bbf12d50f593e7280646687463dd47730fd4f4d5d257 \ + --hash=sha256:2895bf0518361a9728773083908801a376743bcc37dfa252b801af8fd281b1ca \ + --hash=sha256:39cb34b1f1afbf23e9562501673e7146777efe95da24fab5707b88f7fb11649b \ + --hash=sha256:45cc09cc4c3b43fb10b59ef4d07318d9a3ecdbff03abd2e36e77b6dd9f9a5c85 \ + --hash=sha256:4986db5e8880b0e6b7cd52ba36255d4793bf5cdc95bd6264806c233173b1ec0b \ + --hash=sha256:5369136a6474678e02426bd984466343924d1df8e2fd94a9b443cb7e3aa20d19 \ + --hash=sha256:557ba04c816d23ce98a06e70af6abaa0485f6d94994ec78a42b05d1c03dcbd50 \ + --hash=sha256:6a4db54edea37d1058b08947c789a2354ee02972ed5d1e0dca9b0b820f4c7f92 \ + --hash=sha256:6a80d5cae8c265842c7419c560b9961561556c4361b297b4c431903f8c33b269 \ + --hash=sha256:6a9c71a0b02985b4b0b6d14b875a6c86ddea2fdbebd0c9a720a806a8bbffc69f \ + --hash=sha256:6c47bdd680009b11c9ac382163e05ca43baf4127954c5f6d0250e7d772d2b80c \ + --hash=sha256:6e949a8a94186bced05b6508faa61b7adacc911115664ccb1923b9ad1f1ccf7b \ + --hash=sha256:73c7a935e62033bd5e8f0da33a4dcb763da2361921a69a5a95aaf6c93aa03a87 \ + --hash=sha256:76ad8484379695f3fe46228962017a7e1337e9acadafed67eb20aabb175df98b \ + --hash=sha256:8350d4055505412a426b6ad8c521bc7d367d1637a762c70fdd93a3a0d595990b \ + --hash=sha256:87e9df830022488e235dd601478c15ad73a0389628588ba0b028cb74eb72fed8 \ + --hash=sha256:8f9a542c979df62098ae9c58b19e03ad3df1c9d8c6895d96c0d51da17b243b1c \ + --hash=sha256:8fec441f5adcf81dd240a5fe78e3d83767999771630b5ddfc5867827a34fa3d3 \ + --hash=sha256:9a03e16e55465177d416699331b0f3564138f1807ecc5f2de9d55d8f188d08c7 \ + --hash=sha256:ba30a896166f0fee83183cec913298151b73164160d965af2e93a20bbd2ab605 \ + --hash=sha256:c17d98799f32e3f55f181f19dd2021d762eb38fdd381b4a748b9f5a36738e935 \ + --hash=sha256:c522392acc5e962bcac3b22b9592493ffd06d1fc5d755954e6be9f4990de932b \ + --hash=sha256:d0f9bd1fd919134d459d8abf954f63886745f4660ef66480b9d753a7c9d40927 \ + --hash=sha256:d18d7f18a47de6863cd480734613502904611730f8def45fc52a5d97503e5101 \ + --hash=sha256:d31481ccf4694a8416b681544c23bd271f5a123162ab603c7d7d2dd7dd901a07 \ + --hash=sha256:e3e7065cbdabe6183ab82199d7a4f6b3ba0a438c5a512a68559846ccb76a78ec \ + --hash=sha256:eed82cdf79cd7f0232e2fdc1ad05b06a5e102a43e331f7d041e5f0e0a34a51c4 \ + --hash=sha256:f970663fa4f7e80401a7b0cbeec00fa801bf0287d93d48368fc3e6fa32716245 \ + --hash=sha256:f9b2fdca47dc855516b2d66eef3c39f2672cbf7e7a42e7e67ad2cbfcd6ba107d # via mkdocs webencodings==0.5.1 \ --hash=sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78 \ diff --git a/requirements/docs-user.txt b/requirements/docs-user.txt index 11d6d6fc72fa..a898c6359b64 100644 --- a/requirements/docs-user.txt +++ b/requirements/docs-user.txt @@ -213,9 +213,9 @@ mkdocs-macros-plugin==1.0.5 \ --hash=sha256:f60e26f711f5a830ddf1e7980865bf5c0f1180db56109803cdd280073c1a050a \ --hash=sha256:fe348d75f01c911f362b6d998c57b3d85b505876dde69db924f2c512c395c328 # via -r requirements/docs-user.in -mkdocs-material==9.5.7 \ - --hash=sha256:0be8ce8bcfebb52bae9b00cf9b851df45b8a92d629afcfd7f2c09b2dfa155ea3 \ - --hash=sha256:16110292575d88a338d2961f3cb665cf12943ff8829e551a9b364f24019e46af +mkdocs-material==9.5.10 \ + --hash=sha256:3c6c46b57d2ee3c8890e6e0406e68b6863cf65768f0f436990a742702d198442 \ + --hash=sha256:6ad626dbb31070ebbaedff813323a16a406629620e04b96458f16e6e9c7008fe # via -r requirements/docs-user.in mkdocs-material-extensions==1.3.1 \ --hash=sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443 \ @@ -411,32 +411,34 @@ urllib3==1.26.18 \ --hash=sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07 \ --hash=sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0 # via requests -watchdog==3.0.0 \ - --hash=sha256:0e06ab8858a76e1219e68c7573dfeba9dd1c0219476c5a44d5333b01d7e1743a \ - --hash=sha256:13bbbb462ee42ec3c5723e1205be8ced776f05b100e4737518c67c8325cf6100 \ - --hash=sha256:233b5817932685d39a7896b1090353fc8efc1ef99c9c054e46c8002561252fb8 \ - --hash=sha256:25f70b4aa53bd743729c7475d7ec41093a580528b100e9a8c5b5efe8899592fc \ - --hash=sha256:2b57a1e730af3156d13b7fdddfc23dea6487fceca29fc75c5a868beed29177ae \ - --hash=sha256:336adfc6f5cc4e037d52db31194f7581ff744b67382eb6021c868322e32eef41 \ - --hash=sha256:3aa7f6a12e831ddfe78cdd4f8996af9cf334fd6346531b16cec61c3b3c0d8da0 \ - --hash=sha256:3ed7c71a9dccfe838c2f0b6314ed0d9b22e77d268c67e015450a29036a81f60f \ - --hash=sha256:4c9956d27be0bb08fc5f30d9d0179a855436e655f046d288e2bcc11adfae893c \ - --hash=sha256:4d98a320595da7a7c5a18fc48cb633c2e73cda78f93cac2ef42d42bf609a33f9 \ - --hash=sha256:4f94069eb16657d2c6faada4624c39464f65c05606af50bb7902e036e3219be3 \ - --hash=sha256:5113334cf8cf0ac8cd45e1f8309a603291b614191c9add34d33075727a967709 \ - --hash=sha256:51f90f73b4697bac9c9a78394c3acbbd331ccd3655c11be1a15ae6fe289a8c83 \ - --hash=sha256:5d9f3a10e02d7371cd929b5d8f11e87d4bad890212ed3901f9b4d68767bee759 \ - --hash=sha256:7ade88d0d778b1b222adebcc0927428f883db07017618a5e684fd03b83342bd9 \ - --hash=sha256:7c5f84b5194c24dd573fa6472685b2a27cc5a17fe5f7b6fd40345378ca6812e3 \ - --hash=sha256:7e447d172af52ad204d19982739aa2346245cc5ba6f579d16dac4bfec226d2e7 \ - --hash=sha256:8ae9cda41fa114e28faf86cb137d751a17ffd0316d1c34ccf2235e8a84365c7f \ - --hash=sha256:8f3ceecd20d71067c7fd4c9e832d4e22584318983cabc013dbf3f70ea95de346 \ - --hash=sha256:9fac43a7466eb73e64a9940ac9ed6369baa39b3bf221ae23493a9ec4d0022674 \ - --hash=sha256:a70a8dcde91be523c35b2bf96196edc5730edb347e374c7de7cd20c43ed95397 \ - --hash=sha256:adfdeab2da79ea2f76f87eb42a3ab1966a5313e5a69a0213a3cc06ef692b0e96 \ - --hash=sha256:ba07e92756c97e3aca0912b5cbc4e5ad802f4557212788e72a72a47ff376950d \ - --hash=sha256:c07253088265c363d1ddf4b3cdb808d59a0468ecd017770ed716991620b8f77a \ - --hash=sha256:c9d8c8ec7efb887333cf71e328e39cffbf771d8f8f95d308ea4125bf5f90ba64 \ - --hash=sha256:d00e6be486affb5781468457b21a6cbe848c33ef43f9ea4a73b4882e5f188a44 \ - --hash=sha256:d429c2430c93b7903914e4db9a966c7f2b068dd2ebdd2fa9b9ce094c7d459f33 +watchdog==4.0.0 \ + --hash=sha256:11e12fafb13372e18ca1bbf12d50f593e7280646687463dd47730fd4f4d5d257 \ + --hash=sha256:2895bf0518361a9728773083908801a376743bcc37dfa252b801af8fd281b1ca \ + --hash=sha256:39cb34b1f1afbf23e9562501673e7146777efe95da24fab5707b88f7fb11649b \ + --hash=sha256:45cc09cc4c3b43fb10b59ef4d07318d9a3ecdbff03abd2e36e77b6dd9f9a5c85 \ + --hash=sha256:4986db5e8880b0e6b7cd52ba36255d4793bf5cdc95bd6264806c233173b1ec0b \ + --hash=sha256:5369136a6474678e02426bd984466343924d1df8e2fd94a9b443cb7e3aa20d19 \ + --hash=sha256:557ba04c816d23ce98a06e70af6abaa0485f6d94994ec78a42b05d1c03dcbd50 \ + --hash=sha256:6a4db54edea37d1058b08947c789a2354ee02972ed5d1e0dca9b0b820f4c7f92 \ + --hash=sha256:6a80d5cae8c265842c7419c560b9961561556c4361b297b4c431903f8c33b269 \ + --hash=sha256:6a9c71a0b02985b4b0b6d14b875a6c86ddea2fdbebd0c9a720a806a8bbffc69f \ + --hash=sha256:6c47bdd680009b11c9ac382163e05ca43baf4127954c5f6d0250e7d772d2b80c \ + --hash=sha256:6e949a8a94186bced05b6508faa61b7adacc911115664ccb1923b9ad1f1ccf7b \ + --hash=sha256:73c7a935e62033bd5e8f0da33a4dcb763da2361921a69a5a95aaf6c93aa03a87 \ + --hash=sha256:76ad8484379695f3fe46228962017a7e1337e9acadafed67eb20aabb175df98b \ + --hash=sha256:8350d4055505412a426b6ad8c521bc7d367d1637a762c70fdd93a3a0d595990b \ + --hash=sha256:87e9df830022488e235dd601478c15ad73a0389628588ba0b028cb74eb72fed8 \ + --hash=sha256:8f9a542c979df62098ae9c58b19e03ad3df1c9d8c6895d96c0d51da17b243b1c \ + --hash=sha256:8fec441f5adcf81dd240a5fe78e3d83767999771630b5ddfc5867827a34fa3d3 \ + --hash=sha256:9a03e16e55465177d416699331b0f3564138f1807ecc5f2de9d55d8f188d08c7 \ + --hash=sha256:ba30a896166f0fee83183cec913298151b73164160d965af2e93a20bbd2ab605 \ + --hash=sha256:c17d98799f32e3f55f181f19dd2021d762eb38fdd381b4a748b9f5a36738e935 \ + --hash=sha256:c522392acc5e962bcac3b22b9592493ffd06d1fc5d755954e6be9f4990de932b \ + --hash=sha256:d0f9bd1fd919134d459d8abf954f63886745f4660ef66480b9d753a7c9d40927 \ + --hash=sha256:d18d7f18a47de6863cd480734613502904611730f8def45fc52a5d97503e5101 \ + --hash=sha256:d31481ccf4694a8416b681544c23bd271f5a123162ab603c7d7d2dd7dd901a07 \ + --hash=sha256:e3e7065cbdabe6183ab82199d7a4f6b3ba0a438c5a512a68559846ccb76a78ec \ + --hash=sha256:eed82cdf79cd7f0232e2fdc1ad05b06a5e102a43e331f7d041e5f0e0a34a51c4 \ + --hash=sha256:f970663fa4f7e80401a7b0cbeec00fa801bf0287d93d48368fc3e6fa32716245 \ + --hash=sha256:f9b2fdca47dc855516b2d66eef3c39f2672cbf7e7a42e7e67ad2cbfcd6ba107d # via mkdocs diff --git a/requirements/lint.in b/requirements/lint.in index 07e2042a8c32..96cf5fceffdf 100644 --- a/requirements/lint.in +++ b/requirements/lint.in @@ -2,7 +2,7 @@ doc8>=1.1.0 flake8 curlylint pep8-naming -black==23.12.1 +black==24.2.0 isort>=5.13.1 pyupgrade mypy diff --git a/requirements/lint.txt b/requirements/lint.txt index 2c2e2836caa8..9343fe156ff7 100644 --- a/requirements/lint.txt +++ b/requirements/lint.txt @@ -8,29 +8,29 @@ attrs==23.2.0 \ --hash=sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30 \ --hash=sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1 # via curlylint -black==23.12.1 \ - --hash=sha256:0808494f2b2df923ffc5723ed3c7b096bd76341f6213989759287611e9837d50 \ - --hash=sha256:1fa88a0f74e50e4487477bc0bb900c6781dbddfdfa32691e780bf854c3b4a47f \ - --hash=sha256:25e57fd232a6d6ff3f4478a6fd0580838e47c93c83eaf1ccc92d4faf27112c4e \ - --hash=sha256:2d9e13db441c509a3763a7a3d9a49ccc1b4e974a47be4e08ade2a228876500ec \ - --hash=sha256:3e1b38b3135fd4c025c28c55ddfc236b05af657828a8a6abe5deec419a0b7055 \ - --hash=sha256:3fa4be75ef2a6b96ea8d92b1587dd8cb3a35c7e3d51f0738ced0781c3aa3a5a3 \ - --hash=sha256:4ce3ef14ebe8d9509188014d96af1c456a910d5b5cbf434a09fef7e024b3d0d5 \ - --hash=sha256:4f0031eaa7b921db76decd73636ef3a12c942ed367d8c3841a0739412b260a54 \ - --hash=sha256:602cfb1196dc692424c70b6507593a2b29aac0547c1be9a1d1365f0d964c353b \ - --hash=sha256:6d1bd9c210f8b109b1762ec9fd36592fdd528485aadb3f5849b2740ef17e674e \ - --hash=sha256:78baad24af0f033958cad29731e27363183e140962595def56423e626f4bee3e \ - --hash=sha256:8d4df77958a622f9b5a4c96edb4b8c0034f8434032ab11077ec6c56ae9f384ba \ - --hash=sha256:97e56155c6b737854e60a9ab1c598ff2533d57e7506d97af5481141671abf3ea \ - --hash=sha256:9c4352800f14be5b4864016882cdba10755bd50805c95f728011bcb47a4afd59 \ - --hash=sha256:a4d6a9668e45ad99d2f8ec70d5c8c04ef4f32f648ef39048d010b0689832ec6d \ - --hash=sha256:a920b569dc6b3472513ba6ddea21f440d4b4c699494d2e972a1753cdc25df7b0 \ - --hash=sha256:ae76c22bde5cbb6bfd211ec343ded2163bba7883c7bc77f6b756a1049436fbb9 \ - --hash=sha256:b18fb2ae6c4bb63eebe5be6bd869ba2f14fd0259bda7d18a46b764d8fb86298a \ - --hash=sha256:c04b6d9d20e9c13f43eee8ea87d44156b8505ca8a3c878773f68b4e4812a421e \ - --hash=sha256:c88b3711d12905b74206227109272673edce0cb29f27e1385f33b0163c414bba \ - --hash=sha256:dd15245c8b68fe2b6bd0f32c1556509d11bb33aec9b5d0866dd8e2ed3dba09c2 \ - --hash=sha256:e0aaf6041986767a5e0ce663c7a2f0e9eaf21e6ff87a5f95cbf3675bfd4c41d2 +black==24.2.0 \ + --hash=sha256:057c3dc602eaa6fdc451069bd027a1b2635028b575a6c3acfd63193ced20d9c8 \ + --hash=sha256:08654d0797e65f2423f850fc8e16a0ce50925f9337fb4a4a176a7aa4026e63f8 \ + --hash=sha256:163baf4ef40e6897a2a9b83890e59141cc8c2a98f2dda5080dc15c00ee1e62cd \ + --hash=sha256:1e08fb9a15c914b81dd734ddd7fb10513016e5ce7e6704bdd5e1251ceee51ac9 \ + --hash=sha256:4dd76e9468d5536abd40ffbc7a247f83b2324f0c050556d9c371c2b9a9a95e31 \ + --hash=sha256:4f9de21bafcba9683853f6c96c2d515e364aee631b178eaa5145fc1c61a3cc92 \ + --hash=sha256:61a0391772490ddfb8a693c067df1ef5227257e72b0e4108482b8d41b5aee13f \ + --hash=sha256:6981eae48b3b33399c8757036c7f5d48a535b962a7c2310d19361edeef64ce29 \ + --hash=sha256:7e53a8c630f71db01b28cd9602a1ada68c937cbf2c333e6ed041390d6968faf4 \ + --hash=sha256:810d445ae6069ce64030c78ff6127cd9cd178a9ac3361435708b907d8a04c693 \ + --hash=sha256:93601c2deb321b4bad8f95df408e3fb3943d85012dddb6121336b8e24a0d1218 \ + --hash=sha256:992e451b04667116680cb88f63449267c13e1ad134f30087dec8527242e9862a \ + --hash=sha256:9db528bccb9e8e20c08e716b3b09c6bdd64da0dd129b11e160bf082d4642ac23 \ + --hash=sha256:a0057f800de6acc4407fe75bb147b0c2b5cbb7c3ed110d3e5999cd01184d53b0 \ + --hash=sha256:ba15742a13de85e9b8f3239c8f807723991fbfae24bad92d34a2b12e81904982 \ + --hash=sha256:bce4f25c27c3435e4dace4815bcb2008b87e167e3bf4ee47ccdc5ce906eb4894 \ + --hash=sha256:ca610d29415ee1a30a3f30fab7a8f4144e9d34c89a235d81292a1edb2b55f540 \ + --hash=sha256:d533d5e3259720fdbc1b37444491b024003e012c5173f7d06825a77508085430 \ + --hash=sha256:d84f29eb3ee44859052073b7636533ec995bd0f64e2fb43aeceefc70090e752b \ + --hash=sha256:e37c99f89929af50ffaf912454b3e3b47fd64109659026b678c091a4cd450fb2 \ + --hash=sha256:e8a6ae970537e67830776488bca52000eaa37fa63b9988e8c487458d9cd5ace6 \ + --hash=sha256:faf2ee02e6612577ba0181f4347bcbcf591eb122f7841ae5ba233d12c39dcb4d # via -r requirements/lint.in boto3-stubs==1.28.63 \ --hash=sha256:bd1becb0f8781d0a3022261a41d33f757a121117bf84ea6476b4761cb9e3cfd5 \ @@ -104,39 +104,39 @@ click==8.1.7 \ # via # black # curlylint -cryptography==42.0.2 \ - --hash=sha256:087887e55e0b9c8724cf05361357875adb5c20dec27e5816b653492980d20380 \ - --hash=sha256:09a77e5b2e8ca732a19a90c5bca2d124621a1edb5438c5daa2d2738bfeb02589 \ - --hash=sha256:130c0f77022b2b9c99d8cebcdd834d81705f61c68e91ddd614ce74c657f8b3ea \ - --hash=sha256:141e2aa5ba100d3788c0ad7919b288f89d1fe015878b9659b307c9ef867d3a65 \ - --hash=sha256:28cb2c41f131a5758d6ba6a0504150d644054fd9f3203a1e8e8d7ac3aea7f73a \ - --hash=sha256:2f9f14185962e6a04ab32d1abe34eae8a9001569ee4edb64d2304bf0d65c53f3 \ - --hash=sha256:320948ab49883557a256eab46149df79435a22d2fefd6a66fe6946f1b9d9d008 \ - --hash=sha256:36d4b7c4be6411f58f60d9ce555a73df8406d484ba12a63549c88bd64f7967f1 \ - --hash=sha256:3b15c678f27d66d247132cbf13df2f75255627bcc9b6a570f7d2fd08e8c081d2 \ - --hash=sha256:3dbd37e14ce795b4af61b89b037d4bc157f2cb23e676fa16932185a04dfbf635 \ - --hash=sha256:4383b47f45b14459cab66048d384614019965ba6c1a1a141f11b5a551cace1b2 \ - --hash=sha256:44c95c0e96b3cb628e8452ec060413a49002a247b2b9938989e23a2c8291fc90 \ - --hash=sha256:4b063d3413f853e056161eb0c7724822a9740ad3caa24b8424d776cebf98e7ee \ - --hash=sha256:52ed9ebf8ac602385126c9a2fe951db36f2cb0c2538d22971487f89d0de4065a \ - --hash=sha256:55d1580e2d7e17f45d19d3b12098e352f3a37fe86d380bf45846ef257054b242 \ - --hash=sha256:5ef9bc3d046ce83c4bbf4c25e1e0547b9c441c01d30922d812e887dc5f125c12 \ - --hash=sha256:5fa82a26f92871eca593b53359c12ad7949772462f887c35edaf36f87953c0e2 \ - --hash=sha256:61321672b3ac7aade25c40449ccedbc6db72c7f5f0fdf34def5e2f8b51ca530d \ - --hash=sha256:701171f825dcab90969596ce2af253143b93b08f1a716d4b2a9d2db5084ef7be \ - --hash=sha256:841ec8af7a8491ac76ec5a9522226e287187a3107e12b7d686ad354bb78facee \ - --hash=sha256:8a06641fb07d4e8f6c7dda4fc3f8871d327803ab6542e33831c7ccfdcb4d0ad6 \ - --hash=sha256:8e88bb9eafbf6a4014d55fb222e7360eef53e613215085e65a13290577394529 \ - --hash=sha256:a00aee5d1b6c20620161984f8ab2ab69134466c51f58c052c11b076715e72929 \ - --hash=sha256:a047682d324ba56e61b7ea7c7299d51e61fd3bca7dad2ccc39b72bd0118d60a1 \ - --hash=sha256:a7ef8dd0bf2e1d0a27042b231a3baac6883cdd5557036f5e8df7139255feaac6 \ - --hash=sha256:ad28cff53f60d99a928dfcf1e861e0b2ceb2bc1f08a074fdd601b314e1cc9e0a \ - --hash=sha256:b9097a208875fc7bbeb1286d0125d90bdfed961f61f214d3f5be62cd4ed8a446 \ - --hash=sha256:b97fe7d7991c25e6a31e5d5e795986b18fbbb3107b873d5f3ae6dc9a103278e9 \ - --hash=sha256:e0ec52ba3c7f1b7d813cd52649a5b3ef1fc0d433219dc8c93827c57eab6cf888 \ - --hash=sha256:ea2c3ffb662fec8bbbfce5602e2c159ff097a4631d96235fcf0fb00e59e3ece4 \ - --hash=sha256:fa3dec4ba8fb6e662770b74f62f1a0c7d4e37e25b58b2bf2c1be4c95372b4a33 \ - --hash=sha256:fbeb725c9dc799a574518109336acccaf1303c30d45c075c665c0793c2f79a7f +cryptography==42.0.4 \ + --hash=sha256:01911714117642a3f1792c7f376db572aadadbafcd8d75bb527166009c9f1d1b \ + --hash=sha256:0e89f7b84f421c56e7ff69f11c441ebda73b8a8e6488d322ef71746224c20fce \ + --hash=sha256:12d341bd42cdb7d4937b0cabbdf2a94f949413ac4504904d0cdbdce4a22cbf88 \ + --hash=sha256:15a1fb843c48b4a604663fa30af60818cd28f895572386e5f9b8a665874c26e7 \ + --hash=sha256:1cdcdbd117681c88d717437ada72bdd5be9de117f96e3f4d50dab3f59fd9ab20 \ + --hash=sha256:1df6fcbf60560d2113b5ed90f072dc0b108d64750d4cbd46a21ec882c7aefce9 \ + --hash=sha256:3c6048f217533d89f2f8f4f0fe3044bf0b2090453b7b73d0b77db47b80af8dff \ + --hash=sha256:3e970a2119507d0b104f0a8e281521ad28fc26f2820687b3436b8c9a5fcf20d1 \ + --hash=sha256:44a64043f743485925d3bcac548d05df0f9bb445c5fcca6681889c7c3ab12764 \ + --hash=sha256:4e36685cb634af55e0677d435d425043967ac2f3790ec652b2b88ad03b85c27b \ + --hash=sha256:5f8907fcf57392cd917892ae83708761c6ff3c37a8e835d7246ff0ad251d9298 \ + --hash=sha256:69b22ab6506a3fe483d67d1ed878e1602bdd5912a134e6202c1ec672233241c1 \ + --hash=sha256:6bfadd884e7280df24d26f2186e4e07556a05d37393b0f220a840b083dc6a824 \ + --hash=sha256:6d0fbe73728c44ca3a241eff9aefe6496ab2656d6e7a4ea2459865f2e8613257 \ + --hash=sha256:6ffb03d419edcab93b4b19c22ee80c007fb2d708429cecebf1dd3258956a563a \ + --hash=sha256:810bcf151caefc03e51a3d61e53335cd5c7316c0a105cc695f0959f2c638b129 \ + --hash=sha256:831a4b37accef30cccd34fcb916a5d7b5be3cbbe27268a02832c3e450aea39cb \ + --hash=sha256:887623fe0d70f48ab3f5e4dbf234986b1329a64c066d719432d0698522749929 \ + --hash=sha256:a0298bdc6e98ca21382afe914c642620370ce0470a01e1bef6dd9b5354c36854 \ + --hash=sha256:a1327f280c824ff7885bdeef8578f74690e9079267c1c8bd7dc5cc5aa065ae52 \ + --hash=sha256:c1f25b252d2c87088abc8bbc4f1ecbf7c919e05508a7e8628e6875c40bc70923 \ + --hash=sha256:c3a5cbc620e1e17009f30dd34cb0d85c987afd21c41a74352d1719be33380885 \ + --hash=sha256:ce8613beaffc7c14f091497346ef117c1798c202b01153a8cc7b8e2ebaaf41c0 \ + --hash=sha256:d2a27aca5597c8a71abbe10209184e1a8e91c1fd470b5070a2ea60cafec35bcd \ + --hash=sha256:dad9c385ba8ee025bb0d856714f71d7840020fe176ae0229de618f14dae7a6e2 \ + --hash=sha256:db4b65b02f59035037fde0998974d84244a64c3265bdef32a827ab9b63d61b18 \ + --hash=sha256:e09469a2cec88fb7b078e16d4adec594414397e8879a4341c6ace96013463d5b \ + --hash=sha256:e53dc41cda40b248ebc40b83b31516487f7db95ab8ceac1f042626bc43a2f992 \ + --hash=sha256:f1e85a178384bf19e36779d91ff35c7617c885da487d689b05c1366f9933ad74 \ + --hash=sha256:f47be41843200f7faec0683ad751e5ef11b9a56a220d57f300376cd8aba81660 \ + --hash=sha256:fb0cef872d8193e487fc6bdb08559c3aa41b659a7d9be48b2e10747f47863925 \ + --hash=sha256:ffc73996c4fca3d2b6c1c8c12bfd3ad00def8621da24f547626bf06441400449 # via # types-pyopenssl # types-redis @@ -317,9 +317,9 @@ pygments==2.17.2 \ --hash=sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c \ --hash=sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367 # via doc8 -pyupgrade==3.15.0 \ - --hash=sha256:8dc8ebfaed43566e2c65994162795017c7db11f531558a74bc8aa077907bc305 \ - --hash=sha256:a7fde381060d7c224f55aef7a30fae5ac93bbc428367d27e70a603bc2acd4f00 +pyupgrade==3.15.1 \ + --hash=sha256:7690857cae0f6253f39241dcd2e57118c333c438b78609fc3c17a5aa61227b7d \ + --hash=sha256:c5e005de2805edcd333d1deb04553200ec69da85e4bc9db37b16345ed9e27ed9 # via -r requirements/lint.in restructuredtext-lint==1.4.0 \ --hash=sha256:1b235c0c922341ab6c530390892eb9e92f90b9b75046063e047cacfb0f050c45 @@ -356,9 +356,9 @@ types-first==2.0.5.2 \ --hash=sha256:342c2d99d3a2bf43c281b2de5d08f83c65ac1b2320af04b0f878ea2bc8ba390c \ --hash=sha256:4115d28bdb52542531f70ccb1a044802f597d8fb3e4e55f65c23f522e1086d1f # via -r requirements/lint.in -types-html5lib==1.1.11.20240106 \ - --hash=sha256:61993cb89220107481e0f1da65c388ff8cf3d8c5f6e8483c97559639a596b697 \ - --hash=sha256:fc3a1b18eb601b3eeaf92c900bd67675c0a4fa1dd1d2a2893ebdb46923547ee9 +types-html5lib==1.1.11.20240221 \ + --hash=sha256:9a42aeb7ad2102bce7a3e34d0adfca4b25d3daa2d54d7e0952a8845f3ad909a2 \ + --hash=sha256:e3d57650835d899b7eb215333cbcd81f1a0ef2654b451755bcee19dcd119786b # via -r requirements/lint.in types-itsdangerous==1.1.6 \ --hash=sha256:21c6966c10e353a5d35d36c82aaa2c5598d3bc32ddc8e0591276da5ad2e3c638 \ @@ -382,9 +382,9 @@ types-pytz==2024.1.0.20240203 \ # via # -r requirements/lint.in # types-babel -types-redis==4.6.0.20240106 \ - --hash=sha256:2b2fa3a78f84559616242d23f86de5f4130dfd6c3b83fb2d8ce3329e503f756e \ - --hash=sha256:912de6507b631934bd225cdac310b04a58def94391003ba83939e5a10e99568d +types-redis==4.6.0.20240218 \ + --hash=sha256:5103d7e690e5c74c974a161317b2d59ac2303cf8bef24175b04c2a4c3486cb39 \ + --hash=sha256:dc9c45a068240e33a04302aec5655cf41e80f91eecffccbb2df215b2f6fc375d # via -r requirements/lint.in types-requests==2.31.0.6 \ --hash=sha256:a2db9cb228a81da8348b49ad6db3f5519452dd20a9c1e1a868c83c5fe88fd1a9 \ @@ -394,9 +394,9 @@ types-s3transfer==0.10.0 \ --hash=sha256:35e4998c25df7f8985ad69dedc8e4860e8af3b43b7615e940d53c00d413bdc69 \ --hash=sha256:44fcdf0097b924a9aab1ee4baa1179081a9559ca62a88c807e2b256893ce688f # via boto3-stubs -types-setuptools==69.0.0.20240125 \ - --hash=sha256:00835f959ff24ebc32c55da8df9d46e8df25e3c4bfacb43e98b61fde51a4bc41 \ - --hash=sha256:22ad498cb585b22ce8c97ada1fccdf294a2e0dd7dc984a28535a84ea82f45b3f +types-setuptools==69.1.0.20240217 \ + --hash=sha256:243fecc8850b6f7fbfa84bab18ec93407046a4e91130056fd5a7caef971aaff9 \ + --hash=sha256:8b60e14a652b48bda292801c5a0c1251c190ad587c295f7839e901634913bb96 # via # -r requirements/lint.in # types-babel @@ -431,43 +431,43 @@ zope-event==5.0 \ --hash=sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26 \ --hash=sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd # via zope-schema -zope-interface==6.1 \ - --hash=sha256:0c8cf55261e15590065039696607f6c9c1aeda700ceee40c70478552d323b3ff \ - --hash=sha256:13b7d0f2a67eb83c385880489dbb80145e9d344427b4262c49fbf2581677c11c \ - --hash=sha256:1f294a15f7723fc0d3b40701ca9b446133ec713eafc1cc6afa7b3d98666ee1ac \ - --hash=sha256:239a4a08525c080ff833560171d23b249f7f4d17fcbf9316ef4159f44997616f \ - --hash=sha256:2f8d89721834524a813f37fa174bac074ec3d179858e4ad1b7efd4401f8ac45d \ - --hash=sha256:2fdc7ccbd6eb6b7df5353012fbed6c3c5d04ceaca0038f75e601060e95345309 \ - --hash=sha256:34c15ca9248f2e095ef2e93af2d633358c5f048c49fbfddf5fdfc47d5e263736 \ - --hash=sha256:387545206c56b0315fbadb0431d5129c797f92dc59e276b3ce82db07ac1c6179 \ - --hash=sha256:43b576c34ef0c1f5a4981163b551a8781896f2a37f71b8655fd20b5af0386abb \ - --hash=sha256:57d0a8ce40ce440f96a2c77824ee94bf0d0925e6089df7366c2272ccefcb7941 \ - --hash=sha256:5a804abc126b33824a44a7aa94f06cd211a18bbf31898ba04bd0924fbe9d282d \ - --hash=sha256:67be3ca75012c6e9b109860820a8b6c9a84bfb036fbd1076246b98e56951ca92 \ - --hash=sha256:6af47f10cfc54c2ba2d825220f180cc1e2d4914d783d6fc0cd93d43d7bc1c78b \ - --hash=sha256:6dc998f6de015723196a904045e5a2217f3590b62ea31990672e31fbc5370b41 \ - --hash=sha256:70d2cef1bf529bff41559be2de9d44d47b002f65e17f43c73ddefc92f32bf00f \ - --hash=sha256:7ebc4d34e7620c4f0da7bf162c81978fce0ea820e4fa1e8fc40ee763839805f3 \ - --hash=sha256:964a7af27379ff4357dad1256d9f215047e70e93009e532d36dcb8909036033d \ - --hash=sha256:97806e9ca3651588c1baaebb8d0c5ee3db95430b612db354c199b57378312ee8 \ - --hash=sha256:9b9bc671626281f6045ad61d93a60f52fd5e8209b1610972cf0ef1bbe6d808e3 \ - --hash=sha256:9ffdaa5290422ac0f1688cb8adb1b94ca56cee3ad11f29f2ae301df8aecba7d1 \ - --hash=sha256:a0da79117952a9a41253696ed3e8b560a425197d4e41634a23b1507efe3273f1 \ - --hash=sha256:a41f87bb93b8048fe866fa9e3d0c51e27fe55149035dcf5f43da4b56732c0a40 \ - --hash=sha256:aa6fd016e9644406d0a61313e50348c706e911dca29736a3266fc9e28ec4ca6d \ - --hash=sha256:ad54ed57bdfa3254d23ae04a4b1ce405954969c1b0550cc2d1d2990e8b439de1 \ - --hash=sha256:b012d023b4fb59183909b45d7f97fb493ef7a46d2838a5e716e3155081894605 \ - --hash=sha256:b51b64432eed4c0744241e9ce5c70dcfecac866dff720e746d0a9c82f371dfa7 \ - --hash=sha256:bbe81def9cf3e46f16ce01d9bfd8bea595e06505e51b7baf45115c77352675fd \ - --hash=sha256:c9559138690e1bd4ea6cd0954d22d1e9251e8025ce9ede5d0af0ceae4a401e43 \ - --hash=sha256:e30506bcb03de8983f78884807e4fd95d8db6e65b69257eea05d13d519b83ac0 \ - --hash=sha256:e33e86fd65f369f10608b08729c8f1c92ec7e0e485964670b4d2633a4812d36b \ - --hash=sha256:e441e8b7d587af0414d25e8d05e27040d78581388eed4c54c30c0c91aad3a379 \ - --hash=sha256:e8bb9c990ca9027b4214fa543fd4025818dc95f8b7abce79d61dc8a2112b561a \ - --hash=sha256:ef43ee91c193f827e49599e824385ec7c7f3cd152d74cb1dfe02cb135f264d83 \ - --hash=sha256:ef467d86d3cfde8b39ea1b35090208b0447caaabd38405420830f7fd85fbdd56 \ - --hash=sha256:f89b28772fc2562ed9ad871c865f5320ef761a7fcc188a935e21fe8b31a38ca9 \ - --hash=sha256:fddbab55a2473f1d3b8833ec6b7ac31e8211b0aa608df5ab09ce07f3727326de +zope-interface==6.2 \ + --hash=sha256:02adbab560683c4eca3789cc0ac487dcc5f5a81cc48695ec247f00803cafe2fe \ + --hash=sha256:14e02a6fc1772b458ebb6be1c276528b362041217b9ca37e52ecea2cbdce9fac \ + --hash=sha256:25e0af9663eeac6b61b231b43c52293c2cb7f0c232d914bdcbfd3e3bd5c182ad \ + --hash=sha256:2606955a06c6852a6cff4abeca38346ed01e83f11e960caa9a821b3626a4467b \ + --hash=sha256:396f5c94654301819a7f3a702c5830f0ea7468d7b154d124ceac823e2419d000 \ + --hash=sha256:3b240883fb43160574f8f738e6d09ddbdbf8fa3e8cea051603d9edfd947d9328 \ + --hash=sha256:3b6c62813c63c543a06394a636978b22dffa8c5410affc9331ce6cdb5bfa8565 \ + --hash=sha256:4ae9793f114cee5c464cc0b821ae4d36e1eba961542c6086f391a61aee167b6f \ + --hash=sha256:4bce517b85f5debe07b186fc7102b332676760f2e0c92b7185dd49c138734b70 \ + --hash=sha256:4d45d2ba8195850e3e829f1f0016066a122bfa362cc9dc212527fc3d51369037 \ + --hash=sha256:4dd374927c00764fcd6fe1046bea243ebdf403fba97a937493ae4be2c8912c2b \ + --hash=sha256:506f5410b36e5ba494136d9fa04c548eaf1a0d9c442b0b0e7a0944db7620e0ab \ + --hash=sha256:59f7374769b326a217d0b2366f1c176a45a4ff21e8f7cebb3b4a3537077eff85 \ + --hash=sha256:5ee9789a20b0081dc469f65ff6c5007e67a940d5541419ca03ef20c6213dd099 \ + --hash=sha256:6fc711acc4a1c702ca931fdbf7bf7c86f2a27d564c85c4964772dadf0e3c52f5 \ + --hash=sha256:75d2ec3d9b401df759b87bc9e19d1b24db73083147089b43ae748aefa63067ef \ + --hash=sha256:76e0531d86523be7a46e15d379b0e975a9db84316617c0efe4af8338dc45b80c \ + --hash=sha256:8af82afc5998e1f307d5e72712526dba07403c73a9e287d906a8aa2b1f2e33dd \ + --hash=sha256:8f5d2c39f3283e461de3655e03faf10e4742bb87387113f787a7724f32db1e48 \ + --hash=sha256:97785604824981ec8c81850dd25c8071d5ce04717a34296eeac771231fbdd5cd \ + --hash=sha256:a3046e8ab29b590d723821d0785598e0b2e32b636a0272a38409be43e3ae0550 \ + --hash=sha256:abb0b3f2cb606981c7432f690db23506b1db5899620ad274e29dbbbdd740e797 \ + --hash=sha256:ac7c2046d907e3b4e2605a130d162b1b783c170292a11216479bb1deb7cadebe \ + --hash=sha256:af27b3fe5b6bf9cd01b8e1c5ddea0a0d0a1b8c37dc1c7452f1e90bf817539c6d \ + --hash=sha256:b386b8b9d2b6a5e1e4eadd4e62335571244cb9193b7328c2b6e38b64cfda4f0e \ + --hash=sha256:b66335bbdbb4c004c25ae01cc4a54fd199afbc1fd164233813c6d3c2293bb7e1 \ + --hash=sha256:d54f66c511ea01b9ef1d1a57420a93fbb9d48a08ec239f7d9c581092033156d0 \ + --hash=sha256:de125151a53ecdb39df3cb3deb9951ed834dd6a110a9e795d985b10bb6db4532 \ + --hash=sha256:de7916380abaef4bb4891740879b1afcba2045aee51799dfd6d6ca9bdc71f35f \ + --hash=sha256:e2fefad268ff5c5b314794e27e359e48aeb9c8bb2cbb5748a071757a56f6bb8f \ + --hash=sha256:e7b2bed4eea047a949296e618552d3fed00632dc1b795ee430289bdd0e3717f3 \ + --hash=sha256:e87698e2fea5ca2f0a99dff0a64ce8110ea857b640de536c76d92aaa2a91ff3a \ + --hash=sha256:ede888382882f07b9e4cd942255921ffd9f2901684198b88e247c7eabd27a000 \ + --hash=sha256:f444de0565db46d26c9fa931ca14f497900a295bd5eba480fc3fad25af8c763e \ + --hash=sha256:fa994e8937e8ccc7e87395b7b35092818905cf27c651e3ff3e7f29729f5ce3ce \ + --hash=sha256:febceb04ee7dd2aef08c2ff3d6f8a07de3052fc90137c507b0ede3ea80c21440 # via # mypy-zope # zope-schema @@ -477,9 +477,9 @@ zope-schema==7.0.1 \ # via mypy-zope # The following packages are considered to be unsafe in a requirements file: -setuptools==69.0.3 \ - --hash=sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05 \ - --hash=sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78 +setuptools==69.1.0 \ + --hash=sha256:850894c4195f09c4ed30dba56213bf7c3f21d86ed6bdaafb5df5972593bfc401 \ + --hash=sha256:c054629b81b946d63a9c6e732bc8b2513a7c3ea645f11d0139a2191d735c60c6 # via # zope-event # zope-interface diff --git a/requirements/main.txt b/requirements/main.txt index d8dab91751b1..e2324baa20c7 100644 --- a/requirements/main.txt +++ b/requirements/main.txt @@ -8,11 +8,11 @@ alembic==1.13.1 \ --hash=sha256:2edcc97bed0bd3272611ce3a98d98279e9c209e7186e43e75bbb1b2bdfdbcc43 \ --hash=sha256:4932c8558bf68f2ee92b9bbcb8218671c627064d5b08939437af6d77dc05e595 # via - # -r requirements/main.in + # -r main.in # alembic-postgresql-enum -alembic-postgresql-enum==1.0.2 \ - --hash=sha256:6b776fee08d9a85eb807f1746b80330352a1a000782a97fb7140f7f33371b661 \ - --hash=sha256:efb4ea5afc440670b9455e1ed6cf552958449ef26581593d48ceb833428e34e0 +alembic-postgresql-enum==1.1.2 \ + --hash=sha256:0c7f3d818ab94bdfcc9b092fc672a1ab9495c7208f3561c3ae33d2da214d13ea \ + --hash=sha256:7c23303b4383d23bbb8aafbd9541cd744e37b476e93fa8dc6d27a157c7c8e597 # via -r requirements/main.in amqp==5.2.0 \ --hash=sha256:827cb12fb0baa892aad844fd95258143bce4027fdac4fccddbc43330fd281637 \ @@ -25,7 +25,7 @@ annotated-types==0.6.0 \ argon2-cffi==23.1.0 \ --hash=sha256:879c3e79a2729ce768ebb7d36d4609e3a78a4ca2ec3a9f12286ca057e3d0db08 \ --hash=sha256:c670642b78ba29641818ab2e68bd4e6a78ba53b7eff7b4c3815ae16abf91c7ea - # via -r requirements/main.in + # via -r main.in argon2-cffi-bindings==21.2.0 \ --hash=sha256:20ef543a89dee4db46a1a6e206cd015360e5a75822f76df533845c3cbaf72670 \ --hash=sha256:2c3e3cc67fdb7d82c4718f19b4e7a87123caf8a93fde7e23cf66ac0337d3cb3f \ @@ -63,14 +63,14 @@ automat==22.10.0 \ --hash=sha256:c3164f8742b9dc440f3682482d32aaff7bb53f71740dd018533f9de286b64180 \ --hash=sha256:e56beb84edad19dcc11d30e8d9b895f75deeb5ef5e96b84a467066b3b84bb04e # via -r requirements/main.in -b2sdk==1.30.1 \ - --hash=sha256:9217c30bfb5891e468c726aae2aace996e628411885752437bdf0d4b8a3ba29d \ - --hash=sha256:e659dbe3b73e65c1bd057b41a9d875fe91171547b87aac719cc077594e828438 +b2sdk==1.31.0 \ + --hash=sha256:3e0e4a78e273ee4cafd622a849c1c20ba75900f4f66a252b0cc7174792c21d2d \ + --hash=sha256:75509102915ec0f7cfe42a08c40a9e6a2737ca50700b4b4cacf5fb1c115bfb76 # via -r requirements/main.in babel==2.14.0 \ --hash=sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363 \ --hash=sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287 - # via -r requirements/main.in + # via -r main.in bcrypt==4.1.2 \ --hash=sha256:02d9ef8915f72dd6daaef40e0baeef8a017ce624369f09754baf32bb32dba25f \ --hash=sha256:1c28973decf4e0e69cee78c68e30a523be441972c826703bb93099868a8ff5b5 \ @@ -99,7 +99,7 @@ bcrypt==4.1.2 \ --hash=sha256:eb3bd3321517916696233b5e0c67fd7d6281f0ef48e66812db35fc963a422a1c \ --hash=sha256:f70d9c61f9c4ca7d57f3bfe88a5ccf62546ffbadf3681bb1e268d9d2e41c91a7 \ --hash=sha256:fbe188b878313d01b7718390f31528be4010fed1faa798c5a1d0469c9c48c369 - # via -r requirements/main.in + # via -r main.in billiard==4.2.0 \ --hash=sha256:07aa978b308f334ff8282bd4a746e681b3513db5c9a514cbdd810cbbdc19714d \ --hash=sha256:9a3c3184cb275aa17a732f93f65b20c525d3d9f253722d26a82194803ade5a2c @@ -108,7 +108,7 @@ boto3==1.28.63 \ --hash=sha256:65d052ec13197460586ee385aa2d6bba0e7378d2d2c7f3e93c044c43ae1ca782 \ --hash=sha256:94218aba2feb5b404b665b8d76c172dc654f79b4c5fa0e9e92459c098da87bf4 # via - # -r requirements/main.in + # -r main.in # celery # kombu botocore==1.31.63 \ @@ -127,60 +127,60 @@ cattrs==23.2.3 \ --hash=sha256:0341994d94971052e9ee70662542699a3162ea1e0c62f7ce1b4a57f563685108 \ --hash=sha256:a934090d95abaa9e911dac357e3a8699e0b4b14f8529bcc7d2b1ad9d51672b9f # via linehaul -cbor2==5.6.1 \ - --hash=sha256:04077dabb22a5c1748446635254760f11da33cf22e099f02986bc96bc3223c44 \ - --hash=sha256:0423908995c4f87598a59d630ece5478138c816ddd169ab5b2ee1035d8aff88a \ - --hash=sha256:0d36554819084e711208da701173f1c70a851f5395fe52a225acab45dc5e1e32 \ - --hash=sha256:20534a0f3376ed9cef5c969faf1870048cdd0d617064cd52fbd87ad647795ecf \ - --hash=sha256:2466e31d4f6d3350c5e4eeb6032ab2b969de38da934fa9ea76c7015c673d4fc8 \ - --hash=sha256:29a5903d1390dbc8b6cec2fd97f0d9f04ced97d1006fb5a28e656b59fb677901 \ - --hash=sha256:2babf033c203d25e77fac8b3db6cf2fe89713cbed729279cc6edc5d6eb101b03 \ - --hash=sha256:2f56ecdf51bd047b7724f8a31f35777a3b69e74b035119040010d438013c8e7f \ - --hash=sha256:2f7281c840f1511bfc51365ea9a14f99eb23b92ffe98c74221a5cde366c167e8 \ - --hash=sha256:36332d928bfd474c26ff10927eea70472ad03aba22eb8dc656e674c04ffbe7a4 \ - --hash=sha256:39ebe0cdced8e7950829bcf5bf664c7a8d5d32db6c09772e40c899abdd37f7cc \ - --hash=sha256:45b765211fbfd39e56b496bcd04123211425d616c47ba5fb7a7ddd837ea1de80 \ - --hash=sha256:4cfa9cab496f43d6eee43f55633e4ad2d7f3581502e1a1b6e8d89d8695b63f29 \ - --hash=sha256:513075a06e25de57582992730a0bceb98b522285d6d2307df5512431244a6a3e \ - --hash=sha256:51f0001bfcefced0bfc153636d7e47d6adc4c804112d59aa0c712579b739b7a6 \ - --hash=sha256:5285fb8190ab6070b4d0801638c92404ddf00c93583fd3242a581b07a60c25cc \ - --hash=sha256:5832037489d5bcba9312482fd590ae4744fe2b1f7eda21890163b7ccfe212545 \ - --hash=sha256:58a86439f5251dc981220be5f34fba5b952bfd21c026a390e310a8e5566ae302 \ - --hash=sha256:58d4bcc7816b7130cf16fc6ad8ab06caa2d341992f4db756a88293592c2440e6 \ - --hash=sha256:63c693de37b88207421cc62c4c50ac17b9d89441a81384237a4d93e56a21037e \ - --hash=sha256:6b783bb572b11226fb43f01921276d224460b019d33c1b69cfef3cd30e1cf4b3 \ - --hash=sha256:752130e38644319f3dae7f91fabb08f58bc454ca671b72875e6228d423890efd \ - --hash=sha256:787e2da7ca871c01e154d0b81d7785457dd31ee8db31c54c663145ec29c2971a \ - --hash=sha256:79bf66958809dc9400319b548995dfcb556d811489cca8a19c6525fe609c7a7a \ - --hash=sha256:7ff7516cefed97ab550d5c69bc7285a28e6c14243e38b423e92953a59435a81b \ - --hash=sha256:99476a97b9b75d6fa1f6904d917ad970f46098fc7323dcc709955ba50cf9e0f5 \ - --hash=sha256:9d047c2340ad932b4f485b79d29664d3938f79a35c5bcc3a048c6e2d8efec6f7 \ - --hash=sha256:9fc734e4041b43c7031d1dd8aefc9bbc717539552c4be928d1db7f95da1948af \ - --hash=sha256:a0e9f79eccc837da1320eb39dd6a54c593d8ff177da3e26a001772b289571154 \ - --hash=sha256:b56e3c59bce8ad01c1909f9fa509b4db36162051a44df9c0f18a3f4b09a9e745 \ - --hash=sha256:c39b52f0a2d7921642ce8fa83a6f2b969db766810e1e2b9f52934f462510fa93 \ - --hash=sha256:d38578dd25a1aa484c5bea5e9a322cb00e14d0f77a88ce9363c5c4a13607e83a \ - --hash=sha256:dfc57ae4298fb61508385c35bcca516c9284e4e9daabb4a8e285f33ccd7d0891 \ - --hash=sha256:e7d844a158eed04ca36721a67a2247a874ebca833dbbb41db0b7ac15b509ee9d \ - --hash=sha256:f1bed7da33c0d625907687b154e30b1aaf11f4babaeae1268d9bf299c8c6827c \ - --hash=sha256:f4ffee186afe9fbd33305a87e87baae807f0919664460a082675009f5b3d4e99 \ - --hash=sha256:fb9dcc20ca2be813ac8b7b181cdca3156dacd22b1dd8c7f2a729c3c88a594e8a +cbor2==5.6.2 \ + --hash=sha256:03e5b68867b9d89ff2abd14ef7c6d42fbd991adc3e734a19a294935f22a4d05a \ + --hash=sha256:1b8b504b590367a51fe8c0d9b8cb458a614d782d37b24483097e2b1e93ed0fff \ + --hash=sha256:1fde9e704e96751e0729cc58b912d0e77c34387fb6bcceea0817069e8683df45 \ + --hash=sha256:211a1e18e65ac71e04434ff5b58bde5c53f85b9c5bc92a3c0e2265089d3034f3 \ + --hash=sha256:22996159b491d545ecfd489392d3c71e5d0afb9a202dfc0edc8b2cf413a58326 \ + --hash=sha256:22c24fe9ef1696a84b8fd80ff66eb0e5234505d8b9a9711fc6db57bce10771f3 \ + --hash=sha256:30e9ba8f4896726ca61869efacda50b6859aff92162ae5a0e192859664f36c81 \ + --hash=sha256:377cfe9d5560c682486faef6d856226abf8b2801d95fa29d4e5d75b1615eb091 \ + --hash=sha256:3a4a3420f80d6b942874d66eaad07658066370df994ddee4125b48b2cbc61ece \ + --hash=sha256:3c7f223f1fedc74d33f363d184cb2bab9e4bdf24998f73b5e3bef366d6c41628 \ + --hash=sha256:42eaf0f768bd27afcb38135d5bfc361d3a157f1f5c7dddcd8d391f7fa43d9de8 \ + --hash=sha256:44bf7457fca23209e14dab8181dff82466a83b72e55b444dbbfe90fa67659492 \ + --hash=sha256:4f687e6731b1198811223576800258a712ddbfdcfa86c0aee2cc8269193e6b96 \ + --hash=sha256:516b8390936bb172ff18d7b609a452eaa51991513628949b0a9bf25cbe5a7129 \ + --hash=sha256:5b28d8ff0e726224a7429281700c28afe0e665f83f9ae79648cbae3f1a391cbf \ + --hash=sha256:6031a284d93fc953fc2a2918f261c4f5100905bd064ca3b46961643e7312a828 \ + --hash=sha256:7221b83000ee01d674572eec1d1caa366eac109d1d32c14d7af9a4aaaf496563 \ + --hash=sha256:7ea9e150029c3976c46ee9870b6dcdb0a5baae21008fe3290564886b11aa2b64 \ + --hash=sha256:8839b73befa010358477736680657b9d08c1ed935fd973decb1909712a41afdc \ + --hash=sha256:8af7162fcf7aa2649f02563bdb18b2fa6478b751eee4df0257bffe19ea8f107a \ + --hash=sha256:8d1c0021d9a1f673066de7c8941f71a59abb11909cc355892dda01e79a2b3045 \ + --hash=sha256:922e06710e5cf6f56b82b0b90d2f356aa229b99e570994534206985f675fd307 \ + --hash=sha256:94981277b4bf448a2754c1f34a9d0055a9d1c5a8d102c933ffe95c80f1085bae \ + --hash=sha256:9aca73b63bdc6561e1a0d38618e78b9c204c942260d51e663c92c4ba6c961684 \ + --hash=sha256:9e94043d99fe779f62a15a5e156768588a2a7047bb3a127fa312ac1135ff5ecb \ + --hash=sha256:9faa0712d414a88cc1244c78cd4b28fced44f1827dbd8c1649e3c40588aa670f \ + --hash=sha256:ac85eb731c524d148f608b9bdb2069fa79e374a10ed5d10a2405eba9a6561e60 \ + --hash=sha256:b01a718e083e6de8b43296c3ccdb3aa8af6641f6bbb3ea1700427c6af73db28a \ + --hash=sha256:b7513c2dea8868991fad7ef8899890ebcf8b199b9b4461c3c11d7ad3aef4820d \ + --hash=sha256:c0b53a65673550fde483724ff683753f49462d392d45d7b6576364b39e76e54c \ + --hash=sha256:c10ede9462458998f1b9c488e25fe3763aa2491119b7af472b72bf538d789e24 \ + --hash=sha256:cc29c068687aa2e7778f63b653f1346065b858427a2555df4dc2191f4a0de8ce \ + --hash=sha256:ea686dfb5e54d690e704ce04993bc8ca0052a7cd2d4b13dd333a41cca8a05a05 \ + --hash=sha256:ea7ecd81c5c6e02c2635973f52a0dd1e19c0bf5ef51f813d8cd5e3e7ed072726 \ + --hash=sha256:f30c8a9a9df79f26e72d8d5fa51ef08eb250d9869a711bcf9539f1865916c983 \ + --hash=sha256:f70db0ebcf005c25408e8d5cc4b9558c899f13a3e2f8281fa3d3be4894e0e821 \ + --hash=sha256:fdc564ef2e9228bcd96ec8c6cdaa431a48ab03b3fb8326ead4b3f986330e5b9e # via webauthn celery[sqs]==5.3.1 \ --hash=sha256:27f8f3f3b58de6e0ab4f174791383bbd7445aff0471a43e99cfd77727940753f \ --hash=sha256:f84d1c21a1520c116c2b7d26593926581191435a03aa74b77c941b93ca1c6210 # via - # -r requirements/main.in + # -r main.in # celery # celery-redbeat celery-redbeat==2.2.0 \ --hash=sha256:71104729380d4eaefd91a9b7d270ba7851bfff9ad55a43ac8365acbb0c608b77 - # via -r requirements/main.in + # via -r main.in certifi==2024.2.2 \ --hash=sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f \ --hash=sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1 # via - # -r requirements/main.in + # -r main.in # elasticsearch # requests # sentry-sdk @@ -338,7 +338,7 @@ click==8.1.7 \ --hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \ --hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de # via - # -r requirements/main.in + # -r main.in # celery # click-didyoumean # click-plugins @@ -416,41 +416,41 @@ cmarkgfm==2024.1.14 \ --hash=sha256:fdecbdad66b7738c711db33471d510c6279a01196920c43294d8071e51192807 \ --hash=sha256:ff0bc7dbb86d1a6877b771ed715dbe0ab071872e8c6f5beb782528b70ac7eedc # via readme-renderer -cryptography==42.0.2 \ - --hash=sha256:087887e55e0b9c8724cf05361357875adb5c20dec27e5816b653492980d20380 \ - --hash=sha256:09a77e5b2e8ca732a19a90c5bca2d124621a1edb5438c5daa2d2738bfeb02589 \ - --hash=sha256:130c0f77022b2b9c99d8cebcdd834d81705f61c68e91ddd614ce74c657f8b3ea \ - --hash=sha256:141e2aa5ba100d3788c0ad7919b288f89d1fe015878b9659b307c9ef867d3a65 \ - --hash=sha256:28cb2c41f131a5758d6ba6a0504150d644054fd9f3203a1e8e8d7ac3aea7f73a \ - --hash=sha256:2f9f14185962e6a04ab32d1abe34eae8a9001569ee4edb64d2304bf0d65c53f3 \ - --hash=sha256:320948ab49883557a256eab46149df79435a22d2fefd6a66fe6946f1b9d9d008 \ - --hash=sha256:36d4b7c4be6411f58f60d9ce555a73df8406d484ba12a63549c88bd64f7967f1 \ - --hash=sha256:3b15c678f27d66d247132cbf13df2f75255627bcc9b6a570f7d2fd08e8c081d2 \ - --hash=sha256:3dbd37e14ce795b4af61b89b037d4bc157f2cb23e676fa16932185a04dfbf635 \ - --hash=sha256:4383b47f45b14459cab66048d384614019965ba6c1a1a141f11b5a551cace1b2 \ - --hash=sha256:44c95c0e96b3cb628e8452ec060413a49002a247b2b9938989e23a2c8291fc90 \ - --hash=sha256:4b063d3413f853e056161eb0c7724822a9740ad3caa24b8424d776cebf98e7ee \ - --hash=sha256:52ed9ebf8ac602385126c9a2fe951db36f2cb0c2538d22971487f89d0de4065a \ - --hash=sha256:55d1580e2d7e17f45d19d3b12098e352f3a37fe86d380bf45846ef257054b242 \ - --hash=sha256:5ef9bc3d046ce83c4bbf4c25e1e0547b9c441c01d30922d812e887dc5f125c12 \ - --hash=sha256:5fa82a26f92871eca593b53359c12ad7949772462f887c35edaf36f87953c0e2 \ - --hash=sha256:61321672b3ac7aade25c40449ccedbc6db72c7f5f0fdf34def5e2f8b51ca530d \ - --hash=sha256:701171f825dcab90969596ce2af253143b93b08f1a716d4b2a9d2db5084ef7be \ - --hash=sha256:841ec8af7a8491ac76ec5a9522226e287187a3107e12b7d686ad354bb78facee \ - --hash=sha256:8a06641fb07d4e8f6c7dda4fc3f8871d327803ab6542e33831c7ccfdcb4d0ad6 \ - --hash=sha256:8e88bb9eafbf6a4014d55fb222e7360eef53e613215085e65a13290577394529 \ - --hash=sha256:a00aee5d1b6c20620161984f8ab2ab69134466c51f58c052c11b076715e72929 \ - --hash=sha256:a047682d324ba56e61b7ea7c7299d51e61fd3bca7dad2ccc39b72bd0118d60a1 \ - --hash=sha256:a7ef8dd0bf2e1d0a27042b231a3baac6883cdd5557036f5e8df7139255feaac6 \ - --hash=sha256:ad28cff53f60d99a928dfcf1e861e0b2ceb2bc1f08a074fdd601b314e1cc9e0a \ - --hash=sha256:b9097a208875fc7bbeb1286d0125d90bdfed961f61f214d3f5be62cd4ed8a446 \ - --hash=sha256:b97fe7d7991c25e6a31e5d5e795986b18fbbb3107b873d5f3ae6dc9a103278e9 \ - --hash=sha256:e0ec52ba3c7f1b7d813cd52649a5b3ef1fc0d433219dc8c93827c57eab6cf888 \ - --hash=sha256:ea2c3ffb662fec8bbbfce5602e2c159ff097a4631d96235fcf0fb00e59e3ece4 \ - --hash=sha256:fa3dec4ba8fb6e662770b74f62f1a0c7d4e37e25b58b2bf2c1be4c95372b4a33 \ - --hash=sha256:fbeb725c9dc799a574518109336acccaf1303c30d45c075c665c0793c2f79a7f +cryptography==42.0.4 \ + --hash=sha256:01911714117642a3f1792c7f376db572aadadbafcd8d75bb527166009c9f1d1b \ + --hash=sha256:0e89f7b84f421c56e7ff69f11c441ebda73b8a8e6488d322ef71746224c20fce \ + --hash=sha256:12d341bd42cdb7d4937b0cabbdf2a94f949413ac4504904d0cdbdce4a22cbf88 \ + --hash=sha256:15a1fb843c48b4a604663fa30af60818cd28f895572386e5f9b8a665874c26e7 \ + --hash=sha256:1cdcdbd117681c88d717437ada72bdd5be9de117f96e3f4d50dab3f59fd9ab20 \ + --hash=sha256:1df6fcbf60560d2113b5ed90f072dc0b108d64750d4cbd46a21ec882c7aefce9 \ + --hash=sha256:3c6048f217533d89f2f8f4f0fe3044bf0b2090453b7b73d0b77db47b80af8dff \ + --hash=sha256:3e970a2119507d0b104f0a8e281521ad28fc26f2820687b3436b8c9a5fcf20d1 \ + --hash=sha256:44a64043f743485925d3bcac548d05df0f9bb445c5fcca6681889c7c3ab12764 \ + --hash=sha256:4e36685cb634af55e0677d435d425043967ac2f3790ec652b2b88ad03b85c27b \ + --hash=sha256:5f8907fcf57392cd917892ae83708761c6ff3c37a8e835d7246ff0ad251d9298 \ + --hash=sha256:69b22ab6506a3fe483d67d1ed878e1602bdd5912a134e6202c1ec672233241c1 \ + --hash=sha256:6bfadd884e7280df24d26f2186e4e07556a05d37393b0f220a840b083dc6a824 \ + --hash=sha256:6d0fbe73728c44ca3a241eff9aefe6496ab2656d6e7a4ea2459865f2e8613257 \ + --hash=sha256:6ffb03d419edcab93b4b19c22ee80c007fb2d708429cecebf1dd3258956a563a \ + --hash=sha256:810bcf151caefc03e51a3d61e53335cd5c7316c0a105cc695f0959f2c638b129 \ + --hash=sha256:831a4b37accef30cccd34fcb916a5d7b5be3cbbe27268a02832c3e450aea39cb \ + --hash=sha256:887623fe0d70f48ab3f5e4dbf234986b1329a64c066d719432d0698522749929 \ + --hash=sha256:a0298bdc6e98ca21382afe914c642620370ce0470a01e1bef6dd9b5354c36854 \ + --hash=sha256:a1327f280c824ff7885bdeef8578f74690e9079267c1c8bd7dc5cc5aa065ae52 \ + --hash=sha256:c1f25b252d2c87088abc8bbc4f1ecbf7c919e05508a7e8628e6875c40bc70923 \ + --hash=sha256:c3a5cbc620e1e17009f30dd34cb0d85c987afd21c41a74352d1719be33380885 \ + --hash=sha256:ce8613beaffc7c14f091497346ef117c1798c202b01153a8cc7b8e2ebaaf41c0 \ + --hash=sha256:d2a27aca5597c8a71abbe10209184e1a8e91c1fd470b5070a2ea60cafec35bcd \ + --hash=sha256:dad9c385ba8ee025bb0d856714f71d7840020fe176ae0229de618f14dae7a6e2 \ + --hash=sha256:db4b65b02f59035037fde0998974d84244a64c3265bdef32a827ab9b63d61b18 \ + --hash=sha256:e09469a2cec88fb7b078e16d4adec594414397e8879a4341c6ace96013463d5b \ + --hash=sha256:e53dc41cda40b248ebc40b83b31516487f7db95ab8ceac1f042626bc43a2f992 \ + --hash=sha256:f1e85a178384bf19e36779d91ff35c7617c885da487d689b05c1366f9933ad74 \ + --hash=sha256:f47be41843200f7faec0683ad751e5ef11b9a56a220d57f300376cd8aba81660 \ + --hash=sha256:fb0cef872d8193e487fc6bdb08559c3aa41b659a7d9be48b2e10747f47863925 \ + --hash=sha256:ffc73996c4fca3d2b6c1c8c12bfd3ad00def8621da24f547626bf06441400449 # via - # -r requirements/main.in + # -r main.in # pyjwt # pyopenssl # webauthn @@ -465,18 +465,18 @@ cssutils==2.9.0 \ datadog==0.48.0 \ --hash=sha256:c3f819e2dc632a546a5b4e8d45409e996d4fa18c60df7814c82eda548e0cca59 \ --hash=sha256:d4d661358c3e7f801fbfe15118f5ccf08b9bd9b1f45b8b910605965283edad64 - # via -r requirements/main.in + # via -r main.in deprecated==1.2.14 \ --hash=sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c \ --hash=sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3 # via limits -disposable-email-domains==0.0.95 \ - --hash=sha256:36fa1ee104c19e618a7ea33c12e1edf1d3cd5007411cb6d58262639f4256a983 \ - --hash=sha256:38770e55db520f280bcf03fc51757d75f080128d7723ce4c41054ae2420ed161 +disposable-email-domains==0.0.96 \ + --hash=sha256:5c408ce56cf065c8e726b7deff5143eae245ae8403e1ec27441a93fc015f418d \ + --hash=sha256:7886355acd6444fc68508a1fc0c1de1b35a88ecb2b8fec045639136f91f43953 # via -r requirements/main.in -dnspython==2.5.0 \ - --hash=sha256:6facdf76b73c742ccf2d07add296f178e629da60be23ce4b0a9c927b1e02c3a6 \ - --hash=sha256:a0034815a59ba9ae888946be7ccca8f7c157b286f8455b379c692efb51022a15 +dnspython==2.6.1 \ + --hash=sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50 \ + --hash=sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc # via email-validator docutils==0.20.1 \ --hash=sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6 \ @@ -486,12 +486,12 @@ elasticsearch==7.10.1 \ --hash=sha256:4ebd34fd223b31c99d9f3b6b6236d3ac18b3046191a37231e8235b06ae7db955 \ --hash=sha256:a725dd923d349ca0652cf95d6ce23d952e2153740cf4ab6daf4a2d804feeed48 # via - # -r requirements/main.in + # -r main.in # elasticsearch-dsl elasticsearch-dsl==7.4.1 \ --hash=sha256:07ee9c87dc28cc3cae2daa19401e1e18a172174ad9e5ca67938f752e3902a1d5 \ --hash=sha256:97f79239a252be7c4cce554c29e64695d7ef6a4828372316a5e5ff815e7a7498 - # via -r requirements/main.in + # via -r main.in email-validator==2.1.0.post1 \ --hash=sha256:a4b0bd1cf55f073b924258d19321b1f3aa74b4b5a71a42c305575dba920e1a44 \ --hash=sha256:c973053efbeddfef924dc0bd93f6e77a1ea7ee0fce935aea7103c7a3d6d2d637 @@ -499,32 +499,32 @@ email-validator==2.1.0.post1 \ first==2.0.2 \ --hash=sha256:8d8e46e115ea8ac652c76123c0865e3ff18372aef6f03c22809ceefcea9dec86 \ --hash=sha256:ff285b08c55f8c97ce4ea7012743af2495c9f1291785f163722bd36f6af6d3bf - # via -r requirements/main.in + # via -r main.in forcediphttpsadapter==1.1.0 \ --hash=sha256:0d224cf6e8e50eb788c9f5994a7afa6d389bac6dbe540b7dfd77a32590ad0153 \ --hash=sha256:5e7662ece61735585332d09b87d94fffe4752469d5c0d3feff48746e5d70744b - # via -r requirements/main.in + # via -r main.in github-reserved-names==2023.9.1 \ --hash=sha256:16bb1a8ff505dd082c7167670b4115b31c00b8ffa77e0c1b7676807117c0169a \ --hash=sha256:707551fd0521a74274c5d3e73810a10b3e72762e3c69a12e0d96f87ce64c845f # via -r requirements/main.in -google-api-core==2.16.2 \ - --hash=sha256:032d37b45d1d6bdaf68fb11ff621e2593263a239fa9246e2e94325f9c47876d2 \ - --hash=sha256:449ca0e3f14c179b4165b664256066c7861610f70b6ffe54bb01a04e9b466929 +google-api-core==2.17.1 \ + --hash=sha256:610c5b90092c360736baccf17bd3efbcb30dd380e7a6dc28a71059edb8bd0d8e \ + --hash=sha256:9df18a1f87ee0df0bc4eea2770ebc4228392d8cc4066655b320e2cfccb15db95 # via # google-cloud-bigquery # google-cloud-core # google-cloud-storage -google-auth==2.27.0 \ - --hash=sha256:8e4bad367015430ff253fe49d500fdc3396c1a434db5740828c728e45bcce245 \ - --hash=sha256:e863a56ccc2d8efa83df7a80272601e43487fa9a728a376205c86c26aaefa821 +google-auth==2.28.0 \ + --hash=sha256:3cfc1b6e4e64797584fb53fc9bd0b7afa9b7c0dba2004fa7dcc9349e58cc3195 \ + --hash=sha256:7634d29dcd1e101f5226a23cbc4a0c6cda6394253bf80e281d9c5c6797869c53 # via # google-api-core # google-cloud-core # google-cloud-storage -google-cloud-bigquery==3.17.1 \ - --hash=sha256:0ae07b90d5052ba3a296a2210a2144c28469300d71f6f455881f94c2df543057 \ - --hash=sha256:7a9a92c7b1f6a6bf8b4c05c150e49f4ad1a03dd591dbd4522381b3f23bf07c73 +google-cloud-bigquery==3.17.2 \ + --hash=sha256:6e1cf669a40e567ab3289c7b5f2056363da9fcb85d9a4736ee90240d4a7d84ea \ + --hash=sha256:cdadf5283dca55a1a350bacf8c8a7466169d3cf46c5a0a3abc5e9aa0b0a51dee # via -r requirements/main.in google-cloud-core==2.4.1 \ --hash=sha256:9b7749272a812bde58fff28868d0c5e2f585b82f37e09a1f6ed2d4d10f134073 \ @@ -535,7 +535,7 @@ google-cloud-core==2.4.1 \ google-cloud-storage==2.14.0 \ --hash=sha256:2d23fcf59b55e7b45336729c148bb1c464468c69d5efbaee30f7201dd90eb97e \ --hash=sha256:8641243bbf2a2042c16a6399551fbb13f062cbc9a2de38d6c0bb5426962e9dbd - # via -r requirements/main.in + # via -r main.in google-crc32c==1.5.0 \ --hash=sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a \ --hash=sha256:02c65b9817512edc6a4ae7c7e987fea799d2e0ee40c53ec573a692bee24de876 \ @@ -788,15 +788,15 @@ hiredis==2.3.2 \ --hash=sha256:f9de7586522e5da6bee83c9cf0dcccac0857a43249cb4d721a2e312d98a684d1 \ --hash=sha256:f9f606e810858207d4b4287b4ef0dc622c2aa469548bf02b59dcc616f134f811 \ --hash=sha256:fa45f7d771094b8145af10db74704ab0f698adb682fbf3721d8090f90e42cc49 - # via -r requirements/main.in + # via -r main.in html5lib==1.1 \ --hash=sha256:0d78f8fde1c230e99fe37986a60526d7049ed4bf8a9fadbad5f00e22e58e041d \ --hash=sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f - # via -r requirements/main.in + # via -r main.in humanize==4.9.0 \ --hash=sha256:582a265c931c683a7e9b8ed9559089dea7edcf6cc95be39a3cbc2c5d5ac2bcfa \ --hash=sha256:ce284a76d5b1377fd8836733b983bfb0b76f1aa1c090de2566fcf008d7f6ab16 - # via -r requirements/main.in + # via -r main.in hupper==1.12.1 \ --hash=sha256:06bf54170ff4ecf4c84ad5f188dee3901173ab449c2608ad05b9bfd6b13e32eb \ --hash=sha256:e872b959f09d90be5fb615bd2e62de89a0b57efc037bdf9637fb09cdf8552b19 @@ -814,12 +814,12 @@ importlib-resources==6.1.1 \ itsdangerous==2.1.2 \ --hash=sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44 \ --hash=sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a - # via -r requirements/main.in + # via -r main.in jinja2==3.1.3 \ --hash=sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa \ --hash=sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90 # via - # -r requirements/main.in + # -r main.in # pyramid-jinja2 jmespath==1.0.1 \ --hash=sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980 \ @@ -831,17 +831,17 @@ kombu[sqs]==5.3.1 \ --hash=sha256:48ee589e8833126fd01ceaa08f8a2041334e9f5894e5763c8486a550454551e9 \ --hash=sha256:fbd7572d92c0bf71c112a6b45163153dea5a7b6a701ec16b568c27d0fd2370f2 # via - # -r requirements/main.in + # -r main.in # celery # kombu -limits==3.7.0 \ - --hash=sha256:124c6a04d2f4b20990fb1de019eec9474d6c1346c70d8fd0561609b86998b64a \ - --hash=sha256:c528817b7fc15f3e86ad091ba3e40231f6430a91b753db864767684cda8a7f2e +limits==3.9.0 \ + --hash=sha256:6dce07d1a4d7bd3361d36f59f3f43c4f39675001daeeae2617c3be42d718daa8 \ + --hash=sha256:7b44aa4d05c539276928372681190136914958cccbb99c30ecc5df72a179661a # via -r requirements/main.in linehaul==1.0.1 \ --hash=sha256:09d71b1f6a9ab92dd8c763b3d099e4ae05c2845ee783a02d5fe731e6e2a6a997 \ --hash=sha256:d19ca669008dad910868dfae7f904dfc5362583729bda344799cf7ea2ad5ef12 - # via -r requirements/main.in + # via -r main.in logfury==1.0.1 \ --hash=sha256:130a5daceab9ad534924252ddf70482aa2c96662b3a3825a7d30981d03b76a26 \ --hash=sha256:b4f04be1701a1df644afc3384d6167d64c899f8036b7eefc3b6c570c6a9b290b @@ -922,7 +922,7 @@ lxml==5.1.0 \ --hash=sha256:f4c9bda132ad108b387c33fabfea47866af87f4ea6ffb79418004f0521e63204 \ --hash=sha256:f643ffd2669ffd4b5a3e9b41c909b72b2a1d5e4915da90a77e119b8d48ce867a # via - # -r requirements/main.in + # -r main.in # premailer mako==1.3.2 \ --hash=sha256:2a0c8ad7f6274271b3bb7467dd37cf9cc6dab4bc19cb69a4ef10669402de698e \ @@ -997,7 +997,7 @@ markupsafe==2.1.5 \ mistune==3.0.2 \ --hash=sha256:71481854c30fdbc938963d3605b72501f5c10a9320ecd412c121c163a1c7d205 \ --hash=sha256:fc7f93ded930c92394ef2cb6f04a8aabab4117a91449e72dcc8dfa646a508be8 - # via -r requirements/main.in + # via -r main.in msgpack==1.0.7 \ --hash=sha256:04ad6069c86e531682f9e1e71b71c1c3937d6014a7c3e9edd2aa81ad58842862 \ --hash=sha256:0bfdd914e55e0d2c9e1526de210f6fe8ffe9705f2b1dfcc4aecc92a4cb4b533d \ @@ -1055,11 +1055,11 @@ msgpack==1.0.7 \ --hash=sha256:f6ffbc252eb0d229aeb2f9ad051200668fc3a9aaa8994e49f0cb2ffe2b7867e7 \ --hash=sha256:f9a7c509542db4eceed3dcf21ee5267ab565a83555c9b88a8109dcecc4709002 \ --hash=sha256:ff1d0899f104f3921d94579a5638847f783c9b04f2d5f229392ca77fba5b82fc - # via -r requirements/main.in + # via -r main.in natsort==8.4.0 \ --hash=sha256:45312c4a0e5507593da193dedd04abb1469253b601ecaf63445ad80f0a1ea581 \ --hash=sha256:4732914fb471f56b5cce04d7bae6f164a592c7712e1c85f9ef585e197299521c - # via -r requirements/main.in + # via -r main.in nh3==0.2.15 \ --hash=sha256:0d02d0ff79dfd8208ed25a39c12cbda092388fff7f1662466e27d97ad011b770 \ --hash=sha256:3277481293b868b2715907310c7be0f1b9d10491d5adf9fce11756a97e97eddf \ @@ -1078,63 +1078,63 @@ nh3==0.2.15 \ --hash=sha256:d1e30ff2d8d58fb2a14961f7aac1bbb1c51f9bdd7da727be35c63826060b0bf3 \ --hash=sha256:f3b53ba93bb7725acab1e030bc2ecd012a817040fd7851b332f86e2f9bb98dc6 # via readme-renderer -orjson==3.9.13 \ - --hash=sha256:031df1026c7ea8303332d78711f180231e3ae8b564271fb748a03926587c5546 \ - --hash=sha256:0d3ba9d88e20765335260d7b25547d7c571eee2b698200f97afa7d8c7cd668fc \ - --hash=sha256:0d691c44604941945b00e0a13b19a7d9c1a19511abadf0080f373e98fdeb6b31 \ - --hash=sha256:0fd9a2101d04e85086ea6198786a3f016e45475f800712e6833e14bf9ce2832f \ - --hash=sha256:16946d095212a3dec552572c5d9bca7afa40f3116ad49695a397be07d529f1fa \ - --hash=sha256:1ab9dbdec3f13f3ea6f937564ce21651844cfbf2725099f2f490426acf683c23 \ - --hash=sha256:23f21faf072ed3b60b5954686f98157e073f6a8068eaa58dbde83e87212eda84 \ - --hash=sha256:266e55c83f81248f63cc93d11c5e3a53df49a5d2598fa9e9db5f99837a802d5d \ - --hash=sha256:2cc03a35bfc71c8ebf96ce49b82c2a7be6af4b3cd3ac34166fdb42ac510bbfff \ - --hash=sha256:2f37f0cdd026ef777a4336e599d8194c8357fc14760c2a5ddcfdf1965d45504b \ - --hash=sha256:31372ba3a9fe8ad118e7d22fba46bbc18e89039e3bfa89db7bc8c18ee722dca8 \ - --hash=sha256:31fb66b41fb2c4c817d9610f0bc7d31345728d7b5295ac78b63603407432a2b2 \ - --hash=sha256:3869d65561f10071d3e7f35ae58fd377056f67d7aaed5222f318390c3ad30339 \ - --hash=sha256:3deadd8dc0e9ff844b5b656fa30a48dbee1c3b332d8278302dd9637f6b09f627 \ - --hash=sha256:43fd6036b16bb6742d03dae62f7bdf8214d06dea47e4353cde7e2bd1358d186f \ - --hash=sha256:446d9ad04204e79229ae19502daeea56479e55cbc32634655d886f5a39e91b44 \ - --hash=sha256:4584e8eb727bc431baaf1bf97e35a1d8a0109c924ec847395673dfd5f4ef6d6f \ - --hash=sha256:49b7e3fe861cb246361825d1a238f2584ed8ea21e714bf6bb17cebb86772e61c \ - --hash=sha256:5b98cd948372f0eb219bc309dee4633db1278687161e3280d9e693b6076951d2 \ - --hash=sha256:5ef58869f3399acbbe013518d8b374ee9558659eef14bca0984f67cb1fbd3c37 \ - --hash=sha256:60da7316131185d0110a1848e9ad15311e6c8938ee0b5be8cbd7261e1d80ee8f \ - --hash=sha256:62e9a99879c4d5a04926ac2518a992134bfa00d546ea5a4cae4b9be454d35a22 \ - --hash=sha256:63ef57a53bfc2091a7cd50a640d9ae866bd7d92a5225a1bab6baa60ef62583f2 \ - --hash=sha256:6e47153db080f5e87e8ba638f1a8b18995eede6b0abb93964d58cf11bcea362f \ - --hash=sha256:730385fdb99a21fce9bb84bb7fcbda72c88626facd74956bda712834b480729d \ - --hash=sha256:7ccd5bd222e5041069ad9d9868ab59e6dbc53ecde8d8c82b919954fbba43b46b \ - --hash=sha256:7e8e4a571d958910272af8d53a9cbe6599f9f5fd496a1bc51211183bb2072cbd \ - --hash=sha256:811ac076855e33e931549340288e0761873baf29276ad00f221709933c644330 \ - --hash=sha256:828c502bb261588f7de897e06cb23c4b122997cb039d2014cb78e7dabe92ef0c \ - --hash=sha256:838b898e8c1f26eb6b8d81b180981273f6f5110c76c22c384979aca854194f1b \ - --hash=sha256:860d0f5b42d0c0afd73fa4177709f6e1b966ba691fcd72175affa902052a81d6 \ - --hash=sha256:8a730bf07feacb0863974e67b206b7c503a62199de1cece2eb0d4c233ec29c11 \ - --hash=sha256:9156b96afa38db71344522f5517077eaedf62fcd2c9148392ff93d801128809c \ - --hash=sha256:9171e8e1a1f221953e38e84ae0abffe8759002fd8968106ee379febbb5358b33 \ - --hash=sha256:978117122ca4cc59b28af5322253017f6c5fc03dbdda78c7f4b94ae984c8dd43 \ - --hash=sha256:9b1b5adc5adf596c59dca57156b71ad301d73956f5bab4039b0e34dbf50b9fa0 \ - --hash=sha256:9bcf56efdb83244cde070e82a69c0f03c47c235f0a5cb6c81d9da23af7fbaae4 \ - --hash=sha256:a8c83718346de08d68b3cb1105c5d91e5fc39885d8610fdda16613d4e3941459 \ - --hash=sha256:ae77275a28667d9c82d4522b681504642055efa0368d73108511647c6499b31c \ - --hash=sha256:b57c0954a9fdd2b05b9cec0f5a12a0bdce5bf021a5b3b09323041613972481ab \ - --hash=sha256:b812417199eeb169c25f67815cfb66fd8de7ff098bf57d065e8c1943a7ba5c8f \ - --hash=sha256:cfad553a36548262e7da0f3a7464270e13900b898800fb571a5d4b298c3f8356 \ - --hash=sha256:d3222db9df629ef3c3673124f2e05fb72bc4a320c117e953fec0d69dde82e36d \ - --hash=sha256:d714595d81efab11b42bccd119977d94b25d12d3a806851ff6bfd286a4bce960 \ - --hash=sha256:d92a3e835a5100f1d5b566fff79217eab92223ca31900dba733902a182a35ab0 \ - --hash=sha256:ddc089315d030c54f0f03fb38286e2667c05009a78d659f108a8efcfbdf2e585 \ - --hash=sha256:e3b0c4da61f39899561e08e571f54472a09fa71717d9797928af558175ae5243 \ - --hash=sha256:eaaf80957c38e9d3f796f355a80fad945e72cd745e6b64c210e635b7043b673e \ - --hash=sha256:fa6b67f8bef277c2a4aadd548d58796854e7d760964126c3209b19bccc6a74f1 \ - --hash=sha256:fc6bc65b0cf524ee042e0bc2912b9206ef242edfba7426cf95763e4af01f527a +orjson==3.9.14 \ + --hash=sha256:0572f174f50b673b7df78680fb52cd0087a8585a6d06d295a5f790568e1064c6 \ + --hash=sha256:06fb40f8e49088ecaa02f1162581d39e2cf3fd9dbbfe411eb2284147c99bad79 \ + --hash=sha256:08e722a8d06b13b67a51f247a24938d1a94b4b3862e40e0eef3b2e98c99cd04c \ + --hash=sha256:135d518f73787ce323b1a5e21fb854fe22258d7a8ae562b81a49d6c7f826f2a3 \ + --hash=sha256:19cdea0664aec0b7f385be84986d4defd3334e9c3c799407686ee1c26f7b8251 \ + --hash=sha256:1f7b6f3ef10ae8e3558abb729873d033dbb5843507c66b1c0767e32502ba96bb \ + --hash=sha256:20837e10835c98973673406d6798e10f821e7744520633811a5a3d809762d8cc \ + --hash=sha256:236230433a9a4968ab895140514c308fdf9f607cb8bee178a04372b771123860 \ + --hash=sha256:23d1528db3c7554f9d6eeb09df23cb80dd5177ec56eeb55cc5318826928de506 \ + --hash=sha256:26280a7fcb62d8257f634c16acebc3bec626454f9ab13558bbf7883b9140760e \ + --hash=sha256:29512eb925b620e5da2fd7585814485c67cc6ba4fe739a0a700c50467a8a8065 \ + --hash=sha256:2eefc41ba42e75ed88bc396d8fe997beb20477f3e7efa000cd7a47eda452fbb2 \ + --hash=sha256:3014ccbda9be0b1b5f8ea895121df7e6524496b3908f4397ff02e923bcd8f6dd \ + --hash=sha256:449bf090b2aa4e019371d7511a6ea8a5a248139205c27d1834bb4b1e3c44d936 \ + --hash=sha256:4dc1c132259b38d12c6587d190cd09cd76e3b5273ce71fe1372437b4cbc65f6f \ + --hash=sha256:58b36f54da759602d8e2f7dad958752d453dfe2c7122767bc7f765e17dc59959 \ + --hash=sha256:5bf597530544db27a8d76aced49cfc817ee9503e0a4ebf0109cd70331e7bbe0c \ + --hash=sha256:6f39a10408478f4c05736a74da63727a1ae0e83e3533d07b19443400fe8591ca \ + --hash=sha256:6f52ac2eb49e99e7373f62e2a68428c6946cda52ce89aa8fe9f890c7278e2d3a \ + --hash=sha256:7183cc68ee2113b19b0b8714221e5e3b07b3ba10ca2bb108d78fd49cefaae101 \ + --hash=sha256:751250a31fef2bac05a2da2449aae7142075ea26139271f169af60456d8ad27a \ + --hash=sha256:75fc593cf836f631153d0e21beaeb8d26e144445c73645889335c2247fcd71a0 \ + --hash=sha256:7913079b029e1b3501854c9a78ad938ed40d61fe09bebab3c93e60ff1301b189 \ + --hash=sha256:793f6c9448ab6eb7d4974b4dde3f230345c08ca6c7995330fbceeb43a5c8aa5e \ + --hash=sha256:814f288c011efdf8f115c5ebcc1ab94b11da64b207722917e0ceb42f52ef30a3 \ + --hash=sha256:90903d2908158a2c9077a06f11e27545de610af690fb178fd3ba6b32492d4d1c \ + --hash=sha256:917311d6a64d1c327c0dfda1e41f3966a7fb72b11ca7aa2e7a68fcccc7db35d9 \ + --hash=sha256:95c03137b0cf66517c8baa65770507a756d3a89489d8ecf864ea92348e1beabe \ + --hash=sha256:978f416bbff9da8d2091e3cf011c92da68b13f2c453dcc2e8109099b2a19d234 \ + --hash=sha256:9a1af21160a38ee8be3f4fcf24ee4b99e6184cadc7f915d599f073f478a94d2c \ + --hash=sha256:a2591faa0c031cf3f57e5bce1461cfbd6160f3f66b5a72609a130924917cb07d \ + --hash=sha256:a603161318ff699784943e71f53899983b7dee571b4dd07c336437c9c5a272b0 \ + --hash=sha256:a6bc7928d161840096adc956703494b5c0193ede887346f028216cac0af87500 \ + --hash=sha256:a88cafb100af68af3b9b29b5ccd09fdf7a48c63327916c8c923a94c336d38dd3 \ + --hash=sha256:ab90c02cb264250b8a58cedcc72ed78a4a257d956c8d3c8bebe9751b818dfad8 \ + --hash=sha256:abcda41ecdc950399c05eff761c3de91485d9a70d8227cb599ad3a66afe93bcc \ + --hash=sha256:ac0c7eae7ad3a223bde690565442f8a3d620056bd01196f191af8be58a5248e1 \ + --hash=sha256:ac650d49366fa41fe702e054cb560171a8634e2865537e91f09a8d05ea5b1d37 \ + --hash=sha256:b7c11667421df2d8b18b021223505dcc3ee51be518d54e4dc49161ac88ac2b87 \ + --hash=sha256:ba3518b999f88882ade6686f1b71e207b52e23546e180499be5bbb63a2f9c6e6 \ + --hash=sha256:c19009ff37f033c70acd04b636380379499dac2cba27ae7dfc24f304deabbc81 \ + --hash=sha256:ce6f095eef0026eae76fc212f20f786011ecf482fc7df2f4c272a8ae6dd7b1ef \ + --hash=sha256:d2cf1d0557c61c75e18cf7d69fb689b77896e95553e212c0cc64cf2087944b84 \ + --hash=sha256:d450a8e0656efb5d0fcb062157b918ab02dcca73278975b4ee9ea49e2fcf5bd5 \ + --hash=sha256:df3266d54246cb56b8bb17fa908660d2a0f2e3f63fbc32451ffc1b1505051d07 \ + --hash=sha256:df76ecd17b1b3627bddfd689faaf206380a1a38cc9f6c4075bd884eaedcf46c2 \ + --hash=sha256:e2450d87dd7b4f277f4c5598faa8b49a0c197b91186c47a2c0b88e15531e4e3e \ + --hash=sha256:ea890e6dc1711aeec0a33b8520e395c2f3d59ead5b4351a788e06bf95fc7ba81 \ + --hash=sha256:f75823cc1674a840a151e999a7dfa0d86c911150dd6f951d0736ee9d383bf415 \ + --hash=sha256:fca33fdd0b38839b01912c57546d4f412ba7bfa0faf9bf7453432219aec2df07 # via -r requirements/main.in packaging==23.2 \ --hash=sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5 \ --hash=sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7 # via - # -r requirements/main.in + # -r main.in # b2sdk # forcediphttpsadapter # google-cloud-bigquery @@ -1145,20 +1145,20 @@ packaging==23.2 \ packaging-legacy==23.0.post0 \ --hash=sha256:6cd21cd283c09409349bccc10bb55bfd837b4aab86a7b0f87bfcb8dd9831a8a3 \ --hash=sha256:c974a42291a77112313f0198b87ad96e07a3c357295d572560a4b9c368f7d9db - # via -r requirements/main.in + # via -r main.in paginate==0.5.6 \ --hash=sha256:5e6007b6a9398177a7e1648d04fdd9f8c9766a1a945bceac82f1929e8c78af2d # via - # -r requirements/main.in + # -r main.in # paginate-sqlalchemy paginate-sqlalchemy==0.3.1 \ --hash=sha256:9ee349b74a5f2f52455cb1280d0ca24222c8137e638363df5877e6e76c369b5b \ --hash=sha256:e022f79ed798e62092a8618c528158ed619a1b604dce43c9dbc5441f5325137e - # via -r requirements/main.in + # via -r main.in passlib==1.7.4 \ --hash=sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1 \ --hash=sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04 - # via -r requirements/main.in + # via -r main.in pastedeploy==3.1.0 \ --hash=sha256:76388ad53a661448d436df28c798063108f70e994ddc749540d733cdbd1b38cf \ --hash=sha256:9ddbaf152f8095438a9fe81f82c78a6714b92ae8e066bed418b6a7ff6a095a95 @@ -1176,30 +1176,30 @@ plaster-pastedeploy==1.0.1 \ premailer==3.10.0 \ --hash=sha256:021b8196364d7df96d04f9ade51b794d0b77bcc19e998321c515633a2273be1a \ --hash=sha256:d1875a8411f5dc92b53ef9f193db6c0f879dc378d618e0ad292723e388bfe4c2 - # via -r requirements/main.in + # via -r main.in prompt-toolkit==3.0.43 \ --hash=sha256:3527b7af26106cbc65a040bcc84839a3566ec1b051bb0bfe953631e704b0ff7d \ --hash=sha256:a11a29cb3bf0a28a387fe5122cdb649816a957cd9261dcedf8c9f1fef33eacf6 # via click-repl -protobuf==4.25.2 \ - --hash=sha256:10894a2885b7175d3984f2be8d9850712c57d5e7587a2410720af8be56cdaf62 \ - --hash=sha256:2db9f8fa64fbdcdc93767d3cf81e0f2aef176284071507e3ede160811502fd3d \ - --hash=sha256:33a1aeef4b1927431d1be780e87b641e322b88d654203a9e9d93f218ee359e61 \ - --hash=sha256:47f3de503fe7c1245f6f03bea7e8d3ec11c6c4a2ea9ef910e3221c8a15516d62 \ - --hash=sha256:5e5c933b4c30a988b52e0b7c02641760a5ba046edc5e43d3b94a74c9fc57c1b3 \ - --hash=sha256:8f62574857ee1de9f770baf04dde4165e30b15ad97ba03ceac65f760ff018ac9 \ - --hash=sha256:a8b7a98d4ce823303145bf3c1a8bdb0f2f4642a414b196f04ad9853ed0c8f830 \ - --hash=sha256:b50c949608682b12efb0b2717f53256f03636af5f60ac0c1d900df6213910fd6 \ - --hash=sha256:d66a769b8d687df9024f2985d5137a337f957a0916cf5464d1513eee96a63ff0 \ - --hash=sha256:fc381d1dd0516343f1440019cedf08a7405f791cd49eef4ae1ea06520bc1c020 \ - --hash=sha256:fe599e175cb347efc8ee524bcd4b902d11f7262c0e569ececcb89995c15f0a5e +protobuf==4.25.3 \ + --hash=sha256:19b270aeaa0099f16d3ca02628546b8baefe2955bbe23224aaf856134eccf1e4 \ + --hash=sha256:209ba4cc916bab46f64e56b85b090607a676f66b473e6b762e6f1d9d591eb2e8 \ + --hash=sha256:25b5d0b42fd000320bd7830b349e3b696435f3b329810427a6bcce6a5492cc5c \ + --hash=sha256:7c8daa26095f82482307bc717364e7c13f4f1c99659be82890dcfc215194554d \ + --hash=sha256:c053062984e61144385022e53678fbded7aea14ebb3e0305ae3592fb219ccfa4 \ + --hash=sha256:d4198877797a83cbfe9bffa3803602bbe1625dc30d8a097365dbc762e5790faa \ + --hash=sha256:e3c97a1555fd6388f857770ff8b9703083de6bf1f9274a002a332d65fbb56c8c \ + --hash=sha256:e7cb0ae90dd83727f0c0718634ed56837bfeeee29a5f82a7514c03ee1364c019 \ + --hash=sha256:f0700d54bcf45424477e46a9f0944155b46fb0639d69728739c0e47bab83f2b9 \ + --hash=sha256:f1279ab38ecbfae7e456a108c5c0681e4956d5b1090027c1de0f934dfdb4b35c \ + --hash=sha256:f4f118245c4a087776e0a8408be33cf09f6c547442c00395fbfb116fac2f8ac2 # via # google-api-core # googleapis-common-protos psycopg[c]==3.1.18 \ --hash=sha256:31144d3fb4c17d78094d9e579826f047d4af1da6a10427d91dfcfb6ecdf6f12b \ --hash=sha256:4d5a0a5a8590906daa58ebd5f3cfc34091377354a1acced269dd10faf55da60e - # via -r requirements/main.in + # via -r main.in psycopg-c==3.1.18 \ --hash=sha256:ffff0c4a9c0e0b7aadb1acb7b61eb8f886365dd8ef00120ce14676235846ba73 # via psycopg @@ -1217,17 +1217,52 @@ pycparser==2.21 \ --hash=sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9 \ --hash=sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206 # via cffi -pycurl==7.45.2 \ - --hash=sha256:5730590be0271364a5bddd9e245c9cc0fb710c4cbacbdd95264a3122d23224ca +pycurl==7.45.3 \ + --hash=sha256:0c41a172d5e8a5cdd8328cc8134f47b2a57960ac677f7cda8520eaa9fbe7d990 \ + --hash=sha256:0f0e1251a608ffd75fc502f4014442e554c67d3d7a1b0a839c35efb6ad2f8bf8 \ + --hash=sha256:13006b62c157bb4483c58e1abdced6df723c9399255a4f5f6bb7f8e425106679 \ + --hash=sha256:1610cc45b5bc8b39bc18b981d0473e59ef41226ee467eaa8fbfc7276603ef5af \ + --hash=sha256:1e0d32d6ed3a7ba13dbbd3a6fb50ca76c40c70e6bc6fe347f90677478d3422c7 \ + --hash=sha256:205983e87d6aa0b6e93ec7320060de44efaa905ecc5d13f70cbe38c65684c5c4 \ + --hash=sha256:27f4c5c20c86a9a823677316724306fb1ce3b25ec568efd52026dc6c563e5b29 \ + --hash=sha256:2c8a2ce568193f9f84763717d8961cec0db4ec1aa08c6bcf4d90da5eb72bec86 \ + --hash=sha256:2facab1c35600088cb82b5b093bd700bfbd1e3191deab24f7d1803d9dc5b76fc \ + --hash=sha256:3648ed9a57a6b704673faeab3dc64d1469cc69f2bc1ed8227ffa0f84e147c500 \ + --hash=sha256:3d07c5daef2d0d85949e32ec254ee44232bb57febb0634194379dd14d1ff4f87 \ + --hash=sha256:43c5e61a58783ddf78ef84949f6bb6e52e092a13ec67678e9a9e21071ecf5b80 \ + --hash=sha256:483f3aa5d1bc8cff5657ad96f68e1d89281f971a7b6aa93408a31e3199981ea9 \ + --hash=sha256:51a40a56c58e63dac6145829f9e9bd66e5867a9f0741bcb9ffefab619851d44f \ + --hash=sha256:5ebc6a0ac60c371a9efaf7d55dec5820f76fdafb43a3be1e390011339dc329ae \ + --hash=sha256:7cfca02d70579853041063e53ca713d31161b8831b98d4f68c3554dc0448beec \ + --hash=sha256:80ac7c17e69ca6b76ccccb4255f7c29a2a36e5b69eb10c2adba82135d43afe8c \ + --hash=sha256:8451e8475051f16eb4776380384699cb8ddd10ea8410bcbfaee5a6fc4c046de6 \ + --hash=sha256:86f66d334deaaab20a576fb785587566081407adc703318203fe26e43277ef12 \ + --hash=sha256:8c2471af9079ad798e1645ec0b0d3d4223db687379d17dd36a70637449f81d6b \ + --hash=sha256:921c9db0c3128481954f625b3b1bc10c730100aa944d54643528f716676439ee \ + --hash=sha256:936afd9c5ff7fe7457065e878a279811787778f472f9a4e8c5df79e7728358e2 \ + --hash=sha256:9f7afe5ef0e4750ac4515baebc251ee94aaefe5de6e2e8a24668473128d69904 \ + --hash=sha256:a0f920582b8713ca87d5a288a7532607bc4454275d733fc880650d602dbe3c67 \ + --hash=sha256:b129e9ee07f80b4af957607917af46ab517b0c4e746692f6d9e50e973edba8d8 \ + --hash=sha256:beaaa4450e23d41dd0c2f2f47a4f8a171210271543550c2c556090c7eeea88f5 \ + --hash=sha256:bf613844a1647fe3d2bba1f5c9c96a62a85280123a57a8a0c8d2f37d518bc10a \ + --hash=sha256:c0915ea139f66a289edc4f9de10cb45078af1bb950491c5612969864236a2e7e \ + --hash=sha256:c2c246bc29e8762ff4c8a833ac5b4da4c797d16ab138286e8aec9b0c0a0da2d4 \ + --hash=sha256:c7c13e4268550cde14a6f4743cc8bd8c035d4cd36514d58eff70276d68954b6f \ + --hash=sha256:c854885398410fa6e88fc29f7a420a3c13b88bae9b4e10a804437b582e24f58b \ + --hash=sha256:dbf816a6d0cb71e7fd06609246bbea4eaf100649d9decf49e4eb329594f70be7 \ + --hash=sha256:dd33fd9de8907a6275c70113124aeb7eea672c1324f5d5423f203738b341697d \ + --hash=sha256:e08a06802c8c8a9d04cf3319f9230ec09062c55d2550bd48f8ada1df1431adcf \ + --hash=sha256:fa7751b614d9aa82d7a0f49ca90924c29c6cedf85a2f8687fb6a772dbfe48711 \ + --hash=sha256:fbd4a6b8654b779089c5a44af1c65c1419c2cd60718780df6d8f354eb35d6d55 # via - # -r requirements/main.in + # -r main.in # celery # kombu pydantic==2.6.1 \ --hash=sha256:0b6a909df3192245cb736509a92ff69e4fef76116feffec68e93a567347bae6f \ --hash=sha256:4fd5c182a2488dc63e6d32737ff19937888001e2a6d86e94b3f233104a5d1fa9 # via - # -r requirements/main.in + # -r main.in # webauthn pydantic-core==2.16.2 \ --hash=sha256:02906e7306cb8c5901a1feb61f9ab5e5c690dbbeaa04d84c1b9ae2a01ebe9379 \ @@ -1318,12 +1353,12 @@ pyjwt[crypto]==2.8.0 \ --hash=sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de \ --hash=sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320 # via - # -r requirements/main.in + # -r main.in # pyjwt pymacaroons==0.13.0 \ --hash=sha256:1e6bba42a5f66c245adf38a5a4006a99dcc06a0703786ea636098667d42903b8 \ --hash=sha256:3e14dff6a262fdbf1a15e769ce635a8aea72e6f8f91e408f9a97166c53b91907 - # via -r requirements/main.in + # via -r main.in pynacl==1.5.0 \ --hash=sha256:06b8f6fa7f5de8d5d2f7573fe8c863c051225a27b61e6860fd047b1775807858 \ --hash=sha256:0c84947a22519e013607c9be43706dd42513f9e6ae5d39d3613ca1e142fba44d \ @@ -1347,42 +1382,42 @@ pyparsing==3.1.1 \ pyqrcode==1.2.1 \ --hash=sha256:1b2812775fa6ff5c527977c4cd2ccb07051ca7d0bc0aecf937a43864abe5eff6 \ --hash=sha256:fdbf7634733e56b72e27f9bce46e4550b75a3a2c420414035cae9d9d26b234d5 - # via -r requirements/main.in + # via -r main.in pyramid==2.0.2 \ --hash=sha256:2e6585ac55c147f0a51bc00dadf72075b3bdd9a871b332ff9e5e04117ccd76fa \ --hash=sha256:372138a738e4216535cc76dcce6eddd5a1aaca95130f2354fb834264c06f18de # via - # -r requirements/main.in + # -r main.in # pyramid-jinja2 # pyramid-mailer # pyramid-retry # pyramid-rpc # pyramid-services # pyramid-tm -pyramid-jinja2==2.10 \ - --hash=sha256:3d551e8aadde8efb925efd2126d7ecd8764eac6c62766cf8729dec9f6838efca \ - --hash=sha256:f271069d9e9acbac7adb6912190a848f6338f7e57afbaf3e95237fe83cc8f4d2 +pyramid-jinja2==2.10.1 \ + --hash=sha256:425a52a0c1cc2a83e183d22bb15cdd999112aa4c7b1e0f4e21c49a6b523ad6e1 \ + --hash=sha256:8c508cb35c135f95149ca236110f9c8875343575740d16c5cb73a50ef1c21677 # via -r requirements/main.in pyramid-mailer==0.15.1 \ --hash=sha256:28d4a7829ebc19dd40e712d8cb1998cec03c296ba675b2c112a503539738bdc1 \ --hash=sha256:ec0aff54d9179b2aa2922ff82c2016a4dc8d1da5dc3408d6594f0e2096446f9b - # via -r requirements/main.in + # via -r main.in pyramid-retry==2.1.1 \ --hash=sha256:b5129a60eb9d7409234ea52839006426d2ae887b4a1f0530c75ec336cabf2476 \ --hash=sha256:baa8276ae68babad09e5f2f94efc4f7421f3b8fb526151df522052f8cd3ec0c9 - # via -r requirements/main.in + # via -r main.in pyramid-rpc==0.8 \ --hash=sha256:0ad0368404d4f5c7afd31e801b48efed6866bdbeb0f5d14413d2a0bb17f00bd6 \ --hash=sha256:5dcd59a52d28ed5594df897ddbd473f68e599d48d4c86546e688db9752fa1f3a - # via -r requirements/main.in + # via -r main.in pyramid-services==2.2 \ --hash=sha256:01d175d84752e2a4178519cbd26cf900aa62a2e267cdd71370e5b88d06092b38 \ --hash=sha256:459f4da035198592b776fe80f51e72439984f50abd4f2f1719584d37aa763639 - # via -r requirements/main.in + # via -r main.in pyramid-tm==2.5 \ --hash=sha256:5c81dcecd33770f5e3596687d2be35ffc4f8ce5eda00a31acb00ae35a51430d0 \ --hash=sha256:6638721946e809de8b4bf3f405bd2daaaa76d58442cbdf46be30ebc259f1a354 - # via -r requirements/main.in + # via -r main.in python-dateutil==2.8.2 \ --hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \ --hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9 @@ -1392,25 +1427,25 @@ python-dateutil==2.8.2 \ # celery-redbeat # elasticsearch-dsl # google-cloud-bigquery -python-slugify==8.0.3 \ - --hash=sha256:c71189c161e8c671f1b141034d9a56308a8a5978cd13d40446c879569212fdd1 \ - --hash=sha256:e04cba5f1c562502a1175c84a8bc23890c54cdaf23fccaaf0bf78511508cabed +python-slugify==8.0.4 \ + --hash=sha256:276540b79961052b66b7d116620b36518847f52d5fd9e3a70164fc8c50faa6b8 \ + --hash=sha256:59202371d1d05b54a9e7720c5e038f928f45daaffe41dd10822f3907b937c856 # via -r requirements/main.in pytz==2024.1 \ --hash=sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812 \ --hash=sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319 - # via -r requirements/main.in + # via -r main.in readme-renderer[md]==42.0 \ --hash=sha256:13d039515c1f24de668e2c93f2e877b9dbe6c6c32328b90a40a49d8b2b85f36d \ --hash=sha256:2d55489f83be4992fe4454939d1a051c33edbab778e82761d060c9fc6b308cd1 # via - # -r requirements/main.in + # -r main.in # readme-renderer redis==5.0.1 \ --hash=sha256:0dab495cd5753069d3bc650a0dde8a8f9edde16fc5691b689a566eda58100d0f \ --hash=sha256:ed4802971884ae19d640775ba3b03aa2e7bd5e8fb8dfaed2decce4d0fc48391f # via - # -r requirements/main.in + # -r main.in # celery-redbeat repoze-sendmail==4.4.1 \ --hash=sha256:7a8ea37914a5d38bad38052a83eac1d867b171ff4cc8b4d4994e892c05b0d424 \ @@ -1420,7 +1455,7 @@ requests==2.31.0 \ --hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \ --hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1 # via - # -r requirements/main.in + # -r main.in # b2sdk # datadog # forcediphttpsadapter @@ -1433,11 +1468,11 @@ requests==2.31.0 \ requests-aws4auth==1.2.3 \ --hash=sha256:8070a5207e95fa5fe88e87d9a75f34e768cbab35bb3557ef20cbbf9426dee4d5 \ --hash=sha256:d4c73c19f37f80d4aa9c5bd4fa376cfd0c69299c48b00a8eb2ae6b0416164fb8 - # via -r requirements/main.in + # via -r main.in rfc3986==2.0.0 \ --hash=sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd \ --hash=sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c - # via -r requirements/main.in + # via -r main.in rsa==4.9 \ --hash=sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7 \ --hash=sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21 @@ -1446,9 +1481,9 @@ s3transfer==0.7.0 \ --hash=sha256:10d6923c6359175f264811ef4bf6161a3156ce8e350e705396a7557d6293c33a \ --hash=sha256:fd3889a66f5fe17299fe75b82eae6cf722554edca744ca5d5fe308b104883d2e # via boto3 -sentry-sdk==1.40.0 \ - --hash=sha256:34ad8cfc9b877aaa2a8eb86bfe5296a467fffe0619b931a05b181c45f6da59bf \ - --hash=sha256:78575620331186d32f34b7ece6edea97ce751f58df822547d3ab85517881a27a +sentry-sdk==1.40.5 \ + --hash=sha256:d188b407c9bacbe2a50a824e1f8fb99ee1aeb309133310488c570cb6d7056643 \ + --hash=sha256:d2dca2392cc5c9a2cc9bb874dd7978ebb759682fe4fe889ee7e970ee8dd1c61e # via -r requirements/main.in six==1.16.0 \ --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ @@ -1460,58 +1495,58 @@ six==1.16.0 \ # pymacaroons # python-dateutil # requests-aws4auth -sqlalchemy[asyncio]==2.0.25 \ - --hash=sha256:0d3cab3076af2e4aa5693f89622bef7fa770c6fec967143e4da7508b3dceb9b9 \ - --hash=sha256:0dacf67aee53b16f365c589ce72e766efaabd2b145f9de7c917777b575e3659d \ - --hash=sha256:10331f129982a19df4284ceac6fe87353ca3ca6b4ca77ff7d697209ae0a5915e \ - --hash=sha256:14a6f68e8fc96e5e8f5647ef6cda6250c780612a573d99e4d881581432ef1669 \ - --hash=sha256:1b1180cda6df7af84fe72e4530f192231b1f29a7496951db4ff38dac1687202d \ - --hash=sha256:29049e2c299b5ace92cbed0c1610a7a236f3baf4c6b66eb9547c01179f638ec5 \ - --hash=sha256:342d365988ba88ada8af320d43df4e0b13a694dbd75951f537b2d5e4cb5cd002 \ - --hash=sha256:420362338681eec03f53467804541a854617faed7272fe71a1bfdb07336a381e \ - --hash=sha256:4344d059265cc8b1b1be351bfb88749294b87a8b2bbe21dfbe066c4199541ebd \ - --hash=sha256:4f7a7d7fcc675d3d85fbf3b3828ecd5990b8d61bd6de3f1b260080b3beccf215 \ - --hash=sha256:555651adbb503ac7f4cb35834c5e4ae0819aab2cd24857a123370764dc7d7e24 \ - --hash=sha256:59a21853f5daeb50412d459cfb13cb82c089ad4c04ec208cd14dddd99fc23b39 \ - --hash=sha256:5fdd402169aa00df3142149940b3bf9ce7dde075928c1886d9a1df63d4b8de62 \ - --hash=sha256:605b6b059f4b57b277f75ace81cc5bc6335efcbcc4ccb9066695e515dbdb3900 \ - --hash=sha256:665f0a3954635b5b777a55111ababf44b4fc12b1f3ba0a435b602b6387ffd7cf \ - --hash=sha256:6f9e2e59cbcc6ba1488404aad43de005d05ca56e069477b33ff74e91b6319735 \ - --hash=sha256:736ea78cd06de6c21ecba7416499e7236a22374561493b456a1f7ffbe3f6cdb4 \ - --hash=sha256:74b080c897563f81062b74e44f5a72fa44c2b373741a9ade701d5f789a10ba23 \ - --hash=sha256:75432b5b14dc2fff43c50435e248b45c7cdadef73388e5610852b95280ffd0e9 \ - --hash=sha256:75f99202324383d613ddd1f7455ac908dca9c2dd729ec8584c9541dd41822a2c \ - --hash=sha256:790f533fa5c8901a62b6fef5811d48980adeb2f51f1290ade8b5e7ba990ba3de \ - --hash=sha256:798f717ae7c806d67145f6ae94dc7c342d3222d3b9a311a784f371a4333212c7 \ - --hash=sha256:7c88f0c7dcc5f99bdb34b4fd9b69b93c89f893f454f40219fe923a3a2fd11625 \ - --hash=sha256:7d505815ac340568fd03f719446a589162d55c52f08abd77ba8964fbb7eb5b5f \ - --hash=sha256:84daa0a2055df9ca0f148a64fdde12ac635e30edbca80e87df9b3aaf419e144a \ - --hash=sha256:87d91043ea0dc65ee583026cb18e1b458d8ec5fc0a93637126b5fc0bc3ea68c4 \ - --hash=sha256:87f6e732bccd7dcf1741c00f1ecf33797383128bd1c90144ac8adc02cbb98643 \ - --hash=sha256:884272dcd3ad97f47702965a0e902b540541890f468d24bd1d98bcfe41c3f018 \ - --hash=sha256:8b8cb63d3ea63b29074dcd29da4dc6a97ad1349151f2d2949495418fd6e48db9 \ - --hash=sha256:91f7d9d1c4dd1f4f6e092874c128c11165eafcf7c963128f79e28f8445de82d5 \ - --hash=sha256:a2c69a7664fb2d54b8682dd774c3b54f67f84fa123cf84dda2a5f40dcaa04e08 \ - --hash=sha256:a3be4987e3ee9d9a380b66393b77a4cd6d742480c951a1c56a23c335caca4ce3 \ - --hash=sha256:a86b4240e67d4753dc3092d9511886795b3c2852abe599cffe108952f7af7ac3 \ - --hash=sha256:aa9373708763ef46782d10e950b49d0235bfe58facebd76917d3f5cbf5971aed \ - --hash=sha256:b64b183d610b424a160b0d4d880995e935208fc043d0302dd29fee32d1ee3f95 \ - --hash=sha256:b801154027107461ee992ff4b5c09aa7cc6ec91ddfe50d02bca344918c3265c6 \ - --hash=sha256:bb209a73b8307f8fe4fe46f6ad5979649be01607f11af1eb94aa9e8a3aaf77f0 \ - --hash=sha256:bc8b7dabe8e67c4832891a5d322cec6d44ef02f432b4588390017f5cec186a84 \ - --hash=sha256:c51db269513917394faec5e5c00d6f83829742ba62e2ac4fa5c98d58be91662f \ - --hash=sha256:c55731c116806836a5d678a70c84cb13f2cedba920212ba7dcad53260997666d \ - --hash=sha256:cf18ff7fc9941b8fc23437cc3e68ed4ebeff3599eec6ef5eebf305f3d2e9a7c2 \ - --hash=sha256:d24f571990c05f6b36a396218f251f3e0dda916e0c687ef6fdca5072743208f5 \ - --hash=sha256:db854730a25db7c956423bb9fb4bdd1216c839a689bf9cc15fada0a7fb2f4570 \ - --hash=sha256:dc55990143cbd853a5d038c05e79284baedf3e299661389654551bd02a6a68d7 \ - --hash=sha256:e607cdd99cbf9bb80391f54446b86e16eea6ad309361942bf88318bcd452363c \ - --hash=sha256:ecf6d4cda1f9f6cb0b45803a01ea7f034e2f1aed9475e883410812d9f9e3cfcf \ - --hash=sha256:f2a159111a0f58fb034c93eeba211b4141137ec4b0a6e75789ab7a3ef3c7e7e3 \ - --hash=sha256:f37c0caf14b9e9b9e8f6dbc81bc56db06acb4363eba5a633167781a48ef036ed \ - --hash=sha256:f5693145220517b5f42393e07a6898acdfe820e136c98663b971906120549da5 +sqlalchemy[asyncio]==2.0.27 \ + --hash=sha256:03f448ffb731b48323bda68bcc93152f751436ad6037f18a42b7e16af9e91c07 \ + --hash=sha256:0de1263aac858f288a80b2071990f02082c51d88335a1db0d589237a3435fe71 \ + --hash=sha256:0fb3bffc0ced37e5aa4ac2416f56d6d858f46d4da70c09bb731a246e70bff4d5 \ + --hash=sha256:120af1e49d614d2525ac247f6123841589b029c318b9afbfc9e2b70e22e1827d \ + --hash=sha256:1306102f6d9e625cebaca3d4c9c8f10588735ef877f0360b5cdb4fdfd3fd7131 \ + --hash=sha256:15e19a84b84528f52a68143439d0c7a3a69befcd4f50b8ef9b7b69d2628ae7c4 \ + --hash=sha256:1ab4e0448018d01b142c916cc7119ca573803a4745cfe341b8f95657812700ac \ + --hash=sha256:1fc19ae2e07a067663dd24fca55f8ed06a288384f0e6e3910420bf4b1270cc51 \ + --hash=sha256:2f5c9dfb0b9ab5e3a8a00249534bdd838d943ec4cfb9abe176a6c33408430230 \ + --hash=sha256:30d81cc1192dc693d49d5671cd40cdec596b885b0ce3b72f323888ab1c3863d5 \ + --hash=sha256:33e8bde8fff203de50399b9039c4e14e42d4d227759155c21f8da4a47fc8053c \ + --hash=sha256:4535c49d961fe9a77392e3a630a626af5baa967172d42732b7a43496c8b28876 \ + --hash=sha256:48217be1de7d29a5600b5c513f3f7664b21d32e596d69582be0a94e36b8309cb \ + --hash=sha256:5ada0438f5b74c3952d916c199367c29ee4d6858edff18eab783b3978d0db16d \ + --hash=sha256:5b78aa9f4f68212248aaf8943d84c0ff0f74efc65a661c2fc68b82d498311fd5 \ + --hash=sha256:5cd20f58c29bbf2680039ff9f569fa6d21453fbd2fa84dbdb4092f006424c2e6 \ + --hash=sha256:611068511b5531304137bcd7fe8117c985d1b828eb86043bd944cebb7fae3910 \ + --hash=sha256:680b9a36029b30cf063698755d277885d4a0eab70a2c7c6e71aab601323cba45 \ + --hash=sha256:6c5bad7c60a392850d2f0fee8f355953abaec878c483dd7c3836e0089f046bf6 \ + --hash=sha256:6c7a596d0be71b7baa037f4ac10d5e057d276f65a9a611c46970f012752ebf2d \ + --hash=sha256:7f470327d06400a0aa7926b375b8e8c3c31d335e0884f509fe272b3c700a7254 \ + --hash=sha256:86a6ed69a71fe6b88bf9331594fa390a2adda4a49b5c06f98e47bf0d392534f8 \ + --hash=sha256:8dfc936870507da96aebb43e664ae3a71a7b96278382bcfe84d277b88e379b18 \ + --hash=sha256:954d9735ee9c3fa74874c830d089a815b7b48df6f6b6e357a74130e478dbd951 \ + --hash=sha256:9e56afce6431450442f3ab5973156289bd5ec33dd618941283847c9fd5ff06bf \ + --hash=sha256:a3012ab65ea42de1be81fff5fb28d6db893ef978950afc8130ba707179b4284a \ + --hash=sha256:ad862295ad3f644e3c2c0d8b10a988e1600d3123ecb48702d2c0f26771f1c396 \ + --hash=sha256:b1d9d1bfd96eef3c3faedb73f486c89e44e64e40e5bfec304ee163de01cf996f \ + --hash=sha256:b86abba762ecfeea359112b2bb4490802b340850bbee1948f785141a5e020de8 \ + --hash=sha256:b90053be91973a6fb6020a6e44382c97739736a5a9d74e08cc29b196639eb979 \ + --hash=sha256:c4fbe6a766301f2e8a4519f4500fe74ef0a8509a59e07a4085458f26228cd7cc \ + --hash=sha256:ca891af9f3289d24a490a5fde664ea04fe2f4984cd97e26de7442a4251bd4b7c \ + --hash=sha256:cb0845e934647232b6ff5150df37ceffd0b67b754b9fdbb095233deebcddbd4a \ + --hash=sha256:ce850db091bf7d2a1f2fdb615220b968aeff3849007b1204bf6e3e50a57b3d32 \ + --hash=sha256:d04e579e911562f1055d26dab1868d3e0bb905db3bccf664ee8ad109f035618a \ + --hash=sha256:d07ee7793f2aeb9b80ec8ceb96bc8cc08a2aec8a1b152da1955d64e4825fcbac \ + --hash=sha256:d177b7e82f6dd5e1aebd24d9c3297c70ce09cd1d5d37b43e53f39514379c029c \ + --hash=sha256:d7b5a3e2120982b8b6bd1d5d99e3025339f7fb8b8267551c679afb39e9c7c7f1 \ + --hash=sha256:d873c21b356bfaf1589b89090a4011e6532582b3a8ea568a00e0c3aab09399dd \ + --hash=sha256:d997c5938a08b5e172c30583ba6b8aad657ed9901fc24caf3a7152eeccb2f1b4 \ + --hash=sha256:dbcd77c4d94b23e0753c5ed8deba8c69f331d4fd83f68bfc9db58bc8983f49cd \ + --hash=sha256:e36aa62b765cf9f43a003233a8c2d7ffdeb55bc62eaa0a0380475b228663a38f \ + --hash=sha256:e97cf143d74a7a5a0f143aa34039b4fecf11343eed66538610debc438685db4a \ + --hash=sha256:eb15ef40b833f5b2f19eeae65d65e191f039e71790dd565c2af2a3783f72262f \ + --hash=sha256:ec1f5a328464daf7a1e4e385e4f5652dd9b1d12405075ccba1df842f7774b4fc \ + --hash=sha256:f9374e270e2553653d710ece397df67db9d19c60d2647bcd35bfc616f1622dcd \ + --hash=sha256:fa67d821c1fd268a5a87922ef4940442513b4e6c377553506b9db3b83beebbd8 \ + --hash=sha256:fd8aafda7cdff03b905d4426b714601c0978725a19efc39f5f207b86d188ba01 \ + --hash=sha256:ff2f1b7c963961d41403b650842dc2039175b906ab2093635d8319bef0b7d620 # via - # -r requirements/main.in + # -r main.in # alembic # alembic-postgresql-enum # paginate-sqlalchemy @@ -1521,14 +1556,14 @@ stdlib-list==0.10.0 \ --hash=sha256:6519c50d645513ed287657bfe856d527f277331540691ddeaf77b25459964a14 \ --hash=sha256:b3a911bc441d03e0332dd1a9e7d0870ba3bb0a542a74d7524f54fb431256e214 # via -r requirements/main.in -stripe==8.1.0 \ - --hash=sha256:3bd203e61dde45202b57003c3e9152e065adad4d72930f3141f5a051e24527ed \ - --hash=sha256:59b5b143bd83ad83daabd9e4caf8e10473d63f28a4ca370b47a3b53c8da7b46c +stripe==8.3.0 \ + --hash=sha256:3f867530d2e0b4b997d6001e5a7edaa5fdfbbc76414e65e806fc5f37703ede5b \ + --hash=sha256:e1a430723e6126b0240ff483249d8aa1b6c0ded29db0dc34e192e19af5b6049c # via -r requirements/main.in structlog==24.1.0 \ --hash=sha256:3f6efe7d25fab6e86f277713c218044669906537bb717c1807a09d46bca0714d \ --hash=sha256:41a09886e4d55df25bdcb9b5c9674bccfab723ff43e0a86a1b7b236be8e57b16 - # via -r requirements/main.in + # via -r main.in tenacity==8.2.3 \ --hash=sha256:5398ef0d78e63f40007c1fb4c0bff96e1911394d2fa8d194f77619c05ff6cc8a \ --hash=sha256:ce510e327a630c9e1beaf17d42e6ffacc88185044ad85cf74c0a8887c6a0f88c @@ -1537,15 +1572,15 @@ text-unidecode==1.3 \ --hash=sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8 \ --hash=sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93 # via python-slugify -tqdm==4.66.1 \ - --hash=sha256:d302b3c5b53d47bce91fea46679d9c3c6508cf6332229aa1e7d8653723793386 \ - --hash=sha256:d88e651f9db8d8551a62556d3cff9e3034274ca5d66e93197cf2490e2dcb69c7 +tqdm==4.66.2 \ + --hash=sha256:1ee4f8a893eb9bef51c6e35730cebf234d5d0b6bd112b0271e10ed7c24a02bd9 \ + --hash=sha256:6cd52cdf0fef0e0f543299cfc96fec90d7b8a7e88745f411ec33eb44d5ed3531 # via b2sdk transaction==4.0 \ --hash=sha256:68035db913f60d1be12f6563d201daab36c83e763de15899ff8338f26e5e62f2 \ --hash=sha256:e2519a316a05b14b3d483ac777df311087daaffeeafd3e9f7de62fc087ce3209 # via - # -r requirements/main.in + # -r main.in # pyramid-mailer # pyramid-tm # repoze-sendmail @@ -1557,7 +1592,7 @@ translationstring==1.4 \ trove-classifiers==2024.1.31 \ --hash=sha256:854aba3358f3cf10e5c0916aa533f5a39e27aadd8ade26a54cdc2a93257e39c4 \ --hash=sha256:bfdfe60bbf64985c524416afb637ecc79c558e0beb4b7f52b0039e01044b0229 - # via -r requirements/main.in + # via -r main.in typing-extensions==4.9.0 \ --hash=sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783 \ --hash=sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd @@ -1570,19 +1605,19 @@ typing-extensions==4.9.0 \ # pydantic-core # sqlalchemy # stripe -tzdata==2023.4 \ - --hash=sha256:aa3ace4329eeacda5b7beb7ea08ece826c28d761cda36e747cfbf97996d39bf3 \ - --hash=sha256:dd54c94f294765522c77399649b4fefd95522479a664a0cec87f41bebc6148c9 +tzdata==2024.1 \ + --hash=sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd \ + --hash=sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252 # via celery ua-parser==0.18.0 \ --hash=sha256:9d94ac3a80bcb0166823956a779186c746b50ea4c9fd9bf30fdb758553c38950 \ --hash=sha256:db51f1b59bfaa82ed9e2a1d99a54d3e4153dddf99ac1435d51828165422e624e - # via -r requirements/main.in + # via -r main.in urllib3==1.26.18 \ --hash=sha256:34b97092d7e0a3a8cf7cd10e386f401b3737364026c45e622aa02903dffe0f07 \ --hash=sha256:f8ecc1bba5667413457c529ab955bf8c67b45db799d159066261719e328580a0 # via - # -r requirements/main.in + # -r main.in # botocore # celery # elasticsearch @@ -1609,7 +1644,7 @@ wcwidth==0.2.13 \ webauthn==1.11.1 \ --hash=sha256:13592ee71489b571cb6e4a5d8b3c34f7b040cd3539a9d94b6b7d23fa88df5dfb \ --hash=sha256:24eda57903897369797f52a377f8c470e7057e79da5525779d0720a9fcc11926 - # via -r requirements/main.in + # via -r main.in webencodings==0.5.1 \ --hash=sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78 \ --hash=sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923 @@ -1621,7 +1656,7 @@ webob==1.8.7 \ whitenoise==6.6.0 \ --hash=sha256:8998f7370973447fac1e8ef6e8ded2c5209a7b1f67c1012866dbcd09681c3251 \ --hash=sha256:b1f9db9bf67dc183484d760b99f4080185633136a273a03f6436034a41064146 - # via -r requirements/main.in + # via -r main.in wired==0.3 \ --hash=sha256:08dcb4ccfe8c0ee5d7a65e73520b4cd838420491a640b5c8d12d39e26aee8e84 \ --hash=sha256:1122ff2df20aed18e8fe318daba13afc80640bc81f4bb0c2499c73c6a1dc4df0 @@ -1702,7 +1737,7 @@ wtforms[email]==3.1.2 \ --hash=sha256:bf831c042829c8cdbad74c27575098d541d039b1faa74c771545ecac916f2c07 \ --hash=sha256:f8d76180d7239c94c6322f7990ae1216dae3659b7aa1cee94b6318bdffb474b9 # via - # -r requirements/main.in + # -r main.in # wtforms zope-deprecation==5.0 \ --hash=sha256:28c2ee983812efb4676d33c7a8c6ade0df191c1c6d652bbbfe6e2eeee067b2d4 \ @@ -1710,43 +1745,43 @@ zope-deprecation==5.0 \ # via # pyramid # pyramid-jinja2 -zope-interface==6.1 \ - --hash=sha256:0c8cf55261e15590065039696607f6c9c1aeda700ceee40c70478552d323b3ff \ - --hash=sha256:13b7d0f2a67eb83c385880489dbb80145e9d344427b4262c49fbf2581677c11c \ - --hash=sha256:1f294a15f7723fc0d3b40701ca9b446133ec713eafc1cc6afa7b3d98666ee1ac \ - --hash=sha256:239a4a08525c080ff833560171d23b249f7f4d17fcbf9316ef4159f44997616f \ - --hash=sha256:2f8d89721834524a813f37fa174bac074ec3d179858e4ad1b7efd4401f8ac45d \ - --hash=sha256:2fdc7ccbd6eb6b7df5353012fbed6c3c5d04ceaca0038f75e601060e95345309 \ - --hash=sha256:34c15ca9248f2e095ef2e93af2d633358c5f048c49fbfddf5fdfc47d5e263736 \ - --hash=sha256:387545206c56b0315fbadb0431d5129c797f92dc59e276b3ce82db07ac1c6179 \ - --hash=sha256:43b576c34ef0c1f5a4981163b551a8781896f2a37f71b8655fd20b5af0386abb \ - --hash=sha256:57d0a8ce40ce440f96a2c77824ee94bf0d0925e6089df7366c2272ccefcb7941 \ - --hash=sha256:5a804abc126b33824a44a7aa94f06cd211a18bbf31898ba04bd0924fbe9d282d \ - --hash=sha256:67be3ca75012c6e9b109860820a8b6c9a84bfb036fbd1076246b98e56951ca92 \ - --hash=sha256:6af47f10cfc54c2ba2d825220f180cc1e2d4914d783d6fc0cd93d43d7bc1c78b \ - --hash=sha256:6dc998f6de015723196a904045e5a2217f3590b62ea31990672e31fbc5370b41 \ - --hash=sha256:70d2cef1bf529bff41559be2de9d44d47b002f65e17f43c73ddefc92f32bf00f \ - --hash=sha256:7ebc4d34e7620c4f0da7bf162c81978fce0ea820e4fa1e8fc40ee763839805f3 \ - --hash=sha256:964a7af27379ff4357dad1256d9f215047e70e93009e532d36dcb8909036033d \ - --hash=sha256:97806e9ca3651588c1baaebb8d0c5ee3db95430b612db354c199b57378312ee8 \ - --hash=sha256:9b9bc671626281f6045ad61d93a60f52fd5e8209b1610972cf0ef1bbe6d808e3 \ - --hash=sha256:9ffdaa5290422ac0f1688cb8adb1b94ca56cee3ad11f29f2ae301df8aecba7d1 \ - --hash=sha256:a0da79117952a9a41253696ed3e8b560a425197d4e41634a23b1507efe3273f1 \ - --hash=sha256:a41f87bb93b8048fe866fa9e3d0c51e27fe55149035dcf5f43da4b56732c0a40 \ - --hash=sha256:aa6fd016e9644406d0a61313e50348c706e911dca29736a3266fc9e28ec4ca6d \ - --hash=sha256:ad54ed57bdfa3254d23ae04a4b1ce405954969c1b0550cc2d1d2990e8b439de1 \ - --hash=sha256:b012d023b4fb59183909b45d7f97fb493ef7a46d2838a5e716e3155081894605 \ - --hash=sha256:b51b64432eed4c0744241e9ce5c70dcfecac866dff720e746d0a9c82f371dfa7 \ - --hash=sha256:bbe81def9cf3e46f16ce01d9bfd8bea595e06505e51b7baf45115c77352675fd \ - --hash=sha256:c9559138690e1bd4ea6cd0954d22d1e9251e8025ce9ede5d0af0ceae4a401e43 \ - --hash=sha256:e30506bcb03de8983f78884807e4fd95d8db6e65b69257eea05d13d519b83ac0 \ - --hash=sha256:e33e86fd65f369f10608b08729c8f1c92ec7e0e485964670b4d2633a4812d36b \ - --hash=sha256:e441e8b7d587af0414d25e8d05e27040d78581388eed4c54c30c0c91aad3a379 \ - --hash=sha256:e8bb9c990ca9027b4214fa543fd4025818dc95f8b7abce79d61dc8a2112b561a \ - --hash=sha256:ef43ee91c193f827e49599e824385ec7c7f3cd152d74cb1dfe02cb135f264d83 \ - --hash=sha256:ef467d86d3cfde8b39ea1b35090208b0447caaabd38405420830f7fd85fbdd56 \ - --hash=sha256:f89b28772fc2562ed9ad871c865f5320ef761a7fcc188a935e21fe8b31a38ca9 \ - --hash=sha256:fddbab55a2473f1d3b8833ec6b7ac31e8211b0aa608df5ab09ce07f3727326de +zope-interface==6.2 \ + --hash=sha256:02adbab560683c4eca3789cc0ac487dcc5f5a81cc48695ec247f00803cafe2fe \ + --hash=sha256:14e02a6fc1772b458ebb6be1c276528b362041217b9ca37e52ecea2cbdce9fac \ + --hash=sha256:25e0af9663eeac6b61b231b43c52293c2cb7f0c232d914bdcbfd3e3bd5c182ad \ + --hash=sha256:2606955a06c6852a6cff4abeca38346ed01e83f11e960caa9a821b3626a4467b \ + --hash=sha256:396f5c94654301819a7f3a702c5830f0ea7468d7b154d124ceac823e2419d000 \ + --hash=sha256:3b240883fb43160574f8f738e6d09ddbdbf8fa3e8cea051603d9edfd947d9328 \ + --hash=sha256:3b6c62813c63c543a06394a636978b22dffa8c5410affc9331ce6cdb5bfa8565 \ + --hash=sha256:4ae9793f114cee5c464cc0b821ae4d36e1eba961542c6086f391a61aee167b6f \ + --hash=sha256:4bce517b85f5debe07b186fc7102b332676760f2e0c92b7185dd49c138734b70 \ + --hash=sha256:4d45d2ba8195850e3e829f1f0016066a122bfa362cc9dc212527fc3d51369037 \ + --hash=sha256:4dd374927c00764fcd6fe1046bea243ebdf403fba97a937493ae4be2c8912c2b \ + --hash=sha256:506f5410b36e5ba494136d9fa04c548eaf1a0d9c442b0b0e7a0944db7620e0ab \ + --hash=sha256:59f7374769b326a217d0b2366f1c176a45a4ff21e8f7cebb3b4a3537077eff85 \ + --hash=sha256:5ee9789a20b0081dc469f65ff6c5007e67a940d5541419ca03ef20c6213dd099 \ + --hash=sha256:6fc711acc4a1c702ca931fdbf7bf7c86f2a27d564c85c4964772dadf0e3c52f5 \ + --hash=sha256:75d2ec3d9b401df759b87bc9e19d1b24db73083147089b43ae748aefa63067ef \ + --hash=sha256:76e0531d86523be7a46e15d379b0e975a9db84316617c0efe4af8338dc45b80c \ + --hash=sha256:8af82afc5998e1f307d5e72712526dba07403c73a9e287d906a8aa2b1f2e33dd \ + --hash=sha256:8f5d2c39f3283e461de3655e03faf10e4742bb87387113f787a7724f32db1e48 \ + --hash=sha256:97785604824981ec8c81850dd25c8071d5ce04717a34296eeac771231fbdd5cd \ + --hash=sha256:a3046e8ab29b590d723821d0785598e0b2e32b636a0272a38409be43e3ae0550 \ + --hash=sha256:abb0b3f2cb606981c7432f690db23506b1db5899620ad274e29dbbbdd740e797 \ + --hash=sha256:ac7c2046d907e3b4e2605a130d162b1b783c170292a11216479bb1deb7cadebe \ + --hash=sha256:af27b3fe5b6bf9cd01b8e1c5ddea0a0d0a1b8c37dc1c7452f1e90bf817539c6d \ + --hash=sha256:b386b8b9d2b6a5e1e4eadd4e62335571244cb9193b7328c2b6e38b64cfda4f0e \ + --hash=sha256:b66335bbdbb4c004c25ae01cc4a54fd199afbc1fd164233813c6d3c2293bb7e1 \ + --hash=sha256:d54f66c511ea01b9ef1d1a57420a93fbb9d48a08ec239f7d9c581092033156d0 \ + --hash=sha256:de125151a53ecdb39df3cb3deb9951ed834dd6a110a9e795d985b10bb6db4532 \ + --hash=sha256:de7916380abaef4bb4891740879b1afcba2045aee51799dfd6d6ca9bdc71f35f \ + --hash=sha256:e2fefad268ff5c5b314794e27e359e48aeb9c8bb2cbb5748a071757a56f6bb8f \ + --hash=sha256:e7b2bed4eea047a949296e618552d3fed00632dc1b795ee430289bdd0e3717f3 \ + --hash=sha256:e87698e2fea5ca2f0a99dff0a64ce8110ea857b640de536c76d92aaa2a91ff3a \ + --hash=sha256:ede888382882f07b9e4cd942255921ffd9f2901684198b88e247c7eabd27a000 \ + --hash=sha256:f444de0565db46d26c9fa931ca14f497900a295bd5eba480fc3fad25af8c763e \ + --hash=sha256:fa994e8937e8ccc7e87395b7b35092818905cf27c651e3ff3e7f29729f5ce3ce \ + --hash=sha256:febceb04ee7dd2aef08c2ff3d6f8a07de3052fc90137c507b0ede3ea80c21440 # via # pyramid # pyramid-retry @@ -1758,17 +1793,17 @@ zope-interface==6.1 \ zope-sqlalchemy==3.1 \ --hash=sha256:d9c2c3be695c213c5e22b7f7c6a4a214fa8eb5940b033465ba1c10a9d8b346db \ --hash=sha256:fdc7d65d8da335a34b90fb993e8217ef12808bad3025d2e3a6720db4138e4985 - # via -r requirements/main.in + # via -r main.in zxcvbn==4.4.28 \ --hash=sha256:151bd816817e645e9064c354b13544f85137ea3320ca3be1fb6873ea75ef7dc1 - # via -r requirements/main.in + # via -r main.in # The following packages are considered to be unsafe in a requirements file: -setuptools==69.0.3 \ - --hash=sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05 \ - --hash=sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78 +setuptools==69.1.0 \ + --hash=sha256:850894c4195f09c4ed30dba56213bf7c3f21d86ed6bdaafb5df5972593bfc401 \ + --hash=sha256:c054629b81b946d63a9c6e732bc8b2513a7c3ea645f11d0139a2191d735c60c6 # via - # -r requirements/main.in + # -r main.in # b2sdk # pyramid # repoze-sendmail diff --git a/requirements/tests.txt b/requirements/tests.txt index 6683dda992f9..5a5b73c887d8 100644 --- a/requirements/tests.txt +++ b/requirements/tests.txt @@ -104,67 +104,67 @@ charset-normalizer==3.3.2 \ --hash=sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519 \ --hash=sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561 # via requests -coverage==7.4.1 \ - --hash=sha256:0193657651f5399d433c92f8ae264aff31fc1d066deee4b831549526433f3f61 \ - --hash=sha256:02f2edb575d62172aa28fe00efe821ae31f25dc3d589055b3fb64d51e52e4ab1 \ - --hash=sha256:0491275c3b9971cdbd28a4595c2cb5838f08036bca31765bad5e17edf900b2c7 \ - --hash=sha256:077d366e724f24fc02dbfe9d946534357fda71af9764ff99d73c3c596001bbd7 \ - --hash=sha256:10e88e7f41e6197ea0429ae18f21ff521d4f4490aa33048f6c6f94c6045a6a75 \ - --hash=sha256:18e961aa13b6d47f758cc5879383d27b5b3f3dcd9ce8cdbfdc2571fe86feb4dd \ - --hash=sha256:1a78b656a4d12b0490ca72651fe4d9f5e07e3c6461063a9b6265ee45eb2bdd35 \ - --hash=sha256:1ed4b95480952b1a26d863e546fa5094564aa0065e1e5f0d4d0041f293251d04 \ - --hash=sha256:23b27b8a698e749b61809fb637eb98ebf0e505710ec46a8aa6f1be7dc0dc43a6 \ - --hash=sha256:23f5881362dcb0e1a92b84b3c2809bdc90db892332daab81ad8f642d8ed55042 \ - --hash=sha256:32a8d985462e37cfdab611a6f95b09d7c091d07668fdc26e47a725ee575fe166 \ - --hash=sha256:3468cc8720402af37b6c6e7e2a9cdb9f6c16c728638a2ebc768ba1ef6f26c3a1 \ - --hash=sha256:379d4c7abad5afbe9d88cc31ea8ca262296480a86af945b08214eb1a556a3e4d \ - --hash=sha256:3cacfaefe6089d477264001f90f55b7881ba615953414999c46cc9713ff93c8c \ - --hash=sha256:3e3424c554391dc9ef4a92ad28665756566a28fecf47308f91841f6c49288e66 \ - --hash=sha256:46342fed0fff72efcda77040b14728049200cbba1279e0bf1188f1f2078c1d70 \ - --hash=sha256:536d609c6963c50055bab766d9951b6c394759190d03311f3e9fcf194ca909e1 \ - --hash=sha256:5d6850e6e36e332d5511a48a251790ddc545e16e8beaf046c03985c69ccb2676 \ - --hash=sha256:6008adeca04a445ea6ef31b2cbaf1d01d02986047606f7da266629afee982630 \ - --hash=sha256:64e723ca82a84053dd7bfcc986bdb34af8d9da83c521c19d6b472bc6880e191a \ - --hash=sha256:6b00e21f86598b6330f0019b40fb397e705135040dbedc2ca9a93c7441178e74 \ - --hash=sha256:6d224f0c4c9c98290a6990259073f496fcec1b5cc613eecbd22786d398ded3ad \ - --hash=sha256:6dceb61d40cbfcf45f51e59933c784a50846dc03211054bd76b421a713dcdf19 \ - --hash=sha256:7ac8f8eb153724f84885a1374999b7e45734bf93a87d8df1e7ce2146860edef6 \ - --hash=sha256:85ccc5fa54c2ed64bd91ed3b4a627b9cce04646a659512a051fa82a92c04a448 \ - --hash=sha256:869b5046d41abfea3e381dd143407b0d29b8282a904a19cb908fa24d090cc018 \ - --hash=sha256:8bdb0285a0202888d19ec6b6d23d5990410decb932b709f2b0dfe216d031d218 \ - --hash=sha256:8dfc5e195bbef80aabd81596ef52a1277ee7143fe419efc3c4d8ba2754671756 \ - --hash=sha256:8e738a492b6221f8dcf281b67129510835461132b03024830ac0e554311a5c54 \ - --hash=sha256:918440dea04521f499721c039863ef95433314b1db00ff826a02580c1f503e45 \ - --hash=sha256:9641e21670c68c7e57d2053ddf6c443e4f0a6e18e547e86af3fad0795414a628 \ - --hash=sha256:9d2f9d4cc2a53b38cabc2d6d80f7f9b7e3da26b2f53d48f05876fef7956b6968 \ - --hash=sha256:a07f61fc452c43cd5328b392e52555f7d1952400a1ad09086c4a8addccbd138d \ - --hash=sha256:a3277f5fa7483c927fe3a7b017b39351610265308f5267ac6d4c2b64cc1d8d25 \ - --hash=sha256:a4a3907011d39dbc3e37bdc5df0a8c93853c369039b59efa33a7b6669de04c60 \ - --hash=sha256:aeb2c2688ed93b027eb0d26aa188ada34acb22dceea256d76390eea135083950 \ - --hash=sha256:b094116f0b6155e36a304ff912f89bbb5067157aff5f94060ff20bbabdc8da06 \ - --hash=sha256:b8ffb498a83d7e0305968289441914154fb0ef5d8b3157df02a90c6695978295 \ - --hash=sha256:b9bb62fac84d5f2ff523304e59e5c439955fb3b7f44e3d7b2085184db74d733b \ - --hash=sha256:c61f66d93d712f6e03369b6a7769233bfda880b12f417eefdd4f16d1deb2fc4c \ - --hash=sha256:ca6e61dc52f601d1d224526360cdeab0d0712ec104a2ce6cc5ccef6ed9a233bc \ - --hash=sha256:ca7b26a5e456a843b9b6683eada193fc1f65c761b3a473941efe5a291f604c74 \ - --hash=sha256:d12c923757de24e4e2110cf8832d83a886a4cf215c6e61ed506006872b43a6d1 \ - --hash=sha256:d17bbc946f52ca67adf72a5ee783cd7cd3477f8f8796f59b4974a9b59cacc9ee \ - --hash=sha256:dfd1e1b9f0898817babf840b77ce9fe655ecbe8b1b327983df485b30df8cc011 \ - --hash=sha256:e0860a348bf7004c812c8368d1fc7f77fe8e4c095d661a579196a9533778e156 \ - --hash=sha256:f2f5968608b1fe2a1d00d01ad1017ee27efd99b3437e08b83ded9b7af3f6f766 \ - --hash=sha256:f3771b23bb3675a06f5d885c3630b1d01ea6cac9e84a01aaf5508706dba546c5 \ - --hash=sha256:f68ef3660677e6624c8cace943e4765545f8191313a07288a53d3da188bd8581 \ - --hash=sha256:f86f368e1c7ce897bf2457b9eb61169a44e2ef797099fb5728482b8d69f3f016 \ - --hash=sha256:f90515974b39f4dea2f27c0959688621b46d96d5a626cf9c53dbc653a895c05c \ - --hash=sha256:fe558371c1bdf3b8fa03e097c523fb9645b8730399c14fe7721ee9c9e2a545d3 +coverage==7.4.2 \ + --hash=sha256:006d220ba2e1a45f1de083d5022d4955abb0aedd78904cd5a779b955b019ec73 \ + --hash=sha256:06fe398145a2e91edaf1ab4eee66149c6776c6b25b136f4a86fcbbb09512fd10 \ + --hash=sha256:175f56572f25e1e1201d2b3e07b71ca4d201bf0b9cb8fad3f1dfae6a4188de86 \ + --hash=sha256:18cac867950943fe93d6cd56a67eb7dcd2d4a781a40f4c1e25d6f1ed98721a55 \ + --hash=sha256:1a5ee18e3a8d766075ce9314ed1cb695414bae67df6a4b0805f5137d93d6f1cb \ + --hash=sha256:20a875bfd8c282985c4720c32aa05056f77a68e6d8bbc5fe8632c5860ee0b49b \ + --hash=sha256:2412e98e70f16243be41d20836abd5f3f32edef07cbf8f407f1b6e1ceae783ac \ + --hash=sha256:2599972b21911111114100d362aea9e70a88b258400672626efa2b9e2179609c \ + --hash=sha256:2ed37e16cf35c8d6e0b430254574b8edd242a367a1b1531bd1adc99c6a5e00fe \ + --hash=sha256:32b4ab7e6c924f945cbae5392832e93e4ceb81483fd6dc4aa8fb1a97b9d3e0e1 \ + --hash=sha256:34423abbaad70fea9d0164add189eabaea679068ebdf693baa5c02d03e7db244 \ + --hash=sha256:3507427d83fa961cbd73f11140f4a5ce84208d31756f7238d6257b2d3d868405 \ + --hash=sha256:3733545eb294e5ad274abe131d1e7e7de4ba17a144505c12feca48803fea5f64 \ + --hash=sha256:3ff5bdb08d8938d336ce4088ca1a1e4b6c8cd3bef8bb3a4c0eb2f37406e49643 \ + --hash=sha256:3ff7f92ae5a456101ca8f48387fd3c56eb96353588e686286f50633a611afc95 \ + --hash=sha256:42a9e754aa250fe61f0f99986399cec086d7e7a01dd82fd863a20af34cbce962 \ + --hash=sha256:51593a1f05c39332f623d64d910445fdec3d2ac2d96b37ce7f331882d5678ddf \ + --hash=sha256:5b11f9c6587668e495cc7365f85c93bed34c3a81f9f08b0920b87a89acc13469 \ + --hash=sha256:69f1665165ba2fe7614e2f0c1aed71e14d83510bf67e2ee13df467d1c08bf1e8 \ + --hash=sha256:78cdcbf7b9cb83fe047ee09298e25b1cd1636824067166dc97ad0543b079d22f \ + --hash=sha256:7df95fdd1432a5d2675ce630fef5f239939e2b3610fe2f2b5bf21fa505256fa3 \ + --hash=sha256:81a5fb41b0d24447a47543b749adc34d45a2cf77b48ca74e5bf3de60a7bd9edc \ + --hash=sha256:840456cb1067dc350af9080298c7c2cfdddcedc1cb1e0b30dceecdaf7be1a2d3 \ + --hash=sha256:8562ca91e8c40864942615b1d0b12289d3e745e6b2da901d133f52f2d510a1e3 \ + --hash=sha256:861d75402269ffda0b33af94694b8e0703563116b04c681b1832903fac8fd647 \ + --hash=sha256:8b98c89db1b150d851a7840142d60d01d07677a18f0f46836e691c38134ed18b \ + --hash=sha256:a178b7b1ac0f1530bb28d2e51f88c0bab3e5949835851a60dda80bff6052510c \ + --hash=sha256:a8ddbd158e069dded57738ea69b9744525181e99974c899b39f75b2b29a624e2 \ + --hash=sha256:ac4bab32f396b03ebecfcf2971668da9275b3bb5f81b3b6ba96622f4ef3f6e17 \ + --hash=sha256:ac9e95cefcf044c98d4e2c829cd0669918585755dd9a92e28a1a7012322d0a95 \ + --hash=sha256:adbdfcda2469d188d79771d5696dc54fab98a16d2ef7e0875013b5f56a251047 \ + --hash=sha256:b3c8bbb95a699c80a167478478efe5e09ad31680931ec280bf2087905e3b95ec \ + --hash=sha256:b3f2b1eb229f23c82898eedfc3296137cf1f16bb145ceab3edfd17cbde273fb7 \ + --hash=sha256:b4ae777bebaed89e3a7e80c4a03fac434a98a8abb5251b2a957d38fe3fd30088 \ + --hash=sha256:b953275d4edfab6cc0ed7139fa773dfb89e81fee1569a932f6020ce7c6da0e8f \ + --hash=sha256:bf54c3e089179d9d23900e3efc86d46e4431188d9a657f345410eecdd0151f50 \ + --hash=sha256:bf711d517e21fb5bc429f5c4308fbc430a8585ff2a43e88540264ae87871e36a \ + --hash=sha256:c00e54f0bd258ab25e7f731ca1d5144b0bf7bec0051abccd2bdcff65fa3262c9 \ + --hash=sha256:c11ca2df2206a4e3e4c4567f52594637392ed05d7c7fb73b4ea1c658ba560265 \ + --hash=sha256:c5f9683be6a5b19cd776ee4e2f2ffb411424819c69afab6b2db3a0a364ec6642 \ + --hash=sha256:cf89ab85027427d351f1de918aff4b43f4eb5f33aff6835ed30322a86ac29c9e \ + --hash=sha256:d1b750a8409bec61caa7824bfd64a8074b6d2d420433f64c161a8335796c7c6b \ + --hash=sha256:d779a48fac416387dd5673fc5b2d6bd903ed903faaa3247dc1865c65eaa5a93e \ + --hash=sha256:d9a1ef0f173e1a19738f154fb3644f90d0ada56fe6c9b422f992b04266c55d5a \ + --hash=sha256:ddb79414c15c6f03f56cc68fa06994f047cf20207c31b5dad3f6bab54a0f66ef \ + --hash=sha256:ef00d31b7569ed3cb2036f26565f1984b9fc08541731ce01012b02a4c238bf03 \ + --hash=sha256:f40ac873045db4fd98a6f40387d242bde2708a3f8167bd967ccd43ad46394ba2 \ + --hash=sha256:f593a4a90118d99014517c2679e04a4ef5aee2d81aa05c26c734d271065efcb6 \ + --hash=sha256:f5df76c58977bc35a49515b2fbba84a1d952ff0ec784a4070334dfbec28a2def \ + --hash=sha256:f72cdd2586f9a769570d4b5714a3837b3a59a53b096bb954f1811f6a0afad305 \ + --hash=sha256:f8e845d894e39fb53834da826078f6dc1a933b32b1478cf437007367efaf6f6a \ + --hash=sha256:fe6e43c8b510719b48af7db9631b5fbac910ade4bd90e6378c85ac5ac706382c # via -r requirements/tests.in factory-boy==3.3.0 \ --hash=sha256:a2cdbdb63228177aa4f1c52f4b6d83fab2b8623bf602c7dedd7eb83c0f69c04c \ --hash=sha256:bc76d97d1a65bbd9842a6d722882098eb549ec8ee1081f9fb2e8ff29f0c300f1 # via -r requirements/tests.in -faker==22.7.0 \ - --hash=sha256:d12edbac08a82a75ecd588f299f44f12e33f000c15fe414abc417f0836cb51ae \ - --hash=sha256:f797529ebeb9bd9e1851106b99e156c9bebe67d2730c8393a1705ed1c864f1bf +faker==23.2.1 \ + --hash=sha256:0520a6b97e07c658b2798d7140971c1d5bc4bcd5013e7937fe075fd054aa320c \ + --hash=sha256:f07b64d27f67b62c7f0536a72f47813015b3b51cd4664918454011094321e464 # via factory-boy freezegun==1.4.0 \ --hash=sha256:10939b0ba0ff5adaecf3b06a5c2f73071d9678e507c5eaedb23c761d56ac774b \ @@ -228,9 +228,9 @@ psycopg==3.1.18 \ --hash=sha256:31144d3fb4c17d78094d9e579826f047d4af1da6a10427d91dfcfb6ecdf6f12b \ --hash=sha256:4d5a0a5a8590906daa58ebd5f3cfc34091377354a1acced269dd10faf55da60e # via pytest-postgresql -pytest==7.4.4 \ - --hash=sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280 \ - --hash=sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8 +pytest==8.0.1 \ + --hash=sha256:267f6563751877d772019b13aacbe4e860d73fe8f651f28112e9ac37de7513ae \ + --hash=sha256:3e4f16fe1c0a9dc9d9389161c127c3edc5d810c38d6793042fb81d9f48a59fca # via # -r requirements/tests.in # pytest-icdiff @@ -309,9 +309,9 @@ requests==2.31.0 \ --hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \ --hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1 # via responses -responses==0.24.1 \ - --hash=sha256:a2b43f4c08bfb9c9bd242568328c65a34b318741d3fab884ac843c5ceeb543f9 \ - --hash=sha256:b127c6ca3f8df0eb9cc82fd93109a3007a86acb24871834c47b77765152ecf8c +responses==0.25.0 \ + --hash=sha256:01ae6a02b4f34e39bffceb0fc6786b67a25eae919c6368d05eabc8d9576c2a66 \ + --hash=sha256:2f0b9c2b6437db4b528619a77e5d565e4ec2a9532162ac1a131a83529db7be1a # via -r requirements/tests.in six==1.16.0 \ --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ @@ -345,7 +345,7 @@ webtest==3.0.0 \ # via -r requirements/tests.in # The following packages are considered to be unsafe in a requirements file: -setuptools==69.0.3 \ - --hash=sha256:385eb4edd9c9d5c17540511303e39a147ce2fc04bc55289c322b9e5904fe2c05 \ - --hash=sha256:be1af57fc409f93647f2e8e4573a142ed38724b8cdd389706a867bb4efcf1e78 +setuptools==69.1.0 \ + --hash=sha256:850894c4195f09c4ed30dba56213bf7c3f21d86ed6bdaafb5df5972593bfc401 \ + --hash=sha256:c054629b81b946d63a9c6e732bc8b2513a7c3ea645f11d0139a2191d735c60c6 # via pytest-postgresql diff --git a/setup.cfg b/setup.cfg index 4ff760318f94..6b097e3a129d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,7 +1,7 @@ [flake8] max-line-length = 88 exclude = *.egg,*/interfaces.py,node_modules,.state -ignore = W503,E203 +ignore = W503,E203,E701 select = E,W,F,N [flake8:local-plugins] diff --git a/tests/common/db/oidc.py b/tests/common/db/oidc.py index 59b71311a106..63354349755b 100644 --- a/tests/common/db/oidc.py +++ b/tests/common/db/oidc.py @@ -15,9 +15,11 @@ from warehouse.oidc.models import ( ActiveStatePublisher, GitHubPublisher, + GitLabPublisher, GooglePublisher, PendingActiveStatePublisher, PendingGitHubPublisher, + PendingGitLabPublisher, PendingGooglePublisher, ) @@ -51,6 +53,30 @@ class Meta: added_by = factory.SubFactory(UserFactory) +class GitLabPublisherFactory(WarehouseFactory): + class Meta: + model = GitLabPublisher + + id = factory.Faker("uuid4", cast_to=None) + project = factory.Faker("pystr", max_chars=12) + namespace = factory.Faker("pystr", max_chars=12) + workflow_filepath = "subfolder/example.yml" + environment = "production" + + +class PendingGitLabPublisherFactory(WarehouseFactory): + class Meta: + model = PendingGitLabPublisher + + id = factory.Faker("uuid4", cast_to=None) + project_name = "fake-nonexistent-project" + project = factory.Faker("pystr", max_chars=12) + namespace = factory.Faker("pystr", max_chars=12) + workflow_filepath = "subfolder/example.yml" + environment = "production" + added_by = factory.SubFactory(UserFactory) + + class GooglePublisherFactory(WarehouseFactory): class Meta: model = GooglePublisher diff --git a/tests/conftest.py b/tests/conftest.py index 5b1ba5d2f265..60d062d25c8a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -33,6 +33,7 @@ from pyramid.static import ManifestCacheBuster from pyramid_jinja2 import IJinja2Environment from pyramid_mailer.mailer import DummyMailer +from pytest_postgresql.config import get_config from pytest_postgresql.janitor import DatabaseJanitor from sqlalchemy import event @@ -248,12 +249,13 @@ def cli(): @pytest.fixture(scope="session") -def database(postgresql_proc, request): - pg_host = postgresql_proc.host - pg_port = postgresql_proc.port - pg_user = postgresql_proc.user - pg_db = postgresql_proc.dbname - pg_version = postgresql_proc.version +def database(request): + config = get_config(request) + pg_host = config.get("host") + pg_port = config.get("port") or os.environ.get("PGPORT", 5432) + pg_user = config.get("user") + pg_db = config.get("db", "tests") + pg_version = config.get("version", 14.4) janitor = DatabaseJanitor(pg_user, pg_host, pg_port, pg_db, pg_version) diff --git a/tests/unit/accounts/test_views.py b/tests/unit/accounts/test_views.py index bd9aca3ac90f..bc2a80abadd3 100644 --- a/tests/unit/accounts/test_views.py +++ b/tests/unit/accounts/test_views.py @@ -60,6 +60,7 @@ from warehouse.oidc.models import ( PendingActiveStatePublisher, PendingGitHubPublisher, + PendingGitLabPublisher, PendingGooglePublisher, ) from warehouse.organizations.models import ( @@ -2964,9 +2965,11 @@ def test_verify_project_role( assert isinstance(result, HTTPSeeOther) assert result.headers["Location"] == "/" assert db_request.route_path.calls == [ - pretend.call("manage.project.roles", project_name=project.name) - if desired_role == "Owner" - else pretend.call("packaging.project", name=project.name) + ( + pretend.call("manage.project.roles", project_name=project.name) + if desired_role == "Owner" + else pretend.call("packaging.project", name=project.name) + ) ] @pytest.mark.parametrize( @@ -3369,6 +3372,13 @@ def test_manage_publishing(self, metrics, monkeypatch): monkeypatch.setattr( views, "PendingGitHubPublisherForm", pending_github_publisher_form_cls ) + pending_gitlab_publisher_form_obj = pretend.stub() + pending_gitlab_publisher_form_cls = pretend.call_recorder( + lambda *a, **kw: pending_gitlab_publisher_form_obj + ) + monkeypatch.setattr( + views, "PendingGitLabPublisherForm", pending_gitlab_publisher_form_cls + ) pending_google_publisher_form_obj = pretend.stub() pending_google_publisher_form_cls = pretend.call_recorder( lambda *a, **kw: pending_google_publisher_form_obj @@ -3391,10 +3401,12 @@ def test_manage_publishing(self, metrics, monkeypatch): assert view.manage_publishing() == { "disabled": { "GitHub": False, + "GitLab": False, "Google": False, "ActiveState": False, }, "pending_github_publisher_form": pending_github_publisher_form_obj, + "pending_gitlab_publisher_form": pending_gitlab_publisher_form_obj, "pending_google_publisher_form": pending_google_publisher_form_obj, "pending_activestate_publisher_form": pending_activestate_publisher_form_obj, # noqa: E501 } @@ -3402,6 +3414,7 @@ def test_manage_publishing(self, metrics, monkeypatch): assert request.flags.disallow_oidc.calls == [ pretend.call(), pretend.call(AdminFlagValue.DISALLOW_GITHUB_OIDC), + pretend.call(AdminFlagValue.DISALLOW_GITLAB_OIDC), pretend.call(AdminFlagValue.DISALLOW_GOOGLE_OIDC), pretend.call(AdminFlagValue.DISALLOW_ACTIVESTATE_OIDC), ] @@ -3413,6 +3426,12 @@ def test_manage_publishing(self, metrics, monkeypatch): project_factory=project_factory, ) ] + assert pending_gitlab_publisher_form_cls.calls == [ + pretend.call( + request.POST, + project_factory=project_factory, + ) + ] def test_manage_publishing_admin_disabled(self, monkeypatch, pyramid_request): pyramid_request.user = pretend.stub() @@ -3439,6 +3458,13 @@ def test_manage_publishing_admin_disabled(self, monkeypatch, pyramid_request): monkeypatch.setattr( views, "PendingGitHubPublisherForm", pending_github_publisher_form_cls ) + pending_gitlab_publisher_form_obj = pretend.stub() + pending_gitlab_publisher_form_cls = pretend.call_recorder( + lambda *a, **kw: pending_gitlab_publisher_form_obj + ) + monkeypatch.setattr( + views, "PendingGitLabPublisherForm", pending_gitlab_publisher_form_cls + ) pending_google_publisher_form_obj = pretend.stub() pending_google_publisher_form_cls = pretend.call_recorder( lambda *a, **kw: pending_google_publisher_form_obj @@ -3461,10 +3487,12 @@ def test_manage_publishing_admin_disabled(self, monkeypatch, pyramid_request): assert view.manage_publishing() == { "disabled": { "GitHub": True, + "GitLab": True, "Google": True, "ActiveState": True, }, "pending_github_publisher_form": pending_github_publisher_form_obj, + "pending_gitlab_publisher_form": pending_gitlab_publisher_form_obj, "pending_google_publisher_form": pending_google_publisher_form_obj, "pending_activestate_publisher_form": pending_activestate_publisher_form_obj, # noqa: E501 } @@ -3472,6 +3500,7 @@ def test_manage_publishing_admin_disabled(self, monkeypatch, pyramid_request): assert pyramid_request.flags.disallow_oidc.calls == [ pretend.call(), pretend.call(AdminFlagValue.DISALLOW_GITHUB_OIDC), + pretend.call(AdminFlagValue.DISALLOW_GITLAB_OIDC), pretend.call(AdminFlagValue.DISALLOW_GOOGLE_OIDC), pretend.call(AdminFlagValue.DISALLOW_ACTIVESTATE_OIDC), ] @@ -3491,6 +3520,12 @@ def test_manage_publishing_admin_disabled(self, monkeypatch, pyramid_request): project_factory=project_factory, ) ] + assert pending_gitlab_publisher_form_cls.calls == [ + pretend.call( + pyramid_request.POST, + project_factory=project_factory, + ) + ] @pytest.mark.parametrize( "view_name, flag, publisher_name", @@ -3500,6 +3535,11 @@ def test_manage_publishing_admin_disabled(self, monkeypatch, pyramid_request): AdminFlagValue.DISALLOW_GITHUB_OIDC, "GitHub", ), + ( + "add_pending_gitlab_oidc_publisher", + AdminFlagValue.DISALLOW_GITLAB_OIDC, + "GitLab", + ), ( "add_pending_google_oidc_publisher", AdminFlagValue.DISALLOW_GOOGLE_OIDC, @@ -3550,6 +3590,13 @@ def test_add_pending_oidc_publisher_admin_disabled( "PendingActiveStatePublisherForm", pending_activestate_publisher_form_cls, ) + pending_gitlab_publisher_form_obj = pretend.stub() + pending_gitlab_publisher_form_cls = pretend.call_recorder( + lambda *a, **kw: pending_gitlab_publisher_form_obj + ) + monkeypatch.setattr( + views, "PendingGitLabPublisherForm", pending_gitlab_publisher_form_cls + ) pending_google_publisher_form_obj = pretend.stub() pending_google_publisher_form_cls = pretend.call_recorder( lambda *a, **kw: pending_google_publisher_form_obj @@ -3563,20 +3610,24 @@ def test_add_pending_oidc_publisher_admin_disabled( assert getattr(view, view_name)() == { "disabled": { "GitHub": True, + "GitLab": True, "Google": True, "ActiveState": True, }, "pending_github_publisher_form": pending_github_publisher_form_obj, + "pending_gitlab_publisher_form": pending_gitlab_publisher_form_obj, "pending_google_publisher_form": pending_google_publisher_form_obj, "pending_activestate_publisher_form": pending_activestate_publisher_form_obj, # noqa: E501 } assert pyramid_request.flags.disallow_oidc.calls == [ pretend.call(AdminFlagValue.DISALLOW_GITHUB_OIDC), + pretend.call(AdminFlagValue.DISALLOW_GITLAB_OIDC), pretend.call(AdminFlagValue.DISALLOW_GOOGLE_OIDC), pretend.call(AdminFlagValue.DISALLOW_ACTIVESTATE_OIDC), pretend.call(flag), pretend.call(AdminFlagValue.DISALLOW_GITHUB_OIDC), + pretend.call(AdminFlagValue.DISALLOW_GITLAB_OIDC), pretend.call(AdminFlagValue.DISALLOW_GOOGLE_OIDC), pretend.call(AdminFlagValue.DISALLOW_ACTIVESTATE_OIDC), ] @@ -3597,6 +3648,12 @@ def test_add_pending_oidc_publisher_admin_disabled( project_factory=project_factory, ) ] + assert pending_gitlab_publisher_form_cls.calls == [ + pretend.call( + pyramid_request.POST, + project_factory=project_factory, + ) + ] @pytest.mark.parametrize( "view_name, flag, publisher_name", @@ -3606,6 +3663,11 @@ def test_add_pending_oidc_publisher_admin_disabled( AdminFlagValue.DISALLOW_GITHUB_OIDC, "GitHub", ), + ( + "add_pending_gitlab_oidc_publisher", + AdminFlagValue.DISALLOW_GITLAB_OIDC, + "GitLab", + ), ( "add_pending_google_oidc_publisher", AdminFlagValue.DISALLOW_GOOGLE_OIDC, @@ -3652,6 +3714,13 @@ def test_add_pending_oidc_publisher_user_cannot_register( monkeypatch.setattr( views, "PendingGitHubPublisherForm", pending_github_publisher_form_cls ) + pending_gitlab_publisher_form_obj = pretend.stub() + pending_gitlab_publisher_form_cls = pretend.call_recorder( + lambda *a, **kw: pending_gitlab_publisher_form_obj + ) + monkeypatch.setattr( + views, "PendingGitLabPublisherForm", pending_gitlab_publisher_form_cls + ) pending_google_publisher_form_obj = pretend.stub() pending_google_publisher_form_cls = pretend.call_recorder( lambda *a, **kw: pending_google_publisher_form_obj @@ -3674,20 +3743,24 @@ def test_add_pending_oidc_publisher_user_cannot_register( assert getattr(view, view_name)() == { "disabled": { "GitHub": False, + "GitLab": False, "Google": False, "ActiveState": False, }, "pending_github_publisher_form": pending_github_publisher_form_obj, + "pending_gitlab_publisher_form": pending_gitlab_publisher_form_obj, "pending_google_publisher_form": pending_google_publisher_form_obj, "pending_activestate_publisher_form": pending_activestate_publisher_form_obj, # noqa: E501 } assert pyramid_request.flags.disallow_oidc.calls == [ pretend.call(AdminFlagValue.DISALLOW_GITHUB_OIDC), + pretend.call(AdminFlagValue.DISALLOW_GITLAB_OIDC), pretend.call(AdminFlagValue.DISALLOW_GOOGLE_OIDC), pretend.call(AdminFlagValue.DISALLOW_ACTIVESTATE_OIDC), pretend.call(flag), pretend.call(AdminFlagValue.DISALLOW_GITHUB_OIDC), + pretend.call(AdminFlagValue.DISALLOW_GITLAB_OIDC), pretend.call(AdminFlagValue.DISALLOW_GOOGLE_OIDC), pretend.call(AdminFlagValue.DISALLOW_ACTIVESTATE_OIDC), ] @@ -3714,6 +3787,12 @@ def test_add_pending_oidc_publisher_user_cannot_register( project_factory=project_factory, ) ] + assert pending_gitlab_publisher_form_cls.calls == [ + pretend.call( + pyramid_request.POST, + project_factory=project_factory, + ) + ] @pytest.mark.parametrize( "view_name, flag, publisher_name, make_publisher, publisher_class", @@ -3733,6 +3812,20 @@ def test_add_pending_oidc_publisher_user_cannot_register( ), PendingGitHubPublisher, ), + ( + "add_pending_gitlab_oidc_publisher", + AdminFlagValue.DISALLOW_GITLAB_OIDC, + "GitLab", + lambda i, user_id: PendingGitLabPublisher( + project_name="some-project-name-" + str(i), + project="some-repository" + str(i), + namespace="some-namespace", + workflow_filepath="some-filepath", + environment="", + added_by_id=user_id, + ), + PendingGitLabPublisher, + ), ( "add_pending_google_oidc_publisher", AdminFlagValue.DISALLOW_GOOGLE_OIDC, @@ -3803,13 +3896,16 @@ def test_add_pending_github_oidc_publisher_too_many_already( assert getattr(view, view_name)() == view.default_response assert db_request.flags.disallow_oidc.calls == [ pretend.call(AdminFlagValue.DISALLOW_GITHUB_OIDC), + pretend.call(AdminFlagValue.DISALLOW_GITLAB_OIDC), pretend.call(AdminFlagValue.DISALLOW_GOOGLE_OIDC), pretend.call(AdminFlagValue.DISALLOW_ACTIVESTATE_OIDC), pretend.call(flag), pretend.call(AdminFlagValue.DISALLOW_GITHUB_OIDC), + pretend.call(AdminFlagValue.DISALLOW_GITLAB_OIDC), pretend.call(AdminFlagValue.DISALLOW_GOOGLE_OIDC), pretend.call(AdminFlagValue.DISALLOW_ACTIVESTATE_OIDC), pretend.call(AdminFlagValue.DISALLOW_GITHUB_OIDC), + pretend.call(AdminFlagValue.DISALLOW_GITLAB_OIDC), pretend.call(AdminFlagValue.DISALLOW_GOOGLE_OIDC), pretend.call(AdminFlagValue.DISALLOW_ACTIVESTATE_OIDC), ] @@ -3837,6 +3933,10 @@ def test_add_pending_github_oidc_publisher_too_many_already( "add_pending_github_oidc_publisher", "GitHub", ), + ( + "add_pending_gitlab_oidc_publisher", + "GitLab", + ), ( "add_pending_google_oidc_publisher", "Google", @@ -3907,6 +4007,10 @@ def test_add_pending_oidc_publisher_ratelimited( "add_pending_github_oidc_publisher", "GitHub", ), + ( + "add_pending_gitlab_oidc_publisher", + "GitLab", + ), ( "add_pending_google_oidc_publisher", "Google", @@ -4017,6 +4121,27 @@ def test_add_pending_oidc_publisher_invalid_form( } ), ), + ( + "add_pending_gitlab_oidc_publisher", + "GitLab", + lambda user_id: PendingGitLabPublisher( + project_name="some-project-name", + namespace="some-owner", + project="some-repository", + workflow_filepath="subfolder/some-workflow-filename.yml", + environment="some-environment", + added_by_id=user_id, + ), + MultiDict( + { + "namespace": "some-owner", + "project": "some-repository", + "workflow_filepath": "subfolder/some-workflow-filename.yml", + "environment": "some-environment", + "project_name": "some-project-name", + } + ), + ), ( "add_pending_google_oidc_publisher", "Google", @@ -4153,6 +4278,20 @@ def test_add_pending_oidc_publisher_already_exists( ), PendingGitHubPublisher, ), + ( + "add_pending_gitlab_oidc_publisher", + "GitLab", + MultiDict( + { + "namespace": "some-owner", + "project": "some-repository", + "workflow_filepath": "subfolder/some-workflow-filename.yml", + "environment": "some-environment", + "project_name": "some-project-name", + } + ), + PendingGitLabPublisher, + ), ( "add_pending_google_oidc_publisher", "Google", @@ -4303,6 +4442,13 @@ def test_delete_pending_oidc_publisher_admin_disabled( monkeypatch.setattr( views, "PendingGitHubPublisherForm", pending_github_publisher_form_cls ) + pending_gitlab_publisher_form_obj = pretend.stub() + pending_gitlab_publisher_form_cls = pretend.call_recorder( + lambda *a, **kw: pending_gitlab_publisher_form_obj + ) + monkeypatch.setattr( + views, "PendingGitLabPublisherForm", pending_gitlab_publisher_form_cls + ) pending_google_publisher_form_obj = pretend.stub() pending_google_publisher_form_cls = pretend.call_recorder( lambda *a, **kw: pending_google_publisher_form_obj @@ -4325,10 +4471,12 @@ def test_delete_pending_oidc_publisher_admin_disabled( assert view.delete_pending_oidc_publisher() == { "disabled": { "GitHub": True, + "GitLab": True, "Google": True, "ActiveState": True, }, "pending_github_publisher_form": pending_github_publisher_form_obj, + "pending_gitlab_publisher_form": pending_gitlab_publisher_form_obj, "pending_google_publisher_form": pending_google_publisher_form_obj, "pending_activestate_publisher_form": pending_activestate_publisher_form_obj, # noqa: E501 } @@ -4336,6 +4484,7 @@ def test_delete_pending_oidc_publisher_admin_disabled( assert pyramid_request.flags.disallow_oidc.calls == [ pretend.call(), pretend.call(AdminFlagValue.DISALLOW_GITHUB_OIDC), + pretend.call(AdminFlagValue.DISALLOW_GITLAB_OIDC), pretend.call(AdminFlagValue.DISALLOW_GOOGLE_OIDC), pretend.call(AdminFlagValue.DISALLOW_ACTIVESTATE_OIDC), ] @@ -4355,6 +4504,12 @@ def test_delete_pending_oidc_publisher_admin_disabled( project_factory=project_factory, ) ] + assert pending_gitlab_publisher_form_cls.calls == [ + pretend.call( + pyramid_request.POST, + project_factory=project_factory, + ) + ] def test_delete_pending_oidc_publisher_invalid_form( self, monkeypatch, pyramid_request @@ -4401,6 +4556,17 @@ def test_delete_pending_oidc_publisher_invalid_form( ), PendingGitHubPublisher, ), + ( + lambda user_id: PendingGitLabPublisher( + project_name="some-project-name", + namespace="some-owner", + project="some-repository", + workflow_filepath="subfolder/some-filename", + environment="", + added_by_id=user_id, + ), + PendingGitLabPublisher, + ), ( lambda user_id: PendingGooglePublisher( project_name="some-project-name", @@ -4472,6 +4638,17 @@ def test_delete_pending_oidc_publisher_not_found( ), PendingGitHubPublisher, ), + ( + lambda user_id: PendingGitLabPublisher( + project_name="some-project-name", + namespace="some-owner", + project="some-repository", + workflow_filepath="subfolder/some-filename", + environment="", + added_by_id=user_id, + ), + PendingGitLabPublisher, + ), ( lambda user_id: PendingGooglePublisher( project_name="some-project-name", @@ -4536,6 +4713,18 @@ def test_delete_pending_oidc_publisher_no_access( ), PendingGitHubPublisher, ), + ( + "GitLab", + lambda user_id: PendingGitLabPublisher( + project_name="some-project-name", + namespace="some-owner", + project="some-owner", + workflow_filepath="subfolder/some-filename", + environment="", + added_by_id=user_id, + ), + PendingGitLabPublisher, + ), ( "Google", lambda user_id: PendingGooglePublisher( diff --git a/tests/unit/admin/views/test_macaroons.py b/tests/unit/admin/views/test_macaroons.py index 07879ce3fa22..918c5c9f0638 100644 --- a/tests/unit/admin/views/test_macaroons.py +++ b/tests/unit/admin/views/test_macaroons.py @@ -55,7 +55,10 @@ def test_post_invalid_token(self, db_request): with pytest.raises(views.HTTPBadRequest) as excinfo: views.macaroon_decode_token(db_request) - assert excinfo.value.message == "The token cannot be deserialized" + assert excinfo.value.message == ( + "The token cannot be deserialized: InvalidMacaroonError('malformed " + "or nonexistent macaroon')" + ) def test_post_token_found(self, db_request, macaroon_service): user = UserFactory.create() diff --git a/tests/unit/admin/views/test_organizations.py b/tests/unit/admin/views/test_organizations.py index 76476ef05d60..be67992200d4 100644 --- a/tests/unit/admin/views/test_organizations.py +++ b/tests/unit/admin/views/test_organizations.py @@ -462,9 +462,9 @@ def test_organization_application_query(self, enable_organizations, db_request): OrganizationApplicationFactory.create_batch(5), key=lambda o: o.normalized_name, ) - db_request.GET[ - "q" - ] = f"organization:{organization_applications[0].display_name}" + db_request.GET["q"] = ( + f"organization:{organization_applications[0].display_name}" + ) result = views.organization_applications_list(db_request) assert organization_applications[0] in result["organization_applications"] @@ -493,9 +493,9 @@ def test_description_query(self, enable_organizations, db_request): OrganizationApplicationFactory.create_batch(5), key=lambda o: o.normalized_name, ) - db_request.GET[ - "q" - ] = f"description:'{organization_applications[0].description}'" + db_request.GET["q"] = ( + f"description:'{organization_applications[0].description}'" + ) result = views.organization_applications_list(db_request) assert organization_applications[0] in result["organization_applications"] diff --git a/tests/unit/api/test_simple.py b/tests/unit/api/test_simple.py index 685b3f05ba82..76855a773c3f 100644 --- a/tests/unit/api/test_simple.py +++ b/tests/unit/api/test_simple.py @@ -404,14 +404,16 @@ def test_with_files_with_version_multi_digit( "yanked": False, "size": f.size, "upload-time": f.upload_time.isoformat() + "Z", - "data-dist-info-metadata": { - "sha256": "deadbeefdeadbeefdeadbeefdeadbeef" - } - if f.metadata_file_sha256_digest is not None - else False, - "core-metadata": {"sha256": "deadbeefdeadbeefdeadbeefdeadbeef"} - if f.metadata_file_sha256_digest is not None - else False, + "data-dist-info-metadata": ( + {"sha256": "deadbeefdeadbeefdeadbeefdeadbeef"} + if f.metadata_file_sha256_digest is not None + else False + ), + "core-metadata": ( + {"sha256": "deadbeefdeadbeefdeadbeefdeadbeef"} + if f.metadata_file_sha256_digest is not None + else False + ), } for f in files ], diff --git a/tests/unit/email/test_init.py b/tests/unit/email/test_init.py index 71402a10d227..ee2596a70d9e 100644 --- a/tests/unit/email/test_init.py +++ b/tests/unit/email/test_init.py @@ -897,9 +897,9 @@ def test_send_password_reset_email( "user_id": stub_user.id, "additional": { "from_": "noreply@example.com", - "to": "other@example.com" - if stub_email - else "email@example.com", + "to": ( + "other@example.com" if stub_email else "email@example.com" + ), "subject": "Email Subject", "redact_ip": False, }, diff --git a/tests/unit/forklift/test_legacy.py b/tests/unit/forklift/test_legacy.py index bca3ef7d5158..497ad0fa238e 100644 --- a/tests/unit/forklift/test_legacy.py +++ b/tests/unit/forklift/test_legacy.py @@ -3375,25 +3375,29 @@ def test_upload_succeeds_creates_release( # Ensure that all of our events have been created release_event = { - "submitted_by": identity.username - if test_with_user - else "OpenID created token", + "submitted_by": ( + identity.username if test_with_user else "OpenID created token" + ), "canonical_version": release.canonical_version, - "publisher_url": f"{identity.publisher.publisher_url()}/commit/somesha" - if not test_with_user - else None, + "publisher_url": ( + f"{identity.publisher.publisher_url()}/commit/somesha" + if not test_with_user + else None + ), "uploaded_via_trusted_publisher": not test_with_user, } fileadd_event = { "filename": filename, - "submitted_by": identity.username - if test_with_user - else "OpenID created token", + "submitted_by": ( + identity.username if test_with_user else "OpenID created token" + ), "canonical_version": release.canonical_version, - "publisher_url": f"{identity.publisher.publisher_url()}/commit/somesha" - if not test_with_user - else None, + "publisher_url": ( + f"{identity.publisher.publisher_url()}/commit/somesha" + if not test_with_user + else None + ), "project_id": str(project.id), "uploaded_via_trusted_publisher": not test_with_user, } diff --git a/tests/unit/manage/test_views.py b/tests/unit/manage/test_views.py index 706b193842e3..39c50e033f54 100644 --- a/tests/unit/manage/test_views.py +++ b/tests/unit/manage/test_views.py @@ -50,6 +50,7 @@ from warehouse.oidc.models import ( ActiveStatePublisher, GitHubPublisher, + GitLabPublisher, GooglePublisher, OIDCPublisher, ) @@ -5840,9 +5841,15 @@ def test_manage_project_oidc_publishers(self, monkeypatch): view = views.ManageOIDCPublisherViews(project, request) assert view.manage_project_oidc_publishers() == { - "disabled": {"GitHub": False, "Google": False, "ActiveState": False}, + "disabled": { + "GitHub": False, + "GitLab": False, + "Google": False, + "ActiveState": False, + }, "project": project, "github_publisher_form": view.github_publisher_form, + "gitlab_publisher_form": view.gitlab_publisher_form, "google_publisher_form": view.google_publisher_form, "activestate_publisher_form": view.activestate_publisher_form, } @@ -5850,6 +5857,7 @@ def test_manage_project_oidc_publishers(self, monkeypatch): assert request.flags.disallow_oidc.calls == [ pretend.call(), pretend.call(AdminFlagValue.DISALLOW_GITHUB_OIDC), + pretend.call(AdminFlagValue.DISALLOW_GITLAB_OIDC), pretend.call(AdminFlagValue.DISALLOW_GOOGLE_OIDC), pretend.call(AdminFlagValue.DISALLOW_ACTIVESTATE_OIDC), ] @@ -5876,9 +5884,15 @@ def test_manage_project_oidc_publishers_admin_disabled( view = views.ManageOIDCPublisherViews(project, pyramid_request) assert view.manage_project_oidc_publishers() == { - "disabled": {"GitHub": True, "Google": True, "ActiveState": True}, + "disabled": { + "GitHub": True, + "GitLab": True, + "Google": True, + "ActiveState": True, + }, "project": project, "github_publisher_form": view.github_publisher_form, + "gitlab_publisher_form": view.gitlab_publisher_form, "google_publisher_form": view.google_publisher_form, "activestate_publisher_form": view.activestate_publisher_form, } @@ -5886,6 +5900,7 @@ def test_manage_project_oidc_publishers_admin_disabled( assert pyramid_request.flags.disallow_oidc.calls == [ pretend.call(), pretend.call(AdminFlagValue.DISALLOW_GITHUB_OIDC), + pretend.call(AdminFlagValue.DISALLOW_GITLAB_OIDC), pretend.call(AdminFlagValue.DISALLOW_GOOGLE_OIDC), pretend.call(AdminFlagValue.DISALLOW_ACTIVESTATE_OIDC), ] @@ -5924,6 +5939,27 @@ def test_manage_project_oidc_publishers_admin_disabled( normalized_environment=publisher.environment, ), ), + ( + "add_gitlab_oidc_publisher", + pretend.stub( + id="fakeid", + publisher_name="GitLab", + project="fakerepo", + publisher_url=( + lambda x=None: "https://gitlab.com/fakeowner/fakerepo" + ), + namespace="fakeowner", + workflow_filepath="subfolder/fakeworkflow.yml", + environment="some-environment", + ), + lambda publisher: pretend.stub( + validate=pretend.call_recorder(lambda: True), + project=pretend.stub(data=publisher.project), + namespace=pretend.stub(data=publisher.namespace), + workflow_filepath=pretend.stub(data=publisher.workflow_filepath), + normalized_environment=publisher.environment, + ), + ), ( "add_google_oidc_publisher", pretend.stub( @@ -6002,6 +6038,7 @@ def test_add_oidc_publisher_preexisting( publisher_form_obj = make_form(publisher) publisher_form_cls = pretend.call_recorder(lambda *a, **kw: publisher_form_obj) monkeypatch.setattr(views, "GitHubPublisherForm", publisher_form_cls) + monkeypatch.setattr(views, "GitLabPublisherForm", publisher_form_cls) monkeypatch.setattr(views, "GooglePublisherForm", publisher_form_cls) monkeypatch.setattr(views, "ActiveStatePublisherForm", publisher_form_cls) @@ -6070,6 +6107,17 @@ def test_add_oidc_publisher_preexisting( ), pretend.stub(publisher_name="GitHub"), ), + ( + "add_gitlab_oidc_publisher", + pretend.stub( + validate=pretend.call_recorder(lambda: True), + project=pretend.stub(data="fakerepo"), + namespace=pretend.stub(data="fakeowner"), + workflow_filepath=pretend.stub(data="subfolder/fakeworkflow.yml"), + normalized_environment="some-environment", + ), + pretend.stub(publisher_name="GitLab"), + ), ( "add_google_oidc_publisher", pretend.stub( @@ -6132,6 +6180,7 @@ def test_add_oidc_publisher_created( publisher_form_cls = pretend.call_recorder(lambda *a, **kw: publisher_form_obj) monkeypatch.setattr(views, "GitHubPublisherForm", publisher_form_cls) + monkeypatch.setattr(views, "GitLabPublisherForm", publisher_form_cls) monkeypatch.setattr(views, "GooglePublisherForm", publisher_form_cls) monkeypatch.setattr(views, "ActiveStatePublisherForm", publisher_form_cls) monkeypatch.setattr( @@ -6223,6 +6272,24 @@ def test_add_oidc_publisher_created( } ), ), + ( + "add_gitlab_oidc_publisher", + "GitLab", + GitLabPublisher( + project="some-repository", + namespace="some-owner", + workflow_filepath="subfolder/some-workflow-filename.yml", + environment="some-environment", + ), + MultiDict( + { + "namespace": "some-owner", + "project": "some-repository", + "workflow_filepath": "subfolder/some-workflow-filename.yml", + "environment": "some-environment", + } + ), + ), ( "add_google_oidc_publisher", "Google", @@ -6310,9 +6377,15 @@ def test_add_oidc_publisher_already_registered_with_project( ) assert getattr(view, view_name)() == { - "disabled": {"GitHub": False, "Google": False, "ActiveState": False}, + "disabled": { + "GitHub": False, + "GitLab": False, + "Google": False, + "ActiveState": False, + }, "project": project, "github_publisher_form": view.github_publisher_form, + "gitlab_publisher_form": view.gitlab_publisher_form, "google_publisher_form": view.google_publisher_form, "activestate_publisher_form": view.activestate_publisher_form, } @@ -6334,6 +6407,7 @@ def test_add_oidc_publisher_already_registered_with_project( "view_name, publisher_name", [ ("add_github_oidc_publisher", "GitHub"), + ("add_gitlab_oidc_publisher", "GitLab"), ("add_google_oidc_publisher", "Google"), ("add_activestate_oidc_publisher", "ActiveState"), ], @@ -6383,6 +6457,7 @@ def test_add_oidc_publisher_ratelimited( "view_name, publisher_name", [ ("add_github_oidc_publisher", "GitHub"), + ("add_gitlab_oidc_publisher", "GitLab"), ("add_google_oidc_publisher", "Google"), ("add_activestate_oidc_publisher", "ActiveState"), ], @@ -6425,6 +6500,7 @@ def test_add_oidc_publisher_admin_disabled( "view_name, publisher_name", [ ("add_github_oidc_publisher", "GitHub"), + ("add_gitlab_oidc_publisher", "GitLab"), ("add_google_oidc_publisher", "Google"), ("add_activestate_oidc_publisher", "ActiveState"), ], @@ -6450,12 +6526,14 @@ def test_add_oidc_publisher_invalid_form( ) publisher_form_cls = pretend.call_recorder(lambda *a, **kw: publisher_form_obj) monkeypatch.setattr(views, "GitHubPublisherForm", publisher_form_cls) + monkeypatch.setattr(views, "GitLabPublisherForm", publisher_form_cls) monkeypatch.setattr(views, "GooglePublisherForm", publisher_form_cls) monkeypatch.setattr(views, "ActiveStatePublisherForm", publisher_form_cls) view = views.ManageOIDCPublisherViews(project, request) default_response = { "github_publisher_form": publisher_form_obj, + "gitlab_publisher_form": publisher_form_obj, "google_publisher_form": publisher_form_obj, "activestate_publisher_form": publisher_form_obj, } @@ -6490,6 +6568,12 @@ def test_add_oidc_publisher_invalid_form( workflow_filename="some-workflow-filename.yml", environment="some-environment", ), + GitLabPublisher( + project="some-repository", + namespace="some-owner", + workflow_filepath="subfolder/some-workflow-filename.yml", + environment="some-environment", + ), GooglePublisher( email="some-email@example.com", sub="some-sub", @@ -6598,6 +6682,12 @@ def test_delete_oidc_publisher_registered_to_multiple_projects( workflow_filename="some-workflow-filename.yml", environment="some-environment", ), + GitLabPublisher( + project="some-repository", + namespace="some-owner", + workflow_filepath="subfolder/some-workflow-filename.yml", + environment="some-environment", + ), GooglePublisher( email="some-email@example.com", sub="some-sub", diff --git a/tests/unit/manage/views/test_organizations.py b/tests/unit/manage/views/test_organizations.py index 47c924dbf651..52e51506ea07 100644 --- a/tests/unit/manage/views/test_organizations.py +++ b/tests/unit/manage/views/test_organizations.py @@ -39,6 +39,7 @@ ) from warehouse.accounts import ITokenService, IUserService from warehouse.accounts.interfaces import TokenExpired +from warehouse.authnz import Permissions from warehouse.manage import views from warehouse.manage.views import organizations as org_views from warehouse.organizations import IOrganizationService @@ -2763,7 +2764,9 @@ def test_delete_other_role_as_nonowner(self, db_request, enable_organizations): result = org_views.delete_organization_role(organization, db_request) - assert db_request.has_permission.calls == [pretend.call("manage:organization")] + assert db_request.has_permission.calls == [ + pretend.call(Permissions.OrganizationsManage) + ] assert db_request.session.flash.calls == [ pretend.call( "Cannot remove other people from the organization", queue="error" diff --git a/tests/unit/oidc/forms/test_gitlab.py b/tests/unit/oidc/forms/test_gitlab.py new file mode 100644 index 000000000000..25133694d8c0 --- /dev/null +++ b/tests/unit/oidc/forms/test_gitlab.py @@ -0,0 +1,133 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pretend +import pytest +import wtforms + +from webob.multidict import MultiDict + +from warehouse.oidc.forms import gitlab + + +class TestPendingGitLabPublisherForm: + def test_validate(self, monkeypatch): + project_factory = [] + data = MultiDict( + { + "namespace": "some-owner", + "project": "some-repo", + "workflow_filepath": "subfolder/some-workflow.yml", + "project_name": "some-project", + } + ) + form = gitlab.PendingGitLabPublisherForm( + MultiDict(data), project_factory=project_factory + ) + + assert form._project_factory == project_factory + # We're testing only the basic validation here. + assert form.validate() + + def test_validate_project_name_already_in_use(self): + project_factory = ["some-project"] + form = gitlab.PendingGitLabPublisherForm(project_factory=project_factory) + + field = pretend.stub(data="some-project") + with pytest.raises(wtforms.validators.ValidationError): + form.validate_project_name(field) + + +class TestGitLabPublisherForm: + def test_validate(self): + data = MultiDict( + { + "namespace": "some-owner", + "project": "some-repo", + "workflow_filepath": "subfolder/some-workflow.yml", + } + ) + form = gitlab.GitLabPublisherForm(MultiDict(data)) + + # We're testing only the basic validation here. + assert form.validate(), str(form.errors) + + @pytest.mark.parametrize( + "data", + [ + {"namespace": None, "project": "some", "workflow_filepath": "some"}, + {"namespace": "", "project": "some", "workflow_filepath": "some"}, + { + "namespace": "invalid_characters@", + "project": "some", + "workflow_filepath": "some", + }, + { + "namespace": "invalid_parethen(sis", + "project": "some", + "workflow_filepath": "some", + }, + { + "namespace": "some", + "project": "invalid space", + "workflow_filepath": "some", + }, + { + "namespace": "some", + "project": "invalid+plus", + "workflow_filepath": "some", + }, + {"project": None, "namespace": "some", "workflow_filepath": "some"}, + {"project": "", "namespace": "some", "workflow_filepath": "some"}, + { + "project": "$invalid#characters", + "namespace": "some", + "workflow_filepath": "some", + }, + {"project": "some", "namespace": "some", "workflow_filepath": None}, + {"project": "some", "namespace": "some", "workflow_filepath": ""}, + ], + ) + def test_validate_basic_invalid_fields(self, monkeypatch, data): + form = gitlab.GitLabPublisherForm(MultiDict(data)) + + # We're testing only the basic validation here. + assert not form.validate() + + @pytest.mark.parametrize( + "workflow_filepath", + [ + "missing_suffix", + "/begin_slash.yml", + "end_with_slash.yml/", + "/begin/and/end/slash.yml/", + ], + ) + def test_validate_workflow_filepath(self, workflow_filepath): + form = gitlab.GitLabPublisherForm() + field = pretend.stub(data=workflow_filepath) + + with pytest.raises(wtforms.validators.ValidationError): + form.validate_workflow_filepath(field) + + @pytest.mark.parametrize( + "data, expected", + [ + ("", ""), + (" ", ""), + ("\t\r\n", ""), + (None, ""), + ], + ) + def test_normalized_environment(self, data, expected): + form = gitlab.GitLabPublisherForm(environment=data) + assert form.normalized_environment == expected diff --git a/tests/unit/oidc/models/test_gitlab.py b/tests/unit/oidc/models/test_gitlab.py new file mode 100644 index 000000000000..01c2dadf5538 --- /dev/null +++ b/tests/unit/oidc/models/test_gitlab.py @@ -0,0 +1,520 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pretend +import pytest +import sqlalchemy + +from tests.common.db.oidc import GitLabPublisherFactory, PendingGitLabPublisherFactory +from warehouse.oidc import errors +from warehouse.oidc.models import _core, gitlab + + +@pytest.mark.parametrize("claim", ["", "repo", "repo:"]) +def test_check_sub(claim): + assert gitlab._check_sub(pretend.stub(), claim, pretend.stub()) is False + + +def test_lookup_strategies(): + assert ( + len(gitlab.GitLabPublisher.__lookup_strategies__) + == len(gitlab.PendingGitLabPublisher.__lookup_strategies__) + == 2 + ) + + +class TestGitLabPublisher: + def test_lookup_strategies(self): + assert ( + len(gitlab.GitLabPublisher.__lookup_strategies__) + == len(gitlab.PendingGitLabPublisher.__lookup_strategies__) + == 2 + ) + + def test_gitlab_publisher_all_known_claims(self): + assert gitlab.GitLabPublisher.all_known_claims() == { + # required verifiable claims + "sub", + "project_path", + "ci_config_ref_uri", + # required unverifiable claims + "ref_path", + "sha", + # optional verifiable claims + "environment", + # preverified claims + "iss", + "iat", + "nbf", + "exp", + "aud", + # unchecked claims + "namespace_id", + "namespace_path", + "user_id", + "user_login", + "user_email", + "user_identities", + "pipeline_id", + "pipeline_source", + "job_id", + "ref", + "ref_type", + "ref_protected", + "environment_protected", + "deployment_tier", + "environment_action", + "runner_id", + "runner_environment", + "ci_config_sha", + "project_visibility", + "jti", + } + + def test_gitlab_publisher_computed_properties(self): + publisher = gitlab.GitLabPublisher( + project="fakerepo", + namespace="fakeowner", + workflow_filepath="subfolder/fakeworkflow.yml", + environment="fakeenv", + ) + + for claim_name in publisher.__required_verifiable_claims__.keys(): + assert getattr(publisher, claim_name) is not None + + assert str(publisher) == "subfolder/fakeworkflow.yml" + assert publisher.publisher_url() == "https://gitlab.com/fakeowner/fakerepo" + assert ( + publisher.publisher_url({"sha": "somesha"}) + == "https://gitlab.com/fakeowner/fakerepo/commit/somesha" + ) + assert publisher.stored_claims({"sha": "somesha", "ref_path": "someref"}) == { + "sha": "somesha", + "ref_path": "someref", + } + + def test_gitlab_publisher_unaccounted_claims(self, monkeypatch): + publisher = gitlab.GitLabPublisher( + project="fakerepo", + namespace="fakeowner", + workflow_filepath="subfolder/fakeworkflow.yml", + ) + + scope = pretend.stub() + sentry_sdk = pretend.stub( + capture_message=pretend.call_recorder(lambda s: None), + push_scope=pretend.call_recorder( + lambda: pretend.stub( + __enter__=lambda *a: scope, __exit__=lambda *a: None + ) + ), + ) + monkeypatch.setattr(_core, "sentry_sdk", sentry_sdk) + + # We don't care if these actually verify, only that they're present. + signed_claims = { + claim_name: "fake" + for claim_name in gitlab.GitLabPublisher.all_known_claims() + } + signed_claims["fake-claim"] = "fake" + signed_claims["another-fake-claim"] = "also-fake" + with pytest.raises(errors.InvalidPublisherError) as e: + publisher.verify_claims(signed_claims=signed_claims) + assert str(e.value) == "Check failed for required claim 'sub'" + assert sentry_sdk.capture_message.calls == [ + pretend.call( + "JWT for GitLabPublisher has unaccounted claims: " + "['another-fake-claim', 'fake-claim']" + ) + ] + assert scope.fingerprint == ["another-fake-claim", "fake-claim"] + + @pytest.mark.parametrize("missing", ["sub", "ref_path"]) + def test_gitlab_publisher_missing_claims(self, monkeypatch, missing): + publisher = gitlab.GitLabPublisher( + project="fakerepo", + namespace="fakeowner", + workflow_filepath="subfolder/fakeworkflow.yml", + ) + + scope = pretend.stub() + sentry_sdk = pretend.stub( + capture_message=pretend.call_recorder(lambda s: None), + push_scope=pretend.call_recorder( + lambda: pretend.stub( + __enter__=lambda *a: scope, __exit__=lambda *a: None + ) + ), + ) + monkeypatch.setattr(_core, "sentry_sdk", sentry_sdk) + + signed_claims = { + claim_name: "fake" + for claim_name in gitlab.GitLabPublisher.all_known_claims() + } + # Pop the missing claim, so that it's missing. + signed_claims.pop(missing) + assert missing not in signed_claims + assert publisher.__required_verifiable_claims__ + with pytest.raises(errors.InvalidPublisherError) as e: + publisher.verify_claims(signed_claims=signed_claims) + assert str(e.value) == f"Missing claim {missing!r}" + assert sentry_sdk.capture_message.calls == [ + pretend.call(f"JWT for GitLabPublisher is missing claim: {missing}") + ] + assert scope.fingerprint == [missing] + + def test_gitlab_publisher_missing_optional_claims(self, monkeypatch): + publisher = gitlab.GitLabPublisher( + project="fakerepo", + namespace="fakeowner", + workflow_filepath="subfolder/fakeworkflow.yml", + environment="some-environment", # The optional claim that should be present + ) + + sentry_sdk = pretend.stub(capture_message=pretend.call_recorder(lambda s: None)) + monkeypatch.setattr(_core, "sentry_sdk", sentry_sdk) + + signed_claims = { + claim_name: getattr(publisher, claim_name) + for claim_name in gitlab.GitLabPublisher.__required_verifiable_claims__ + } + signed_claims["ref_path"] = "ref" + signed_claims["sha"] = "sha" + signed_claims["ci_config_ref_uri"] = publisher.ci_config_ref_uri + "@ref" + assert publisher.__required_verifiable_claims__ + with pytest.raises(errors.InvalidPublisherError) as e: + publisher.verify_claims(signed_claims=signed_claims) + assert str(e.value) == "Check failed for optional claim 'environment'" + assert sentry_sdk.capture_message.calls == [] + + @pytest.mark.parametrize("environment", [None, "some-environment"]) + @pytest.mark.parametrize( + "missing_claims", + [set(), gitlab.GitLabPublisher.__optional_verifiable_claims__.keys()], + ) + def test_gitlab_publisher_verifies(self, monkeypatch, environment, missing_claims): + publisher = gitlab.GitLabPublisher( + project="fakerepo", + namespace="fakeowner", + workflow_filepath="subfolder/fakeworkflow.yml", + environment="environment", + ) + + noop_check = pretend.call_recorder(lambda gt, sc, ac: True) + verifiable_claims = { + claim_name: noop_check + for claim_name in publisher.__required_verifiable_claims__ + } + monkeypatch.setattr( + publisher, "__required_verifiable_claims__", verifiable_claims + ) + optional_verifiable_claims = { + claim_name: noop_check + for claim_name in publisher.__optional_verifiable_claims__ + } + monkeypatch.setattr( + publisher, "__optional_verifiable_claims__", optional_verifiable_claims + ) + + signed_claims = { + claim_name: "fake" + for claim_name in gitlab.GitLabPublisher.all_known_claims() + if claim_name not in missing_claims + } + assert publisher.verify_claims(signed_claims=signed_claims) + assert len(noop_check.calls) == len(verifiable_claims) + len( + optional_verifiable_claims + ) + + @pytest.mark.parametrize( + ("claim", "ref_path", "sha", "valid", "expected"), + [ + # okay: workflow name, followed by a nonempty ref_path + ( + "gitlab.com/foo/bar//workflows/baz.yml@refs/tags/v0.0.1", + "refs/tags/v0.0.1", + "somesha", + True, + None, + ), + ( + "gitlab.com/foo/bar//workflows/baz.yml@refs/pulls/6", + "refs/pulls/6", + "somesha", + True, + None, + ), + ( + "gitlab.com/foo/bar//workflows/baz.yml@refs/heads/main", + "refs/heads/main", + "somesha", + True, + None, + ), + ( + "gitlab.com/foo/bar//workflows/baz.yml@notrailingslash", + "notrailingslash", + "somesha", + True, + None, + ), + # okay: workflow name, followed by a nonempty sha + ( + "gitlab.com/foo/bar//workflows/baz.yml@somesha", + "someref", + "somesha", + True, + None, + ), + # bad: either ref_path or sha empty + ( + "gitlab.com/foo/bar//workflows/baz.yml@somesha", + None, + "somesha", + False, + "The ref_path and sha claims are empty", + ), + ( + "gitlab.com/foo/bar//workflows/baz.yml@somesha", + "", + "somesha", + False, + "The ref_path and sha claims are empty", + ), + ( + "gitlab.com/foo/bar//workflows/baz.yml@missing", + "someref", + None, + False, + "The ref_path and sha claims are empty", + ), + ( + "gitlab.com/foo/bar//workflows/baz.yml@missing", + "someref", + "", + False, + "The ref_path and sha claims are empty", + ), + # bad: both ref_path and sha are missing + ( + "gitlab.com/foo/bar//workflows/baz.yml@missing", + None, + None, + False, + "The ref_path and sha claims are empty", + ), + ( + "gitlab.com/foo/bar//workflows/baz.yml@missing", + "", + "", + False, + "The ref_path and sha claims are empty", + ), + # bad: workflow name with various attempted impersonations on the ref_path + ( + "gitlab.com/foo/bar//workflows/baz.yml@fake.yml@notrailingslash", + "somesha", + "notrailingslash", + False, + "The ci_config_ref_uri claim does not match, expecting one of " + "['gitlab.com/foo/bar//workflows/baz.yml@notrailingslash', " + "'gitlab.com/foo/bar//workflows/baz.yml@somesha'], " + "got 'gitlab.com/foo/bar//workflows/baz.yml@fake.yml@notrailingslash'", + ), + ( + "gitlab.com/foo/bar//workflows/baz.yml@fake.yml@refs/pulls/6", + "somesha", + "refs/pulls/6", + False, + "The ci_config_ref_uri claim does not match, expecting one of " + "['gitlab.com/foo/bar//workflows/baz.yml@refs/pulls/6', " + "'gitlab.com/foo/bar//workflows/baz.yml@somesha'], " + "got 'gitlab.com/foo/bar//workflows/baz.yml@fake.yml@refs/pulls/6'", + ), + # bad: missing tail or workflow name or otherwise partial + ( + "gitlab.com/foo/bar//workflows/baz.yml@", + "somesha", + "notrailingslash", + False, + "The ci_config_ref_uri claim does not match, expecting one of " + "['gitlab.com/foo/bar//workflows/baz.yml@notrailingslash', " + "'gitlab.com/foo/bar//workflows/baz.yml@somesha'], " + "got 'gitlab.com/foo/bar//workflows/baz.yml@'", + ), + ( + "gitlab.com/foo/bar//workflows/@", + "somesha", + "notrailingslash", + False, + "The ci_config_ref_uri claim does not match, expecting one of " + "['gitlab.com/foo/bar//workflows/baz.yml@notrailingslash', " + "'gitlab.com/foo/bar//workflows/baz.yml@somesha'], " + "got 'gitlab.com/foo/bar//workflows/@'", + ), + ( + "gitlab.com/foo/bar//workflows/", + "somesha", + "notrailingslash", + False, + "The ci_config_ref_uri claim does not match, expecting one of " + "['gitlab.com/foo/bar//workflows/baz.yml@notrailingslash', " + "'gitlab.com/foo/bar//workflows/baz.yml@somesha'], " + "got 'gitlab.com/foo/bar//workflows/'", + ), + ( + "baz.yml", + "somesha", + "notrailingslash", + False, + "The ci_config_ref_uri claim does not match, expecting one of " + "['gitlab.com/foo/bar//workflows/baz.yml@notrailingslash', " + "'gitlab.com/foo/bar//workflows/baz.yml@somesha'], " + "got 'baz.yml'", + ), + ( + "gitlab.com/foo/bar//workflows/baz.yml@malicious.yml@", + "somesha", + "notrailingslash", + False, + "The ci_config_ref_uri claim does not match, expecting one of " + "['gitlab.com/foo/bar//workflows/baz.yml@notrailingslash', " + "'gitlab.com/foo/bar//workflows/baz.yml@somesha'], " + "got 'gitlab.com/foo/bar//workflows/baz.yml@malicious.yml@'", + ), + ( + "gitlab.com/foo/bar//workflows/baz.yml@@", + "somesha", + "notrailingslash", + False, + "The ci_config_ref_uri claim does not match, expecting one of " + "['gitlab.com/foo/bar//workflows/baz.yml@notrailingslash', " + "'gitlab.com/foo/bar//workflows/baz.yml@somesha'], " + "got 'gitlab.com/foo/bar//workflows/baz.yml@@'", + ), + ("", None, None, False, "The ci_config_ref_uri claim is empty"), + ], + ) + def test_gitlab_publisher_ci_config_ref_uri( + self, claim, ref_path, sha, valid, expected + ): + publisher = gitlab.GitLabPublisher( + project="bar", + namespace="foo", + workflow_filepath="workflows/baz.yml", + ) + + check = gitlab.GitLabPublisher.__required_verifiable_claims__[ + "ci_config_ref_uri" + ] + claims = {"ref_path": ref_path, "sha": sha} + if valid: + assert check(publisher.ci_config_ref_uri, claim, claims) is True + else: + with pytest.raises(errors.InvalidPublisherError) as e: + check(publisher.ci_config_ref_uri, claim, claims) is True + assert str(e.value) == expected + + @pytest.mark.parametrize( + ("truth", "claim", "valid"), + [ + ("repo:foo/bar", "repo:foo/bar:someotherstuff", True), + ("repo:foo/bar", "repo:foo/bar:", True), + ("repo:fOo/BaR", "repo:foo/bar", True), + ("repo:foo/bar", "repo:fOo/BaR:", True), + ("repo:foo/bar:someotherstuff", "repo:foo/bar", False), + ("repo:foo/bar-baz", "repo:foo/bar", False), + ("repo:foo/bar", "repo:foo/bar-baz", False), + ], + ) + def test_gitlab_publisher_sub_claim(self, truth, claim, valid): + check = gitlab.GitLabPublisher.__required_verifiable_claims__["sub"] + assert check(truth, claim, pretend.stub()) is valid + + @pytest.mark.parametrize( + ("truth", "claim", "valid"), + [ + ("", None, True), + ("", "", True), + ("", "some-environment", True), + ("some-environment", "some-environment", True), + ("some-environment", "sOmE-eNvIrOnMeNt", False), + ("some-environment", None, False), + ("some-environment", "some-other-environment", False), + ], + ) + def test_gitlab_publisher_environment_claim(self, truth, claim, valid): + check = gitlab.GitLabPublisher.__optional_verifiable_claims__["environment"] + assert check(truth, claim, pretend.stub()) is valid + + def test_gitlab_publisher_duplicates_cant_be_created(self, db_request): + publisher1 = gitlab.GitLabPublisher( + project="repository_name", + namespace="repository_owner", + workflow_filepath="subfolder/worflow_filename.yml", + environment="", + ) + + db_request.db.add(publisher1) + db_request.db.commit() + + with pytest.raises(sqlalchemy.exc.IntegrityError): + publisher2 = gitlab.GitLabPublisher( + project="repository_name", + namespace="repository_owner", + workflow_filepath="subfolder/worflow_filename.yml", + environment="", + ) + db_request.db.add(publisher2) + db_request.db.commit() + + +class TestPendingGitLabPublisher: + def test_reify_does_not_exist_yet(self, db_request): + pending_publisher = PendingGitLabPublisherFactory.create() + assert ( + db_request.db.query(gitlab.GitLabPublisher) + .filter_by( + project=pending_publisher.project, + namespace=pending_publisher.namespace, + workflow_filepath=pending_publisher.workflow_filepath, + environment=pending_publisher.environment, + ) + .one_or_none() + is None + ) + publisher = pending_publisher.reify(db_request.db) + + # If an OIDC publisher for this pending publisher does not already exist, + # a new one is created and the pending publisher is marked for deletion. + assert isinstance(publisher, gitlab.GitLabPublisher) + assert pending_publisher in db_request.db.deleted + assert publisher.project == pending_publisher.project + assert publisher.namespace == pending_publisher.namespace + assert publisher.workflow_filepath == pending_publisher.workflow_filepath + assert publisher.environment == pending_publisher.environment + + def test_reify_already_exists(self, db_request): + existing_publisher = GitLabPublisherFactory.create() + pending_publisher = PendingGitLabPublisherFactory.create( + project=existing_publisher.project, + namespace=existing_publisher.namespace, + workflow_filepath=existing_publisher.workflow_filepath, + environment=existing_publisher.environment, + ) + publisher = pending_publisher.reify(db_request.db) + + # If an OIDC publisher for this pending publisher already exists, + # it is returned and the pending publisher is marked for deletion. + assert existing_publisher == publisher + assert pending_publisher in db_request.db.deleted diff --git a/tests/unit/oidc/test_utils.py b/tests/unit/oidc/test_utils.py index 7dbc628ffc75..9f6b432d42c8 100644 --- a/tests/unit/oidc/test_utils.py +++ b/tests/unit/oidc/test_utils.py @@ -20,6 +20,7 @@ from tests.common.db.oidc import ( ActiveStatePublisherFactory, GitHubPublisherFactory, + GitLabPublisherFactory, GooglePublisherFactory, ) from warehouse.oidc import errors, utils @@ -78,6 +79,47 @@ def test_find_publisher_by_issuer_github(db_request, environment, expected_id): ) +@pytest.mark.parametrize( + "environment, expected_id", + [ + (None, uuid.UUID("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")), + ("some_other_environment", uuid.UUID("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")), + ("some_environment", uuid.UUID("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb")), + ], +) +def test_find_publisher_by_issuer_gitlab(db_request, environment, expected_id): + GitLabPublisherFactory( + id="aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", + namespace="foo", + project="bar", + workflow_filepath="workflows/ci.yml", + environment="", # No environment + ) + GitLabPublisherFactory( + id="bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb", + namespace="foo", + project="bar", + workflow_filepath="workflows/ci.yml", + environment="some_environment", # Environment set + ) + + signed_claims = { + "project_path": "foo/bar", + "ci_config_ref_uri": "gitlab.com/foo/bar//workflows/ci.yml@refs/heads/main", + } + if environment: + signed_claims["environment"] = environment + + assert ( + utils.find_publisher_by_issuer( + db_request.db, + utils.GITLAB_OIDC_ISSUER_URL, + signed_claims, + ).id + == expected_id + ) + + @pytest.mark.parametrize( "sub, expected_id", [ diff --git a/tests/unit/oidc/test_views.py b/tests/unit/oidc/test_views.py index ac30d2527f15..02bb83341dae 100644 --- a/tests/unit/oidc/test_views.py +++ b/tests/unit/oidc/test_views.py @@ -262,6 +262,14 @@ def body(self): ), "google", ), + ( + ( + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2dpd" + "GxhYi5jb20iLCJpYXQiOjE3MDYwMjYxNjR9.EcmGXp-aFWLrwbNm5QIjDAQ_mR" + "sHtF7obbcnu4w_ZSU" + ), + "gitlab", + ), ], ) def test_mint_token_from_oidc_creates_expected_service( diff --git a/tests/unit/organizations/test_models.py b/tests/unit/organizations/test_models.py index 1c6eadab406f..63dd925d8bf8 100644 --- a/tests/unit/organizations/test_models.py +++ b/tests/unit/organizations/test_models.py @@ -131,26 +131,26 @@ def test_acl(self, db_session): Allow, f"user:{owner1.user.id}", [ - "view:organization", - "view:team", - "manage:organization", - "manage:team", - "manage:billing", - "add:project", - "remove:project", + Permissions.OrganizationsRead, + Permissions.OrganizationTeamsRead, + Permissions.OrganizationsManage, + Permissions.OrganizationTeamsManage, + Permissions.OrganizationsBillingManage, + Permissions.OrganizationProjectsAdd, + Permissions.OrganizationProjectsRemove, ], ), ( Allow, f"user:{owner2.user.id}", [ - "view:organization", - "view:team", - "manage:organization", - "manage:team", - "manage:billing", - "add:project", - "remove:project", + Permissions.OrganizationsRead, + Permissions.OrganizationTeamsRead, + Permissions.OrganizationsManage, + Permissions.OrganizationTeamsManage, + Permissions.OrganizationsBillingManage, + Permissions.OrganizationProjectsAdd, + Permissions.OrganizationProjectsRemove, ], ), ], @@ -160,12 +160,20 @@ def test_acl(self, db_session): ( Allow, f"user:{billing_mgr1.user.id}", - ["view:organization", "view:team", "manage:billing"], + [ + Permissions.OrganizationsRead, + Permissions.OrganizationTeamsRead, + Permissions.OrganizationsBillingManage, + ], ), ( Allow, f"user:{billing_mgr2.user.id}", - ["view:organization", "view:team", "manage:billing"], + [ + Permissions.OrganizationsRead, + Permissions.OrganizationTeamsRead, + Permissions.OrganizationsBillingManage, + ], ), ], key=lambda x: x[1], @@ -174,19 +182,37 @@ def test_acl(self, db_session): ( Allow, f"user:{account_mgr1.user.id}", - ["view:organization", "view:team", "manage:team", "add:project"], + [ + Permissions.OrganizationsRead, + Permissions.OrganizationTeamsRead, + Permissions.OrganizationTeamsManage, + Permissions.OrganizationProjectsAdd, + ], ), ( Allow, f"user:{account_mgr2.user.id}", - ["view:organization", "view:team", "manage:team", "add:project"], + [ + Permissions.OrganizationsRead, + Permissions.OrganizationTeamsRead, + Permissions.OrganizationTeamsManage, + Permissions.OrganizationProjectsAdd, + ], ), ], key=lambda x: x[1], ) + sorted( [ - (Allow, f"user:{member1.user.id}", ["view:organization", "view:team"]), - (Allow, f"user:{member2.user.id}", ["view:organization", "view:team"]), + ( + Allow, + f"user:{member1.user.id}", + [Permissions.OrganizationsRead, Permissions.OrganizationTeamsRead], + ), + ( + Allow, + f"user:{member2.user.id}", + [Permissions.OrganizationsRead, Permissions.OrganizationTeamsRead], + ), ], key=lambda x: x[1], ) @@ -322,26 +348,26 @@ def test_acl(self, db_session): Allow, f"user:{owner1.user.id}", [ - "view:organization", - "view:team", - "manage:organization", - "manage:team", - "manage:billing", - "add:project", - "remove:project", + Permissions.OrganizationsRead, + Permissions.OrganizationTeamsRead, + Permissions.OrganizationsManage, + Permissions.OrganizationTeamsManage, + Permissions.OrganizationsBillingManage, + Permissions.OrganizationProjectsAdd, + Permissions.OrganizationProjectsRemove, ], ), ( Allow, f"user:{owner2.user.id}", [ - "view:organization", - "view:team", - "manage:organization", - "manage:team", - "manage:billing", - "add:project", - "remove:project", + Permissions.OrganizationsRead, + Permissions.OrganizationTeamsRead, + Permissions.OrganizationsManage, + Permissions.OrganizationTeamsManage, + Permissions.OrganizationsBillingManage, + Permissions.OrganizationProjectsAdd, + Permissions.OrganizationProjectsRemove, ], ), ], @@ -351,12 +377,20 @@ def test_acl(self, db_session): ( Allow, f"user:{billing_mgr1.user.id}", - ["view:organization", "view:team", "manage:billing"], + [ + Permissions.OrganizationsRead, + Permissions.OrganizationTeamsRead, + Permissions.OrganizationsBillingManage, + ], ), ( Allow, f"user:{billing_mgr2.user.id}", - ["view:organization", "view:team", "manage:billing"], + [ + Permissions.OrganizationsRead, + Permissions.OrganizationTeamsRead, + Permissions.OrganizationsBillingManage, + ], ), ], key=lambda x: x[1], @@ -365,19 +399,37 @@ def test_acl(self, db_session): ( Allow, f"user:{account_mgr1.user.id}", - ["view:organization", "view:team", "manage:team", "add:project"], + [ + Permissions.OrganizationsRead, + Permissions.OrganizationTeamsRead, + Permissions.OrganizationTeamsManage, + Permissions.OrganizationProjectsAdd, + ], ), ( Allow, f"user:{account_mgr2.user.id}", - ["view:organization", "view:team", "manage:team", "add:project"], + [ + Permissions.OrganizationsRead, + Permissions.OrganizationTeamsRead, + Permissions.OrganizationTeamsManage, + Permissions.OrganizationProjectsAdd, + ], ), ], key=lambda x: x[1], ) + sorted( [ - (Allow, f"user:{member1.user.id}", ["view:organization", "view:team"]), - (Allow, f"user:{member2.user.id}", ["view:organization", "view:team"]), + ( + Allow, + f"user:{member1.user.id}", + [Permissions.OrganizationsRead, Permissions.OrganizationTeamsRead], + ), + ( + Allow, + f"user:{member2.user.id}", + [Permissions.OrganizationsRead, Permissions.OrganizationTeamsRead], + ), ], key=lambda x: x[1], ) diff --git a/tests/unit/packaging/test_tasks.py b/tests/unit/packaging/test_tasks.py index 5c5afe0ff756..4abafe4c0cf7 100644 --- a/tests/unit/packaging/test_tasks.py +++ b/tests/unit/packaging/test_tasks.py @@ -16,12 +16,13 @@ from contextlib import contextmanager from itertools import product from pathlib import Path +from zipfile import BadZipFile import pretend import pytest from google.cloud.bigquery import SchemaField -from pip._internal.exceptions import UnsupportedWheel +from pip._internal.exceptions import InvalidWheel, UnsupportedWheel from wtforms import Field, Form, StringField import warehouse.packaging.tasks @@ -296,9 +297,9 @@ def test_reconcile_file_storages_borked( storage_service = pretend.stub(get_checksum=lambda pth: f"{pth}-deadbeef") bad_storage_service = pretend.stub( - get_checksum=lambda pth: None - if pth == borked.path + borked_ext - else f"{pth}-deadbeef" + get_checksum=lambda pth: ( + None if pth == borked.path + borked_ext else f"{pth}-deadbeef" + ) ) db_request.find_service = pretend.call_recorder( lambda svc, name=None, context=None: { @@ -343,9 +344,9 @@ def test_not_all_files(db_request, monkeypatch, metrics, borked_ext, metrics_tag storage_service = pretend.stub(get_checksum=lambda pth: f"{pth}-deadbeef") bad_storage_service = pretend.stub( - get_checksum=lambda pth: None - if pth == just_dist.path + borked_ext - else f"{pth}-deadbeef" + get_checksum=lambda pth: ( + None if pth == just_dist.path + borked_ext else f"{pth}-deadbeef" + ) ) db_request.find_service = pretend.call_recorder( lambda svc, name=None, context=None: { @@ -986,9 +987,9 @@ def mock_open(filename, perms): monkeypatch.setattr(builtins, "open", mock_open) - db_request.registry.settings[ - "files.url" - ] = "https://files.example.com/packages/{path}" + db_request.registry.settings["files.url"] = ( + "https://files.example.com/packages/{path}" + ) metadata_backfill_individual(db_request, backfillable_file.id) @@ -1028,7 +1029,12 @@ def mock_open(filename, perms): ] -def test_metadata_backfill_file_unbackfillable(db_request, monkeypatch, metrics): +@pytest.mark.parametrize( + "exception", [InvalidWheel("foo", "bar"), UnsupportedWheel, BadZipFile] +) +def test_metadata_backfill_file_invalid_wheel( + db_request, monkeypatch, metrics, exception +): project = ProjectFactory() release = ReleaseFactory(project=project) backfillable_file = FileFactory( @@ -1037,10 +1043,58 @@ def test_metadata_backfill_file_unbackfillable(db_request, monkeypatch, metrics) stub_session = pretend.stub() monkeypatch.setattr(warehouse.packaging.tasks, "PipSession", lambda: stub_session) - dist_from_wheel_url = pretend.raiser(UnsupportedWheel) + dist_from_wheel_url = pretend.raiser(exception) + monkeypatch.setattr( + warehouse.packaging.tasks, "dist_from_wheel_url", dist_from_wheel_url + ) + db_request.find_service = pretend.call_recorder( + lambda iface, name=None, context=None: { + IFileStorage: { + "archive": pretend.stub(), + "cache": pretend.stub(), + }, + IMetricsService: {None: metrics}, + }[iface][name] + ) + db_request.registry.settings["files.url"] = ( + "https://files.example.com/packages/{path}" + ) + + assert backfillable_file.metadata_file_unbackfillable is False + + metadata_backfill_individual(db_request, backfillable_file.id) + + assert backfillable_file.metadata_file_unbackfillable is True + assert metrics.increment.calls == [] + + +def test_metadata_backfill_file_oserror(db_request, monkeypatch, metrics): + project = ProjectFactory() + release = ReleaseFactory(project=project) + backfillable_file = FileFactory( + release=release, packagetype="bdist_wheel", metadata_file_sha256_digest=None + ) + + metadata_contents = b"some\nmetadata\ncontents" + stub_dist = pretend.stub( + _dist=pretend.stub(_files={Path("METADATA"): metadata_contents}) + ) + stub_session = pretend.stub() + dist_from_wheel_url = pretend.call_recorder( + lambda project_name, file_url, session: stub_dist + ) monkeypatch.setattr( warehouse.packaging.tasks, "dist_from_wheel_url", dist_from_wheel_url ) + stub_session = pretend.stub() + monkeypatch.setattr(warehouse.packaging.tasks, "PipSession", lambda: stub_session) + + @contextmanager + def mock_open(filename, perms): + raise OSError + + monkeypatch.setattr(builtins, "open", mock_open) + db_request.find_service = pretend.call_recorder( lambda iface, name=None, context=None: { IFileStorage: { @@ -1050,9 +1104,9 @@ def test_metadata_backfill_file_unbackfillable(db_request, monkeypatch, metrics) IMetricsService: {None: metrics}, }[iface][name] ) - db_request.registry.settings[ - "files.url" - ] = "https://files.example.com/packages/{path}" + db_request.registry.settings["files.url"] = ( + "https://files.example.com/packages/{path}" + ) assert backfillable_file.metadata_file_unbackfillable is False @@ -1078,9 +1132,9 @@ def test_metadata_backfill_file_delorted(db_request, monkeypatch, metrics): IMetricsService: {None: metrics}, }[iface][name] ) - db_request.registry.settings[ - "files.url" - ] = "https://files.example.com/packages/{path}" + db_request.registry.settings["files.url"] = ( + "https://files.example.com/packages/{path}" + ) metadata_backfill_individual(db_request, "66642069-0000-0000-0000-000000000000") diff --git a/tests/unit/packaging/test_utils.py b/tests/unit/packaging/test_utils.py index d95e24772ab7..afa7bd2056fe 100644 --- a/tests/unit/packaging/test_utils.py +++ b/tests/unit/packaging/test_utils.py @@ -21,6 +21,17 @@ from ...common.db.packaging import FileFactory, ProjectFactory, ReleaseFactory +def test_simple_detail_empty_string(db_request): + project = ProjectFactory.create() + release = ReleaseFactory.create(project=project, version="1.0", requires_python="") + FileFactory.create(release=release) + + db_request.route_url = lambda *a, **kw: "the-url" + expected_content = _simple_detail(project, db_request) + + assert expected_content["files"][0]["requires-python"] is None + + def test_render_simple_detail(db_request, monkeypatch, jinja): project = ProjectFactory.create() release1 = ReleaseFactory.create(project=project, version="1.0") diff --git a/tests/unit/test_search.py b/tests/unit/test_search.py index b25b188bc6b4..2aacefbfb982 100644 --- a/tests/unit/test_search.py +++ b/tests/unit/test_search.py @@ -64,9 +64,9 @@ def test_quoted_query(self, terms, expected_prefix, expected_type): { "multi_match": { "fields": EXPECTED_SEARCH_FIELDS, - "query": "foo bar" - if terms != '"a"' - else "a", + "query": ( + "foo bar" if terms != '"a"' else "a" + ), "type": expected_type, } }, diff --git a/warehouse/accounts/forms.py b/warehouse/accounts/forms.py index a9ef8ae9e8ed..fa6ecae466ee 100644 --- a/warehouse/accounts/forms.py +++ b/warehouse/accounts/forms.py @@ -307,7 +307,6 @@ def validate_email(self, field): class HoneypotMixin: - """A mixin to catch spammers. This field should always be blank""" confirm_form = wtforms.StringField() diff --git a/warehouse/accounts/views.py b/warehouse/accounts/views.py index 4c8031c32d3a..8a3ad8086b10 100644 --- a/warehouse/accounts/views.py +++ b/warehouse/accounts/views.py @@ -82,12 +82,14 @@ DeletePublisherForm, PendingActiveStatePublisherForm, PendingGitHubPublisherForm, + PendingGitLabPublisherForm, PendingGooglePublisherForm, ) from warehouse.oidc.interfaces import TooManyOIDCRegistrations from warehouse.oidc.models import ( PendingActiveStatePublisher, PendingGitHubPublisher, + PendingGitLabPublisher, PendingGooglePublisher, PendingOIDCPublisher, ) @@ -1481,6 +1483,10 @@ def __init__(self, request): api_token=self.request.registry.settings.get("github.token"), project_factory=self.project_factory, ) + self.pending_gitlab_publisher_form = PendingGitLabPublisherForm( + self.request.POST, + project_factory=self.project_factory, + ) self.pending_google_publisher_form = PendingGooglePublisherForm( self.request.POST, project_factory=self.project_factory, @@ -1524,12 +1530,16 @@ def _check_ratelimits(self): def default_response(self): return { "pending_github_publisher_form": self.pending_github_publisher_form, + "pending_gitlab_publisher_form": self.pending_gitlab_publisher_form, "pending_google_publisher_form": self.pending_google_publisher_form, "pending_activestate_publisher_form": self.pending_activestate_publisher_form, # noqa: E501 "disabled": { "GitHub": self.request.flags.disallow_oidc( AdminFlagValue.DISALLOW_GITHUB_OIDC ), + "GitLab": self.request.flags.disallow_oidc( + AdminFlagValue.DISALLOW_GITLAB_OIDC + ), "Google": self.request.flags.disallow_oidc( AdminFlagValue.DISALLOW_GOOGLE_OIDC ), @@ -1752,6 +1762,33 @@ def add_pending_activestate_oidc_publisher(self): ), ) + @view_config( + request_method="POST", + request_param=PendingGitLabPublisherForm.__params__, + ) + def add_pending_gitlab_oidc_publisher(self): + form = self.default_response["pending_gitlab_publisher_form"] + return self._add_pending_oidc_publisher( + publisher_name="GitLab", + publisher_class=PendingGitLabPublisher, + admin_flag=AdminFlagValue.DISALLOW_GITLAB_OIDC, + form=form, + make_pending_publisher=lambda request, form: PendingGitLabPublisher( + project_name=form.project_name.data, + added_by=self.request.user, + namespace=form.namespace.data, + project=form.project.data, + workflow_filepath=form.workflow_filepath.data, + environment=form.normalized_environment, + ), + make_existence_filters=lambda form: dict( + namespace=form.namespace.data, + project=form.project.data, + workflow_filepath=form.workflow_filepath.data, + environment=form.normalized_environment, + ), + ) + @view_config( request_method="POST", request_param=DeletePublisherForm.__params__, diff --git a/warehouse/admin/flags.py b/warehouse/admin/flags.py index d2b23bf7a5ef..497a659982d6 100644 --- a/warehouse/admin/flags.py +++ b/warehouse/admin/flags.py @@ -26,6 +26,7 @@ class AdminFlagValue(enum.Enum): DISALLOW_NEW_USER_REGISTRATION = "disallow-new-user-registration" DISALLOW_OIDC = "disallow-oidc" DISALLOW_GITHUB_OIDC = "disallow-github-oidc" + DISALLOW_GITLAB_OIDC = "disallow-gitlab-oidc" DISALLOW_GOOGLE_OIDC = "disallow-google-oidc" DISALLOW_ACTIVESTATE_OIDC = "disallow-activestate-oidc" READ_ONLY = "read-only" diff --git a/warehouse/admin/views/macaroons.py b/warehouse/admin/views/macaroons.py index 751376c16a95..fef5949fc31f 100644 --- a/warehouse/admin/views/macaroons.py +++ b/warehouse/admin/views/macaroons.py @@ -55,7 +55,7 @@ def macaroon_decode_token(request): try: macaroon = deserialize_raw_macaroon(token) except InvalidMacaroonError as e: - raise HTTPBadRequest("The token cannot be deserialized") from e + raise HTTPBadRequest(f"The token cannot be deserialized: {e!r}") from e # Try to find the database record for this macaroon macaroon_service = request.find_service(IMacaroonService, context=None) diff --git a/warehouse/authnz/_permissions.py b/warehouse/authnz/_permissions.py index 8634ef016306..fa861c00f021 100644 --- a/warehouse/authnz/_permissions.py +++ b/warehouse/authnz/_permissions.py @@ -96,3 +96,9 @@ class Permissions(StrEnum): # Organization Permissions OrganizationsManage = "organizations:manage" + OrganizationsBillingManage = "organizations:billing:manage" + OrganizationsRead = "organizations:read" + OrganizationProjectsAdd = "organizations:projects:add" + OrganizationProjectsRemove = "organizations:projects:remove" # TODO: unused? + OrganizationTeamsManage = "organizations:teams:manage" + OrganizationTeamsRead = "organizations:teams:read" diff --git a/warehouse/db.py b/warehouse/db.py index 9438daad72f0..fa1a6ffc9e9e 100644 --- a/warehouse/db.py +++ b/warehouse/db.py @@ -62,8 +62,7 @@ # A generic wrapper exception that we'll raise when the database isn't available, we # use this so we can catch it later and turn it into a generic 5xx error. -class DatabaseNotAvailableError(Exception): - ... +class DatabaseNotAvailableError(Exception): ... # The Global metadata object. diff --git a/warehouse/email/__init__.py b/warehouse/email/__init__.py index e951aa934812..311b9213659b 100644 --- a/warehouse/email/__init__.py +++ b/warehouse/email/__init__.py @@ -197,9 +197,11 @@ def wrapper(request, user_or_users, **kwargs): tags=[ f"template_name:{name}", f"allow_unverified:{allow_unverified}", - f"repeat_window:{repeat_window.total_seconds()}" - if repeat_window - else "repeat_window:none", + ( + f"repeat_window:{repeat_window.total_seconds()}" + if repeat_window + else "repeat_window:none" + ), ], ) diff --git a/warehouse/events/models.py b/warehouse/events/models.py index 4018b7ce861d..10572bcd3a8c 100644 --- a/warehouse/events/models.py +++ b/warehouse/events/models.py @@ -229,15 +229,21 @@ def record_event(self, *, tag, request: Request, additional=None): else: additional = additional or {} additional["user_agent_info"] = { - "installer": parsed_user_agent.installer.name - if parsed_user_agent and parsed_user_agent.installer - else None, - "implementation": parsed_user_agent.implementation.name - if parsed_user_agent and parsed_user_agent.implementation - else None, - "system": parsed_user_agent.system.name - if parsed_user_agent and parsed_user_agent.system - else None, + "installer": ( + parsed_user_agent.installer.name + if parsed_user_agent and parsed_user_agent.installer + else None + ), + "implementation": ( + parsed_user_agent.implementation.name + if parsed_user_agent and parsed_user_agent.implementation + else None + ), + "system": ( + parsed_user_agent.system.name + if parsed_user_agent and parsed_user_agent.system + else None + ), } except linehaul_user_agent_parser.UnknownUserAgentError: pass diff --git a/warehouse/forklift/legacy.py b/warehouse/forklift/legacy.py index e44598f9161c..f9fd6316bf79 100644 --- a/warehouse/forklift/legacy.py +++ b/warehouse/forklift/legacy.py @@ -1140,15 +1140,15 @@ def file_upload(request): tag=EventTag.Project.ReleaseAdd, request=request, additional={ - "submitted_by": request.user.username - if request.user - else "OpenID created token", + "submitted_by": ( + request.user.username if request.user else "OpenID created token" + ), "canonical_version": release.canonical_version, - "publisher_url": request.oidc_publisher.publisher_url( - request.oidc_claims - ) - if request.oidc_publisher - else None, + "publisher_url": ( + request.oidc_publisher.publisher_url(request.oidc_claims) + if request.oidc_publisher + else None + ), "uploaded_via_trusted_publisher": bool(request.oidc_publisher), }, ) @@ -1415,15 +1415,15 @@ def file_upload(request): request=request, additional={ "filename": file_.filename, - "submitted_by": request.user.username - if request.user - else "OpenID created token", + "submitted_by": ( + request.user.username if request.user else "OpenID created token" + ), "canonical_version": release.canonical_version, - "publisher_url": request.oidc_publisher.publisher_url( - request.oidc_claims - ) - if request.oidc_publisher - else None, + "publisher_url": ( + request.oidc_publisher.publisher_url(request.oidc_claims) + if request.oidc_publisher + else None + ), "project_id": str(project.id), "uploaded_via_trusted_publisher": bool(request.oidc_publisher), }, diff --git a/warehouse/locale/messages.pot b/warehouse/locale/messages.pot index dff70fc32548..8ea87b5c30e1 100644 --- a/warehouse/locale/messages.pot +++ b/warehouse/locale/messages.pot @@ -90,247 +90,249 @@ msgid "" "different email." msgstr "" -#: warehouse/accounts/forms.py:336 warehouse/manage/forms.py:139 +#: warehouse/accounts/forms.py:335 warehouse/manage/forms.py:139 msgid "The name is too long. Choose a name with 100 characters or less." msgstr "" -#: warehouse/accounts/forms.py:427 +#: warehouse/accounts/forms.py:426 msgid "Invalid TOTP code." msgstr "" -#: warehouse/accounts/forms.py:444 +#: warehouse/accounts/forms.py:443 msgid "Invalid WebAuthn assertion: Bad payload" msgstr "" -#: warehouse/accounts/forms.py:513 +#: warehouse/accounts/forms.py:512 msgid "Invalid recovery code." msgstr "" -#: warehouse/accounts/forms.py:522 +#: warehouse/accounts/forms.py:521 msgid "Recovery code has been previously used." msgstr "" -#: warehouse/accounts/forms.py:552 +#: warehouse/accounts/forms.py:551 msgid "The username isn't valid. Try again." msgstr "" -#: warehouse/accounts/views.py:118 +#: warehouse/accounts/views.py:120 msgid "" "There have been too many unsuccessful login attempts. You have been " "locked out for {}. Please try again later." msgstr "" -#: warehouse/accounts/views.py:135 +#: warehouse/accounts/views.py:137 msgid "" "Too many emails have been added to this account without verifying them. " "Check your inbox and follow the verification links. (IP: ${ip})" msgstr "" -#: warehouse/accounts/views.py:147 +#: warehouse/accounts/views.py:149 msgid "" "Too many password resets have been requested for this account without " "completing them. Check your inbox and follow the verification links. (IP:" " ${ip})" msgstr "" -#: warehouse/accounts/views.py:329 warehouse/accounts/views.py:398 -#: warehouse/accounts/views.py:400 warehouse/accounts/views.py:429 -#: warehouse/accounts/views.py:431 warehouse/accounts/views.py:537 +#: warehouse/accounts/views.py:331 warehouse/accounts/views.py:400 +#: warehouse/accounts/views.py:402 warehouse/accounts/views.py:431 +#: warehouse/accounts/views.py:433 warehouse/accounts/views.py:539 msgid "Invalid or expired two factor login." msgstr "" -#: warehouse/accounts/views.py:392 +#: warehouse/accounts/views.py:394 msgid "Already authenticated" msgstr "" -#: warehouse/accounts/views.py:472 +#: warehouse/accounts/views.py:474 msgid "Successful WebAuthn assertion" msgstr "" -#: warehouse/accounts/views.py:568 warehouse/manage/views/__init__.py:833 +#: warehouse/accounts/views.py:570 warehouse/manage/views/__init__.py:835 msgid "Recovery code accepted. The supplied code cannot be used again." msgstr "" -#: warehouse/accounts/views.py:660 +#: warehouse/accounts/views.py:662 msgid "" "New user registration temporarily disabled. See https://pypi.org/help" "#admin-intervention for details." msgstr "" -#: warehouse/accounts/views.py:797 +#: warehouse/accounts/views.py:799 msgid "Expired token: request a new password reset link" msgstr "" -#: warehouse/accounts/views.py:799 +#: warehouse/accounts/views.py:801 msgid "Invalid token: request a new password reset link" msgstr "" -#: warehouse/accounts/views.py:801 warehouse/accounts/views.py:914 -#: warehouse/accounts/views.py:1018 warehouse/accounts/views.py:1187 +#: warehouse/accounts/views.py:803 warehouse/accounts/views.py:916 +#: warehouse/accounts/views.py:1020 warehouse/accounts/views.py:1189 msgid "Invalid token: no token supplied" msgstr "" -#: warehouse/accounts/views.py:805 +#: warehouse/accounts/views.py:807 msgid "Invalid token: not a password reset token" msgstr "" -#: warehouse/accounts/views.py:810 +#: warehouse/accounts/views.py:812 msgid "Invalid token: user not found" msgstr "" -#: warehouse/accounts/views.py:832 +#: warehouse/accounts/views.py:834 msgid "Invalid token: user has logged in since this token was requested" msgstr "" -#: warehouse/accounts/views.py:850 +#: warehouse/accounts/views.py:852 msgid "" "Invalid token: password has already been changed since this token was " "requested" msgstr "" -#: warehouse/accounts/views.py:882 +#: warehouse/accounts/views.py:884 msgid "You have reset your password" msgstr "" -#: warehouse/accounts/views.py:910 +#: warehouse/accounts/views.py:912 msgid "Expired token: request a new email verification link" msgstr "" -#: warehouse/accounts/views.py:912 +#: warehouse/accounts/views.py:914 msgid "Invalid token: request a new email verification link" msgstr "" -#: warehouse/accounts/views.py:918 +#: warehouse/accounts/views.py:920 msgid "Invalid token: not an email verification token" msgstr "" -#: warehouse/accounts/views.py:927 +#: warehouse/accounts/views.py:929 msgid "Email not found" msgstr "" -#: warehouse/accounts/views.py:930 +#: warehouse/accounts/views.py:932 msgid "Email already verified" msgstr "" -#: warehouse/accounts/views.py:947 +#: warehouse/accounts/views.py:949 msgid "You can now set this email as your primary address" msgstr "" -#: warehouse/accounts/views.py:951 +#: warehouse/accounts/views.py:953 msgid "This is your primary address" msgstr "" -#: warehouse/accounts/views.py:956 +#: warehouse/accounts/views.py:958 msgid "Email address ${email_address} verified. ${confirm_message}." msgstr "" -#: warehouse/accounts/views.py:1014 +#: warehouse/accounts/views.py:1016 msgid "Expired token: request a new organization invitation" msgstr "" -#: warehouse/accounts/views.py:1016 +#: warehouse/accounts/views.py:1018 msgid "Invalid token: request a new organization invitation" msgstr "" -#: warehouse/accounts/views.py:1022 +#: warehouse/accounts/views.py:1024 msgid "Invalid token: not an organization invitation token" msgstr "" -#: warehouse/accounts/views.py:1026 +#: warehouse/accounts/views.py:1028 msgid "Organization invitation is not valid." msgstr "" -#: warehouse/accounts/views.py:1035 +#: warehouse/accounts/views.py:1037 msgid "Organization invitation no longer exists." msgstr "" -#: warehouse/accounts/views.py:1086 +#: warehouse/accounts/views.py:1088 msgid "Invitation for '${organization_name}' is declined." msgstr "" -#: warehouse/accounts/views.py:1149 +#: warehouse/accounts/views.py:1151 msgid "You are now ${role} of the '${organization_name}' organization." msgstr "" -#: warehouse/accounts/views.py:1183 +#: warehouse/accounts/views.py:1185 msgid "Expired token: request a new project role invitation" msgstr "" -#: warehouse/accounts/views.py:1185 +#: warehouse/accounts/views.py:1187 msgid "Invalid token: request a new project role invitation" msgstr "" -#: warehouse/accounts/views.py:1191 +#: warehouse/accounts/views.py:1193 msgid "Invalid token: not a collaboration invitation token" msgstr "" -#: warehouse/accounts/views.py:1195 +#: warehouse/accounts/views.py:1197 msgid "Role invitation is not valid." msgstr "" -#: warehouse/accounts/views.py:1210 +#: warehouse/accounts/views.py:1212 msgid "Role invitation no longer exists." msgstr "" -#: warehouse/accounts/views.py:1241 +#: warehouse/accounts/views.py:1243 msgid "Invitation for '${project_name}' is declined." msgstr "" -#: warehouse/accounts/views.py:1307 +#: warehouse/accounts/views.py:1309 msgid "You are now ${role} of the '${project_name}' project." msgstr "" -#: warehouse/accounts/views.py:1546 warehouse/accounts/views.py:1762 -#: warehouse/manage/views/__init__.py:1205 +#: warehouse/accounts/views.py:1556 warehouse/accounts/views.py:1799 +#: warehouse/manage/views/__init__.py:1212 msgid "" "Trusted publishing is temporarily disabled. See https://pypi.org/help" "#admin-intervention for details." msgstr "" -#: warehouse/accounts/views.py:1567 +#: warehouse/accounts/views.py:1577 msgid "disabled. See https://pypi.org/help#admin-intervention for details." msgstr "" -#: warehouse/accounts/views.py:1583 +#: warehouse/accounts/views.py:1593 msgid "" "You must have a verified email in order to register a pending trusted " "publisher. See https://pypi.org/help#openid-connect for details." msgstr "" -#: warehouse/accounts/views.py:1596 +#: warehouse/accounts/views.py:1606 msgid "You can't register more than 3 pending trusted publishers at once." msgstr "" -#: warehouse/accounts/views.py:1612 warehouse/manage/views/__init__.py:1240 -#: warehouse/manage/views/__init__.py:1353 -#: warehouse/manage/views/__init__.py:1463 +#: warehouse/accounts/views.py:1622 warehouse/manage/views/__init__.py:1247 +#: warehouse/manage/views/__init__.py:1360 +#: warehouse/manage/views/__init__.py:1472 +#: warehouse/manage/views/__init__.py:1582 msgid "" "There have been too many attempted trusted publisher registrations. Try " "again later." msgstr "" -#: warehouse/accounts/views.py:1623 warehouse/manage/views/__init__.py:1254 -#: warehouse/manage/views/__init__.py:1367 -#: warehouse/manage/views/__init__.py:1477 +#: warehouse/accounts/views.py:1633 warehouse/manage/views/__init__.py:1261 +#: warehouse/manage/views/__init__.py:1374 +#: warehouse/manage/views/__init__.py:1486 +#: warehouse/manage/views/__init__.py:1596 msgid "The trusted publisher could not be registered" msgstr "" -#: warehouse/accounts/views.py:1637 +#: warehouse/accounts/views.py:1647 msgid "" "This trusted publisher has already been registered. Please contact PyPI's" " admins if this wasn't intentional." msgstr "" -#: warehouse/accounts/views.py:1664 +#: warehouse/accounts/views.py:1674 msgid "Registered a new pending publisher to create " msgstr "" -#: warehouse/accounts/views.py:1776 warehouse/accounts/views.py:1789 -#: warehouse/accounts/views.py:1796 +#: warehouse/accounts/views.py:1813 warehouse/accounts/views.py:1826 +#: warehouse/accounts/views.py:1833 msgid "Invalid publisher ID" msgstr "" -#: warehouse/accounts/views.py:1802 +#: warehouse/accounts/views.py:1839 msgid "Removed trusted publisher for project " msgstr "" @@ -367,6 +369,7 @@ msgid "Select project" msgstr "" #: warehouse/manage/forms.py:495 warehouse/oidc/forms/_core.py:23 +#: warehouse/oidc/forms/gitlab.py:44 msgid "Specify project name" msgstr "" @@ -420,152 +423,158 @@ msgstr "" msgid "This team name has already been used. Choose a different team name." msgstr "" -#: warehouse/manage/views/__init__.py:201 +#: warehouse/manage/views/__init__.py:203 msgid "Account details updated" msgstr "" -#: warehouse/manage/views/__init__.py:230 +#: warehouse/manage/views/__init__.py:232 msgid "Email ${email_address} added - check your email for a verification link" msgstr "" -#: warehouse/manage/views/__init__.py:781 +#: warehouse/manage/views/__init__.py:783 msgid "Recovery codes already generated" msgstr "" -#: warehouse/manage/views/__init__.py:782 +#: warehouse/manage/views/__init__.py:784 msgid "Generating new recovery codes will invalidate your existing codes." msgstr "" -#: warehouse/manage/views/__init__.py:891 +#: warehouse/manage/views/__init__.py:893 msgid "Verify your email to create an API token." msgstr "" -#: warehouse/manage/views/__init__.py:991 +#: warehouse/manage/views/__init__.py:993 msgid "API Token does not exist." msgstr "" -#: warehouse/manage/views/__init__.py:1023 +#: warehouse/manage/views/__init__.py:1025 msgid "Invalid credentials. Try again" msgstr "" -#: warehouse/manage/views/__init__.py:1221 +#: warehouse/manage/views/__init__.py:1228 msgid "" "GitHub-based trusted publishing is temporarily disabled. See " "https://pypi.org/help#admin-intervention for details." msgstr "" -#: warehouse/manage/views/__init__.py:1334 +#: warehouse/manage/views/__init__.py:1341 +msgid "" +"GitLab-based trusted publishing is temporarily disabled. See " +"https://pypi.org/help#admin-intervention for details." +msgstr "" + +#: warehouse/manage/views/__init__.py:1453 msgid "" "Google-based trusted publishing is temporarily disabled. See " "https://pypi.org/help#admin-intervention for details." msgstr "" -#: warehouse/manage/views/__init__.py:1443 +#: warehouse/manage/views/__init__.py:1562 msgid "" "ActiveState-based trusted publishing is temporarily disabled. See " "https://pypi.org/help#admin-intervention for details." msgstr "" -#: warehouse/manage/views/__init__.py:1678 -#: warehouse/manage/views/__init__.py:1979 -#: warehouse/manage/views/__init__.py:2087 +#: warehouse/manage/views/__init__.py:1797 +#: warehouse/manage/views/__init__.py:2098 +#: warehouse/manage/views/__init__.py:2206 msgid "" "Project deletion temporarily disabled. See https://pypi.org/help#admin-" "intervention for details." msgstr "" -#: warehouse/manage/views/__init__.py:1810 -#: warehouse/manage/views/__init__.py:1895 -#: warehouse/manage/views/__init__.py:1996 -#: warehouse/manage/views/__init__.py:2096 +#: warehouse/manage/views/__init__.py:1929 +#: warehouse/manage/views/__init__.py:2014 +#: warehouse/manage/views/__init__.py:2115 +#: warehouse/manage/views/__init__.py:2215 msgid "Confirm the request" msgstr "" -#: warehouse/manage/views/__init__.py:1822 +#: warehouse/manage/views/__init__.py:1941 msgid "Could not yank release - " msgstr "" -#: warehouse/manage/views/__init__.py:1907 +#: warehouse/manage/views/__init__.py:2026 msgid "Could not un-yank release - " msgstr "" -#: warehouse/manage/views/__init__.py:2008 +#: warehouse/manage/views/__init__.py:2127 msgid "Could not delete release - " msgstr "" -#: warehouse/manage/views/__init__.py:2108 +#: warehouse/manage/views/__init__.py:2227 msgid "Could not find file" msgstr "" -#: warehouse/manage/views/__init__.py:2112 +#: warehouse/manage/views/__init__.py:2231 msgid "Could not delete file - " msgstr "" -#: warehouse/manage/views/__init__.py:2262 +#: warehouse/manage/views/__init__.py:2381 msgid "Team '${team_name}' already has ${role_name} role for project" msgstr "" -#: warehouse/manage/views/__init__.py:2369 +#: warehouse/manage/views/__init__.py:2488 msgid "User '${username}' already has ${role_name} role for project" msgstr "" -#: warehouse/manage/views/__init__.py:2436 +#: warehouse/manage/views/__init__.py:2555 msgid "${username} is now ${role} of the '${project_name}' project." msgstr "" -#: warehouse/manage/views/__init__.py:2468 +#: warehouse/manage/views/__init__.py:2587 msgid "" "User '${username}' does not have a verified primary email address and " "cannot be added as a ${role_name} for project" msgstr "" -#: warehouse/manage/views/__init__.py:2481 -#: warehouse/manage/views/organizations.py:879 +#: warehouse/manage/views/__init__.py:2600 +#: warehouse/manage/views/organizations.py:881 msgid "User '${username}' already has an active invite. Please try again later." msgstr "" -#: warehouse/manage/views/__init__.py:2546 -#: warehouse/manage/views/organizations.py:944 +#: warehouse/manage/views/__init__.py:2665 +#: warehouse/manage/views/organizations.py:946 msgid "Invitation sent to '${username}'" msgstr "" -#: warehouse/manage/views/__init__.py:2579 +#: warehouse/manage/views/__init__.py:2698 msgid "Could not find role invitation." msgstr "" -#: warehouse/manage/views/__init__.py:2590 +#: warehouse/manage/views/__init__.py:2709 msgid "Invitation already expired." msgstr "" -#: warehouse/manage/views/__init__.py:2622 -#: warehouse/manage/views/organizations.py:1131 +#: warehouse/manage/views/__init__.py:2741 +#: warehouse/manage/views/organizations.py:1133 msgid "Invitation revoked from '${username}'." msgstr "" -#: warehouse/manage/views/organizations.py:855 +#: warehouse/manage/views/organizations.py:857 msgid "User '${username}' already has ${role_name} role for organization" msgstr "" -#: warehouse/manage/views/organizations.py:866 +#: warehouse/manage/views/organizations.py:868 msgid "" "User '${username}' does not have a verified primary email address and " "cannot be added as a ${role_name} for organization" msgstr "" -#: warehouse/manage/views/organizations.py:1027 -#: warehouse/manage/views/organizations.py:1069 +#: warehouse/manage/views/organizations.py:1029 +#: warehouse/manage/views/organizations.py:1071 msgid "Could not find organization invitation." msgstr "" -#: warehouse/manage/views/organizations.py:1037 +#: warehouse/manage/views/organizations.py:1039 msgid "Organization invitation could not be re-sent." msgstr "" -#: warehouse/manage/views/organizations.py:1084 +#: warehouse/manage/views/organizations.py:1086 msgid "Expired invitation for '${username}' deleted." msgstr "" -#: warehouse/oidc/forms/_core.py:25 +#: warehouse/oidc/forms/_core.py:25 warehouse/oidc/forms/gitlab.py:46 msgid "Invalid project name" msgstr "" @@ -668,6 +677,30 @@ msgstr "" msgid "Workflow filename must be a filename only, without directories" msgstr "" +#: warehouse/oidc/forms/gitlab.py:33 +msgid "Specify GitLab namespace (username or group/subgroup)" +msgstr "" + +#: warehouse/oidc/forms/gitlab.py:37 +msgid "Invalid GitLab username or group/subgroup name." +msgstr "" + +#: warehouse/oidc/forms/gitlab.py:53 +msgid "Specify workflow filepath" +msgstr "" + +#: warehouse/oidc/forms/gitlab.py:61 +msgid "Invalid environment name" +msgstr "" + +#: warehouse/oidc/forms/gitlab.py:76 +msgid "Workflow file path must end with .yml or .yaml" +msgstr "" + +#: warehouse/oidc/forms/gitlab.py:80 +msgid "Workflow file path cannot start or end with /" +msgstr "" + #: warehouse/subscriptions/models.py:35 #: warehouse/templates/manage/project/history.html:230 msgid "Active" @@ -1311,10 +1344,15 @@ msgstr "" #: warehouse/templates/manage/account/publishing.html:159 #: warehouse/templates/manage/account/publishing.html:174 #: warehouse/templates/manage/account/publishing.html:189 -#: warehouse/templates/manage/account/publishing.html:224 -#: warehouse/templates/manage/account/publishing.html:246 -#: warehouse/templates/manage/account/publishing.html:268 -#: warehouse/templates/manage/account/publishing.html:290 +#: warehouse/templates/manage/account/publishing.html:204 +#: warehouse/templates/manage/account/publishing.html:219 +#: warehouse/templates/manage/account/publishing.html:261 +#: warehouse/templates/manage/account/publishing.html:276 +#: warehouse/templates/manage/account/publishing.html:291 +#: warehouse/templates/manage/account/publishing.html:326 +#: warehouse/templates/manage/account/publishing.html:348 +#: warehouse/templates/manage/account/publishing.html:370 +#: warehouse/templates/manage/account/publishing.html:392 #: warehouse/templates/manage/account/recovery_codes-burn.html:70 #: warehouse/templates/manage/account/token.html:133 #: warehouse/templates/manage/account/token.html:150 @@ -1340,9 +1378,13 @@ msgstr "" #: warehouse/templates/manage/project/publishing.html:95 #: warehouse/templates/manage/project/publishing.html:142 #: warehouse/templates/manage/project/publishing.html:157 -#: warehouse/templates/manage/project/publishing.html:192 -#: warehouse/templates/manage/project/publishing.html:214 -#: warehouse/templates/manage/project/publishing.html:236 +#: warehouse/templates/manage/project/publishing.html:172 +#: warehouse/templates/manage/project/publishing.html:187 +#: warehouse/templates/manage/project/publishing.html:229 +#: warehouse/templates/manage/project/publishing.html:244 +#: warehouse/templates/manage/project/publishing.html:279 +#: warehouse/templates/manage/project/publishing.html:301 +#: warehouse/templates/manage/project/publishing.html:323 #: warehouse/templates/manage/project/roles.html:273 #: warehouse/templates/manage/project/roles.html:289 #: warehouse/templates/manage/project/roles.html:305 @@ -2506,15 +2548,15 @@ msgstr "" #: warehouse/templates/email/trusted-publisher-added/body.html:39 #: warehouse/templates/email/trusted-publisher-removed/body.html:37 #: warehouse/templates/includes/accounts/profile-public-email.html:17 -#: warehouse/templates/manage/account/publishing.html:172 -#: warehouse/templates/manage/project/publishing.html:140 +#: warehouse/templates/manage/account/publishing.html:274 +#: warehouse/templates/manage/project/publishing.html:227 msgid "Email" msgstr "" #: warehouse/templates/email/trusted-publisher-added/body.html:41 #: warehouse/templates/email/trusted-publisher-removed/body.html:39 -#: warehouse/templates/manage/account/publishing.html:187 -#: warehouse/templates/manage/project/publishing.html:155 +#: warehouse/templates/manage/account/publishing.html:289 +#: warehouse/templates/manage/project/publishing.html:242 msgid "Subject" msgstr "" @@ -2525,16 +2567,16 @@ msgstr "" #: warehouse/templates/email/trusted-publisher-added/body.html:45 #: warehouse/templates/email/trusted-publisher-removed/body.html:43 -#: warehouse/templates/manage/account/publishing.html:244 -#: warehouse/templates/manage/project/publishing.html:190 +#: warehouse/templates/manage/account/publishing.html:346 +#: warehouse/templates/manage/project/publishing.html:277 #: warehouse/templates/organizations/profile.html:30 msgid "Organization" msgstr "" #: warehouse/templates/email/trusted-publisher-added/body.html:46 #: warehouse/templates/email/trusted-publisher-removed/body.html:44 -#: warehouse/templates/manage/account/publishing.html:266 -#: warehouse/templates/manage/project/publishing.html:212 +#: warehouse/templates/manage/account/publishing.html:368 +#: warehouse/templates/manage/project/publishing.html:299 msgid "ActiveState Project name" msgstr "" @@ -3776,7 +3818,7 @@ msgstr "" #: warehouse/templates/manage/manage_base.html:80 #: warehouse/templates/manage/manage_base.html:97 #: warehouse/templates/manage/manage_base.html:100 -#: warehouse/templates/manage/manage_base.html:566 +#: warehouse/templates/manage/manage_base.html:575 #: warehouse/templates/manage/organization/roles.html:202 #: warehouse/templates/manage/organization/roles.html:204 #: warehouse/templates/manage/organization/roles.html:209 @@ -3961,11 +4003,12 @@ msgid "" msgstr "" #: warehouse/templates/manage/manage_base.html:546 -#: warehouse/templates/manage/manage_base.html:554 +#: warehouse/templates/manage/manage_base.html:555 +#: warehouse/templates/manage/manage_base.html:563 msgid "Any" msgstr "" -#: warehouse/templates/manage/manage_base.html:573 +#: warehouse/templates/manage/manage_base.html:582 #: warehouse/templates/manage/organization/history.html:166 #: warehouse/templates/manage/project/history.html:43 #: warehouse/templates/manage/project/history.html:97 @@ -3976,7 +4019,7 @@ msgstr "" msgid "Added by:" msgstr "" -#: warehouse/templates/manage/manage_base.html:575 +#: warehouse/templates/manage/manage_base.html:584 #: warehouse/templates/manage/organization/history.html:171 #: warehouse/templates/manage/project/history.html:62 #: warehouse/templates/manage/project/history.html:128 @@ -3987,24 +4030,24 @@ msgstr "" msgid "Removed by:" msgstr "" -#: warehouse/templates/manage/manage_base.html:577 +#: warehouse/templates/manage/manage_base.html:586 msgid "Submitted by:" msgstr "" -#: warehouse/templates/manage/manage_base.html:580 +#: warehouse/templates/manage/manage_base.html:589 #: warehouse/templates/manage/project/history.html:247 msgid "Workflow:" msgstr "" -#: warehouse/templates/manage/manage_base.html:582 +#: warehouse/templates/manage/manage_base.html:591 msgid "Specifier:" msgstr "" -#: warehouse/templates/manage/manage_base.html:585 +#: warehouse/templates/manage/manage_base.html:594 msgid "Publisher:" msgstr "" -#: warehouse/templates/manage/manage_base.html:587 +#: warehouse/templates/manage/manage_base.html:596 #: warehouse/templates/manage/project/history.html:52 #: warehouse/templates/manage/project/history.html:106 msgid "URL:" @@ -4280,19 +4323,22 @@ msgstr "" #: warehouse/templates/manage/account/publishing.html:38 #: warehouse/templates/manage/account/publishing.html:157 -#: warehouse/templates/manage/account/publishing.html:222 +#: warehouse/templates/manage/account/publishing.html:259 +#: warehouse/templates/manage/account/publishing.html:324 msgid "PyPI Project Name" msgstr "" #: warehouse/templates/manage/account/publishing.html:43 #: warehouse/templates/manage/account/publishing.html:162 -#: warehouse/templates/manage/account/publishing.html:228 +#: warehouse/templates/manage/account/publishing.html:264 +#: warehouse/templates/manage/account/publishing.html:330 msgid "project name" msgstr "" #: warehouse/templates/manage/account/publishing.html:45 #: warehouse/templates/manage/account/publishing.html:164 -#: warehouse/templates/manage/account/publishing.html:236 +#: warehouse/templates/manage/account/publishing.html:266 +#: warehouse/templates/manage/account/publishing.html:338 msgid "The project (on PyPI) that will be created when this publisher is used" msgstr "" @@ -4340,19 +4386,25 @@ msgid "" msgstr "" #: warehouse/templates/manage/account/publishing.html:110 +#: warehouse/templates/manage/account/publishing.html:217 #: warehouse/templates/manage/project/publishing.html:93 +#: warehouse/templates/manage/project/publishing.html:185 msgid "Environment name" msgstr "" #: warehouse/templates/manage/account/publishing.html:114 -#: warehouse/templates/manage/account/publishing.html:191 +#: warehouse/templates/manage/account/publishing.html:221 +#: warehouse/templates/manage/account/publishing.html:293 #: warehouse/templates/manage/project/publishing.html:97 -#: warehouse/templates/manage/project/publishing.html:159 +#: warehouse/templates/manage/project/publishing.html:189 +#: warehouse/templates/manage/project/publishing.html:246 msgid "(optional)" msgstr "" #: warehouse/templates/manage/account/publishing.html:118 +#: warehouse/templates/manage/account/publishing.html:224 #: warehouse/templates/manage/project/publishing.html:101 +#: warehouse/templates/manage/project/publishing.html:192 msgid "release" msgstr "" @@ -4369,11 +4421,13 @@ msgid "" msgstr "" #: warehouse/templates/manage/account/publishing.html:139 -#: warehouse/templates/manage/account/publishing.html:210 -#: warehouse/templates/manage/account/publishing.html:307 +#: warehouse/templates/manage/account/publishing.html:241 +#: warehouse/templates/manage/account/publishing.html:312 +#: warehouse/templates/manage/account/publishing.html:409 #: warehouse/templates/manage/project/publishing.html:122 -#: warehouse/templates/manage/project/publishing.html:178 -#: warehouse/templates/manage/project/publishing.html:253 +#: warehouse/templates/manage/project/publishing.html:209 +#: warehouse/templates/manage/project/publishing.html:265 +#: warehouse/templates/manage/project/publishing.html:340 #: warehouse/templates/manage/project/roles.html:341 #: warehouse/templates/manage/team/roles.html:131 msgid "Add" @@ -4383,26 +4437,99 @@ msgstr "" #: warehouse/templates/manage/project/publishing.html:129 #, python-format msgid "" -"Read more about Google's OpenID Connect support here." msgstr "" +#: warehouse/templates/manage/account/publishing.html:172 +#: warehouse/templates/manage/project/publishing.html:140 +msgid "Namespace" +msgstr "" + #: warehouse/templates/manage/account/publishing.html:177 #: warehouse/templates/manage/project/publishing.html:145 -msgid "email" +msgid "namespace" msgstr "" #: warehouse/templates/manage/account/publishing.html:179 #: warehouse/templates/manage/project/publishing.html:147 +msgid "" +"The GitLab username or GitLab group/subgroup namespace that the project " +"is under" +msgstr "" + +#: warehouse/templates/manage/account/publishing.html:187 +#: warehouse/templates/manage/project/documentation.html:35 +#: warehouse/templates/manage/project/publishing.html:155 +#: warehouse/templates/manage/project/release.html:129 +#: warehouse/templates/pages/stats.html:42 +msgid "Project name" +msgstr "" + +#: warehouse/templates/manage/account/publishing.html:192 +#: warehouse/templates/manage/project/publishing.html:160 +msgid "project" +msgstr "" + +#: warehouse/templates/manage/account/publishing.html:194 +#: warehouse/templates/manage/project/publishing.html:162 +msgid "The name of the GitLab project that contains the publishing workflow" +msgstr "" + +#: warehouse/templates/manage/account/publishing.html:202 +#: warehouse/templates/manage/project/publishing.html:170 +msgid "Workflow file path" +msgstr "" + +#: warehouse/templates/manage/account/publishing.html:207 +#: warehouse/templates/manage/project/publishing.html:175 +msgid ".gitlab-ci.yml" +msgstr "" + +#: warehouse/templates/manage/account/publishing.html:209 +#: warehouse/templates/manage/project/publishing.html:177 +msgid "" +"The file path of the publishing workflow, relative to the project's root." +" This file should exist in the project configured above (external " +"workflows are not supported)." +msgstr "" + +#: warehouse/templates/manage/account/publishing.html:226 +#: warehouse/templates/manage/project/publishing.html:194 +#, python-format +msgid "" +"The name of the GitLab CI/CD environment that " +"the above workflow uses for publishing. This should be configured under " +"the project's settings. While not required, a dedicated publishing " +"environment is strongly encouraged, " +"especially if your project has maintainers with commit " +"access who shouldn't have PyPI publishing access." +msgstr "" + +#: warehouse/templates/manage/account/publishing.html:248 +#: warehouse/templates/manage/project/publishing.html:216 +#, python-format +msgid "" +"Read more about Google's OpenID Connect support here." +msgstr "" + +#: warehouse/templates/manage/account/publishing.html:279 +#: warehouse/templates/manage/project/publishing.html:232 +msgid "email" +msgstr "" + +#: warehouse/templates/manage/account/publishing.html:281 +#: warehouse/templates/manage/project/publishing.html:234 msgid "The email address of the account or service account used to publish." msgstr "" -#: warehouse/templates/manage/account/publishing.html:195 -#: warehouse/templates/manage/project/publishing.html:163 +#: warehouse/templates/manage/account/publishing.html:297 +#: warehouse/templates/manage/project/publishing.html:250 msgid "subject" msgstr "" -#: warehouse/templates/manage/account/publishing.html:203 +#: warehouse/templates/manage/account/publishing.html:305 #, python-format msgid "" "The subject is the numeric ID that represents the principal making the " @@ -4410,86 +4537,86 @@ msgid "" "identity used for publishing. More details here." msgstr "" -#: warehouse/templates/manage/account/publishing.html:250 -#: warehouse/templates/manage/project/publishing.html:196 +#: warehouse/templates/manage/account/publishing.html:352 +#: warehouse/templates/manage/project/publishing.html:283 msgid "my-organization" msgstr "" -#: warehouse/templates/manage/account/publishing.html:258 -#: warehouse/templates/manage/project/publishing.html:204 +#: warehouse/templates/manage/account/publishing.html:360 +#: warehouse/templates/manage/project/publishing.html:291 msgid "The ActiveState organization name that owns the project" msgstr "" -#: warehouse/templates/manage/account/publishing.html:272 -#: warehouse/templates/manage/project/publishing.html:218 +#: warehouse/templates/manage/account/publishing.html:374 +#: warehouse/templates/manage/project/publishing.html:305 msgid "my-project" msgstr "" -#: warehouse/templates/manage/account/publishing.html:280 -#: warehouse/templates/manage/project/publishing.html:226 +#: warehouse/templates/manage/account/publishing.html:382 +#: warehouse/templates/manage/project/publishing.html:313 msgid "The ActiveState project that will build your Python artifact." msgstr "" -#: warehouse/templates/manage/account/publishing.html:288 -#: warehouse/templates/manage/project/publishing.html:234 +#: warehouse/templates/manage/account/publishing.html:390 +#: warehouse/templates/manage/project/publishing.html:321 msgid "Actor Username" msgstr "" -#: warehouse/templates/manage/account/publishing.html:294 -#: warehouse/templates/manage/project/publishing.html:240 +#: warehouse/templates/manage/account/publishing.html:396 +#: warehouse/templates/manage/project/publishing.html:327 msgid "my-username" msgstr "" -#: warehouse/templates/manage/account/publishing.html:300 -#: warehouse/templates/manage/project/publishing.html:246 +#: warehouse/templates/manage/account/publishing.html:402 +#: warehouse/templates/manage/project/publishing.html:333 msgid "" "The username for the ActiveState account that will trigger the build of " "your Python artifact." msgstr "" -#: warehouse/templates/manage/account/publishing.html:318 +#: warehouse/templates/manage/account/publishing.html:420 msgid "Manage publishers" msgstr "" -#: warehouse/templates/manage/account/publishing.html:328 +#: warehouse/templates/manage/account/publishing.html:430 msgid "Project" msgstr "" -#: warehouse/templates/manage/account/publishing.html:350 +#: warehouse/templates/manage/account/publishing.html:452 msgid "" "No publishers are currently configured. Publishers for existing projects " "can be added in the publishing configuration for each individual project." msgstr "" -#: warehouse/templates/manage/account/publishing.html:362 +#: warehouse/templates/manage/account/publishing.html:464 msgid "Pending project name" msgstr "" -#: warehouse/templates/manage/account/publishing.html:363 -#: warehouse/templates/manage/project/publishing.html:280 +#: warehouse/templates/manage/account/publishing.html:465 +#: warehouse/templates/manage/project/publishing.html:367 msgid "Publisher" msgstr "" -#: warehouse/templates/manage/account/publishing.html:364 -#: warehouse/templates/manage/project/publishing.html:281 +#: warehouse/templates/manage/account/publishing.html:466 +#: warehouse/templates/manage/project/publishing.html:368 msgid "Details" msgstr "" -#: warehouse/templates/manage/account/publishing.html:376 +#: warehouse/templates/manage/account/publishing.html:478 msgid "" "No pending publishers are currently configured. Publishers for projects " "that don't exist yet can be added below." msgstr "" -#: warehouse/templates/manage/account/publishing.html:384 +#: warehouse/templates/manage/account/publishing.html:486 msgid "Add a new pending publisher" msgstr "" -#: warehouse/templates/manage/account/publishing.html:387 +#: warehouse/templates/manage/account/publishing.html:489 msgid "You can use this page to register \"pending\" trusted publishers." msgstr "" -#: warehouse/templates/manage/account/publishing.html:393 +#: warehouse/templates/manage/account/publishing.html:495 #, python-format msgid "" "These publishers behave similarly to trusted publishers registered " @@ -4500,8 +4627,8 @@ msgid "" "trusted publishers here." msgstr "" -#: warehouse/templates/manage/account/publishing.html:432 -#: warehouse/templates/manage/project/publishing.html:328 +#: warehouse/templates/manage/account/publishing.html:535 +#: warehouse/templates/manage/project/publishing.html:416 #, python-format msgid "" "You must first enable two-factor authentication " @@ -5517,12 +5644,6 @@ msgstr "" msgid "Destroy Documentation for project" msgstr "" -#: warehouse/templates/manage/project/documentation.html:35 -#: warehouse/templates/manage/project/release.html:129 -#: warehouse/templates/pages/stats.html:42 -msgid "Project name" -msgstr "" - #: warehouse/templates/manage/project/documentation.html:39 msgid "Project documentation" msgstr "" @@ -5721,7 +5842,7 @@ msgstr "" msgid "Back to projects" msgstr "" -#: warehouse/templates/manage/project/publishing.html:171 +#: warehouse/templates/manage/project/publishing.html:258 #, python-format msgid "" "The subject is the numeric ID that represents the principal making the " @@ -5730,20 +5851,20 @@ msgid "" "here." msgstr "" -#: warehouse/templates/manage/project/publishing.html:272 +#: warehouse/templates/manage/project/publishing.html:359 msgid "Manage current publishers" msgstr "" -#: warehouse/templates/manage/project/publishing.html:276 +#: warehouse/templates/manage/project/publishing.html:363 #, python-format msgid "OpenID Connect publishers associated with %(project_name)s" msgstr "" -#: warehouse/templates/manage/project/publishing.html:292 +#: warehouse/templates/manage/project/publishing.html:379 msgid "No publishers are currently configured." msgstr "" -#: warehouse/templates/manage/project/publishing.html:297 +#: warehouse/templates/manage/project/publishing.html:384 msgid "Add a new publisher" msgstr "" diff --git a/warehouse/macaroons/errors.py b/warehouse/macaroons/errors.py index 52a5e94e6f62..26ee1c9f7416 100644 --- a/warehouse/macaroons/errors.py +++ b/warehouse/macaroons/errors.py @@ -11,5 +11,4 @@ # limitations under the License. -class InvalidMacaroonError(Exception): - ... +class InvalidMacaroonError(Exception): ... diff --git a/warehouse/manage/views/__init__.py b/warehouse/manage/views/__init__.py index 0f7b981ca12c..c6155f39dd6f 100644 --- a/warehouse/manage/views/__init__.py +++ b/warehouse/manage/views/__init__.py @@ -104,12 +104,14 @@ ActiveStatePublisherForm, DeletePublisherForm, GitHubPublisherForm, + GitLabPublisherForm, GooglePublisherForm, ) from warehouse.oidc.interfaces import TooManyOIDCRegistrations from warehouse.oidc.models import ( ActiveStatePublisher, GitHubPublisher, + GitLabPublisher, GooglePublisher, OIDCPublisher, ) @@ -310,9 +312,9 @@ def change_primary_email(self): tag=EventTag.Account.EmailPrimaryChange, request=self.request, additional={ - "old_primary": previous_primary_email.email - if previous_primary_email - else None, + "old_primary": ( + previous_primary_email.email if previous_primary_email else None + ), "new_primary": new_primary_email.email, }, ) @@ -1145,6 +1147,7 @@ def __init__(self, project, request): self.request.POST, api_token=self.request.registry.settings.get("github.token"), ) + self.gitlab_publisher_form = GitLabPublisherForm(self.request.POST) self.google_publisher_form = GooglePublisherForm(self.request.POST) self.activestate_publisher_form = ActiveStatePublisherForm(self.request.POST) @@ -1183,12 +1186,16 @@ def default_response(self): return { "project": self.project, "github_publisher_form": self.github_publisher_form, + "gitlab_publisher_form": self.gitlab_publisher_form, "google_publisher_form": self.google_publisher_form, "activestate_publisher_form": self.activestate_publisher_form, "disabled": { "GitHub": self.request.flags.disallow_oidc( AdminFlagValue.DISALLOW_GITHUB_OIDC ), + "GitLab": self.request.flags.disallow_oidc( + AdminFlagValue.DISALLOW_GITLAB_OIDC + ), "Google": self.request.flags.disallow_oidc( AdminFlagValue.DISALLOW_GOOGLE_OIDC ), @@ -1324,6 +1331,118 @@ def add_github_oidc_publisher(self): return HTTPSeeOther(self.request.path) + @view_config( + request_method="POST", + request_param=GitLabPublisherForm.__params__, + ) + def add_gitlab_oidc_publisher(self): + if self.request.flags.disallow_oidc(AdminFlagValue.DISALLOW_GITLAB_OIDC): + self.request.session.flash( + self.request._( + "GitLab-based trusted publishing is temporarily disabled. " + "See https://pypi.org/help#admin-intervention for details." + ), + queue="error", + ) + return self.default_response + + self.metrics.increment( + "warehouse.oidc.add_publisher.attempt", tags=["publisher:GitLab"] + ) + + try: + self._check_ratelimits() + except TooManyOIDCRegistrations as exc: + self.metrics.increment( + "warehouse.oidc.add_publisher.ratelimited", tags=["publisher:GitLab"] + ) + return HTTPTooManyRequests( + self.request._( + "There have been too many attempted trusted publisher " + "registrations. Try again later." + ), + retry_after=exc.resets_in.total_seconds(), + ) + + self._hit_ratelimits() + + response = self.default_response + form = response["gitlab_publisher_form"] + + if not form.validate(): + self.request.session.flash( + self.request._("The trusted publisher could not be registered"), + queue="error", + ) + return response + + # GitLab OIDC publishers are unique on the tuple of + # (namespace, project, workflow_filepath, environment), + # so we check for an already registered one before creating. + publisher = ( + self.request.db.query(GitLabPublisher) + .filter( + GitLabPublisher.namespace == form.namespace.data, + GitLabPublisher.project == form.project.data, + GitLabPublisher.workflow_filepath == form.workflow_filepath.data, + GitLabPublisher.environment == form.normalized_environment, + ) + .one_or_none() + ) + if publisher is None: + publisher = GitLabPublisher( + namespace=form.namespace.data, + project=form.project.data, + workflow_filepath=form.workflow_filepath.data, + environment=form.normalized_environment, + ) + + self.request.db.add(publisher) + + # Each project has a unique set of OIDC publishers; the same + # publisher can't be registered to the project more than once. + if publisher in self.project.oidc_publishers: + self.request.session.flash( + self.request._( + f"{publisher} is already registered with {self.project.name}" + ), + queue="error", + ) + return response + + for user in self.project.users: + send_trusted_publisher_added_email( + self.request, + user, + project_name=self.project.name, + publisher=publisher, + ) + + self.project.oidc_publishers.append(publisher) + + self.project.record_event( + tag=EventTag.Project.OIDCPublisherAdded, + request=self.request, + additional={ + "publisher": publisher.publisher_name, + "id": str(publisher.id), + "specifier": str(publisher), + "url": publisher.publisher_url(), + "submitted_by": self.request.user.username, + }, + ) + + self.request.session.flash( + f"Added {publisher} in {publisher.publisher_url()} to {self.project.name}", + queue="success", + ) + + self.metrics.increment( + "warehouse.oidc.add_publisher.ok", tags=["publisher:GitLab"] + ) + + return HTTPSeeOther(self.request.path) + @view_config( request_method="POST", request_param=GooglePublisherForm.__params__, diff --git a/warehouse/manage/views/organizations.py b/warehouse/manage/views/organizations.py index 63505f331a2a..1936b26399e2 100644 --- a/warehouse/manage/views/organizations.py +++ b/warehouse/manage/views/organizations.py @@ -183,21 +183,23 @@ def default_response(self): organization.name for organization in all_user_organizations["organizations_billing"] ), - "create_organization_application_form": CreateOrganizationApplicationForm( - organization_service=self.organization_service, - user=self.request.user, - ) - if len( - [ - app - for app in self.request.user.organization_applications - if app.is_approved is None + "create_organization_application_form": ( + CreateOrganizationApplicationForm( + organization_service=self.organization_service, + user=self.request.user, + ) + if len( + [ + app + for app in self.request.user.organization_applications + if app.is_approved is None + ] + ) + < self.request.registry.settings[ + "warehouse.organizations.max_undecided_organization_applications" ] - ) - < self.request.registry.settings[ - "warehouse.organizations.max_undecided_organization_applications" - ] - else None, + else None + ), } @view_config(request_method="GET") @@ -252,7 +254,7 @@ def create_organization_application(self): require_active_organization=True, require_csrf=True, require_methods=False, - permission="manage:organization", + permission=Permissions.OrganizationsManage, has_translations=True, require_reauth=True, ) @@ -292,7 +294,7 @@ def default_response(self): "active_projects": self.active_projects, } - @view_config(request_method="GET", permission="view:organization") + @view_config(request_method="GET", permission=Permissions.OrganizationsRead) def manage_organization(self): return self.default_response @@ -459,7 +461,7 @@ def delete_organization(self): require_active_organization=False, # Allow reactivate billing for inactive org. require_csrf=True, require_methods=False, - permission="manage:billing", + permission=Permissions.OrganizationsBillingManage, has_translations=True, require_reauth=True, ) @@ -578,7 +580,7 @@ def create_or_manage_subscription(self): require_active_organization=True, require_csrf=True, require_methods=False, - permission="manage:team", + permission=Permissions.OrganizationTeamsManage, has_translations=True, require_reauth=True, ) @@ -601,7 +603,7 @@ def default_response(self): ), } - @view_config(request_method="GET", permission="view:organization") + @view_config(request_method="GET", permission=Permissions.OrganizationsRead) def manage_teams(self): return self.default_response @@ -666,7 +668,7 @@ def create_team(self): require_active_organization=True, require_csrf=True, require_methods=False, - permission="manage:organization", + permission=Permissions.OrganizationsManage, has_translations=True, require_reauth=True, ) @@ -713,11 +715,11 @@ def default_response(self): ), } - @view_config(request_method="GET", permission="view:organization") + @view_config(request_method="GET", permission=Permissions.OrganizationsRead) def manage_organization_projects(self): return self.default_response - @view_config(request_method="POST", permission="add:project") + @view_config(request_method="POST", permission=Permissions.OrganizationProjectsAdd) def add_organization_project(self): # Get and validate form from default response. default_response = self.default_response @@ -956,7 +958,7 @@ def _send_organization_invitation(request, organization, role_name, user): uses_session=True, require_active_organization=True, require_methods=False, - permission="view:organization", + permission=Permissions.OrganizationsRead, has_translations=True, require_reauth=True, ) @@ -1005,7 +1007,7 @@ def manage_organization_roles( uses_session=True, require_active_organization=True, require_methods=["POST"], - permission="manage:organization", + permission=Permissions.OrganizationsManage, has_translations=True, ) def resend_organization_invitation(organization, request): @@ -1052,7 +1054,7 @@ def resend_organization_invitation(organization, request): uses_session=True, require_active_organization=True, require_methods=["POST"], - permission="manage:organization", + permission=Permissions.OrganizationsManage, has_translations=True, ) def revoke_organization_invitation(organization, request): @@ -1148,7 +1150,7 @@ def revoke_organization_invitation(organization, request): uses_session=True, require_active_organization=True, require_methods=["POST"], - permission="manage:organization", + permission=Permissions.OrganizationsManage, has_translations=True, require_reauth=True, ) @@ -1224,7 +1226,7 @@ def change_organization_role( uses_session=True, require_active_organization=True, require_methods=["POST"], - permission="view:organization", + permission=Permissions.OrganizationsRead, has_translations=True, require_reauth=True, ) @@ -1241,7 +1243,8 @@ def delete_organization_role(organization, request): if not role or role.organization_id != organization.id: request.session.flash("Could not find member", queue="error") elif ( - not request.has_permission("manage:organization") and role.user != request.user + not request.has_permission(Permissions.OrganizationsManage) + and role.user != request.user ): request.session.flash( "Cannot remove other people from the organization", queue="error" @@ -1312,7 +1315,7 @@ def delete_organization_role(organization, request): context=Organization, renderer="manage/organization/history.html", uses_session=True, - permission="manage:organization", + permission=Permissions.OrganizationsManage, has_translations=True, ) def manage_organization_history(organization, request): diff --git a/warehouse/manage/views/teams.py b/warehouse/manage/views/teams.py index f9b80e3cd227..3f429fc1acf0 100644 --- a/warehouse/manage/views/teams.py +++ b/warehouse/manage/views/teams.py @@ -61,7 +61,7 @@ require_active_organization=True, require_csrf=True, require_methods=False, - permission="manage:team", + permission=Permissions.OrganizationTeamsManage, has_translations=True, require_reauth=True, ) @@ -90,7 +90,7 @@ def default_response(self): ), } - @view_config(request_method="GET", permission="view:team") + @view_config(request_method="GET", permission=Permissions.OrganizationTeamsRead) def manage_team(self): return self.default_response @@ -195,7 +195,7 @@ def delete_team(self): require_active_organization=True, require_csrf=True, require_methods=False, - permission="manage:team", + permission=Permissions.OrganizationTeamsManage, has_translations=True, require_reauth=True, ) @@ -226,7 +226,7 @@ def default_response(self): "projects_sole_owned": projects_sole_owned, } - @view_config(request_method="GET", permission="view:team") + @view_config(request_method="GET", permission=Permissions.OrganizationTeamsRead) def manage_team_projects(self): return self.default_response @@ -239,7 +239,7 @@ def manage_team_projects(self): require_active_organization=True, require_csrf=True, require_methods=False, - permission="manage:team", + permission=Permissions.OrganizationTeamsManage, has_translations=True, require_reauth=True, ) @@ -272,7 +272,7 @@ def default_response(self): ), } - @view_config(request_method="GET", permission="view:team") + @view_config(request_method="GET", permission=Permissions.OrganizationTeamsRead) def manage_team_roles(self): return self.default_response @@ -359,7 +359,7 @@ def create_team_role(self): @view_config( request_method="POST", route_name="manage.team.delete_role", - permission="view:team", + permission=Permissions.OrganizationTeamsRead, ) def delete_team_role(self): # Get team role. @@ -369,7 +369,7 @@ def delete_team_role(self): if not role or role.team_id != self.team.id: self.request.session.flash("Could not find member", queue="error") elif ( - not self.request.has_permission("manage:team") + not self.request.has_permission(Permissions.OrganizationTeamsManage) and role.user != self.request.user ): self.request.session.flash( @@ -450,7 +450,7 @@ def delete_team_role(self): context=Team, renderer="manage/team/history.html", uses_session=True, - permission="manage:team", + permission=Permissions.OrganizationTeamsManage, has_translations=True, ) def manage_team_history(team, request): diff --git a/warehouse/migrations/versions/81f9f9a60270_add_gitlab_oidc_models.py b/warehouse/migrations/versions/81f9f9a60270_add_gitlab_oidc_models.py new file mode 100644 index 000000000000..0561fd0868a4 --- /dev/null +++ b/warehouse/migrations/versions/81f9f9a60270_add_gitlab_oidc_models.py @@ -0,0 +1,85 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Add GitLab OIDC models + +Revision ID: 81f9f9a60270 +Revises: 4d1b4fcc4076 +Create Date: 2024-01-16 17:46:16.443395 +""" + +import sqlalchemy as sa + +from alembic import op + +revision = "81f9f9a60270" +down_revision = "4d1b4fcc4076" + + +def upgrade(): + op.execute( + """ + INSERT INTO admin_flags(id, description, enabled, notify) + VALUES ( + 'disallow-gitlab-oidc', + 'Disallow the GitLab OIDC provider', + TRUE, + FALSE + ) + """ + ) + op.create_table( + "gitlab_oidc_publishers", + sa.Column("id", sa.UUID(), nullable=False), + sa.Column("namespace", sa.String(), nullable=False), + sa.Column("project", sa.String(), nullable=False), + sa.Column("workflow_filepath", sa.String(), nullable=False), + sa.Column("environment", sa.String(), nullable=False), + sa.ForeignKeyConstraint( + ["id"], + ["oidc_publishers.id"], + ), + sa.PrimaryKeyConstraint("id"), + sa.UniqueConstraint( + "namespace", + "project", + "workflow_filepath", + "environment", + name="_gitlab_oidc_publisher_uc", + ), + ) + op.create_table( + "pending_gitlab_oidc_publishers", + sa.Column("id", sa.UUID(), nullable=False), + sa.Column("namespace", sa.String(), nullable=False), + sa.Column("project", sa.String(), nullable=False), + sa.Column("workflow_filepath", sa.String(), nullable=False), + sa.Column("environment", sa.String(), nullable=False), + sa.ForeignKeyConstraint( + ["id"], + ["pending_oidc_publishers.id"], + ), + sa.PrimaryKeyConstraint("id"), + sa.UniqueConstraint( + "namespace", + "project", + "workflow_filepath", + "environment", + name="_pending_gitlab_oidc_publisher_uc", + ), + ) + + +def downgrade(): + op.execute("DELETE FROM admin_flags WHERE id = 'disallow-gitlab-oidc'") + op.drop_table("pending_gitlab_oidc_publishers") + op.drop_table("gitlab_oidc_publishers") diff --git a/warehouse/mock/billing.py b/warehouse/mock/billing.py index 6108847a0477..720a1c00aa68 100644 --- a/warehouse/mock/billing.py +++ b/warehouse/mock/billing.py @@ -19,6 +19,7 @@ from pyramid.view import view_config, view_defaults from warehouse.api.billing import handle_billing_webhook_event +from warehouse.authnz import Permissions from warehouse.organizations.models import Organization from warehouse.subscriptions.interfaces import IBillingService from warehouse.subscriptions.services import MockStripeBillingService @@ -28,7 +29,7 @@ context=Organization, uses_session=True, require_methods=False, - permission="manage:billing", + permission=Permissions.OrganizationsBillingManage, has_translations=True, require_reauth=True, ) diff --git a/warehouse/oidc/__init__.py b/warehouse/oidc/__init__.py index 91f15025e497..976a25f0e48d 100644 --- a/warehouse/oidc/__init__.py +++ b/warehouse/oidc/__init__.py @@ -18,6 +18,7 @@ from warehouse.oidc.utils import ( ACTIVESTATE_OIDC_ISSUER_URL, GITHUB_OIDC_ISSUER_URL, + GITLAB_OIDC_ISSUER_URL, GOOGLE_OIDC_ISSUER_URL, ) @@ -36,6 +37,15 @@ def includeme(config): IOIDCPublisherService, name="github", ) + config.register_service_factory( + OIDCPublisherServiceFactory( + publisher="gitlab", + issuer_url=GITLAB_OIDC_ISSUER_URL, + service_class=oidc_publisher_service_class, + ), + IOIDCPublisherService, + name="gitlab", + ) config.register_service_factory( OIDCPublisherServiceFactory( publisher="google", diff --git a/warehouse/oidc/forms/__init__.py b/warehouse/oidc/forms/__init__.py index c37d8e851b1d..e41f6f6e557e 100644 --- a/warehouse/oidc/forms/__init__.py +++ b/warehouse/oidc/forms/__init__.py @@ -16,12 +16,15 @@ PendingActiveStatePublisherForm, ) from warehouse.oidc.forms.github import GitHubPublisherForm, PendingGitHubPublisherForm +from warehouse.oidc.forms.gitlab import GitLabPublisherForm, PendingGitLabPublisherForm from warehouse.oidc.forms.google import GooglePublisherForm, PendingGooglePublisherForm __all__ = [ "DeletePublisherForm", "GitHubPublisherForm", "PendingGitHubPublisherForm", + "GitLabPublisherForm", + "PendingGitLabPublisherForm", "GooglePublisherForm", "PendingGooglePublisherForm", "ActiveStatePublisherForm", diff --git a/warehouse/oidc/forms/gitlab.py b/warehouse/oidc/forms/gitlab.py new file mode 100644 index 000000000000..1b2eb07bcf4f --- /dev/null +++ b/warehouse/oidc/forms/gitlab.py @@ -0,0 +1,104 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import re + +import wtforms + +from warehouse import forms +from warehouse.i18n import localize as _ +from warehouse.oidc.forms._core import PendingPublisherMixin + +# https://docs.gitlab.com/ee/user/reserved_names.html#limitations-on-project-and-group-names +_VALID_GITLAB_PROJECT = re.compile(r"^[a-zA-Z0-9][a-zA-Z0-9-_.]*$") +_VALID_GITLAB_NAMESPACE = re.compile(r"^[a-zA-Z0-9][a-zA-Z0-9-_.]*$") +_VALID_GITLAB_ENVIRONMENT = re.compile(r"^[a-zA-Z0-9\-_/${} ]+$") + + +class GitLabPublisherBase(forms.Form): + __params__ = ["namespace", "project", "workflow_filepath", "environment"] + + namespace = wtforms.StringField( + validators=[ + wtforms.validators.InputRequired( + message=_("Specify GitLab namespace (username or group/subgroup)"), + ), + wtforms.validators.Regexp( + _VALID_GITLAB_NAMESPACE, + message=_("Invalid GitLab username or group/subgroup name."), + ), + ] + ) + + project = wtforms.StringField( + validators=[ + wtforms.validators.InputRequired(message=_("Specify project name")), + wtforms.validators.Regexp( + _VALID_GITLAB_PROJECT, message=_("Invalid project name") + ), + ] + ) + + workflow_filepath = wtforms.StringField( + validators=[ + wtforms.validators.InputRequired(message=_("Specify workflow filepath")) + ] + ) + + environment = wtforms.StringField( + validators=[ + wtforms.validators.Optional(), + wtforms.validators.Regexp( + _VALID_GITLAB_ENVIRONMENT, message=_("Invalid environment name") + ), + ] + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def validate_workflow_filepath(self, field): + workflow_filepath = field.data + + if not ( + workflow_filepath.endswith(".yml") or workflow_filepath.endswith(".yaml") + ): + raise wtforms.validators.ValidationError( + _("Workflow file path must end with .yml or .yaml") + ) + if workflow_filepath.startswith("/") or workflow_filepath.endswith("/"): + raise wtforms.validators.ValidationError( + _("Workflow file path cannot start or end with /") + ) + + @property + def normalized_environment(self): + # NOTE: We explicitly do not compare `self.environment.data` to None, + # since it might also be falsey via an empty string (or might be + # only whitespace, which we also treat as a None case). + return ( + self.environment.data + if self.environment.data and not self.environment.data.isspace() + else "" + ) + + +class PendingGitLabPublisherForm(GitLabPublisherBase, PendingPublisherMixin): + __params__ = GitLabPublisherBase.__params__ + ["project_name"] + + def __init__(self, *args, project_factory, **kwargs): + super().__init__(*args, **kwargs) + self._project_factory = project_factory + + +class GitLabPublisherForm(GitLabPublisherBase): + pass diff --git a/warehouse/oidc/models/__init__.py b/warehouse/oidc/models/__init__.py index 8417b6fd8ea8..a125c451a58e 100644 --- a/warehouse/oidc/models/__init__.py +++ b/warehouse/oidc/models/__init__.py @@ -16,15 +16,18 @@ PendingActiveStatePublisher, ) from warehouse.oidc.models.github import GitHubPublisher, PendingGitHubPublisher +from warehouse.oidc.models.gitlab import GitLabPublisher, PendingGitLabPublisher from warehouse.oidc.models.google import GooglePublisher, PendingGooglePublisher __all__ = [ "OIDCPublisher", "PendingOIDCPublisher", "PendingGitHubPublisher", + "PendingGitLabPublisher", "PendingGooglePublisher", "PendingActiveStatePublisher", "GitHubPublisher", + "GitLabPublisher", "GooglePublisher", "ActiveStatePublisher", ] diff --git a/warehouse/oidc/models/gitlab.py b/warehouse/oidc/models/gitlab.py new file mode 100644 index 000000000000..f119b2c3ba2b --- /dev/null +++ b/warehouse/oidc/models/gitlab.py @@ -0,0 +1,284 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import Any + +from sqlalchemy import ForeignKey, String, UniqueConstraint +from sqlalchemy.dialects.postgresql import UUID +from sqlalchemy.orm import Query, mapped_column +from sqlalchemy.sql.expression import func, literal + +from warehouse.oidc.errors import InvalidPublisherError +from warehouse.oidc.interfaces import SignedClaims +from warehouse.oidc.models._core import ( + CheckClaimCallable, + OIDCPublisher, + PendingOIDCPublisher, + check_claim_binary, +) + + +def _check_ci_config_ref_uri(ground_truth, signed_claim, all_signed_claims): + # We expect a string formatted as follows: + # gitlab.com/OWNER/REPO//WORKFLOW_PATH/WORKFLOW_FILE.yml@REF + # where REF is the value of the `ref_path` claim. + + # Defensive: GitLab should never give us an empty ci_config_ref_uri, + # but we check for one anyway just in case. + if not signed_claim: + raise InvalidPublisherError("The ci_config_ref_uri claim is empty") + + # Same defensive check as above but for ref_path and sha. + ref_path = all_signed_claims.get("ref_path") + sha = all_signed_claims.get("sha") + if not (ref_path and sha): + raise InvalidPublisherError("The ref_path and sha claims are empty") + + expected = {f"{ground_truth}@{_ref}" for _ref in [ref_path, sha] if _ref} + if signed_claim not in expected: + raise InvalidPublisherError( + "The ci_config_ref_uri claim does not match, expecting one of " + f"{sorted(expected)!r}, got {signed_claim!r}" + ) + + return True + + +def _check_environment(ground_truth, signed_claim, all_signed_claims): + # When there is an environment, we expect a string. + # For tokens that are generated outside of an environment, the claim will + # be missing. + + # If we haven't set an environment name for the publisher, we don't need to + # check this claim + if ground_truth == "": + return True + + # Defensive: GitLab might give us an empty environment if this token wasn't + # generated from within an environment, in which case the check should + # fail. + if not signed_claim: + return False + + return ground_truth == signed_claim + + +def _check_sub(ground_truth, signed_claim, _all_signed_claims): + # We expect a string formatted as follows: + # project_path:NAMESPACE/PROJECT[:OPTIONAL-STUFF] + # where :OPTIONAL-STUFF is a concatenation of other job context + # metadata. We currently lack the ground context to verify that + # additional metadata, so we limit our verification to just the + # NAMESPACE/PROJECT component. + + # Defensive: GitLab should never give us an empty subject. + if not signed_claim: + return False + + components = signed_claim.split(":") + if len(components) < 2: + return False + + namespace, project, *_ = components + if not namespace or not project: + return False + + # The sub claim is case-insensitive + return f"{namespace}:{project}".lower() == ground_truth.lower() + + +class GitLabPublisherMixin: + """ + Common functionality for both pending and concrete GitLab OIDC publishers. + """ + + namespace = mapped_column(String, nullable=False) + project = mapped_column(String, nullable=False) + workflow_filepath = mapped_column(String, nullable=False) + environment = mapped_column(String, nullable=False) + + __required_verifiable_claims__: dict[str, CheckClaimCallable[Any]] = { + "sub": _check_sub, + "project_path": check_claim_binary(str.__eq__), + "ci_config_ref_uri": _check_ci_config_ref_uri, + } + + __required_unverifiable_claims__: set[str] = {"ref_path", "sha"} + + __optional_verifiable_claims__: dict[str, CheckClaimCallable[Any]] = { + "environment": _check_environment, + } + + __unchecked_claims__ = { + "namespace_id", + "namespace_path", + "user_id", + "user_login", + "user_email", + "user_identities", + "pipeline_id", + "pipeline_source", + "job_id", + "ref", + "ref_type", + "ref_protected", + "environment_protected", + "deployment_tier", + "environment_action", + "runner_id", + "runner_environment", + "ci_config_sha", + "project_visibility", + "jti", + } + + @staticmethod + def __lookup_all__(klass, signed_claims: SignedClaims) -> Query | None: + # This lookup requires the environment claim to be present; + # if it isn't, bail out early. + if not (environment := signed_claims.get("environment")): + return None + + project_path = signed_claims["project_path"] + ci_config_ref_prefix = f"gitlab.com/{project_path}//" + ci_config_ref = signed_claims["ci_config_ref_uri"].removeprefix( + ci_config_ref_prefix + ) + namespace, project = project_path.rsplit("/", 1) + + return ( + Query(klass) + .filter_by( + namespace=namespace, + project=project, + environment=environment, + ) + .filter( + literal(ci_config_ref).like(func.concat(klass.workflow_filepath, "%")) + ) + ) + + @staticmethod + def __lookup_no_environment__(klass, signed_claims: SignedClaims) -> Query | None: + project_path = signed_claims["project_path"] + ci_config_ref_prefix = f"gitlab.com/{project_path}//" + ci_config_ref = signed_claims["ci_config_ref_uri"].removeprefix( + ci_config_ref_prefix + ) + namespace, project = project_path.rsplit("/", 1) + + return ( + Query(klass) + .filter_by( + namespace=namespace, + project=project, + environment="", + ) + .filter( + literal(ci_config_ref).like(func.concat(klass.workflow_filepath, "%")) + ) + ) + + __lookup_strategies__ = [ + __lookup_all__, + __lookup_no_environment__, + ] + + @property + def project_path(self): + return f"{self.namespace}/{self.project}" + + @property + def sub(self): + return f"project_path:{self.project_path}" + + @property + def ci_config_ref_uri(self): + return f"gitlab.com/{self.project_path}//{self.workflow_filepath}" + + @property + def publisher_name(self): + return "GitLab" + + def publisher_url(self, claims=None): + base = f"https://gitlab.com/{self.project_path}" + return f"{base}/commit/{claims['sha']}" if claims else base + + def stored_claims(self, claims=None): + claims = claims if claims else {} + return {"ref_path": claims.get("ref_path"), "sha": claims.get("sha")} + + def __str__(self): + return self.workflow_filepath + + +class GitLabPublisher(GitLabPublisherMixin, OIDCPublisher): + __tablename__ = "gitlab_oidc_publishers" + __mapper_args__ = {"polymorphic_identity": "gitlab_oidc_publishers"} + __table_args__ = ( + UniqueConstraint( + "namespace", + "project", + "workflow_filepath", + "environment", + name="_gitlab_oidc_publisher_uc", + ), + ) + + id = mapped_column( + UUID(as_uuid=True), ForeignKey(OIDCPublisher.id), primary_key=True + ) + + +class PendingGitLabPublisher(GitLabPublisherMixin, PendingOIDCPublisher): + __tablename__ = "pending_gitlab_oidc_publishers" + __mapper_args__ = {"polymorphic_identity": "pending_gitlab_oidc_publishers"} + __table_args__ = ( + UniqueConstraint( + "namespace", + "project", + "workflow_filepath", + "environment", + name="_pending_gitlab_oidc_publisher_uc", + ), + ) + + id = mapped_column( + UUID(as_uuid=True), ForeignKey(PendingOIDCPublisher.id), primary_key=True + ) + + def reify(self, session): + """ + Returns a `GitLabPublisher` for this `PendingGitLabPublisher`, + deleting the `PendingGitLabPublisher` in the process. + """ + + maybe_publisher = ( + session.query(GitLabPublisher) + .filter( + GitLabPublisher.namespace == self.namespace, + GitLabPublisher.project == self.project, + GitLabPublisher.workflow_filepath == self.workflow_filepath, + GitLabPublisher.environment == self.environment, + ) + .one_or_none() + ) + + publisher = maybe_publisher or GitLabPublisher( + namespace=self.namespace, + project=self.project, + workflow_filepath=self.workflow_filepath, + environment=self.environment, + ) + + session.delete(self) + return publisher diff --git a/warehouse/oidc/utils.py b/warehouse/oidc/utils.py index 690203faabea..155275748077 100644 --- a/warehouse/oidc/utils.py +++ b/warehouse/oidc/utils.py @@ -22,32 +22,38 @@ from warehouse.oidc.models import ( ActiveStatePublisher, GitHubPublisher, + GitLabPublisher, GooglePublisher, OIDCPublisher, PendingActiveStatePublisher, PendingGitHubPublisher, + PendingGitLabPublisher, PendingGooglePublisher, PendingOIDCPublisher, ) GITHUB_OIDC_ISSUER_URL = "https://token.actions.githubusercontent.com" +GITLAB_OIDC_ISSUER_URL = "https://gitlab.com" GOOGLE_OIDC_ISSUER_URL = "https://accounts.google.com" ACTIVESTATE_OIDC_ISSUER_URL = "https://platform.activestate.com/api/v1/oauth/oidc" OIDC_ISSUER_SERVICE_NAMES = { GITHUB_OIDC_ISSUER_URL: "github", + GITLAB_OIDC_ISSUER_URL: "gitlab", GOOGLE_OIDC_ISSUER_URL: "google", ACTIVESTATE_OIDC_ISSUER_URL: "activestate", } OIDC_ISSUER_ADMIN_FLAGS = { GITHUB_OIDC_ISSUER_URL: AdminFlagValue.DISALLOW_GITHUB_OIDC, + GITLAB_OIDC_ISSUER_URL: AdminFlagValue.DISALLOW_GITLAB_OIDC, GOOGLE_OIDC_ISSUER_URL: AdminFlagValue.DISALLOW_GOOGLE_OIDC, ACTIVESTATE_OIDC_ISSUER_URL: AdminFlagValue.DISALLOW_ACTIVESTATE_OIDC, } OIDC_ISSUER_URLS = { GITHUB_OIDC_ISSUER_URL, + GITLAB_OIDC_ISSUER_URL, GOOGLE_OIDC_ISSUER_URL, ACTIVESTATE_OIDC_ISSUER_URL, } @@ -56,6 +62,7 @@ str, dict[bool, type[OIDCPublisher | PendingOIDCPublisher]] ] = { GITHUB_OIDC_ISSUER_URL: {False: GitHubPublisher, True: PendingGitHubPublisher}, + GITLAB_OIDC_ISSUER_URL: {False: GitLabPublisher, True: PendingGitLabPublisher}, GOOGLE_OIDC_ISSUER_URL: {False: GooglePublisher, True: PendingGooglePublisher}, ACTIVESTATE_OIDC_ISSUER_URL: { False: ActiveStatePublisher, diff --git a/warehouse/organizations/models.py b/warehouse/organizations/models.py index 5d7e351e48d8..d45eb6a5d83f 100644 --- a/warehouse/organizations/models.py +++ b/warehouse/organizations/models.py @@ -365,13 +365,13 @@ def __acl__(self): # Allow write access depending on role. if role.role_name == OrganizationRoleType.Owner: # Allowed: - # - View organization ("view:organization") - # - View team ("view:team") - # - Invite/remove organization member ("manage:organization") - # - Create/delete team and add/remove team member ("manage:team") - # - Manage billing ("manage:billing") - # - Add project ("add:project") - # - Remove project ("remove:project") + # - View organization (Permissions.OrganizationsRead) + # - View team (Permissions.OrganizationTeamsRead) + # - Invite/remove organization member (Permissions.OrganizationsManage) + # - Create/delete team and add/remove members (OrganizationTeamsManage) + # - Manage billing (Permissions.OrganizationsBillingManage) + # - Add project (Permissions.OrganizationProjectsAdd) + # - Remove project (Permissions.OrganizationProjectsRemove) # Disallowed: # - (none) acls.append( @@ -379,52 +379,56 @@ def __acl__(self): Allow, f"user:{role.user.id}", [ - "view:organization", - "view:team", - "manage:organization", - "manage:team", - "manage:billing", - "add:project", - "remove:project", + Permissions.OrganizationsRead, + Permissions.OrganizationTeamsRead, + Permissions.OrganizationsManage, + Permissions.OrganizationTeamsManage, + Permissions.OrganizationsBillingManage, + Permissions.OrganizationProjectsAdd, + Permissions.OrganizationProjectsRemove, ], ) ) elif role.role_name == OrganizationRoleType.BillingManager: # Allowed: - # - View organization ("view:organization") - # - View team ("view:team") - # - Manage billing ("manage:billing") + # - View organization (Permissions.OrganizationsRead) + # - View team (Permissions.OrganizationTeamsRead) + # - Manage billing (Permissions.OrganizationsBillingManage) # Disallowed: - # - Invite/remove organization member ("manage:organization") - # - Create/delete team and add/remove team member ("manage:team") - # - Add project ("add:project") - # - Remove project ("remove:project") + # - Invite/remove organization member (Permissions.OrganizationsManage) + # - Create/delete team and add/remove members (OrganizationTeamsManage) + # - Add project (Permissions.OrganizationProjectsAdd) + # - Remove project (Permissions.OrganizationProjectsRemove) acls.append( ( Allow, f"user:{role.user.id}", - ["view:organization", "view:team", "manage:billing"], + [ + Permissions.OrganizationsRead, + Permissions.OrganizationTeamsRead, + Permissions.OrganizationsBillingManage, + ], ) ) elif role.role_name == OrganizationRoleType.Manager: # Allowed: - # - View organization ("view:organization") - # - View team ("view:team") - # - Create/delete team and add/remove team member ("manage:team") - # - Add project ("add:project") + # - View organization (Permissions.OrganizationsRead) + # - View team (Permissions.OrganizationTeamsRead) + # - Create/delete team and add/remove members (OrganizationTeamsManage) + # - Add project (Permissions.OrganizationProjectsAdd) # Disallowed: - # - Invite/remove organization member ("manage:organization") - # - Manage billing ("manage:billing") - # - Remove project ("remove:project") + # - Invite/remove organization member (Permissions.OrganizationsManage) + # - Manage billing (Permissions.OrganizationsBillingManage) + # - Remove project (Permissions.OrganizationProjectsRemove) acls.append( ( Allow, f"user:{role.user.id}", [ - "view:organization", - "view:team", - "manage:team", - "add:project", + Permissions.OrganizationsRead, + Permissions.OrganizationTeamsRead, + Permissions.OrganizationTeamsManage, + Permissions.OrganizationProjectsAdd, ], ) ) @@ -432,16 +436,23 @@ def __acl__(self): # No member-specific write access needed for now. # Allowed: - # - View organization ("view:organization") - # - View team ("view:team") + # - View organization (Permissions.OrganizationsRead) + # - View team (Permissions.OrganizationTeamsRead) # Disallowed: - # - Invite/remove organization member ("manage:organization") - # - Create/delete team and add/remove team member ("manage:team") - # - Manage billing ("manage:billing") - # - Add project ("add:project") - # - Remove project ("remove:project") + # - Invite/remove organization member (Permissions.OrganizationsManage) + # - Create/delete team and add/remove members (OrganizationTeamsManage) + # - Manage billing (Permissions.OrganizationsBillingManage) + # - Add project (Permissions.OrganizationProjectsAdd) + # - Remove project (Permissions.OrganizationProjectsRemove) acls.append( - (Allow, f"user:{role.user.id}", ["view:organization", "view:team"]) + ( + Allow, + f"user:{role.user.id}", + [ + Permissions.OrganizationsRead, + Permissions.OrganizationTeamsRead, + ], + ) ) return acls diff --git a/warehouse/packaging/tasks.py b/warehouse/packaging/tasks.py index 8b69c5a7d458..6c45b31f0dbc 100644 --- a/warehouse/packaging/tasks.py +++ b/warehouse/packaging/tasks.py @@ -19,9 +19,10 @@ from collections import namedtuple from itertools import product from pathlib import Path +from zipfile import BadZipFile from google.cloud.bigquery import LoadJobConfig -from pip._internal.exceptions import UnsupportedWheel +from pip._internal.exceptions import InvalidWheel, UnsupportedWheel from pip._internal.network.lazy_wheel import dist_from_wheel_url from pip._internal.network.session import PipSession from sqlalchemy import desc @@ -106,7 +107,7 @@ def metadata_backfill_individual(request, file_id): file_.release.project.normalized_name, file_url, session ) wheel_metadata_contents = lazy_dist._dist._files[Path("METADATA")] - except UnsupportedWheel: + except (InvalidWheel, UnsupportedWheel, BadZipFile): file_.metadata_file_unbackfillable = True return @@ -123,8 +124,12 @@ def metadata_backfill_individual(request, file_id): with tempfile.TemporaryDirectory() as tmpdir: # Write the metadata to a temporary file temporary_filename = os.path.join(tmpdir, file_.filename) + ".metadata" - with open(temporary_filename, "wb") as fp: - fp.write(wheel_metadata_contents) + try: + with open(temporary_filename, "wb") as fp: + fp.write(wheel_metadata_contents) + except OSError: + file_.metadata_file_unbackfillable = True + return # Store the metadata file via our object storage backend cache_storage.store( diff --git a/warehouse/packaging/utils.py b/warehouse/packaging/utils.py index 496f6704419d..a730a9cb4c6c 100644 --- a/warehouse/packaging/utils.py +++ b/warehouse/packaging/utils.py @@ -64,18 +64,28 @@ def _simple_detail(project, request): "hashes": { "sha256": file.sha256_digest, }, - "requires-python": file.release.requires_python, + "requires-python": ( + file.release.requires_python + if file.release.requires_python + else None + ), "size": file.size, "upload-time": file.upload_time.isoformat() + "Z", - "yanked": file.release.yanked_reason - if file.release.yanked and file.release.yanked_reason - else file.release.yanked, - "data-dist-info-metadata": {"sha256": file.metadata_file_sha256_digest} - if file.metadata_file_sha256_digest - else False, - "core-metadata": {"sha256": file.metadata_file_sha256_digest} - if file.metadata_file_sha256_digest - else False, + "yanked": ( + file.release.yanked_reason + if file.release.yanked and file.release.yanked_reason + else file.release.yanked + ), + "data-dist-info-metadata": ( + {"sha256": file.metadata_file_sha256_digest} + if file.metadata_file_sha256_digest + else False + ), + "core-metadata": ( + {"sha256": file.metadata_file_sha256_digest} + if file.metadata_file_sha256_digest + else False + ), } for file in files ], diff --git a/warehouse/tasks.py b/warehouse/tasks.py index 6265310c0bb5..c04f01e58ac3 100644 --- a/warehouse/tasks.py +++ b/warehouse/tasks.py @@ -34,9 +34,9 @@ # We need to trick Celery into supporting rediss:// URLs which is how redis-py # signals that you should use Redis with TLS. -celery.app.backends.BACKEND_ALIASES[ - "rediss" -] = "warehouse.tasks:TLSRedisBackend" # noqa +celery.app.backends.BACKEND_ALIASES["rediss"] = ( + "warehouse.tasks:TLSRedisBackend" # noqa +) # We need to register that the sqs:// url scheme uses a netloc diff --git a/warehouse/templates/includes/manage/manage-organization-menu.html b/warehouse/templates/includes/manage/manage-organization-menu.html index 1e9472c3e3d9..c3087ce8773e 100644 --- a/warehouse/templates/includes/manage/manage-organization-menu.html +++ b/warehouse/templates/includes/manage/manage-organization-menu.html @@ -34,7 +34,7 @@ {{ organization.teams|length }} - {% if request.has_permission("manage:organization") %} + {% if request.has_permission(Permissions.OrganizationsManage) %}
+ {% trans href="https://docs.gitlab.com/ee/ci/secrets/id_token_authentication.html" %} + Read more about GitLab CI/CD OpenID Connect support here. + {% endtrans %} +
+ + {{ form_error_anchor(pending_gitlab_publisher_form) }} + +{% endmacro %} + {% macro google_form(request, pending_google_publisher_form) %}{% trans href="https://cloud.google.com/iam/docs/service-account-creds" %} @@ -403,6 +505,7 @@
{% trans %}There are no projects in your organization, yet.{% endtrans %} - {% if request.has_permission("add:project") %} + {% if request.has_permission(Permissions.OrganizationProjectsAdd) %} {% trans href='https://packaging.python.org/' %}Get started by adding a project that you own using the form below. To learn how to create a new project, visit the Python Packaging User Guide{% endtrans %} {% endif %}
@@ -97,7 +97,7 @@{% trans %}There are no members in this team, yet.{% endtrans %} - {% if request.has_permission("manage:team") %} + {% if request.has_permission(Permissions.OrganizationTeamsManage) %} {% trans %}Get started by adding a team member below.{% endtrans %} {% endif %}