diff --git a/microservices/gatewayApi/Dockerfile b/microservices/gatewayApi/Dockerfile index 70a3df6a..eb49378b 100644 --- a/microservices/gatewayApi/Dockerfile +++ b/microservices/gatewayApi/Dockerfile @@ -5,7 +5,7 @@ # && go mod download \ # && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o deck \ # -ldflags "-s -w -X github.com/kong/deck/cmd.VERSION=$TAG -X github.com/kong/deck/cmd.COMMIT=$COMMIT" -FROM python:3.11-alpine3.20 +FROM python:3.13-alpine3.22 RUN mkdir /.kube @@ -26,17 +26,17 @@ RUN curl -sL https://github.com/kong/deck/releases/download/v1.5.0/deck_1.5.0_li cp /tmp/deck /usr/local/bin/deck_kong2_150 # gwa api (kong 3) -RUN curl -sL https://github.com/Kong/deck/releases/download/v1.45.0/deck_1.45.0_linux_amd64.tar.gz -o deck.tar.gz && \ +RUN curl -sL https://github.com/Kong/deck/releases/download/v1.53.1/deck_1.53.1_linux_amd64.tar.gz -o deck.tar.gz && \ tar -xf deck.tar.gz -C /tmp && \ cp /tmp/deck /usr/local/bin/deck && \ - cp /tmp/deck /usr/local/bin/deck_kong3_1450 + cp /tmp/deck /usr/local/bin/deck_kong3_1531 RUN python -m pip install --upgrade pip # FIX: No module named 'urllib3.packages.six' RUN pip uninstall urllib3 && pip install urllib3 RUN cd /tmp && \ curl -sSL https://install.python-poetry.org > get-poetry.py && \ - POETRY_HOME=/opt/poetry python get-poetry.py --version 1.8.2 && \ + POETRY_HOME=/opt/poetry python get-poetry.py --version 1.8.5 && \ cd /usr/local/bin && \ ln -s /opt/poetry/bin/poetry && \ poetry config virtualenvs.create false diff --git a/microservices/gatewayApi/patterns/__init__.py b/microservices/gatewayApi/patterns/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/microservices/gatewayApi/patterns/eval.py b/microservices/gatewayApi/patterns/eval.py new file mode 100644 index 00000000..1f9cb62f --- /dev/null +++ b/microservices/gatewayApi/patterns/eval.py @@ -0,0 +1,41 @@ +from patterns.sdx.access_point_r1 import eval_access_point_pattern +from patterns.sdx.keys_r1 import eval_keys_pattern +from patterns.sdx.service_r1 import eval_service_pattern +from patterns.sdx.service_pub_r1 import eval_service_pub_pattern +from patterns.sdx.service_mtls_r1 import eval_service_mtls_pattern +from patterns.sdx.p2p_provider_r1 import eval_p2p_provider_pattern + +from patterns.sdx.p2p_provider_pub_r1 import eval_p2p_provider_pub_pattern +from patterns.sdx.p2p_provider_integrity_r1 import eval_p2p_provider_integrity_pattern +from patterns.sdx.p2p_consumer_r1 import eval_p2p_consumer_pattern +from patterns.sdx.p2p_consumer_pub_r1 import eval_p2p_consumer_pub_pattern +from patterns.sdx.p2p_consumer_integrity_r1 import eval_p2p_consumer_integrity_pattern + +def evaluate_pattern(pattern, context): + """ + Evaluates a pattern against the provided context. + """ + if pattern == 'sdx-access-point-r1': + return eval_access_point_pattern(context) + elif pattern == 'sdx-keys-r1': + return eval_keys_pattern(context) + elif pattern == 'sdx-service-r1': + return eval_service_pattern(context) + elif pattern == 'sdx-service-mtls-r1': + return eval_service_mtls_pattern(context) + elif pattern == 'sdx-service-pub-r1': + return eval_service_pub_pattern(context) + elif pattern == 'sdx-p2p-provider-r1': + return eval_p2p_provider_pattern(context) + elif pattern == 'sdx-p2p-provider-pub-r1': + return eval_p2p_provider_pub_pattern(context) + elif pattern == 'sdx-p2p-provider-integrity-r1': + return eval_p2p_provider_integrity_pattern(context) + elif pattern == 'sdx-p2p-consumer-r1': + return eval_p2p_consumer_pattern(context) + elif pattern == 'sdx-p2p-consumer-pub-r1': + return eval_p2p_consumer_pub_pattern(context) + elif pattern == 'sdx-p2p-consumer-integrity-r1': + return eval_p2p_consumer_integrity_pattern(context) + else: + raise ValueError(f"Unknown pattern: {pattern}") \ No newline at end of file diff --git a/microservices/gatewayApi/patterns/sdx/access_point_r1.py b/microservices/gatewayApi/patterns/sdx/access_point_r1.py new file mode 100644 index 00000000..e856fdcc --- /dev/null +++ b/microservices/gatewayApi/patterns/sdx/access_point_r1.py @@ -0,0 +1,268 @@ + +from string import Template + + + +### +### Access Point +### +### - all routes are protected by mTLS +### - default 401 response for all requests +### +template = Template(""" +_format_version: "3.0" +services: + - name: ${service_name} + url: ${upstream_uri} + tags: [ns.${gateway}.${ns_qualifier}] + plugins: + - name: mtls-auth + tags: [ns.${gateway}.${ns_qualifier}] + enabled: false + config: + error_response_code: 401 + upstream_cert_cn_header: "X-CERT-CN" + upstream_cert_fingerprint_header: "X-CERT-FINGERPRINT" + upstream_cert_i_dn_header: "X-CERT-I-DN" + upstream_cert_s_dn_header: "X-CERT-S-DN" + upstream_cert_serial_header: "X-CERT-SERIAL" + - name: request-termination + tags: [ns.${gateway}.${ns_qualifier}] + config: + status_code: 401 + message: "Access Denied. Route not found." + routes: + - name: ${service_name}.DENY + tags: [ns.${gateway}.${ns_qualifier}, sdx] + hosts: + - ${route_host} + paths: + - ${route_path} + methods: + - GET + - POST + - PUT + - DELETE + - OPTIONS + strip_path: true + https_redirect_status_code: 426 + path_handling: v0 + request_buffering: true + response_buffering: true + + - name: ${service_name}.CONSOLE + url: https://sdx-beta-api-gov-bc-ca-lab.dev.api.gov.bc.ca + tags: [ns.${gateway}.${ns_qualifier}] + routes: + - name: ${service_name}.CONSOLE + tags: [ns.${gateway}.${ns_qualifier}, sdx] + hosts: + - ${route_host} + paths: + - /console + - /api/rd/ + methods: + - GET + strip_path: false + https_redirect_status_code: 426 + path_handling: v0 + request_buffering: true + response_buffering: true + - name: ${service_name}.CONSOLE-DS-API + tags: [ns.${gateway}.${ns_qualifier}, sdx] + hosts: + - ${route_host} + paths: + - /api/ds/ + methods: + - GET + - POST + - PUT + - DELETE + strip_path: false + https_redirect_status_code: 426 + path_handling: v0 + request_buffering: true + response_buffering: true + - name: ${service_name}.AUTH + url: https://httpbin.org + tags: [ns.${gateway}.${ns_qualifier}] + tls_verify: false + routes: + - name: ${service_name}.AUTH-OPTIONS + tags: [ns.${gateway}.${ns_qualifier}, sdx] + hosts: + - ${route_host} + paths: + - /auth + methods: + - OPTIONS + strip_path: false + preserve_host: false + https_redirect_status_code: 426 + path_handling: v0 + request_buffering: true + response_buffering: true + - name: ${service_name}.AUTH + tags: [ns.${gateway}.${ns_qualifier}, sdx] + hosts: + - ${route_host} + paths: + - /auth + methods: + - POST + strip_path: false + preserve_host: false + https_redirect_status_code: 426 + path_handling: v0 + request_buffering: true + response_buffering: true + plugins: + - name: pre-function + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + access: + - | + local os = require "os" + local io = require "io" + local ssl = require('ngx.ssl') + + local http = require "resty.http" + local cjson = require "cjson.safe" + + local httpc = http.new() + local req_body = kong.request.get_raw_body() + + local client_cert_path = os.getenv("KONG_CLIENT_SSL_CERT") + local client_key_path = os.getenv("KONG_CLIENT_SSL_CERT_KEY") + + if req_body then + -- Process the raw body string + kong.log.info("Request body: ", req_body) + end + + local function read_file(filename) + local file = io.open(filename, "r") + if not file then + kong.log.err("Error: Could not open file " .. filename) + return kong.response.exit(500, "Error: Could not open file " .. filename) + end + + local content = file:read("*all") -- Read entire file + file:close() + return content + end + + local config = { + -- Server details + host = "sdx-authz-apps-gov-bc-ca-lab.apps.gov.bc.ca", + port = 443, + path = "/auth/realms/sdx/protocol/openid-connect/token", + + -- Client certificate files + cert_file = assert(ssl.parse_pem_cert(read_file(client_cert_path))), + key_file = assert(ssl.parse_pem_priv_key(read_file(client_key_path))), + + -- Request data + post_data = req_body, + content_type = "application/x-www-form-urlencoded" + } + + if not config.cert_file or not config.key_file then + kong.log.err("Failed to load certificates as cdata") + return kong.response.exit(500, "Failed to load certificates as cdata") + end + + local res, err = httpc:request_uri( + "https://" .. config.host .. ":" .. config.port .. config.path, + { + method = "POST", + headers = { + ["Content-Type"] = config.content_type, + ["Accept"] = "application/json", + ["DPoP"] = kong.request.get_header("DPoP") + }, + body = config.post_data, + ssl_verify = true, + ssl_client_cert = config.cert_file, + ssl_client_priv_key = config.key_file + } + ) + + if not res then + return kong.response.exit(502, "Upstream request failed: " .. (err or "unknown error")) + end + + kong.response.set_header("Content-Type", res.headers["Content-Type"] or "application/json") + return kong.response.exit(res.status, res.body) + + + plugins: + - name: cors + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + origins: + - "*" + methods: + - GET + - POST + - OPTIONS + headers: + - Accept + - Authorization + - Content-Type + - If-None-Match + - X-Client-Id + - DPoP + + - name: ${service_name}.HELLO + url: https://httpbin.org + tags: [ns.${gateway}.${ns_qualifier}] + tls_verify: false + routes: + - name: ${service_name}.HELLO + tags: [ns.${gateway}.${ns_qualifier}, sdx] + hosts: + - ${route_host} + paths: + - /hello + methods: + - GET + strip_path: false + preserve_host: false + https_redirect_status_code: 426 + path_handling: v0 + request_buffering: true + response_buffering: true + + - name: ${service_name}.JWKS + url: https://httpbin.org + tags: [ns.${gateway}.${ns_qualifier}] + tls_verify: false + routes: + - name: ${service_name}.JWKS + tags: [ns.${gateway}.${ns_qualifier}, sdx] + hosts: + - ${route_host} + paths: + - /.well-known/jwks.json + methods: + - GET + strip_path: false + preserve_host: false + https_redirect_status_code: 426 + path_handling: v0 + request_buffering: true + response_buffering: true + plugins: + - name: trust-registry + tags: [ns.${gateway}.${ns_qualifier}] + config: {} + + +""") + +def eval_access_point_pattern (context): + return template.substitute(context) diff --git a/microservices/gatewayApi/patterns/sdx/keys_r1.py b/microservices/gatewayApi/patterns/sdx/keys_r1.py new file mode 100644 index 00000000..48f9dc27 --- /dev/null +++ b/microservices/gatewayApi/patterns/sdx/keys_r1.py @@ -0,0 +1,23 @@ + +from string import Template +import textwrap + +### +### Keys +### +### +template = Template(""" +_format_version: "3.0" +keys: + - name: ${key_name} + kid: ${kid} + pem: + public_key: |- +${public_key_pem} + tags: [ns.${gateway}.${ns_qualifier}] +""") + +def eval_keys_pattern (context): + pem = context["public_key_pem"] + context["public_key_pem"] = textwrap.indent(pem, " ") + return template.substitute(context) diff --git a/microservices/gatewayApi/patterns/sdx/p2p_consumer_integrity_r1.py b/microservices/gatewayApi/patterns/sdx/p2p_consumer_integrity_r1.py new file mode 100644 index 00000000..f89a5ddd --- /dev/null +++ b/microservices/gatewayApi/patterns/sdx/p2p_consumer_integrity_r1.py @@ -0,0 +1,115 @@ + +from string import Template + + + +### +### Application API +### +### - non-mTLS route in for consumers +### - validate user token with DPoP +### - add kong-upstream-jwt for signing +### - mTLS route for upstream service to peer access point +### +template = Template(""" +_format_version: "3.0" +services: + - name: ${service_name} + url: ${upstream_uri} + tags: [ns.${gateway}.${ns_qualifier}] + retries: 0 + tls_verify: true + routes: + - name: ${service_name}-OPTIONS + tags: [ns.${gateway}.${ns_qualifier}, sdx] + hosts: + - ${route_host} + paths: + - ${route_path} + methods: + - OPTIONS + strip_path: false + https_redirect_status_code: 426 + path_handling: v0 + request_buffering: true + response_buffering: true + + - name: ${service_name} + tags: [ns.${gateway}.${ns_qualifier}, sdx] + hosts: + - ${route_host} + paths: + - ${route_path} + methods: + - GET + - POST + - PUT + - DELETE + strip_path: false + https_redirect_status_code: 426 + path_handling: v0 + request_buffering: true + response_buffering: true + plugins: + - name: trust-verify-signature + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + direction: response + manifest_type: signature-only + signature_header_key: X-Edge-Token + + - name: trust-sign + tags: [ns.${gateway}.${ns_qualifier}] + config: + direction: request + private_key_location: "/etc/secrets/sdx-edge-signing-cert/tls.key" + jwks_uri: "https://sdx-min-citz-jwks-api-gov-bc-ca-lab.dev.api.gov.bc.ca" + keyid: ${edge_kid} + alg: ES256 + hash_alg: sha256 + signature_header_key: X-Edge-Token + + - name: rate-limiting + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + policy: local + fault_tolerant: true + second: 50 + limit_by: ip + + - name: cors + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + origins: ["*"] + methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"] + headers: ["Accept", "Accept-Version", "Content-Length", "Content-Type", "Authorization", "X-Client-Id", "X-Sdx-Ap-Sign", "Content-Digest", "Dpop"] + exposed_headers: ["*", "Signature", "Signature-Input", "Content-Digest", "x-trust-ledger-body", "x-trust-ledger-status", "x-trust-timestamp", "x-kong-request-id"] + + - name: request-transformer + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + replace: + headers: + - "Host:${provider_endpoint}" + add: + headers: + - "X-Client-Id:${consumer_uri}" + + - name: response-transformer + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + remove: + headers: + - Set-Cookie + - Server + - Via + - X-Powered-By +""") + +def eval_p2p_consumer_integrity_pattern (context): + return template.substitute(context) diff --git a/microservices/gatewayApi/patterns/sdx/p2p_consumer_pub_r1.py b/microservices/gatewayApi/patterns/sdx/p2p_consumer_pub_r1.py new file mode 100644 index 00000000..9c373df6 --- /dev/null +++ b/microservices/gatewayApi/patterns/sdx/p2p_consumer_pub_r1.py @@ -0,0 +1,84 @@ + +from string import Template + + + +### +### Application API +### +### - non-mTLS route in for consumers +### - validate user token with DPoP +### - add kong-upstream-jwt for signing +### - mTLS route for upstream service to peer access point +### +template = Template(""" +_format_version: "3.0" +services: + - name: ${service_name} + url: ${upstream_uri} + tags: [ns.${gateway}.${ns_qualifier}] + retries: 0 + tls_verify: true + routes: + - name: ${service_name}-OPTIONS + tags: [ns.${gateway}.${ns_qualifier}, sdx] + hosts: + - ${route_host} + paths: + - ${route_path} + methods: + - OPTIONS + strip_path: false + https_redirect_status_code: 426 + path_handling: v0 + request_buffering: true + response_buffering: true + + - name: ${service_name} + tags: [ns.${gateway}.${ns_qualifier}, sdx] + hosts: + - ${route_host} + paths: + - ${route_path} + methods: + - GET + - POST + - PUT + - DELETE + strip_path: false + https_redirect_status_code: 426 + path_handling: v0 + request_buffering: true + response_buffering: true + plugins: + - name: rate-limiting + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + policy: local + fault_tolerant: true + second: 50 + limit_by: ip + + - name: cors + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + origins: ["*"] + methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"] + headers: ["Accept", "Accept-Version", "Content-Length", "Content-Type", "Authorization", "X-Client-Id", "X-Sdx-Ap-Sign", "Content-Digest", "Dpop"] + + - name: request-transformer + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + replace: + headers: + - "Host:${provider_endpoint}" + add: + headers: + - "X-Client-Id:${consumer_uri}" +""") + +def eval_p2p_consumer_pub_pattern (context): + return template.substitute(context) diff --git a/microservices/gatewayApi/patterns/sdx/p2p_consumer_r1.py b/microservices/gatewayApi/patterns/sdx/p2p_consumer_r1.py new file mode 100644 index 00000000..a7476f14 --- /dev/null +++ b/microservices/gatewayApi/patterns/sdx/p2p_consumer_r1.py @@ -0,0 +1,157 @@ + +from string import Template + + + +### +### Application API +### +### - non-mTLS route in for consumers +### - validate user token with DPoP +### - add kong-upstream-jwt for signing +### - mTLS route for upstream service to peer access point +### +template = Template(""" +_format_version: "3.0" +services: + - name: ${service_name} + url: ${upstream_uri} + tags: [ns.${gateway}.${ns_qualifier}] + retries: 0 + tls_verify: true + routes: + - name: ${service_name}-OPTIONS + tags: [ns.${gateway}.${ns_qualifier}, sdx] + hosts: + - ${route_host} + paths: + - ${route_path} + methods: + - OPTIONS + strip_path: false + https_redirect_status_code: 426 + path_handling: v0 + request_buffering: true + response_buffering: true + + - name: ${service_name} + tags: [ns.${gateway}.${ns_qualifier}, sdx] + hosts: + - ${route_host} + paths: + - ${route_path} + methods: + - GET + - POST + - PUT + - DELETE + strip_path: false + https_redirect_status_code: 426 + path_handling: v0 + request_buffering: true + response_buffering: true + plugins: + - name: rate-limiting + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + policy: local + fault_tolerant: true + second: 50 + limit_by: ip + + - name: cors + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + origins: ["*"] + methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"] + headers: ["Accept", "Accept-Version", "Content-Length", "Content-Type", "Authorization", "X-Client-Id", "X-Sdx-Ap-Sign", "Content-Digest", "Dpop"] + exposed_headers: ["*", "Signature", "Signature-Input", "Content-Digest", "x-trust-ledger-body", "x-trust-ledger-status", "x-trust-timestamp", "x-kong-request-id"] + + - name: oidc + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + client_secret: NOT_APPLICABLE + client_id: NOT_APPLICABLE + header_names: ["X-IDP-C-PERSON-PPID", "X-IDP-C-AZP-CLIENT-ID", "X-IDP-C-ISSUER"] + bearer_jwt_auth_allowed_auds: [ ${openid_audience} ] + unauth_action: deny + bearer_only: "yes" + use_jwks: "yes" + bearer_jwt_auth_enable: "yes" + discovery: ${openid_issuer}/.well-known/openid-configuration + header_claims: ["sub", "azp", "iss"] + + # scope and validate_scope do nothing when bearer_jwt_auth_enable is "yes" + # scope: ${openid_scope} + # validate_scope: "yes" + disable_userinfo_header: "yes" + disable_id_token_header: "yes" + + - name: kong-upstream-jwt + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + header: "SDX-AP-AUTH" + include_credential_type: true + key_id: aps-kong-gateway + issuer: https://aps-jwks-upstream-jwt-api-gov-bc-ca-lab.dev.api.gov.bc.ca + + - name: request-transformer + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + add: + headers: + - "X-Client-Id:${consumer_uri}" + + - name: dpop + tags: [ns.${gateway}.${ns_qualifier}] + config: + allowed_algorithms: [ES256] + max_age: 60 + clock_skew: 60 + nonce_cache_ttl: 300 + + - name: trust-verify-digest + tags: [ns.${gateway}.${ns_qualifier}] + config: + direction: request + + - name: trust-sign + tags: [ns.${gateway}.${ns_qualifier}] + config: + private_key_location: "/etc/secrets/sdx-edge-signing-cert/tls.key" + jwks_uri: "https://sdx-min-citz-jwks-api-gov-bc-ca-lab.dev.api.gov.bc.ca" + keyid: ${edge_kid} + alg: ES256 + hash_alg: sha256 + signature_header_key: X-Edge-Token + direction: request + + - name: trust-timestamp + tags: [ns.${gateway}.${ns_qualifier}] + config: + policy_oid: "1.2.1.2.1" + endpoint_url: "https://freetsa.org/tsr" + + - name: trust-ledger + tags: [ns.${gateway}.${ns_qualifier}] + config: + provider: rekor + endpoint_url: "https://rekor.sigstore.dev" + + # - name: oidc + # tags: [ns.gw-0a524] + # enabled: true + # config: + # client_id: gw-94b7c-dpop + # bearer_only: "on" + # bearer_jwt_auth_enable: "on" + # discovery: https://sdx-authz-apps-gov-bc-ca-lab.apps.gov.bc.ca/auth/realms/sdx/.well-known/openid-configuration +""") + +def eval_p2p_consumer_pattern (context): + return template.substitute(context) diff --git a/microservices/gatewayApi/patterns/sdx/p2p_provider_integrity_r1.py b/microservices/gatewayApi/patterns/sdx/p2p_provider_integrity_r1.py new file mode 100644 index 00000000..4655d433 --- /dev/null +++ b/microservices/gatewayApi/patterns/sdx/p2p_provider_integrity_r1.py @@ -0,0 +1,109 @@ + +from string import Template + + + +### +### Service API +### +### - all routes are protected by mTLS +### - default 401 response for all requests +### +template = Template(""" +_format_version: "3.0" +services: + - name: ${service_name} + url: ${upstream_uri} + tags: [ns.${gateway}.${ns_qualifier}] + retries: 0 + tls_verify: false + + plugins: + - name: mtls-auth + tags: [ns.${gateway}.${ns_qualifier}] + config: + error_response_code: 401 + upstream_cert_cn_header: "X-CERT-CN" + upstream_cert_fingerprint_header: "X-CERT-FINGERPRINT" + upstream_cert_i_dn_header: "X-CERT-I-DN" + upstream_cert_s_dn_header: "X-CERT-S-DN" + upstream_cert_serial_header: "X-CERT-SERIAL" + + - name: trust-verify-signature + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + direction: request + manifest_type: signature-only + signature_header_key: X-Edge-Token + + - name: trust-sign + tags: [ns.${gateway}.${ns_qualifier}] + config: + direction: response + private_key_location: "/etc/secrets/sdx-edge-signing-cert/tls.key" + jwks_uri: "https://sdx-min-citz-jwks-api-gov-bc-ca-lab.dev.api.gov.bc.ca" + keyid: ${edge_kid} + alg: ES256 + hash_alg: sha256 + signature_header_key: X-Edge-Token + + - name: request-transformer + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + remove: + headers: + - Cookie + - Forwarded + - Priority + - Sec-Ch-Ua + - Sec-Ch-Ua-Mobile + - Sec-Ch-Ua-Platform + - Sec-Fetch-Dest + - Sec-Fetch-Mode + - Sec-Fetch-Site + - Sec-Fetch-User + - Upgrade-Insecure-Requests + - User-Agent + - Via + - X-Forwarded-Path + - X-Forwarded-Port + - X-Forwarded-Prefix + - X-Real-Ip + + routes: + - name: ${service_name} + tags: [ns.${gateway}.${ns_qualifier}, sdx] + hosts: + - ${route_host} + paths: + - ${route_path} + headers: + "X-Client-Id": [ ${consumer_uri} ] + methods: + - GET + - POST + - PUT + - DELETE + strip_path: true + https_redirect_status_code: 426 + path_handling: v0 + request_buffering: true + response_buffering: true + + +""") + +def eval_p2p_provider_integrity_pattern (context): + mtls_allow_list = context.get("mtls_allow_list", "") + # WORKAROUND - Seems like NGINX Client Cert does not include spaces + # when separating the DN attributes + # if mtls_allow_list has commas, then create two quoted values - one + # with a ", " and the other with just a "," + if "," in mtls_allow_list: + mtls_allow_list = f'{mtls_allow_list.replace(", ", ",")}, {mtls_allow_list}' + else: + mtls_allow_list = f'{mtls_allow_list}' + context["mtls_allow_list"] = mtls_allow_list + return template.substitute(context) diff --git a/microservices/gatewayApi/patterns/sdx/p2p_provider_pub_r1.py b/microservices/gatewayApi/patterns/sdx/p2p_provider_pub_r1.py new file mode 100644 index 00000000..b3d73d45 --- /dev/null +++ b/microservices/gatewayApi/patterns/sdx/p2p_provider_pub_r1.py @@ -0,0 +1,55 @@ + +from string import Template + + + +### +### Service API +### +### - all routes are protected by mTLS +### - default 401 response for all requests +### +template = Template(""" +_format_version: "3.0" +services: + - name: ${service_name} + url: ${upstream_uri} + tags: [ns.${gateway}.${ns_qualifier}] + retries: 0 + tls_verify: false + + routes: + - name: ${service_name} + tags: [ns.${gateway}.${ns_qualifier}, sdx] + hosts: + - ${route_host} + paths: + - ${route_path} + headers: + "X-Client-Id": [ ${consumer_uri} ] + methods: + - GET + - POST + - PUT + - DELETE + strip_path: true + https_redirect_status_code: 426 + path_handling: v0 + request_buffering: true + response_buffering: true + + +""") + +def eval_p2p_provider_pub_pattern (context): + mtls_allow_list = context.get("mtls_allow_list", "") + # WORKAROUND - Seems like NGINX Client Cert does not include spaces + # when separating the DN attributes + # if mtls_allow_list has commas, then create two quoted values - one + # with a ", " and the other with just a "," + if "," in mtls_allow_list: + mtls_allow_list = f'{mtls_allow_list.replace(", ", ",")}, {mtls_allow_list}' + else: + mtls_allow_list = f'{mtls_allow_list}' + context["mtls_allow_list"] = mtls_allow_list + return template.substitute(context) diff --git a/microservices/gatewayApi/patterns/sdx/p2p_provider_r1.py b/microservices/gatewayApi/patterns/sdx/p2p_provider_r1.py new file mode 100644 index 00000000..f375f4fb --- /dev/null +++ b/microservices/gatewayApi/patterns/sdx/p2p_provider_r1.py @@ -0,0 +1,146 @@ + +from string import Template + + + +### +### Service API +### +### - all routes are protected by mTLS +### - default 401 response for all requests +### +template = Template(""" +_format_version: "3.0" +services: + - name: ${service_name} + url: ${upstream_uri} + tags: [ns.${gateway}.${ns_qualifier}] + retries: 0 + tls_verify: false + plugins: + - name: mtls-auth + tags: [ns.${gateway}.${ns_qualifier}] + config: + error_response_code: 401 + upstream_cert_cn_header: "X-CERT-CN" + upstream_cert_fingerprint_header: "X-CERT-FINGERPRINT" + upstream_cert_i_dn_header: "X-CERT-I-DN" + upstream_cert_s_dn_header: "X-CERT-S-DN" + upstream_cert_serial_header: "X-CERT-SERIAL" + - name: mtls-acl + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + certificate_header_name: X-CERT-S-DN + allow: [ ${mtls_allow_list} ] + + - name: jwt-keycloak_1010 + tags: [ns.${gateway}.${ns_qualifier}] + enabled: false + config: + allowed_iss: + - https://aps-jwks-upstream-jwt-api-gov-bc-ca-lab.dev.api.gov.bc.ca + allowed_aud: ${consumer_client_id} + header_names: [ SDX-AP-AUTH ] + + - name: jwt-keycloak + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + allowed_iss: + - ${openid_issuer} + allowed_aud: "${openid_audience}" + scope: [ ${openid_scope} ] + + - name: oidc + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + client_secret: NOT_APPLICABLE + client_id: NOT_APPLICABLE + header_names: ["X-IDP-P-PERSON-PPID", "X-IDP-P-AZP-CLIENT-ID"] + bearer_jwt_auth_allowed_auds: [ ${openid_audience} ] + unauth_action: deny + bearer_only: "yes" + use_jwks: "yes" + bearer_jwt_auth_enable: "yes" + discovery: ${openid_issuer}/.well-known/openid-configuration + header_claims: ["sub", "azp"] + + # scope and validate_scope do nothing when bearer_jwt_auth_enable is "yes" + # scope: ${openid_scope} + # validate_scope: "yes" + disable_userinfo_header: "yes" + disable_id_token_header: "yes" + + - name: trust-verify-signature + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + direction: request + manifest_type: signature-only + signature_header_key: X-Edge-Token + + routes: + - name: ${service_name} + tags: [ns.${gateway}.${ns_qualifier}, sdx] + hosts: + - ${route_host} + paths: + - ${route_path} + headers: + "X-Client-Id": [ ${consumer_uri} ] + methods: + - GET + - POST + - PUT + - DELETE + strip_path: true + https_redirect_status_code: 426 + path_handling: v0 + request_buffering: true + response_buffering: true + + - name: ${service_name}-SIGNED + tags: [ns.${gateway}.${ns_qualifier}, sdx] + hosts: + - ${route_host} + paths: + - ${route_path} + headers: + "X-Client-Id": [ ${consumer_uri} ] + "Accept": ["application/jws+jwt"] + methods: + - GET + - POST + - PUT + - DELETE + strip_path: true + https_redirect_status_code: 426 + path_handling: v0 + request_buffering: true + response_buffering: true + plugins: + - name: response-signer + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + public_key_location: /etc/secrets/kong-upstream-jwt/tls.crt + private_key_location: /etc/secrets/kong-upstream-jwt/tls.key + key_id: "aps-kong-gateway" + issuer: "https://aps-jwks-upstream-jwt-api-gov-bc-ca-lab.dev.api.gov.bc.ca" + +""") + +def eval_p2p_provider_pattern (context): + mtls_allow_list = context.get("mtls_allow_list", "") + # WORKAROUND - Seems like NGINX Client Cert does not include spaces + # when separating the DN attributes + # if mtls_allow_list has commas, then create two quoted values - one + # with a ", " and the other with just a "," + if "," in mtls_allow_list: + mtls_allow_list = f'{mtls_allow_list.replace(", ", ",")}, {mtls_allow_list}' + else: + mtls_allow_list = f'{mtls_allow_list}' + context["mtls_allow_list"] = mtls_allow_list + return template.substitute(context) diff --git a/microservices/gatewayApi/patterns/sdx/service_mtls_r1.py b/microservices/gatewayApi/patterns/sdx/service_mtls_r1.py new file mode 100644 index 00000000..cc943f6c --- /dev/null +++ b/microservices/gatewayApi/patterns/sdx/service_mtls_r1.py @@ -0,0 +1,106 @@ + +from string import Template + + + +### +### Service API +### +### - all routes are protected by mTLS +### - default 401 response for all requests +### +template = Template(""" +_format_version: "3.0" +services: + - name: ${service_name} + url: ${upstream_uri} + tags: [ns.${gateway}.${ns_qualifier}] + retries: 0 + tls_verify: false + plugins: + - name: mtls-auth + tags: [ns.${gateway}.${ns_qualifier}] + config: + error_response_code: 401 + upstream_cert_cn_header: "X-CERT-CN" + upstream_cert_fingerprint_header: "X-CERT-FINGERPRINT" + upstream_cert_i_dn_header: "X-CERT-I-DN" + upstream_cert_s_dn_header: "X-CERT-S-DN" + upstream_cert_serial_header: "X-CERT-SERIAL" + + - name: mtls-acl + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + certificate_header_name: X-CERT-S-DN + allow: [ ${mtls_allow_list} ] + + - name: rate-limiting + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + policy: local + fault_tolerant: true + second: 50 + limit_by: ip + + - name: cors + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + origins: ["*"] + methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"] + headers: ["Accept", "Accept-Version", "Content-Length", "Content-Type", "Authorization", "X-Client-Id", "X-Sdx-Ap-Sign", "DPoP"] + + routes: + - name: ${service_name}.OPTIONS + tags: [ns.${gateway}.${ns_qualifier}, sdx] + hosts: + - ${route_host} + paths: + - ${route_path} + methods: + - OPTIONS + strip_path: false + https_redirect_status_code: 426 + path_handling: v0 + request_buffering: true + response_buffering: true + plugins: + - name: jwt-keycloak + tags: [ns.${gateway}.${ns_qualifier}] + enabled: false + config: + allowed_iss: + - ${openid_issuer} + allowed_aud: "${openid_audience}" + scope: [ ${openid_scope} ] + - name: oidc + tags: [ns.${gateway}.${ns_qualifier}] + enabled: false + config: + client_secret: NOT_APPLICABLE + client_id: NOT_APPLICABLE + discovery: ${openid_issuer}/.well-known/openid-configuration + + - name: ${service_name}.API + tags: [ns.${gateway}.${ns_qualifier}, sdx] + hosts: + - ${route_host} + paths: + - ${route_path} + methods: + - GET + - POST + - PUT + - DELETE + strip_path: true + https_redirect_status_code: 426 + path_handling: v0 + request_buffering: true + response_buffering: true + +""") + +def eval_service_mtls_pattern (context): + return template.substitute(context) diff --git a/microservices/gatewayApi/patterns/sdx/service_pub_r1.py b/microservices/gatewayApi/patterns/sdx/service_pub_r1.py new file mode 100644 index 00000000..b278f711 --- /dev/null +++ b/microservices/gatewayApi/patterns/sdx/service_pub_r1.py @@ -0,0 +1,69 @@ + +from string import Template + + + +### +### Service Pub API +### +### - basic cors +### +template = Template(""" +_format_version: "3.0" +services: + - name: ${service_name} + url: ${upstream_uri} + tags: [ns.${gateway}.${ns_qualifier}] + retries: 0 + tls_verify: false + routes: + - name: ${service_name}.API + tags: [ns.${gateway}.${ns_qualifier}, sdx] + hosts: + - ${route_host} + paths: + - ${route_path} + methods: + - GET + - POST + - PUT + - DELETE + - OPTIONS + strip_path: true + https_redirect_status_code: 426 + path_handling: v0 + request_buffering: true + response_buffering: true + + - name: ${service_name}.SIGNED + tags: [ns.${gateway}.${ns_qualifier}, sdx] + hosts: + - ${route_host} + paths: + - ${route_path} + headers: + "Accept": ["application/jws+jwt"] + methods: + - GET + - POST + - PUT + - DELETE + strip_path: true + https_redirect_status_code: 426 + path_handling: v0 + request_buffering: true + response_buffering: true + plugins: + - name: response-signer + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + public_key_location: /etc/secrets/kong-upstream-jwt/tls.crt + private_key_location: /etc/secrets/kong-upstream-jwt/tls.key + key_id: "aps-kong-gateway" + issuer: "https://aps-jwks-upstream-jwt-api-gov-bc-ca-lab.dev.api.gov.bc.ca" + +""") + +def eval_service_pub_pattern (context): + return template.substitute(context) diff --git a/microservices/gatewayApi/patterns/sdx/service_r1.py b/microservices/gatewayApi/patterns/sdx/service_r1.py new file mode 100644 index 00000000..837ae270 --- /dev/null +++ b/microservices/gatewayApi/patterns/sdx/service_r1.py @@ -0,0 +1,685 @@ + +from string import Template + + + +### +### Service API +### +### - all routes are protected by mTLS +### - default 401 response for all requests +### +template = Template(""" +_format_version: "3.0" +services: + - name: ${service_name} + url: ${upstream_uri} + tags: [ns.${gateway}.${ns_qualifier}] + retries: 0 + tls_verify: false + plugins: + - name: rate-limiting + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + policy: local + fault_tolerant: true + second: 50 + limit_by: ip + + - name: cors + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + origins: ["*"] + methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"] + headers: ["Accept", "Accept-Version", "Content-Length", "Content-Type", "Authorization", "X-Client-Id", "X-Sdx-Ap-Sign", "DPoP"] + + - name: jwt-keycloak + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + allowed_iss: + - ${openid_issuer} + allowed_aud: "${openid_audience}" + scope: [ ${openid_scope} ] + + - name: oidc + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + client_secret: NOT_APPLICABLE + client_id: NOT_APPLICABLE + header_names: ["X-IDP-P-PERSON-PPID", "X-IDP-P-AZP-CLIENT-ID"] + bearer_jwt_auth_allowed_auds: [ ${openid_audience} ] + unauth_action: deny + bearer_only: "yes" + use_jwks: "yes" + bearer_jwt_auth_enable: "yes" + discovery: ${openid_issuer}/.well-known/openid-configuration + header_claims: ["sub", "azp"] + + # scope and validate_scope do nothing when bearer_jwt_auth_enable is "yes" + # scope: ${openid_scope} + # validate_scope: "yes" + disable_userinfo_header: "yes" + disable_id_token_header: "yes" + + - name: openid-authzen + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + # lua_ssl_trusted_certificate has to have the CA's - otherwise "unable to get local issuer certificate" + target_url: https://ping.api.gov.bc.ca + json_locator: [] + # subject_claim: "sub" + # resource_type: "service_name|route_name|uri_path" + # action_name: "read" + + routes: + - name: ${service_name}.OPTIONS + tags: [ns.${gateway}.${ns_qualifier}, sdx] + hosts: + - ${route_host} + paths: + - ${route_path} + methods: + - OPTIONS + strip_path: false + https_redirect_status_code: 426 + path_handling: v0 + request_buffering: true + response_buffering: true + plugins: + - name: jwt-keycloak + tags: [ns.${gateway}.${ns_qualifier}] + enabled: false + config: + allowed_iss: + - ${openid_issuer} + allowed_aud: "${openid_audience}" + scope: [ ${openid_scope} ] + - name: oidc + tags: [ns.${gateway}.${ns_qualifier}] + enabled: false + config: + client_secret: NOT_APPLICABLE + client_id: NOT_APPLICABLE + discovery: ${openid_issuer}/.well-known/openid-configuration + + - name: ${service_name}.API + tags: [ns.${gateway}.${ns_qualifier}, sdx] + hosts: + - ${route_host} + paths: + - ${route_path} + methods: + - GET + - POST + - PUT + - DELETE + strip_path: true + https_redirect_status_code: 426 + path_handling: v0 + request_buffering: true + response_buffering: true + plugins: + - name: pre-function + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + access: + - | + local os = require "os" + local io = require "io" + local ssl = require('ngx.ssl') + + local http = require "resty.http" + local cjson = require "cjson.safe" + + local httpc = http.new() + + local client_cert_path = os.getenv("KONG_CLIENT_SSL_CERT") + local client_key_path = os.getenv("KONG_CLIENT_SSL_CERT_KEY") + + local function urlencode(str) + if not str then return "" end + str = tostring(str) + str = string.gsub(str, "([^%w%.%- ])", function(c) + return string.format("%%%02X", string.byte(c)) + end) + str = string.gsub(str, " ", "+") + return str + end + + local function read_file(filename) + local file = io.open(filename, "r") + if not file then + kong.log.err("Error: Could not open file " .. filename) + return kong.response.exit(500, "Error: Could not open file " .. filename) + end + + local content = file:read("*all") + file:close() + return content + end + + local config = { + host = "sdx-authz-apps-gov-bc-ca-lab.apps.gov.bc.ca", + port = 443, + path = "/auth/realms/sdx/protocol/openid-connect/token/introspect", + + cert_file = assert(ssl.parse_pem_cert(read_file(client_cert_path))), + key_file = assert(ssl.parse_pem_priv_key(read_file(client_key_path))), + + client_id = "gw-introspection", + } + + local data = { + client_id = config.client_id, + token = kong.request.get_header("Authorization"):match("Bearer%s+(.+)"), + token_type_hint = "access_token" + } + + local encoded_body = "" + local first = true + for key, value in pairs(data) do + if not first then + encoded_body = encoded_body .. "&" + end + encoded_body = encoded_body .. urlencode(key) .. "=" .. urlencode(value) + first = false + end + + if not config.cert_file or not config.key_file then + kong.log.err("Failed to load certificates as cdata") + return kong.response.exit(500, "Failed to load certificates as cdata") + end + + local res, err = httpc:request_uri( + "https://" .. config.host .. ":" .. config.port .. config.path, + { + method = "POST", + headers = { + ["Content-Type"] = 'application/x-www-form-urlencoded', + ["Accept"] = "application/jwt", + }, + body = encoded_body, + ssl_verify = true, + ssl_client_cert = config.cert_file, + ssl_client_priv_key = config.key_file + } + ) + + if not res then + return kong.response.exit(502, "Introspection failed: " .. (err or "unknown error")) + end + + if res.status ~= 200 then + return kong.response.exit(401, "Introspection failed: " .. (res.body or "unknown error")) + end + + kong.service.request.set_header("X-INTROSPECTION", res.body) + kong.service.request.set_header("X-INBOUND-TOKEN", kong.request.get_header("Authorization")) + + res.body = cjson.decode(res.body) + if not res.body then + return kong.response.exit(502, "Introspection failed: Could not decode response.") + end + + + if res.body.jwt == nil then + return kong.response.exit(401, "Introspection failed: Expecting jwt in response.") + end + + kong.service.request.set_header("Authorization", "Bearer " .. res.body.jwt) + + - | + local jwt_parser = require "kong.plugins.jwt.jwt_parser" + local cjson = require "cjson.safe" + local crypto = require "resty.openssl" + local digest = require "resty.openssl.digest" + local utils = require "kong.tools.utils" + + local assert = assert + local rep = string.rep + + local base64 = require "ngx.base64" + + local resty_openssl = require "resty.openssl" + local pkey = require "resty.openssl.pkey" + local bn = require "resty.openssl.bn" + local json = require "cjson.safe" + local kong = kong + + local function base64_decode_url(input) + local remainder = #input % 4 + + if remainder > 0 then + local padlen = 4 - remainder + input = input .. rep("=", padlen) + end + + return base64.decode_base64url(input) + end + + local plugin_schema = { + type = "record", + fields = { + { + config = { + type = "record", + fields = { + { max_age = { type = "number", default = 60 } }, + { clock_skew = { type = "number", default = 60 } }, + { allowed_algorithms = { + type = "array", + elements = { type = "string" }, + default = { "RS256", "ES256", "PS256" } + }}, + { nonce_cache_ttl = { type = "number", default = 300 } }, + { anonymous = { type = "string", uuid = true } }, + } + } + } + } + } + + local config = { + max_age = 60, + clock_skew = 60, + allowed_algorithms = { "RS256", "ES256", "PS256" }, + nonce_cache_ttl = 300, + anonymous = nil, + } + + local function send_error_response(status, error_code, description) + kong.response.set_status(status) + kong.response.set_header("Content-Type", "application/json") + local body = { + error = error_code, + error_description = description + } + return kong.response.exit(status, body) + end + + local function extract_dpop_proof() + local dpop_header = kong.request.get_header("DPoP") + if not dpop_header then + return nil, "DPoP header missing" + end + return dpop_header, nil + end + + local function extract_access_token() + local auth_header = kong.request.get_header("Authorization") + if not auth_header then + return nil, "Authorization header missing" + end + local token = auth_header:match("^DPoP%s+(.+)$$") or auth_header:match("^Bearer%s+(.+)$$") + if not token then + return nil, "Invalid Authorization header format" + end + return token, nil + end + + local function create_canonical_jwk_json(jwk) + local canonical = string.format('{"crv":"%s","kty":"%s","x":"%s","y":"%s"}', + jwk.crv, jwk.kty, jwk.x, jwk.y) + return canonical + end + + local function calculate_jwk_thumbprint(jwk) + if not jwk or type(jwk) ~= "table" then + return nil, "Invalid JWK" + end + + local json_str = nil + if jwk.kty == "RSA" then + canonical_jwk = { + e = jwk.e, + kty = jwk.kty, + n = jwk.n + } + elseif jwk.kty == "EC" then + json_str = create_canonical_jwk_json(jwk) + elseif jwk.kty == "OKP" then + canonical_jwk = { + crv = jwk.crv, + kty = jwk.kty, + x = jwk.x + } + else + return nil, "Unsupported key type: " .. tostring(jwk.kty) + end + + local hash = digest.new("sha256") + hash:update(json_str) + local digest_bytes = hash:final() + return base64.encode_base64url(digest_bytes), nil + end + + function ec_jwk_to_key(jwk) + kong.log.warn("JWK: ", cjson.encode(jwk)) + local x_bytes = base64_decode_url(jwk.x) + local y_bytes = base64_decode_url(jwk.y) + + + local curve_map = { + ["P-256"] = "prime256v1", + ["P-384"] = "secp384r1", + ["P-521"] = "secp521r1" + } + + local curve_name = curve_map[jwk.crv] + if not curve_name then + return nil, "Unsupported curve: " .. jwk.crv + end + + local key, err = pkey.new(cjson.encode(jwk), { + format= "JWK" + }) + if not key then + return nil, "Failed to create EC public key: " .. tostring(err) + end + + return key + end + + + local function jwk_to_pem(jwk) + local pkey = require "resty.openssl.pkey" + + if jwk.kty == "RSA" then + local rsa_params = { + n = jwk.n, + e = jwk.e + } + + if rsa_params.n then + rsa_params.n = base64_decode_url(rsa_params.n) + end + if rsa_params.e then + rsa_params.e = base64_decode_url(rsa_params.e) + end + + local key, err = pkey.new({ + type = "RSA", + bits = nil, + rsa_n = rsa_params.n, + rsa_e = rsa_params.e + }) + + if not key then + return nil, "Failed to create RSA public key: " .. tostring(err) + end + + return key, nil + + elseif jwk.kty == "EC" then + local ec_params = { + curve = jwk.crv, + x = jwk.x, + y = jwk.y + } + + if ec_params.x then + ec_params.x = base64_decode_url(ec_params.x) + end + if ec_params.y then + ec_params.y = base64_decode_url(ec_params.y) + end + + local curve_name + if ec_params.curve == "P-256" then + curve_name = "prime256v1" + elseif ec_params.curve == "P-384" then + curve_name = "secp384r1" + elseif ec_params.curve == "P-521" then + curve_name = "secp521r1" + else + return nil, "Unsupported EC curve: " .. tostring(ec_params.curve) + end + + local key, err = pkey.new({ + type = "EC", + curve = curve_name, + ec_x = ec_params.x, + ec_y = ec_params.y + }) + + if not key then + return nil, "Failed to create EC public key: " .. tostring(err) + end + + return key, nil + + else + return nil, "Unsupported key type: " .. tostring(jwk.kty) + end + end + + local function extract_public_key_from_header(header) + if header.jwk then + return ec_jwk_to_key(header.jwk) + end + if header.x5c and #header.x5c > 0 then + return nil, "X.509 certificate processing not implemented" + end + return nil, "No supported public key format found in header" + end + + local function validate_dpop_proof(dpop_proof, access_token, http_method, http_uri, config) + local jwt_parts = utils.split(dpop_proof, ".") + if #jwt_parts ~= 3 then + return false, "Invalid JWT format" + end + + local header_json = base64_decode_url(jwt_parts[1]) + if not header_json then + return false, "Failed to decode JWT header" + end + + local header = cjson.decode(header_json) + if not header then + return false, "Failed to parse JWT header JSON" + end + + local payload_json = base64_decode_url(jwt_parts[2]) + if not payload_json then + return false, "Failed to decode JWT payload" + end + + local claims = cjson.decode(payload_json) + if not claims then + return false, "Failed to parse JWT payload JSON" + end + + if header.typ ~= "dpop+jwt" then + return false, "Invalid DPoP JWT type. Expected 'dpop+jwt', got: " .. tostring(header.typ) + end + + local alg_valid = false + for _, allowed_alg in ipairs(config.allowed_algorithms) do + if header.alg == allowed_alg then + alg_valid = true + break + end + end + if not alg_valid then + return false, "Unsupported algorithm: " .. tostring(header.alg) + end + + local public_key, err = extract_public_key_from_header(header) + if not public_key then + return false, err or "Failed to extract public key" + end + + local signature = jwt_parts[3] + local signature_bytes = base64_decode_url(signature) + if not signature_bytes then + return false, "Failed to decode JWT signature" + end + + local signing_data = jwt_parts[1] .. "." .. jwt_parts[2] + + local verified = false + if header.alg == "ES256" then + assert(#signature_bytes == 64, "Signature must be 64 bytes.") + verified, err = public_key:verify(signature_bytes, signing_data, "sha256", nil, { ecdsa_use_raw = true }) + else + return false, "Unsupported signature algorithm: " .. header.alg + end + + if not verified then + return false, "DPoP proof signature verification failed" + end + + if not claims.jti then + return false, "Missing jti claim" + end + if not claims.htm or claims.htm ~= http_method then + return false, "Invalid htm claim. Expected: " .. http_method .. ", got: " .. tostring(claims.htm) + end + if not claims.htu then + return false, "Missing htu claim" + end + local expected_htu = http_uri:match("^([^%?#]*)") + local provided_htu = claims.htu:match("^([^%?#]*)") + if provided_htu ~= expected_htu then + return false, "Invalid htu claim. Expected: " .. expected_htu .. ", got: " .. provided_htu + end + if not claims.iat then + return false, "Missing iat claim" + end + local current_time = ngx.time() + local iat = claims.iat + if type(iat) ~= "number" then + return false, "Invalid iat claim format" + end + if iat > current_time + config.clock_skew then + return false, "DPoP proof issued in the future" + end + if current_time - iat > config.max_age then + return false, "DPoP proof too old" + end + local cache_key = "dpop_jti:" .. claims.jti + local cached_jti = kong.cache:get(cache_key) + if cached_jti then + return false, "DPoP proof replay detected" + end + + return true, nil, { + public_key = public_key, + jwk = header.jwk, + jti = claims.jti, + iat = claims.iat, + claims = claims + } + end + + local function validate_token_binding(access_token, dpop_public_key) + local token_jwt, err = jwt_parser:new(access_token) + if err then + return false, "Invalid access token format: " .. err + end + local claims = token_jwt.claims + if not claims.cnf or not claims.cnf.jkt then + return false, "Access token not bound to DPoP key (missing cnf.jkt claim)" + end + local dpop_thumbprint, err = calculate_jwk_thumbprint(dpop_public_key) + if not dpop_thumbprint then + return false, "Failed to calculate DPoP key thumbprint: " .. tostring(err) + end + + if claims.cnf.jkt ~= dpop_thumbprint then + return false, "DPoP key thumbprint mismatch" + end + return true, nil, { + token_claims = claims, + dpop_thumbprint = dpop_thumbprint + } + end + + local method = kong.request.get_method() + local scheme = kong.request.get_scheme() + local host = kong.request.get_host() + local port = kong.request.get_port() + local path = kong.request.get_path() + local uri = scheme .. "://" .. host + uri = uri .. path + + local dpop_proof, err = extract_dpop_proof() + if not dpop_proof then + if config.anonymous then + kong.client.authenticate(nil, config.anonymous) + return + end + return send_error_response(401, "invalid_request", err) + end + + local access_token, err = extract_access_token() + if not access_token then + return send_error_response(401, "invalid_request", err) + end + + local valid, err, dpop_data = validate_dpop_proof(dpop_proof, access_token, method, uri, config) + if not valid then + return send_error_response(401, "invalid_dpop_proof", err) + end + + local bound, err, binding_data = validate_token_binding(access_token, dpop_data.jwk) + if not bound then + return send_error_response(401, "invalid_token", err) + end + + kong.ctx.shared.dpop_validated = true + kong.ctx.shared.access_token = access_token + kong.ctx.shared.dpop_public_key = dpop_data.public_key + kong.ctx.shared.dpop_claims = dpop_data.claims + kong.ctx.shared.token_claims = binding_data.token_claims + kong.ctx.shared.dpop_jti = dpop_data.jti + + local subject = binding_data.token_claims.sub + if subject then + kong.client.authenticate({ id = subject }, nil) + end + + kong.log.info("DPoP validation successful for JTI: ", dpop_data.jti) + + kong.service.request.set_header("X-DPoP-Validated", "true" ) + + + + - name: ${service_name}.SIGNED + tags: [ns.${gateway}.${ns_qualifier}, sdx] + hosts: + - ${route_host} + paths: + - ${route_path} + headers: + "Accept": ["application/jws+jwt"] + methods: + - GET + - POST + - PUT + - DELETE + strip_path: true + https_redirect_status_code: 426 + path_handling: v0 + request_buffering: true + response_buffering: true + plugins: + - name: response-signer + tags: [ns.${gateway}.${ns_qualifier}] + enabled: true + config: + public_key_location: /etc/secrets/kong-upstream-jwt/tls.crt + private_key_location: /etc/secrets/kong-upstream-jwt/tls.key + key_id: "aps-kong-gateway" + issuer: "https://aps-jwks-upstream-jwt-api-gov-bc-ca-lab.dev.api.gov.bc.ca" + +""") + +def eval_service_pattern (context): + return template.substitute(context) diff --git a/microservices/gatewayApi/poetry.lock b/microservices/gatewayApi/poetry.lock index d4133d9c..b97cdfb0 100644 --- a/microservices/gatewayApi/poetry.lock +++ b/microservices/gatewayApi/poetry.lock @@ -2,13 +2,13 @@ [[package]] name = "astroid" -version = "3.3.5" +version = "3.3.11" description = "An abstract syntax tree for Python with inference support." optional = false python-versions = ">=3.9.0" files = [ - {file = "astroid-3.3.5-py3-none-any.whl", hash = "sha256:a9d1c946ada25098d790e079ba2a1b112157278f3fb7e718ae6a9252f5835dc8"}, - {file = "astroid-3.3.5.tar.gz", hash = "sha256:5cfc40ae9f68311075d27ef68a4841bdc5cc7f6cf86671b49f00607d30188e2d"}, + {file = "astroid-3.3.11-py3-none-any.whl", hash = "sha256:54c760ae8322ece1abd213057c4b5bba7c49818853fc901ef09719a60dbf9dec"}, + {file = "astroid-3.3.11.tar.gz", hash = "sha256:1e5a5011af2920c7c67a53f65d536d65bfa7116feeaf2354d8b94f29573bb0ce"}, ] [[package]] @@ -43,48 +43,64 @@ files = [ pycodestyle = ">=2.9.1" toml = "*" +[[package]] +name = "blinker" +version = "1.9.0" +description = "Fast, simple object-to-object and broadcast signaling" +optional = false +python-versions = ">=3.9" +files = [ + {file = "blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc"}, + {file = "blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf"}, +] + [[package]] name = "boto3" -version = "1.9.12" +version = "1.40.15" description = "The AWS SDK for Python" optional = false -python-versions = "*" +python-versions = ">=3.9" files = [ - {file = "boto3-1.9.12-py2.py3-none-any.whl", hash = "sha256:4bfa8cab5d1fa695aba8ac3fa0036766660f3f18457e9c00a4656e02c1bc53d9"}, - {file = "boto3-1.9.12.tar.gz", hash = "sha256:b03d4301d9e06157ce597031ecb0c387e89b7a8d66a6b174bb212c0c22b78d75"}, + {file = "boto3-1.40.15-py3-none-any.whl", hash = "sha256:52b8aa78c9906c4e49dcec6817c041df33c9825073bf66e7df8fc00afbe47b4b"}, + {file = "boto3-1.40.15.tar.gz", hash = "sha256:271b379ce5ad35ca82f1009e917528a182eed0e2de197ccffb0c51acadec5c79"}, ] [package.dependencies] -botocore = ">=1.12.12,<1.13.0" -jmespath = ">=0.7.1,<1.0.0" -s3transfer = ">=0.1.10,<0.2.0" +botocore = ">=1.40.15,<1.41.0" +jmespath = ">=0.7.1,<2.0.0" +s3transfer = ">=0.13.0,<0.14.0" + +[package.extras] +crt = ["botocore[crt] (>=1.21.0,<2.0a0)"] [[package]] name = "botocore" -version = "1.12.253" +version = "1.40.15" description = "Low-level, data-driven core of boto 3." optional = false -python-versions = "*" +python-versions = ">=3.9" files = [ - {file = "botocore-1.12.253-py2.py3-none-any.whl", hash = "sha256:dc080aed4f9b220a9e916ca29ca97a9d37e8e1d296fe89cbaeef929bf0c8066b"}, - {file = "botocore-1.12.253.tar.gz", hash = "sha256:3baf129118575602ada9926f5166d82d02273c250d0feb313fc270944b27c48b"}, + {file = "botocore-1.40.15-py3-none-any.whl", hash = "sha256:b364e039d2b67e509cfb089cb39b295251e48a60cc68fd591defbe10b44d83f9"}, + {file = "botocore-1.40.15.tar.gz", hash = "sha256:4960800e4c5a7b43db22550979c22f5a324cbaf75ef494bbb2cf400ef1e6aca7"}, ] [package.dependencies] -docutils = ">=0.10,<0.16" -jmespath = ">=0.7.1,<1.0.0" -python-dateutil = {version = ">=2.1,<3.0.0", markers = "python_version >= \"2.7\""} -urllib3 = {version = ">=1.20,<1.26", markers = "python_version >= \"3.4\""} +jmespath = ">=0.7.1,<2.0.0" +python-dateutil = ">=2.1,<3.0.0" +urllib3 = {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version >= \"3.10\""} + +[package.extras] +crt = ["awscrt (==0.27.6)"] [[package]] name = "certifi" -version = "2024.8.30" +version = "2025.8.3" description = "Python package for providing Mozilla's CA Bundle." optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, - {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, + {file = "certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5"}, + {file = "certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407"}, ] [[package]] @@ -168,127 +184,101 @@ pycparser = "*" [[package]] name = "charset-normalizer" -version = "3.4.0" +version = "3.4.3" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false -python-versions = ">=3.7.0" +python-versions = ">=3.7" files = [ - {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-win32.whl", hash = "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc"}, - {file = "charset_normalizer-3.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-win32.whl", hash = "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99"}, - {file = "charset_normalizer-3.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-win32.whl", hash = "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7"}, - {file = "charset_normalizer-3.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-win32.whl", hash = "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67"}, - {file = "charset_normalizer-3.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-win32.whl", hash = "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149"}, - {file = "charset_normalizer-3.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-win32.whl", hash = "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613"}, - {file = "charset_normalizer-3.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-win32.whl", hash = "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2"}, - {file = "charset_normalizer-3.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca"}, - {file = "charset_normalizer-3.4.0-py3-none-any.whl", hash = "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079"}, - {file = "charset_normalizer-3.4.0.tar.gz", hash = "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:07a0eae9e2787b586e129fdcbe1af6997f8d0e5abaa0bc98c0e20e124d67e601"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:74d77e25adda8581ffc1c720f1c81ca082921329452eba58b16233ab1842141c"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0e909868420b7049dafd3a31d45125b31143eec59235311fc4c57ea26a4acd2"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c6f162aabe9a91a309510d74eeb6507fab5fff92337a15acbe77753d88d9dcf0"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4ca4c094de7771a98d7fbd67d9e5dbf1eb73efa4f744a730437d8a3a5cf994f0"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:02425242e96bcf29a49711b0ca9f37e451da7c70562bc10e8ed992a5a7a25cc0"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:78deba4d8f9590fe4dae384aeff04082510a709957e968753ff3c48399f6f92a"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-win32.whl", hash = "sha256:d79c198e27580c8e958906f803e63cddb77653731be08851c7df0b1a14a8fc0f"}, + {file = "charset_normalizer-3.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:c6e490913a46fa054e03699c70019ab869e990270597018cef1d8562132c2669"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:585f3b2a80fbd26b048a0be90c5aae8f06605d3c92615911c3a2b03a8a3b796f"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e78314bdc32fa80696f72fa16dc61168fda4d6a0c014e0380f9d02f0e5d8a07"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:96b2b3d1a83ad55310de8c7b4a2d04d9277d5591f40761274856635acc5fcb30"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:939578d9d8fd4299220161fdd76e86c6a251987476f5243e8864a7844476ba14"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e8ac75d72fa3775e0b7cb7e4629cec13b7514d928d15ef8ea06bca03ef01cae"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-win32.whl", hash = "sha256:6cf8fd4c04756b6b60146d98cd8a77d0cdae0e1ca20329da2ac85eed779b6849"}, + {file = "charset_normalizer-3.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:31a9a6f775f9bcd865d88ee350f0ffb0e25936a7f930ca98995c05abf1faf21c"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-win32.whl", hash = "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37"}, + {file = "charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-win32.whl", hash = "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce"}, + {file = "charset_normalizer-3.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce"}, + {file = "charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0f2be7e0cf7754b9a30eb01f4295cc3d4358a479843b31f328afd210e2c7598c"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c60e092517a73c632ec38e290eba714e9627abe9d301c8c8a12ec32c314a2a4b"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:252098c8c7a873e17dd696ed98bbe91dbacd571da4b87df3736768efa7a792e4"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3653fad4fe3ed447a596ae8638b437f827234f01a8cd801842e43f3d0a6b281b"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8999f965f922ae054125286faf9f11bc6932184b93011d138925a1773830bbe9"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d95bfb53c211b57198bb91c46dd5a2d8018b3af446583aab40074bf7988401cb"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:5b413b0b1bfd94dbf4023ad6945889f374cd24e3f62de58d6bb102c4d9ae534a"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:b5e3b2d152e74e100a9e9573837aba24aab611d39428ded46f4e4022ea7d1942"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:a2d08ac246bb48479170408d6c19f6385fa743e7157d716e144cad849b2dd94b"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-win32.whl", hash = "sha256:ec557499516fc90fd374bf2e32349a2887a876fbf162c160e3c01b6849eaf557"}, + {file = "charset_normalizer-3.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:5d8d01eac18c423815ed4f4a2ec3b439d654e55ee4ad610e153cf02faf67ea40"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:70bfc5f2c318afece2f5838ea5e4c3febada0be750fcf4775641052bbba14d05"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23b6b24d74478dc833444cbd927c338349d6ae852ba53a0d02a2de1fce45b96e"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:34a7f768e3f985abdb42841e20e17b330ad3aaf4bb7e7aeeb73db2e70f077b99"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb731e5deb0c7ef82d698b0f4c5bb724633ee2a489401594c5c88b02e6cb15f7"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:257f26fed7d7ff59921b78244f3cd93ed2af1800ff048c33f624c87475819dd7"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1ef99f0456d3d46a50945c98de1774da86f8e992ab5c77865ea8b8195341fc19"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:2c322db9c8c89009a990ef07c3bcc9f011a3269bc06782f916cd3d9eed7c9312"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:511729f456829ef86ac41ca78c63a5cb55240ed23b4b737faca0eb1abb1c41bc"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:88ab34806dea0671532d3f82d82b85e8fc23d7b2dd12fa837978dad9bb392a34"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-win32.whl", hash = "sha256:16a8770207946ac75703458e2c743631c79c59c5890c80011d536248f8eaa432"}, + {file = "charset_normalizer-3.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:d22dbedd33326a4a5190dd4fe9e9e693ef12160c77382d9e87919bce54f3d4ca"}, + {file = "charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a"}, + {file = "charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14"}, ] [[package]] name = "click" -version = "8.1.7" +version = "8.2.1" description = "Composable command line interface toolkit" optional = false -python-versions = ">=3.7" +python-versions = ">=3.10" files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, + {file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"}, + {file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"}, ] [package.dependencies] @@ -307,73 +297,99 @@ files = [ [[package]] name = "coverage" -version = "7.6.3" +version = "7.10.4" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.9" files = [ - {file = "coverage-7.6.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6da42bbcec130b188169107ecb6ee7bd7b4c849d24c9370a0c884cf728d8e976"}, - {file = "coverage-7.6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c222958f59b0ae091f4535851cbb24eb57fc0baea07ba675af718fb5302dddb2"}, - {file = "coverage-7.6.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab84a8b698ad5a6c365b08061920138e7a7dd9a04b6feb09ba1bfae68346ce6d"}, - {file = "coverage-7.6.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70a6756ce66cd6fe8486c775b30889f0dc4cb20c157aa8c35b45fd7868255c5c"}, - {file = "coverage-7.6.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c2e6fa98032fec8282f6b27e3f3986c6e05702828380618776ad794e938f53a"}, - {file = "coverage-7.6.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:921fbe13492caf6a69528f09d5d7c7d518c8d0e7b9f6701b7719715f29a71e6e"}, - {file = "coverage-7.6.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6d99198203f0b9cb0b5d1c0393859555bc26b548223a769baf7e321a627ed4fc"}, - {file = "coverage-7.6.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:87cd2e29067ea397a47e352efb13f976eb1b03e18c999270bb50589323294c6e"}, - {file = "coverage-7.6.3-cp310-cp310-win32.whl", hash = "sha256:a3328c3e64ea4ab12b85999eb0779e6139295bbf5485f69d42cf794309e3d007"}, - {file = "coverage-7.6.3-cp310-cp310-win_amd64.whl", hash = "sha256:bca4c8abc50d38f9773c1ec80d43f3768df2e8576807d1656016b9d3eeaa96fd"}, - {file = "coverage-7.6.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c51ef82302386d686feea1c44dbeef744585da16fcf97deea2a8d6c1556f519b"}, - {file = "coverage-7.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0ca37993206402c6c35dc717f90d4c8f53568a8b80f0bf1a1b2b334f4d488fba"}, - {file = "coverage-7.6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c77326300b839c44c3e5a8fe26c15b7e87b2f32dfd2fc9fee1d13604347c9b38"}, - {file = "coverage-7.6.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e484e479860e00da1f005cd19d1c5d4a813324e5951319ac3f3eefb497cc549"}, - {file = "coverage-7.6.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c6c0f4d53ef603397fc894a895b960ecd7d44c727df42a8d500031716d4e8d2"}, - {file = "coverage-7.6.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:37be7b5ea3ff5b7c4a9db16074dc94523b5f10dd1f3b362a827af66a55198175"}, - {file = "coverage-7.6.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:43b32a06c47539fe275106b376658638b418c7cfdfff0e0259fbf877e845f14b"}, - {file = "coverage-7.6.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ee77c7bef0724165e795b6b7bf9c4c22a9b8468a6bdb9c6b4281293c6b22a90f"}, - {file = "coverage-7.6.3-cp311-cp311-win32.whl", hash = "sha256:43517e1f6b19f610a93d8227e47790722c8bf7422e46b365e0469fc3d3563d97"}, - {file = "coverage-7.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:04f2189716e85ec9192df307f7c255f90e78b6e9863a03223c3b998d24a3c6c6"}, - {file = "coverage-7.6.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:27bd5f18d8f2879e45724b0ce74f61811639a846ff0e5c0395b7818fae87aec6"}, - {file = "coverage-7.6.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d546cfa78844b8b9c1c0533de1851569a13f87449897bbc95d698d1d3cb2a30f"}, - {file = "coverage-7.6.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9975442f2e7a5cfcf87299c26b5a45266ab0696348420049b9b94b2ad3d40234"}, - {file = "coverage-7.6.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:583049c63106c0555e3ae3931edab5669668bbef84c15861421b94e121878d3f"}, - {file = "coverage-7.6.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2341a78ae3a5ed454d524206a3fcb3cec408c2a0c7c2752cd78b606a2ff15af4"}, - {file = "coverage-7.6.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a4fb91d5f72b7e06a14ff4ae5be625a81cd7e5f869d7a54578fc271d08d58ae3"}, - {file = "coverage-7.6.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e279f3db904e3b55f520f11f983cc8dc8a4ce9b65f11692d4718ed021ec58b83"}, - {file = "coverage-7.6.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aa23ce39661a3e90eea5f99ec59b763b7d655c2cada10729ed920a38bfc2b167"}, - {file = "coverage-7.6.3-cp312-cp312-win32.whl", hash = "sha256:52ac29cc72ee7e25ace7807249638f94c9b6a862c56b1df015d2b2e388e51dbd"}, - {file = "coverage-7.6.3-cp312-cp312-win_amd64.whl", hash = "sha256:40e8b1983080439d4802d80b951f4a93d991ef3261f69e81095a66f86cf3c3c6"}, - {file = "coverage-7.6.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9134032f5aa445ae591c2ba6991d10136a1f533b1d2fa8f8c21126468c5025c6"}, - {file = "coverage-7.6.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:99670790f21a96665a35849990b1df447993880bb6463a0a1d757897f30da929"}, - {file = "coverage-7.6.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dc7d6b380ca76f5e817ac9eef0c3686e7834c8346bef30b041a4ad286449990"}, - {file = "coverage-7.6.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f7b26757b22faf88fcf232f5f0e62f6e0fd9e22a8a5d0d5016888cdfe1f6c1c4"}, - {file = "coverage-7.6.3-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c59d6a4a4633fad297f943c03d0d2569867bd5372eb5684befdff8df8522e39"}, - {file = "coverage-7.6.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f263b18692f8ed52c8de7f40a0751e79015983dbd77b16906e5b310a39d3ca21"}, - {file = "coverage-7.6.3-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:79644f68a6ff23b251cae1c82b01a0b51bc40c8468ca9585c6c4b1aeee570e0b"}, - {file = "coverage-7.6.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:71967c35828c9ff94e8c7d405469a1fb68257f686bca7c1ed85ed34e7c2529c4"}, - {file = "coverage-7.6.3-cp313-cp313-win32.whl", hash = "sha256:e266af4da2c1a4cbc6135a570c64577fd3e6eb204607eaff99d8e9b710003c6f"}, - {file = "coverage-7.6.3-cp313-cp313-win_amd64.whl", hash = "sha256:ea52bd218d4ba260399a8ae4bb6b577d82adfc4518b93566ce1fddd4a49d1dce"}, - {file = "coverage-7.6.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8d4c6ea0f498c7c79111033a290d060c517853a7bcb2f46516f591dab628ddd3"}, - {file = "coverage-7.6.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:331b200ad03dbaa44151d74daeb7da2cf382db424ab923574f6ecca7d3b30de3"}, - {file = "coverage-7.6.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54356a76b67cf8a3085818026bb556545ebb8353951923b88292556dfa9f812d"}, - {file = "coverage-7.6.3-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ebec65f5068e7df2d49466aab9128510c4867e532e07cb6960075b27658dca38"}, - {file = "coverage-7.6.3-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d33a785ea8354c480515e781554d3be582a86297e41ccbea627a5c632647f2cd"}, - {file = "coverage-7.6.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f7ddb920106bbbbcaf2a274d56f46956bf56ecbde210d88061824a95bdd94e92"}, - {file = "coverage-7.6.3-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:70d24936ca6c15a3bbc91ee9c7fc661132c6f4c9d42a23b31b6686c05073bde5"}, - {file = "coverage-7.6.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c30e42ea11badb147f0d2e387115b15e2bd8205a5ad70d6ad79cf37f6ac08c91"}, - {file = "coverage-7.6.3-cp313-cp313t-win32.whl", hash = "sha256:365defc257c687ce3e7d275f39738dcd230777424117a6c76043459db131dd43"}, - {file = "coverage-7.6.3-cp313-cp313t-win_amd64.whl", hash = "sha256:23bb63ae3f4c645d2d82fa22697364b0046fbafb6261b258a58587441c5f7bd0"}, - {file = "coverage-7.6.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:da29ceabe3025a1e5a5aeeb331c5b1af686daab4ff0fb4f83df18b1180ea83e2"}, - {file = "coverage-7.6.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:df8c05a0f574d480947cba11b947dc41b1265d721c3777881da2fb8d3a1ddfba"}, - {file = "coverage-7.6.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec1e3b40b82236d100d259854840555469fad4db64f669ab817279eb95cd535c"}, - {file = "coverage-7.6.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b4adeb878a374126f1e5cf03b87f66279f479e01af0e9a654cf6d1509af46c40"}, - {file = "coverage-7.6.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43d6a66e33b1455b98fc7312b124296dad97a2e191c80320587234a77b1b736e"}, - {file = "coverage-7.6.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1990b1f4e2c402beb317840030bb9f1b6a363f86e14e21b4212e618acdfce7f6"}, - {file = "coverage-7.6.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:12f9515d875859faedb4144fd38694a761cd2a61ef9603bf887b13956d0bbfbb"}, - {file = "coverage-7.6.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:99ded130555c021d99729fabd4ddb91a6f4cc0707df4b1daf912c7850c373b13"}, - {file = "coverage-7.6.3-cp39-cp39-win32.whl", hash = "sha256:c3a79f56dee9136084cf84a6c7c4341427ef36e05ae6415bf7d787c96ff5eaa3"}, - {file = "coverage-7.6.3-cp39-cp39-win_amd64.whl", hash = "sha256:aac7501ae73d4a02f4b7ac8fcb9dc55342ca98ffb9ed9f2dfb8a25d53eda0e4d"}, - {file = "coverage-7.6.3-pp39.pp310-none-any.whl", hash = "sha256:b9853509b4bf57ba7b1f99b9d866c422c9c5248799ab20e652bbb8a184a38181"}, - {file = "coverage-7.6.3.tar.gz", hash = "sha256:bb7d5fe92bd0dc235f63ebe9f8c6e0884f7360f88f3411bfed1350c872ef2054"}, + {file = "coverage-7.10.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d92d6edb0ccafd20c6fbf9891ca720b39c2a6a4b4a6f9cf323ca2c986f33e475"}, + {file = "coverage-7.10.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7202da14dc0236884fcc45665ffb2d79d4991a53fbdf152ab22f69f70923cc22"}, + {file = "coverage-7.10.4-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ada418633ae24ec8d0fcad5efe6fc7aa3c62497c6ed86589e57844ad04365674"}, + {file = "coverage-7.10.4-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b828e33eca6c3322adda3b5884456f98c435182a44917ded05005adfa1415500"}, + {file = "coverage-7.10.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:802793ba397afcfdbe9f91f89d65ae88b958d95edc8caf948e1f47d8b6b2b606"}, + {file = "coverage-7.10.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d0b23512338c54101d3bf7a1ab107d9d75abda1d5f69bc0887fd079253e4c27e"}, + {file = "coverage-7.10.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:f36b7dcf72d06a8c5e2dd3aca02be2b1b5db5f86404627dff834396efce958f2"}, + {file = "coverage-7.10.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fce316c367a1dc2c411821365592eeb335ff1781956d87a0410eae248188ba51"}, + {file = "coverage-7.10.4-cp310-cp310-win32.whl", hash = "sha256:8c5dab29fc8070b3766b5fc85f8d89b19634584429a2da6d42da5edfadaf32ae"}, + {file = "coverage-7.10.4-cp310-cp310-win_amd64.whl", hash = "sha256:4b0d114616f0fccb529a1817457d5fb52a10e106f86c5fb3b0bd0d45d0d69b93"}, + {file = "coverage-7.10.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:05d5f98ec893d4a2abc8bc5f046f2f4367404e7e5d5d18b83de8fde1093ebc4f"}, + {file = "coverage-7.10.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9267efd28f8994b750d171e58e481e3bbd69e44baed540e4c789f8e368b24b88"}, + {file = "coverage-7.10.4-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:4456a039fdc1a89ea60823d0330f1ac6f97b0dbe9e2b6fb4873e889584b085fb"}, + {file = "coverage-7.10.4-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c2bfbd2a9f7e68a21c5bd191be94bfdb2691ac40d325bac9ef3ae45ff5c753d9"}, + {file = "coverage-7.10.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ab7765f10ae1df7e7fe37de9e64b5a269b812ee22e2da3f84f97b1c7732a0d8"}, + {file = "coverage-7.10.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0a09b13695166236e171ec1627ff8434b9a9bae47528d0ba9d944c912d33b3d2"}, + {file = "coverage-7.10.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5c9e75dfdc0167d5675e9804f04a56b2cf47fb83a524654297000b578b8adcb7"}, + {file = "coverage-7.10.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c751261bfe6481caba15ec005a194cb60aad06f29235a74c24f18546d8377df0"}, + {file = "coverage-7.10.4-cp311-cp311-win32.whl", hash = "sha256:051c7c9e765f003c2ff6e8c81ccea28a70fb5b0142671e4e3ede7cebd45c80af"}, + {file = "coverage-7.10.4-cp311-cp311-win_amd64.whl", hash = "sha256:1a647b152f10be08fb771ae4a1421dbff66141e3d8ab27d543b5eb9ea5af8e52"}, + {file = "coverage-7.10.4-cp311-cp311-win_arm64.whl", hash = "sha256:b09b9e4e1de0d406ca9f19a371c2beefe3193b542f64a6dd40cfcf435b7d6aa0"}, + {file = "coverage-7.10.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a1f0264abcabd4853d4cb9b3d164adbf1565da7dab1da1669e93f3ea60162d79"}, + {file = "coverage-7.10.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:536cbe6b118a4df231b11af3e0f974a72a095182ff8ec5f4868c931e8043ef3e"}, + {file = "coverage-7.10.4-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:9a4c0d84134797b7bf3f080599d0cd501471f6c98b715405166860d79cfaa97e"}, + {file = "coverage-7.10.4-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:7c155fc0f9cee8c9803ea0ad153ab6a3b956baa5d4cd993405dc0b45b2a0b9e0"}, + {file = "coverage-7.10.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a5f2ab6e451d4b07855d8bcf063adf11e199bff421a4ba57f5bb95b7444ca62"}, + {file = "coverage-7.10.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:685b67d99b945b0c221be0780c336b303a7753b3e0ec0d618c795aada25d5e7a"}, + {file = "coverage-7.10.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0c079027e50c2ae44da51c2e294596cbc9dbb58f7ca45b30651c7e411060fc23"}, + {file = "coverage-7.10.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3749aa72b93ce516f77cf5034d8e3c0dfd45c6e8a163a602ede2dc5f9a0bb927"}, + {file = "coverage-7.10.4-cp312-cp312-win32.whl", hash = "sha256:fecb97b3a52fa9bcd5a7375e72fae209088faf671d39fae67261f37772d5559a"}, + {file = "coverage-7.10.4-cp312-cp312-win_amd64.whl", hash = "sha256:26de58f355626628a21fe6a70e1e1fad95702dafebfb0685280962ae1449f17b"}, + {file = "coverage-7.10.4-cp312-cp312-win_arm64.whl", hash = "sha256:67e8885408f8325198862bc487038a4980c9277d753cb8812510927f2176437a"}, + {file = "coverage-7.10.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2b8e1d2015d5dfdbf964ecef12944c0c8c55b885bb5c0467ae8ef55e0e151233"}, + {file = "coverage-7.10.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:25735c299439018d66eb2dccf54f625aceb78645687a05f9f848f6e6c751e169"}, + {file = "coverage-7.10.4-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:715c06cb5eceac4d9b7cdf783ce04aa495f6aff657543fea75c30215b28ddb74"}, + {file = "coverage-7.10.4-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:e017ac69fac9aacd7df6dc464c05833e834dc5b00c914d7af9a5249fcccf07ef"}, + {file = "coverage-7.10.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bad180cc40b3fccb0f0e8c702d781492654ac2580d468e3ffc8065e38c6c2408"}, + {file = "coverage-7.10.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:becbdcd14f685fada010a5f792bf0895675ecf7481304fe159f0cd3f289550bd"}, + {file = "coverage-7.10.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0b485ca21e16a76f68060911f97ebbe3e0d891da1dbbce6af7ca1ab3f98b9097"}, + {file = "coverage-7.10.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6c1d098ccfe8e1e0a1ed9a0249138899948afd2978cbf48eb1cc3fcd38469690"}, + {file = "coverage-7.10.4-cp313-cp313-win32.whl", hash = "sha256:8630f8af2ca84b5c367c3df907b1706621abe06d6929f5045fd628968d421e6e"}, + {file = "coverage-7.10.4-cp313-cp313-win_amd64.whl", hash = "sha256:f68835d31c421736be367d32f179e14ca932978293fe1b4c7a6a49b555dff5b2"}, + {file = "coverage-7.10.4-cp313-cp313-win_arm64.whl", hash = "sha256:6eaa61ff6724ca7ebc5326d1fae062d85e19b38dd922d50903702e6078370ae7"}, + {file = "coverage-7.10.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:702978108876bfb3d997604930b05fe769462cc3000150b0e607b7b444f2fd84"}, + {file = "coverage-7.10.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e8f978e8c5521d9c8f2086ac60d931d583fab0a16f382f6eb89453fe998e2484"}, + {file = "coverage-7.10.4-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:df0ac2ccfd19351411c45e43ab60932b74472e4648b0a9edf6a3b58846e246a9"}, + {file = "coverage-7.10.4-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:73a0d1aaaa3796179f336448e1576a3de6fc95ff4f07c2d7251d4caf5d18cf8d"}, + {file = "coverage-7.10.4-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:873da6d0ed6b3ffc0bc01f2c7e3ad7e2023751c0d8d86c26fe7322c314b031dc"}, + {file = "coverage-7.10.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:c6446c75b0e7dda5daa876a1c87b480b2b52affb972fedd6c22edf1aaf2e00ec"}, + {file = "coverage-7.10.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:6e73933e296634e520390c44758d553d3b573b321608118363e52113790633b9"}, + {file = "coverage-7.10.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:52073d4b08d2cb571234c8a71eb32af3c6923149cf644a51d5957ac128cf6aa4"}, + {file = "coverage-7.10.4-cp313-cp313t-win32.whl", hash = "sha256:e24afb178f21f9ceb1aefbc73eb524769aa9b504a42b26857243f881af56880c"}, + {file = "coverage-7.10.4-cp313-cp313t-win_amd64.whl", hash = "sha256:be04507ff1ad206f4be3d156a674e3fb84bbb751ea1b23b142979ac9eebaa15f"}, + {file = "coverage-7.10.4-cp313-cp313t-win_arm64.whl", hash = "sha256:f3e3ff3f69d02b5dad67a6eac68cc9c71ae343b6328aae96e914f9f2f23a22e2"}, + {file = "coverage-7.10.4-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:a59fe0af7dd7211ba595cf7e2867458381f7e5d7b4cffe46274e0b2f5b9f4eb4"}, + {file = "coverage-7.10.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3a6c35c5b70f569ee38dc3350cd14fdd0347a8b389a18bb37538cc43e6f730e6"}, + {file = "coverage-7.10.4-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:acb7baf49f513554c4af6ef8e2bd6e8ac74e6ea0c7386df8b3eb586d82ccccc4"}, + {file = "coverage-7.10.4-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a89afecec1ed12ac13ed203238b560cbfad3522bae37d91c102e690b8b1dc46c"}, + {file = "coverage-7.10.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:480442727f464407d8ade6e677b7f21f3b96a9838ab541b9a28ce9e44123c14e"}, + {file = "coverage-7.10.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a89bf193707f4a17f1ed461504031074d87f035153239f16ce86dfb8f8c7ac76"}, + {file = "coverage-7.10.4-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:3ddd912c2fc440f0fb3229e764feec85669d5d80a988ff1b336a27d73f63c818"}, + {file = "coverage-7.10.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:8a538944ee3a42265e61c7298aeba9ea43f31c01271cf028f437a7b4075592cf"}, + {file = "coverage-7.10.4-cp314-cp314-win32.whl", hash = "sha256:fd2e6002be1c62476eb862b8514b1ba7e7684c50165f2a8d389e77da6c9a2ebd"}, + {file = "coverage-7.10.4-cp314-cp314-win_amd64.whl", hash = "sha256:ec113277f2b5cf188d95fb66a65c7431f2b9192ee7e6ec9b72b30bbfb53c244a"}, + {file = "coverage-7.10.4-cp314-cp314-win_arm64.whl", hash = "sha256:9744954bfd387796c6a091b50d55ca7cac3d08767795b5eec69ad0f7dbf12d38"}, + {file = "coverage-7.10.4-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:5af4829904dda6aabb54a23879f0f4412094ba9ef153aaa464e3c1b1c9bc98e6"}, + {file = "coverage-7.10.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7bba5ed85e034831fac761ae506c0644d24fd5594727e174b5a73aff343a7508"}, + {file = "coverage-7.10.4-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d57d555b0719834b55ad35045de6cc80fc2b28e05adb6b03c98479f9553b387f"}, + {file = "coverage-7.10.4-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:ba62c51a72048bb1ea72db265e6bd8beaabf9809cd2125bbb5306c6ce105f214"}, + {file = "coverage-7.10.4-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0acf0c62a6095f07e9db4ec365cc58c0ef5babb757e54745a1aa2ea2a2564af1"}, + {file = "coverage-7.10.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e1033bf0f763f5cf49ffe6594314b11027dcc1073ac590b415ea93463466deec"}, + {file = "coverage-7.10.4-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:92c29eff894832b6a40da1789b1f252305af921750b03ee4535919db9179453d"}, + {file = "coverage-7.10.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:822c4c830989c2093527e92acd97be4638a44eb042b1bdc0e7a278d84a070bd3"}, + {file = "coverage-7.10.4-cp314-cp314t-win32.whl", hash = "sha256:e694d855dac2e7cf194ba33653e4ba7aad7267a802a7b3fc4347d0517d5d65cd"}, + {file = "coverage-7.10.4-cp314-cp314t-win_amd64.whl", hash = "sha256:efcc54b38ef7d5bfa98050f220b415bc5bb3d432bd6350a861cf6da0ede2cdcd"}, + {file = "coverage-7.10.4-cp314-cp314t-win_arm64.whl", hash = "sha256:6f3a3496c0fa26bfac4ebc458747b778cff201c8ae94fa05e1391bab0dbc473c"}, + {file = "coverage-7.10.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:48fd4d52600c2a9d5622e52dfae674a7845c5e1dceaf68b88c99feb511fbcfd6"}, + {file = "coverage-7.10.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:56217b470d09d69e6b7dcae38200f95e389a77db801cb129101697a4553b18b6"}, + {file = "coverage-7.10.4-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:44ac3f21a6e28c5ff7f7a47bca5f87885f6a1e623e637899125ba47acd87334d"}, + {file = "coverage-7.10.4-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3387739d72c84d17b4d2f7348749cac2e6700e7152026912b60998ee9a40066b"}, + {file = "coverage-7.10.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3f111ff20d9a6348e0125be892608e33408dd268f73b020940dfa8511ad05503"}, + {file = "coverage-7.10.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:01a852f0a9859734b018a3f483cc962d0b381d48d350b1a0c47d618c73a0c398"}, + {file = "coverage-7.10.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:225111dd06759ba4e37cee4c0b4f3df2b15c879e9e3c37bf986389300b9917c3"}, + {file = "coverage-7.10.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2178d4183bd1ba608f0bb12e71e55838ba1b7dbb730264f8b08de9f8ef0c27d0"}, + {file = "coverage-7.10.4-cp39-cp39-win32.whl", hash = "sha256:93d175fe81913aee7a6ea430abbdf2a79f1d9fd451610e12e334e4fe3264f563"}, + {file = "coverage-7.10.4-cp39-cp39-win_amd64.whl", hash = "sha256:2221a823404bb941c7721cf0ef55ac6ee5c25d905beb60c0bba5e5e85415d353"}, + {file = "coverage-7.10.4-py3-none-any.whl", hash = "sha256:065d75447228d05121e5c938ca8f0e91eed60a1eb2d1258d42d5084fecfc3302"}, + {file = "coverage-7.10.4.tar.gz", hash = "sha256:25f5130af6c8e7297fd14634955ba9e1697f47143f289e2a23284177c0061d27"}, ] [package.extras] @@ -425,27 +441,15 @@ sdist = ["setuptools-rust (>=0.11.4)"] ssh = ["bcrypt (>=3.1.5)"] test = ["hypothesis (>=1.11.4,!=3.79.2)", "iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pytz"] -[[package]] -name = "docutils" -version = "0.15.2" -description = "Docutils -- Python Documentation Utilities" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "docutils-0.15.2-py2-none-any.whl", hash = "sha256:9e4d7ecfc600058e07ba661411a2b7de2fd0fafa17d1a7f7361cd47b1175c827"}, - {file = "docutils-0.15.2-py3-none-any.whl", hash = "sha256:6c4f696463b79f1fb8ba0c594b63840ebd41f059e92b31957c46b74a4599b6d0"}, - {file = "docutils-0.15.2.tar.gz", hash = "sha256:a2aeea129088da402665e92e0b25b04b073c04b2dce4ab65caaa38b7ce2e1a99"}, -] - [[package]] name = "ecdsa" -version = "0.19.0" +version = "0.19.1" description = "ECDSA cryptographic signature library (pure python)" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.6" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.6" files = [ - {file = "ecdsa-0.19.0-py2.py3-none-any.whl", hash = "sha256:2cea9b88407fdac7bbeca0833b189e4c9c53f2ef1e1eaa29f6224dbc809b707a"}, - {file = "ecdsa-0.19.0.tar.gz", hash = "sha256:60eaad1199659900dd0af521ed462b793bbdf867432b3948e87416ae4caf6bf8"}, + {file = "ecdsa-0.19.1-py2.py3-none-any.whl", hash = "sha256:30638e27cf77b7e15c4c4cc1973720149e1033827cfd00661ca5c8cc0cdb24c3"}, + {file = "ecdsa-0.19.1.tar.gz", hash = "sha256:478cba7b62555866fcb3bb3fe985e06decbdb68ef55713c4e5ab98c57d508e61"}, ] [package.dependencies] @@ -457,20 +461,21 @@ gmpy2 = ["gmpy2"] [[package]] name = "flask" -version = "2.1.3" +version = "2.3.3" description = "A simple framework for building complex web applications." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "Flask-2.1.3-py3-none-any.whl", hash = "sha256:9013281a7402ad527f8fd56375164f3aa021ecfaff89bfe3825346c24f87e04c"}, - {file = "Flask-2.1.3.tar.gz", hash = "sha256:15972e5017df0575c3d6c090ba168b6db90259e620ac8d7ea813a396bad5b6cb"}, + {file = "flask-2.3.3-py3-none-any.whl", hash = "sha256:f69fcd559dc907ed196ab9df0e48471709175e696d6e698dd4dbe940f96ce66b"}, + {file = "flask-2.3.3.tar.gz", hash = "sha256:09c347a92aa7ff4a8e7f3206795f30d826654baf38b873d0744cd571ca609efc"}, ] [package.dependencies] -click = ">=8.0" -itsdangerous = ">=2.0" -Jinja2 = ">=3.0" -Werkzeug = ">=2.0" +blinker = ">=1.6.2" +click = ">=8.1.3" +itsdangerous = ">=2.1.2" +Jinja2 = ">=3.1.2" +Werkzeug = ">=2.3.7" [package.extras] async = ["asgiref (>=3.2)"] @@ -536,183 +541,151 @@ flask = "*" [[package]] name = "gevent" -version = "22.10.2" +version = "25.5.1" description = "Coroutine-based network library" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5" +python-versions = ">=3.9" files = [ - {file = "gevent-22.10.2-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:97cd42382421779f5d82ec5007199e8a84aa288114975429e4fd0a98f2290f10"}, - {file = "gevent-22.10.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:1e1286a76f15b5e15f1e898731d50529e249529095a032453f2c101af3fde71c"}, - {file = "gevent-22.10.2-cp27-cp27m-win32.whl", hash = "sha256:59b47e81b399d49a5622f0f503c59f1ce57b7705306ea0196818951dfc2f36c8"}, - {file = "gevent-22.10.2-cp27-cp27m-win_amd64.whl", hash = "sha256:1d543c9407a1e4bca11a8932916988cfb16de00366de5bf7bc9e7a3f61e60b18"}, - {file = "gevent-22.10.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4e2f008c82dc54ec94f4de12ca6feea60e419babb48ec145456907ae61625aa4"}, - {file = "gevent-22.10.2-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:990d7069f14dc40674e0d5cb43c68fd3bad8337048613b9bb94a0c4180ffc176"}, - {file = "gevent-22.10.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f23d0997149a816a2a9045af29c66f67f405a221745b34cefeac5769ed451db8"}, - {file = "gevent-22.10.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b43d500d7d3c0e03070dee813335bb5315215aa1cf6a04c61093dfdd718640b3"}, - {file = "gevent-22.10.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b68f4c9e20e47ad49fe797f37f91d5bbeace8765ce2707f979a8d4ec197e4d"}, - {file = "gevent-22.10.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1f001cac0ba8da76abfeb392a3057f81fab3d67cc916c7df8ea977a44a2cc989"}, - {file = "gevent-22.10.2-cp310-cp310-win_amd64.whl", hash = "sha256:3b7eae8a0653ba95a224faaddf629a913ace408edb67384d3117acf42d7dcf89"}, - {file = "gevent-22.10.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8f2477e7b0a903a01485c55bacf2089110e5f767014967ba4b287ff390ae2638"}, - {file = "gevent-22.10.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ddaa3e310a8f1a45b5c42cf50b54c31003a3028e7d4e085059090ea0e7a5fddd"}, - {file = "gevent-22.10.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98bc510e80f45486ef5b806a1c305e0e89f0430688c14984b0dbdec03331f48b"}, - {file = "gevent-22.10.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:877abdb3a669576b1d51ce6a49b7260b2a96f6b2424eb93287e779a3219d20ba"}, - {file = "gevent-22.10.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d21ad79cca234cdbfa249e727500b0ddcbc7adfff6614a96e6eaa49faca3e4f2"}, - {file = "gevent-22.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e955238f59b2947631c9782a713280dd75884e40e455313b5b6bbc20b92ff73"}, - {file = "gevent-22.10.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:5aa99e4882a9e909b4756ee799c6fa0f79eb0542779fad4cc60efa23ec1b2aa8"}, - {file = "gevent-22.10.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:d82081656a5b9a94d37c718c8646c757e1617e389cdc533ea5e6a6f0b8b78545"}, - {file = "gevent-22.10.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54f4bfd74c178351a4a05c5c7df6f8a0a279ff6f392b57608ce0e83c768207f9"}, - {file = "gevent-22.10.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ff3796692dff50fec2f381b9152438b221335f557c4f9b811f7ded51b7a25a1"}, - {file = "gevent-22.10.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f01c9adbcb605364694b11dcd0542ec468a29ac7aba2fb5665dc6caf17ba4d7e"}, - {file = "gevent-22.10.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:9d85574eb729f981fea9a78998725a06292d90a3ed50ddca74530c3148c0be41"}, - {file = "gevent-22.10.2-cp36-cp36m-win32.whl", hash = "sha256:8c192d2073e558e241f0b592c1e2b34127a4481a5be240cad4796533b88b1a98"}, - {file = "gevent-22.10.2-cp36-cp36m-win_amd64.whl", hash = "sha256:a2237451c721a0f874ef89dbb4af4fdc172b76a964befaa69deb15b8fff10f49"}, - {file = "gevent-22.10.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:53ee7f170ed42c7561fe8aff5d381dc9a4124694e70580d0c02fba6aafc0ea37"}, - {file = "gevent-22.10.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:96c56c280e3c43cfd075efd10b250350ed5ffd3c1514ec99a080b1b92d7c8374"}, - {file = "gevent-22.10.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b6c144e08dfad4106effc043a026e5d0c0eff6ad031904c70bf5090c63f3a6a7"}, - {file = "gevent-22.10.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:018f93de7d5318d2fb440f846839a4464738468c3476d5c9cf7da45bb71c18bd"}, - {file = "gevent-22.10.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7ed2346eb9dc4344f9cb0d7963ce5b74fe16fdd031a2809bb6c2b6eba7ebcd5"}, - {file = "gevent-22.10.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:84c517e33ed604fa06b7d756dc0171169cc12f7fdd68eb7b17708a62eebf4516"}, - {file = "gevent-22.10.2-cp37-cp37m-win32.whl", hash = "sha256:4114f0f439f0b547bb6f1d474fee99ddb46736944ad2207cef3771828f6aa358"}, - {file = "gevent-22.10.2-cp37-cp37m-win_amd64.whl", hash = "sha256:0d581f22a5be6281b11ad6309b38b18f0638cf896931223cbaa5adb904826ef6"}, - {file = "gevent-22.10.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2929377c8ebfb6f4d868d161cd8de2ea6b9f6c7a5fcd4f78bcd537319c16190b"}, - {file = "gevent-22.10.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:efc003b6c1481165af61f0aeac248e0a9ac8d880bb3acbe469b448674b2d5281"}, - {file = "gevent-22.10.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db562a8519838bddad0c439a2b12246bab539dd50e299ea7ff3644274a33b6a5"}, - {file = "gevent-22.10.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1472012493ca1fac103f700d309cb6ef7964dcdb9c788d1768266e77712f5e49"}, - {file = "gevent-22.10.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c04ee32c11e9fcee47c1b431834878dc987a7a2cc4fe126ddcae3bad723ce89"}, - {file = "gevent-22.10.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8729129edef2637a8084258cb9ec4e4d5ca45d97ac77aa7a6ff19ccb530ab731"}, - {file = "gevent-22.10.2-cp38-cp38-win32.whl", hash = "sha256:ae90226074a6089371a95f20288431cd4b3f6b0b096856afd862e4ac9510cddd"}, - {file = "gevent-22.10.2-cp38-cp38-win_amd64.whl", hash = "sha256:494c7f29e94df9a1c3157d67bb7edfa32a46eed786e04d9ee68d39f375e30001"}, - {file = "gevent-22.10.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:58898dbabb5b11e4d0192aae165ad286dc6742c543e1be9d30dc82753547c508"}, - {file = "gevent-22.10.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:4197d423e198265eef39a0dea286ef389da9148e070310f34455ecee8172c391"}, - {file = "gevent-22.10.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da4183f0b9d9a1e25e1758099220d32c51cc2c6340ee0dea3fd236b2b37598e4"}, - {file = "gevent-22.10.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5488eba6a568b4d23c072113da4fc0feb1b5f5ede7381656dc913e0d82204e2"}, - {file = "gevent-22.10.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:319d8b1699b7b8134de66d656cd739b308ab9c45ace14d60ae44de7775b456c9"}, - {file = "gevent-22.10.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f3329bedbba4d3146ae58c667e0f9ac1e6f1e1e6340c7593976cdc60aa7d1a47"}, - {file = "gevent-22.10.2-cp39-cp39-win32.whl", hash = "sha256:172caa66273315f283e90a315921902cb6549762bdcb0587fd60cb712a9d6263"}, - {file = "gevent-22.10.2-cp39-cp39-win_amd64.whl", hash = "sha256:323b207b281ba0405fea042067fa1a61662e5ac0d574ede4ebbda03efd20c350"}, - {file = "gevent-22.10.2-pp27-pypy_73-win_amd64.whl", hash = "sha256:ed7f16613eebf892a6a744d7a4a8f345bc6f066a0ff3b413e2479f9c0a180193"}, - {file = "gevent-22.10.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:a47a4e77e2bc668856aad92a0b8de7ee10768258d93cd03968e6c7ba2e832f76"}, - {file = "gevent-22.10.2.tar.gz", hash = "sha256:1ca01da176ee37b3527a2702f7d40dbc9ffb8cfc7be5a03bfa4f9eec45e55c46"}, + {file = "gevent-25.5.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8e5a0fab5e245b15ec1005b3666b0a2e867c26f411c8fe66ae1afe07174a30e9"}, + {file = "gevent-25.5.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7b80a37f2fb45ee4a8f7e64b77dd8a842d364384046e394227b974a4e9c9a52"}, + {file = "gevent-25.5.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29ab729d50ae85077a68e0385f129f5b01052d01a0ae6d7fdc1824f5337905e4"}, + {file = "gevent-25.5.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80d20592aeabcc4e294fd441fd43d45cb537437fd642c374ea9d964622fad229"}, + {file = "gevent-25.5.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8ba0257542ccbb72a8229dc34d00844ccdfba110417e4b7b34599548d0e20e9"}, + {file = "gevent-25.5.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cad0821dff998c7c60dd238f92cd61380342c47fb9e92e1a8705d9b5ac7c16e8"}, + {file = "gevent-25.5.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:017a7384c0cd1a5907751c991535a0699596e89725468a7fc39228312e10efa1"}, + {file = "gevent-25.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:469c86d02fccad7e2a3d82fe22237e47ecb376fbf4710bc18747b49c50716817"}, + {file = "gevent-25.5.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:12380aba5c316e9ff53cc21d8ab80f4a91c0df3ada58f65d4f5eb2cf693db00e"}, + {file = "gevent-25.5.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f0694daab1a041b69a53f53c2141c12994892b2503870515cabe6a5dbd2a928"}, + {file = "gevent-25.5.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2797885e9aeffdc98e1846723e5aa212e7ce53007dbef40d6fd2add264235c41"}, + {file = "gevent-25.5.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cde6aaac36b54332e10ea2a5bc0de6a8aba6c205c92603fe4396e3777c88e05d"}, + {file = "gevent-25.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24484f80f14befb8822bf29554cfb3a26a26cb69cd1e5a8be9e23b4bd7a96e25"}, + {file = "gevent-25.5.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8fdc7446895fa184890d8ca5ea61e502691114f9db55c9b76adc33f3086c4368"}, + {file = "gevent-25.5.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5b6106e2414b1797133786258fa1962a5e836480e4d5e861577f9fc63b673a5a"}, + {file = "gevent-25.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:bc899212d90f311784c58938a9c09c59802fb6dc287a35fabdc36d180f57f575"}, + {file = "gevent-25.5.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:d87c0a1bd809d8f70f96b9b229779ec6647339830b8888a192beed33ac8d129f"}, + {file = "gevent-25.5.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b87a4b66edb3808d4d07bbdb0deed5a710cf3d3c531e082759afd283758bb649"}, + {file = "gevent-25.5.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f076779050029a82feb0cb1462021d3404d22f80fa76a181b1a7889cd4d6b519"}, + {file = "gevent-25.5.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bb673eb291c19370f69295f7a881a536451408481e2e3deec3f41dedb7c281ec"}, + {file = "gevent-25.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1325ed44225c8309c0dd188bdbbbee79e1df8c11ceccac226b861c7d52e4837"}, + {file = "gevent-25.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:fcd5bcad3102bde686d0adcc341fade6245186050ce14386d547ccab4bd54310"}, + {file = "gevent-25.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1a93062609e8fa67ec97cd5fb9206886774b2a09b24887f40148c9c37e6fb71c"}, + {file = "gevent-25.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:2534c23dc32bed62b659ed4fd9e198906179e68b26c9276a897e04163bdde806"}, + {file = "gevent-25.5.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:a022a9de9275ce0b390b7315595454258c525dc8287a03f1a6cacc5878ab7cbc"}, + {file = "gevent-25.5.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fae8533f9d0ef3348a1f503edcfb531ef7a0236b57da1e24339aceb0ce52922"}, + {file = "gevent-25.5.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c7b32d9c3b5294b39ea9060e20c582e49e1ec81edbfeae6cf05f8ad0829cb13d"}, + {file = "gevent-25.5.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b95815fe44f318ebbfd733b6428b4cb18cc5e68f1c40e8501dd69cc1f42a83d"}, + {file = "gevent-25.5.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d316529b70d325b183b2f3f5cde958911ff7be12eb2b532b5c301f915dbbf1e"}, + {file = "gevent-25.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f6ba33c13db91ffdbb489a4f3d177a261ea1843923e1d68a5636c53fe98fa5ce"}, + {file = "gevent-25.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:37ee34b77c7553777c0b8379915f75934c3f9c8cd32f7cd098ea43c9323c2276"}, + {file = "gevent-25.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:9fa6aa0da224ed807d3b76cdb4ee8b54d4d4d5e018aed2478098e685baae7896"}, + {file = "gevent-25.5.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:0bacf89a65489d26c7087669af89938d5bfd9f7afb12a07b57855b9fad6ccbd0"}, + {file = "gevent-25.5.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e30169ef9cc0a57930bfd8fe14d86bc9d39fb96d278e3891e85cbe7b46058a97"}, + {file = "gevent-25.5.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e72ad5f8d9c92df017fb91a1f6a438cfb63b0eff4b40904ff81b40cb8150078c"}, + {file = "gevent-25.5.1-cp39-cp39-win32.whl", hash = "sha256:e5f358e81e27b1a7f2fb2f5219794e13ab5f59ce05571aa3877cfac63adb97db"}, + {file = "gevent-25.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:b83aff2441c7d4ee93e519989713b7c2607d4510abe990cd1d04f641bc6c03af"}, + {file = "gevent-25.5.1-pp310-pypy310_pp73-macosx_11_0_universal2.whl", hash = "sha256:60ad4ca9ca2c4cc8201b607c229cd17af749831e371d006d8a91303bb5568eb1"}, + {file = "gevent-25.5.1.tar.gz", hash = "sha256:582c948fa9a23188b890d0bc130734a506d039a2e5ad87dae276a456cc683e61"}, ] [package.dependencies] -cffi = {version = ">=1.12.2", markers = "platform_python_implementation == \"CPython\" and sys_platform == \"win32\""} -greenlet = {version = ">=2.0.0", markers = "platform_python_implementation == \"CPython\""} -setuptools = "*" +cffi = {version = ">=1.17.1", markers = "platform_python_implementation == \"CPython\" and sys_platform == \"win32\""} +greenlet = {version = ">=3.2.2", markers = "platform_python_implementation == \"CPython\""} "zope.event" = "*" "zope.interface" = "*" [package.extras] dnspython = ["dnspython (>=1.16.0,<2.0)", "idna"] -docs = ["repoze.sphinx.autointerface", "sphinxcontrib-programoutput", "zope.schema"] +docs = ["furo", "repoze.sphinx.autointerface", "sphinx", "sphinxcontrib-programoutput", "zope.schema"] monitor = ["psutil (>=5.7.0)"] -recommended = ["backports.socketpair", "cffi (>=1.12.2)", "dnspython (>=1.16.0,<2.0)", "idna", "psutil (>=5.7.0)", "selectors2"] -test = ["backports.socketpair", "cffi (>=1.12.2)", "contextvars (==2.4)", "coverage (>=5.0)", "coveralls (>=1.7.0)", "dnspython (>=1.16.0,<2.0)", "futures", "idna", "mock", "objgraph", "psutil (>=5.7.0)", "requests", "selectors2"] +recommended = ["cffi (>=1.17.1)", "dnspython (>=1.16.0,<2.0)", "idna", "psutil (>=5.7.0)"] +test = ["cffi (>=1.17.1)", "coverage (>=5.0)", "dnspython (>=1.16.0,<2.0)", "idna", "objgraph", "psutil (>=5.7.0)", "requests"] [[package]] name = "greenlet" -version = "3.1.1" +version = "3.2.4" description = "Lightweight in-process concurrent programming" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ - {file = "greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563"}, - {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83"}, - {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0"}, - {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120"}, - {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc"}, - {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617"}, - {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7"}, - {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6"}, - {file = "greenlet-3.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80"}, - {file = "greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70"}, - {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159"}, - {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e"}, - {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1"}, - {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383"}, - {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a"}, - {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511"}, - {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395"}, - {file = "greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39"}, - {file = "greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d"}, - {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79"}, - {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa"}, - {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441"}, - {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36"}, - {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9"}, - {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0"}, - {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942"}, - {file = "greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01"}, - {file = "greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1"}, - {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff"}, - {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a"}, - {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e"}, - {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4"}, - {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e"}, - {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1"}, - {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c"}, - {file = "greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761"}, - {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011"}, - {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13"}, - {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475"}, - {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b"}, - {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822"}, - {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01"}, - {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6"}, - {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47da355d8687fd65240c364c90a31569a133b7b60de111c255ef5b606f2ae291"}, - {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98884ecf2ffb7d7fe6bd517e8eb99d31ff7855a840fa6d0d63cd07c037f6a981"}, - {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1d4aeb8891338e60d1ab6127af1fe45def5259def8094b9c7e34690c8858803"}, - {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db32b5348615a04b82240cc67983cb315309e88d444a288934ee6ceaebcad6cc"}, - {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dcc62f31eae24de7f8dce72134c8651c58000d3b1868e01392baea7c32c247de"}, - {file = "greenlet-3.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1d3755bcb2e02de341c55b4fca7a745a24a9e7212ac953f6b3a48d117d7257aa"}, - {file = "greenlet-3.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b8da394b34370874b4572676f36acabac172602abf054cbc4ac910219f3340af"}, - {file = "greenlet-3.1.1-cp37-cp37m-win32.whl", hash = "sha256:a0dfc6c143b519113354e780a50381508139b07d2177cb6ad6a08278ec655798"}, - {file = "greenlet-3.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:54558ea205654b50c438029505def3834e80f0869a70fb15b871c29b4575ddef"}, - {file = "greenlet-3.1.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:346bed03fe47414091be4ad44786d1bd8bef0c3fcad6ed3dee074a032ab408a9"}, - {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfc59d69fc48664bc693842bd57acfdd490acafda1ab52c7836e3fc75c90a111"}, - {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21e10da6ec19b457b82636209cbe2331ff4306b54d06fa04b7c138ba18c8a81"}, - {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37b9de5a96111fc15418819ab4c4432e4f3c2ede61e660b1e33971eba26ef9ba"}, - {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ef9ea3f137e5711f0dbe5f9263e8c009b7069d8a1acea822bd5e9dae0ae49c8"}, - {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85f3ff71e2e60bd4b4932a043fbbe0f499e263c628390b285cb599154a3b03b1"}, - {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:95ffcf719966dd7c453f908e208e14cde192e09fde6c7186c8f1896ef778d8cd"}, - {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:03a088b9de532cbfe2ba2034b2b85e82df37874681e8c470d6fb2f8c04d7e4b7"}, - {file = "greenlet-3.1.1-cp38-cp38-win32.whl", hash = "sha256:8b8b36671f10ba80e159378df9c4f15c14098c4fd73a36b9ad715f057272fbef"}, - {file = "greenlet-3.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:7017b2be767b9d43cc31416aba48aab0d2309ee31b4dbf10a1d38fb7972bdf9d"}, - {file = "greenlet-3.1.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:396979749bd95f018296af156201d6211240e7a23090f50a8d5d18c370084dc3"}, - {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca9d0ff5ad43e785350894d97e13633a66e2b50000e8a183a50a88d834752d42"}, - {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f6ff3b14f2df4c41660a7dec01045a045653998784bf8cfcb5a525bdffffbc8f"}, - {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94ebba31df2aa506d7b14866fed00ac141a867e63143fe5bca82a8e503b36437"}, - {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73aaad12ac0ff500f62cebed98d8789198ea0e6f233421059fa68a5aa7220145"}, - {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63e4844797b975b9af3a3fb8f7866ff08775f5426925e1e0bbcfe7932059a12c"}, - {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7939aa3ca7d2a1593596e7ac6d59391ff30281ef280d8632fa03d81f7c5f955e"}, - {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d0028e725ee18175c6e422797c407874da24381ce0690d6b9396c204c7f7276e"}, - {file = "greenlet-3.1.1-cp39-cp39-win32.whl", hash = "sha256:5e06afd14cbaf9e00899fae69b24a32f2196c19de08fcb9f4779dd4f004e5e7c"}, - {file = "greenlet-3.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:3319aa75e0e0639bc15ff54ca327e8dc7a6fe404003496e3c6925cd3142e0e22"}, - {file = "greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467"}, + {file = "greenlet-3.2.4-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8c68325b0d0acf8d91dde4e6f930967dd52a5302cd4062932a6b2e7c2969f47c"}, + {file = "greenlet-3.2.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:94385f101946790ae13da500603491f04a76b6e4c059dab271b3ce2e283b2590"}, + {file = "greenlet-3.2.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f10fd42b5ee276335863712fa3da6608e93f70629c631bf77145021600abc23c"}, + {file = "greenlet-3.2.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c8c9e331e58180d0d83c5b7999255721b725913ff6bc6cf39fa2a45841a4fd4b"}, + {file = "greenlet-3.2.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58b97143c9cc7b86fc458f215bd0932f1757ce649e05b640fea2e79b54cedb31"}, + {file = "greenlet-3.2.4-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c2ca18a03a8cfb5b25bc1cbe20f3d9a4c80d8c3b13ba3df49ac3961af0b1018d"}, + {file = "greenlet-3.2.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9fe0a28a7b952a21e2c062cd5756d34354117796c6d9215a87f55e38d15402c5"}, + {file = "greenlet-3.2.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8854167e06950ca75b898b104b63cc646573aa5fef1353d4508ecdd1ee76254f"}, + {file = "greenlet-3.2.4-cp310-cp310-win_amd64.whl", hash = "sha256:73f49b5368b5359d04e18d15828eecc1806033db5233397748f4ca813ff1056c"}, + {file = "greenlet-3.2.4-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:96378df1de302bc38e99c3a9aa311967b7dc80ced1dcc6f171e99842987882a2"}, + {file = "greenlet-3.2.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1ee8fae0519a337f2329cb78bd7a8e128ec0f881073d43f023c7b8d4831d5246"}, + {file = "greenlet-3.2.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:94abf90142c2a18151632371140b3dba4dee031633fe614cb592dbb6c9e17bc3"}, + {file = "greenlet-3.2.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:4d1378601b85e2e5171b99be8d2dc85f594c79967599328f95c1dc1a40f1c633"}, + {file = "greenlet-3.2.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0db5594dce18db94f7d1650d7489909b57afde4c580806b8d9203b6e79cdc079"}, + {file = "greenlet-3.2.4-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2523e5246274f54fdadbce8494458a2ebdcdbc7b802318466ac5606d3cded1f8"}, + {file = "greenlet-3.2.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1987de92fec508535687fb807a5cea1560f6196285a4cde35c100b8cd632cc52"}, + {file = "greenlet-3.2.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:55e9c5affaa6775e2c6b67659f3a71684de4c549b3dd9afca3bc773533d284fa"}, + {file = "greenlet-3.2.4-cp311-cp311-win_amd64.whl", hash = "sha256:9c40adce87eaa9ddb593ccb0fa6a07caf34015a29bf8d344811665b573138db9"}, + {file = "greenlet-3.2.4-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3b67ca49f54cede0186854a008109d6ee71f66bd57bb36abd6d0a0267b540cdd"}, + {file = "greenlet-3.2.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ddf9164e7a5b08e9d22511526865780a576f19ddd00d62f8a665949327fde8bb"}, + {file = "greenlet-3.2.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f28588772bb5fb869a8eb331374ec06f24a83a9c25bfa1f38b6993afe9c1e968"}, + {file = "greenlet-3.2.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:5c9320971821a7cb77cfab8d956fa8e39cd07ca44b6070db358ceb7f8797c8c9"}, + {file = "greenlet-3.2.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c60a6d84229b271d44b70fb6e5fa23781abb5d742af7b808ae3f6efd7c9c60f6"}, + {file = "greenlet-3.2.4-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b3812d8d0c9579967815af437d96623f45c0f2ae5f04e366de62a12d83a8fb0"}, + {file = "greenlet-3.2.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:abbf57b5a870d30c4675928c37278493044d7c14378350b3aa5d484fa65575f0"}, + {file = "greenlet-3.2.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:20fb936b4652b6e307b8f347665e2c615540d4b42b3b4c8a321d8286da7e520f"}, + {file = "greenlet-3.2.4-cp312-cp312-win_amd64.whl", hash = "sha256:a7d4e128405eea3814a12cc2605e0e6aedb4035bf32697f72deca74de4105e02"}, + {file = "greenlet-3.2.4-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:1a921e542453fe531144e91e1feedf12e07351b1cf6c9e8a3325ea600a715a31"}, + {file = "greenlet-3.2.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd3c8e693bff0fff6ba55f140bf390fa92c994083f838fece0f63be121334945"}, + {file = "greenlet-3.2.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:710638eb93b1fa52823aa91bf75326f9ecdfd5e0466f00789246a5280f4ba0fc"}, + {file = "greenlet-3.2.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c5111ccdc9c88f423426df3fd1811bfc40ed66264d35aa373420a34377efc98a"}, + {file = "greenlet-3.2.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d76383238584e9711e20ebe14db6c88ddcedc1829a9ad31a584389463b5aa504"}, + {file = "greenlet-3.2.4-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23768528f2911bcd7e475210822ffb5254ed10d71f4028387e5a99b4c6699671"}, + {file = "greenlet-3.2.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:00fadb3fedccc447f517ee0d3fd8fe49eae949e1cd0f6a611818f4f6fb7dc83b"}, + {file = "greenlet-3.2.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:d25c5091190f2dc0eaa3f950252122edbbadbb682aa7b1ef2f8af0f8c0afefae"}, + {file = "greenlet-3.2.4-cp313-cp313-win_amd64.whl", hash = "sha256:554b03b6e73aaabec3745364d6239e9e012d64c68ccd0b8430c64ccc14939a8b"}, + {file = "greenlet-3.2.4-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:49a30d5fda2507ae77be16479bdb62a660fa51b1eb4928b524975b3bde77b3c0"}, + {file = "greenlet-3.2.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:299fd615cd8fc86267b47597123e3f43ad79c9d8a22bebdce535e53550763e2f"}, + {file = "greenlet-3.2.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:c17b6b34111ea72fc5a4e4beec9711d2226285f0386ea83477cbb97c30a3f3a5"}, + {file = "greenlet-3.2.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b4a1870c51720687af7fa3e7cda6d08d801dae660f75a76f3845b642b4da6ee1"}, + {file = "greenlet-3.2.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:061dc4cf2c34852b052a8620d40f36324554bc192be474b9e9770e8c042fd735"}, + {file = "greenlet-3.2.4-cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44358b9bf66c8576a9f57a590d5f5d6e72fa4228b763d0e43fee6d3b06d3a337"}, + {file = "greenlet-3.2.4-cp314-cp314-win_amd64.whl", hash = "sha256:e37ab26028f12dbb0ff65f29a8d3d44a765c61e729647bf2ddfbbed621726f01"}, + {file = "greenlet-3.2.4-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:b6a7c19cf0d2742d0809a4c05975db036fdff50cd294a93632d6a310bf9ac02c"}, + {file = "greenlet-3.2.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:27890167f55d2387576d1f41d9487ef171849ea0359ce1510ca6e06c8bece11d"}, + {file = "greenlet-3.2.4-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:18d9260df2b5fbf41ae5139e1be4e796d99655f023a636cd0e11e6406cca7d58"}, + {file = "greenlet-3.2.4-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:671df96c1f23c4a0d4077a325483c1503c96a1b7d9db26592ae770daa41233d4"}, + {file = "greenlet-3.2.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:16458c245a38991aa19676900d48bd1a6f2ce3e16595051a4db9d012154e8433"}, + {file = "greenlet-3.2.4-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9913f1a30e4526f432991f89ae263459b1c64d1608c0d22a5c79c287b3c70df"}, + {file = "greenlet-3.2.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b90654e092f928f110e0007f572007c9727b5265f7632c2fa7415b4689351594"}, + {file = "greenlet-3.2.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:81701fd84f26330f0d5f4944d4e92e61afe6319dcd9775e39396e39d7c3e5f98"}, + {file = "greenlet-3.2.4-cp39-cp39-win32.whl", hash = "sha256:65458b409c1ed459ea899e939f0e1cdb14f58dbc803f2f93c5eab5694d32671b"}, + {file = "greenlet-3.2.4-cp39-cp39-win_amd64.whl", hash = "sha256:d2e685ade4dafd447ede19c31277a224a239a0a1a4eca4e6390efedf20260cfb"}, + {file = "greenlet-3.2.4.tar.gz", hash = "sha256:0dca0d95ff849f9a364385f36ab49f50065d76964944638be9691e1832e9f86d"}, ] [package.extras] docs = ["Sphinx", "furo"] -test = ["objgraph", "psutil"] +test = ["objgraph", "psutil", "setuptools"] [[package]] name = "gunicorn" -version = "20.1.0" +version = "23.0.0" description = "WSGI HTTP Server for UNIX" optional = false -python-versions = ">=3.5" +python-versions = ">=3.7" files = [ - {file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"}, - {file = "gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"}, + {file = "gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d"}, + {file = "gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec"}, ] [package.dependencies] -setuptools = ">=3.0" +packaging = "*" [package.extras] -eventlet = ["eventlet (>=0.24.1)"] +eventlet = ["eventlet (>=0.24.1,!=0.36.0)"] gevent = ["gevent (>=1.4.0)"] setproctitle = ["setproctitle"] +testing = ["coverage", "eventlet", "gevent", "pytest", "pytest-cov"] tornado = ["tornado (>=0.2)"] [[package]] @@ -731,28 +704,29 @@ all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2 [[package]] name = "iniconfig" -version = "2.0.0" +version = "2.1.0" description = "brain-dead simple config-ini parsing" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, + {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, + {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, ] [[package]] name = "isort" -version = "5.13.2" +version = "6.0.1" description = "A Python utility / library to sort Python imports." optional = false -python-versions = ">=3.8.0" +python-versions = ">=3.9.0" files = [ - {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, - {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, + {file = "isort-6.0.1-py3-none-any.whl", hash = "sha256:2dc5d7f65c9678d94c88dfc29161a320eec67328bc97aad576874cb4be1e9615"}, + {file = "isort-6.0.1.tar.gz", hash = "sha256:1cb5df28dfbc742e490c5e41bad6da41b805b0a8be7bc93cd0fb2a8a890ac450"}, ] [package.extras] -colors = ["colorama (>=0.4.6)"] +colors = ["colorama"] +plugins = ["setuptools"] [[package]] name = "itsdangerous" @@ -767,13 +741,13 @@ files = [ [[package]] name = "jinja2" -version = "3.0.3" +version = "3.1.6" description = "A very fast and expressive template engine." optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "Jinja2-3.0.3-py3-none-any.whl", hash = "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8"}, - {file = "Jinja2-3.0.3.tar.gz", hash = "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"}, + {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, + {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, ] [package.dependencies] @@ -784,83 +758,83 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "jmespath" -version = "0.10.0" +version = "1.0.1" description = "JSON Matching Expressions" optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +python-versions = ">=3.7" files = [ - {file = "jmespath-0.10.0-py2.py3-none-any.whl", hash = "sha256:cdf6525904cc597730141d61b36f2e4b8ecc257c420fa2f4549bac2c2d0cb72f"}, - {file = "jmespath-0.10.0.tar.gz", hash = "sha256:b85d0567b8666149a93172712e68920734333c0ce7e89b78b3e987f71e5ed4f9"}, + {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"}, + {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, ] [[package]] name = "markupsafe" -version = "3.0.1" +version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" files = [ - {file = "MarkupSafe-3.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:db842712984e91707437461930e6011e60b39136c7331e971952bb30465bc1a1"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3ffb4a8e7d46ed96ae48805746755fadd0909fea2306f93d5d8233ba23dda12a"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67c519635a4f64e495c50e3107d9b4075aec33634272b5db1cde839e07367589"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48488d999ed50ba8d38c581d67e496f955821dc183883550a6fbc7f1aefdc170"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f31ae06f1328595d762c9a2bf29dafd8621c7d3adc130cbb46278079758779ca"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:80fcbf3add8790caddfab6764bde258b5d09aefbe9169c183f88a7410f0f6dea"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3341c043c37d78cc5ae6e3e305e988532b072329639007fd408a476642a89fd6"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cb53e2a99df28eee3b5f4fea166020d3ef9116fdc5764bc5117486e6d1211b25"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-win32.whl", hash = "sha256:db15ce28e1e127a0013dfb8ac243a8e392db8c61eae113337536edb28bdc1f97"}, - {file = "MarkupSafe-3.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:4ffaaac913c3f7345579db4f33b0020db693f302ca5137f106060316761beea9"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:26627785a54a947f6d7336ce5963569b5d75614619e75193bdb4e06e21d447ad"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b954093679d5750495725ea6f88409946d69cfb25ea7b4c846eef5044194f583"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:973a371a55ce9ed333a3a0f8e0bcfae9e0d637711534bcb11e130af2ab9334e7"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:244dbe463d5fb6d7ce161301a03a6fe744dac9072328ba9fc82289238582697b"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d98e66a24497637dd31ccab090b34392dddb1f2f811c4b4cd80c230205c074a3"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ad91738f14eb8da0ff82f2acd0098b6257621410dcbd4df20aaa5b4233d75a50"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7044312a928a66a4c2a22644147bc61a199c1709712069a344a3fb5cfcf16915"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a4792d3b3a6dfafefdf8e937f14906a51bd27025a36f4b188728a73382231d91"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-win32.whl", hash = "sha256:fa7d686ed9883f3d664d39d5a8e74d3c5f63e603c2e3ff0abcba23eac6542635"}, - {file = "MarkupSafe-3.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:9ba25a71ebf05b9bb0e2ae99f8bc08a07ee8e98c612175087112656ca0f5c8bf"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8ae369e84466aa70f3154ee23c1451fda10a8ee1b63923ce76667e3077f2b0c4"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40f1e10d51c92859765522cbd79c5c8989f40f0419614bcdc5015e7b6bf97fc5"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a4cb365cb49b750bdb60b846b0c0bc49ed62e59a76635095a179d440540c346"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee3941769bd2522fe39222206f6dd97ae83c442a94c90f2b7a25d847d40f4729"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62fada2c942702ef8952754abfc1a9f7658a4d5460fabe95ac7ec2cbe0d02abc"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4c2d64fdba74ad16138300815cfdc6ab2f4647e23ced81f59e940d7d4a1469d9"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:fb532dd9900381d2e8f48172ddc5a59db4c445a11b9fab40b3b786da40d3b56b"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0f84af7e813784feb4d5e4ff7db633aba6c8ca64a833f61d8e4eade234ef0c38"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-win32.whl", hash = "sha256:cbf445eb5628981a80f54087f9acdbf84f9b7d862756110d172993b9a5ae81aa"}, - {file = "MarkupSafe-3.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:a10860e00ded1dd0a65b83e717af28845bb7bd16d8ace40fe5531491de76b79f"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e81c52638315ff4ac1b533d427f50bc0afc746deb949210bc85f05d4f15fd772"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:312387403cd40699ab91d50735ea7a507b788091c416dd007eac54434aee51da"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ae99f31f47d849758a687102afdd05bd3d3ff7dbab0a8f1587981b58a76152a"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c97ff7fedf56d86bae92fa0a646ce1a0ec7509a7578e1ed238731ba13aabcd1c"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7420ceda262dbb4b8d839a4ec63d61c261e4e77677ed7c66c99f4e7cb5030dd"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:45d42d132cff577c92bfba536aefcfea7e26efb975bd455db4e6602f5c9f45e7"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4c8817557d0de9349109acb38b9dd570b03cc5014e8aabf1cbddc6e81005becd"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a54c43d3ec4cf2a39f4387ad044221c66a376e58c0d0e971d47c475ba79c6b5"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-win32.whl", hash = "sha256:c91b394f7601438ff79a4b93d16be92f216adb57d813a78be4446fe0f6bc2d8c"}, - {file = "MarkupSafe-3.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:fe32482b37b4b00c7a52a07211b479653b7fe4f22b2e481b9a9b099d8a430f2f"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:17b2aea42a7280db02ac644db1d634ad47dcc96faf38ab304fe26ba2680d359a"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:852dc840f6d7c985603e60b5deaae1d89c56cb038b577f6b5b8c808c97580f1d"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0778de17cff1acaeccc3ff30cd99a3fd5c50fc58ad3d6c0e0c4c58092b859396"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:800100d45176652ded796134277ecb13640c1a537cad3b8b53da45aa96330453"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d06b24c686a34c86c8c1fba923181eae6b10565e4d80bdd7bc1c8e2f11247aa4"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:33d1c36b90e570ba7785dacd1faaf091203d9942bc036118fab8110a401eb1a8"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:beeebf760a9c1f4c07ef6a53465e8cfa776ea6a2021eda0d0417ec41043fe984"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:bbde71a705f8e9e4c3e9e33db69341d040c827c7afa6789b14c6e16776074f5a"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-win32.whl", hash = "sha256:82b5dba6eb1bcc29cc305a18a3c5365d2af06ee71b123216416f7e20d2a84e5b"}, - {file = "MarkupSafe-3.0.1-cp313-cp313t-win_amd64.whl", hash = "sha256:730d86af59e0e43ce277bb83970530dd223bf7f2a838e086b50affa6ec5f9295"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4935dd7883f1d50e2ffecca0aa33dc1946a94c8f3fdafb8df5c330e48f71b132"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e9393357f19954248b00bed7c56f29a25c930593a77630c719653d51e7669c2a"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40621d60d0e58aa573b68ac5e2d6b20d44392878e0bfc159012a5787c4e35bc8"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f94190df587738280d544971500b9cafc9b950d32efcb1fba9ac10d84e6aa4e6"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6a387d61fe41cdf7ea95b38e9af11cfb1a63499af2759444b99185c4ab33f5b"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8ad4ad1429cd4f315f32ef263c1342166695fad76c100c5d979c45d5570ed58b"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e24bfe89c6ac4c31792793ad9f861b8f6dc4546ac6dc8f1c9083c7c4f2b335cd"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:2a4b34a8d14649315c4bc26bbfa352663eb51d146e35eef231dd739d54a5430a"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-win32.whl", hash = "sha256:242d6860f1fd9191aef5fae22b51c5c19767f93fb9ead4d21924e0bcb17619d8"}, - {file = "MarkupSafe-3.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:93e8248d650e7e9d49e8251f883eed60ecbc0e8ffd6349e18550925e31bd029b"}, - {file = "markupsafe-3.0.1.tar.gz", hash = "sha256:3e683ee4f5d0fa2dde4db77ed8dd8a876686e3fc417655c2ece9a90576905344"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, + {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, ] [[package]] @@ -894,29 +868,29 @@ yaml = ["PyYAML (>=5.1.0)"] [[package]] name = "packaging" -version = "24.1" +version = "25.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, - {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, + {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, + {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, ] [[package]] name = "pluggy" -version = "1.5.0" +version = "1.6.0" description = "plugin and hook calling mechanisms for python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, - {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, ] [package.extras] dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] +testing = ["coverage", "pytest", "pytest-benchmark"] [[package]] name = "ply" @@ -961,15 +935,29 @@ files = [ {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, ] +[[package]] +name = "pygments" +version = "2.19.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, + {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + [[package]] name = "pyjwt" -version = "2.9.0" +version = "2.10.1" description = "JSON Web Token implementation in Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "PyJWT-2.9.0-py3-none-any.whl", hash = "sha256:3b02fb0f44517787776cf48f2ae25d8e14f300e6d7545a4315cee571a415e850"}, - {file = "pyjwt-2.9.0.tar.gz", hash = "sha256:7e1e5b56cc735432a7369cbfa0efe50fa113ebecdc04ae6922deba8b84582d0c"}, + {file = "PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb"}, + {file = "pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953"}, ] [package.extras] @@ -998,23 +986,24 @@ six = "*" [[package]] name = "pytest" -version = "8.3.3" +version = "8.4.1" description = "pytest: simple powerful testing with Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2"}, - {file = "pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181"}, + {file = "pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7"}, + {file = "pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c"}, ] [package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -iniconfig = "*" -packaging = "*" +colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} +iniconfig = ">=1" +packaging = ">=20" pluggy = ">=1.5,<2" +pygments = ">=2.7.2" [package.extras] -dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-cov" @@ -1036,13 +1025,13 @@ testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] [[package]] name = "pytest-mock" -version = "3.14.0" +version = "3.14.1" description = "Thin-wrapper around the mock package for easier use with pytest" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, - {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, + {file = "pytest_mock-3.14.1-py3-none-any.whl", hash = "sha256:178aefcd11307d874b4cd3100344e7e2d888d9791a6a1d9bfe90fbc1b74fd1d0"}, + {file = "pytest_mock-3.14.1.tar.gz", hash = "sha256:159e9edac4c451ce77a5cdb9fc5d1100708d2dd4ba3c3df572f14097351af80e"}, ] [package.dependencies] @@ -1081,38 +1070,44 @@ cli = ["click (>=5.0)"] [[package]] name = "python-jose" -version = "3.3.0" +version = "3.5.0" description = "JOSE implementation in Python" optional = false -python-versions = "*" +python-versions = ">=3.9" files = [ - {file = "python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a"}, - {file = "python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"}, + {file = "python_jose-3.5.0-py2.py3-none-any.whl", hash = "sha256:abd1202f23d34dfad2c3d28cb8617b90acf34132c7afd60abd0b0b7d3cb55771"}, + {file = "python_jose-3.5.0.tar.gz", hash = "sha256:fb4eaa44dbeb1c26dcc69e4bd7ec54a1cb8dd64d3b4d81ef08d90ff453f2b01b"}, ] [package.dependencies] ecdsa = "!=0.15" -pyasn1 = "*" -rsa = "*" +pyasn1 = ">=0.5.0" +rsa = ">=4.0,<4.1.1 || >4.1.1,<4.4 || >4.4,<5.0" [package.extras] cryptography = ["cryptography (>=3.4.0)"] -pycrypto = ["pyasn1", "pycrypto (>=2.6.0,<2.7.0)"] -pycryptodome = ["pyasn1", "pycryptodome (>=3.3.1,<4.0.0)"] +pycrypto = ["pycrypto (>=2.6.0,<2.7.0)"] +pycryptodome = ["pycryptodome (>=3.3.1,<4.0.0)"] +test = ["pytest", "pytest-cov"] [[package]] name = "python-keycloak" -version = "0.22.0" +version = "0.29.1" description = "python-keycloak is a Python package providing access to the Keycloak API." optional = false -python-versions = "*" +python-versions = ">=3.7" files = [ - {file = "python-keycloak-0.22.0.tar.gz", hash = "sha256:8f2ea277c94cee1de108fd6a018d52d23a603dea6bfb7cdd8eeeb09963bbdade"}, + {file = "python-keycloak-0.29.1.tar.gz", hash = "sha256:92bfc40c648c72704f9733268c079c8d290f0a16b1007e49f9f1b1fdee585018"}, + {file = "python_keycloak-0.29.1-py3-none-any.whl", hash = "sha256:9889543fc87ac8a0475a387ee6e2f1353ce59f4b2650ad0ae8526f8cdfe47756"}, ] [package.dependencies] python-jose = ">=1.4.0" requests = ">=2.20.0" +urllib3 = ">=1.26.0" + +[package.extras] +docs = ["alabaster", "commonmark", "m2r2", "mock", "readthedocs-sphinx-ext", "recommonmark", "sphinx", "sphinx-autoapi", "sphinx-rtd-theme"] [[package]] name = "pyyaml" @@ -1176,18 +1171,18 @@ files = [ [[package]] name = "requests" -version = "2.32.3" +version = "2.32.5" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, - {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, + {file = "requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6"}, + {file = "requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf"}, ] [package.dependencies] certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" +charset_normalizer = ">=2,<4" idna = ">=2.5,<4" urllib3 = ">=1.21.1,<3" @@ -1197,13 +1192,13 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "rsa" -version = "4.9" +version = "4.9.1" description = "Pure-Python RSA implementation" optional = false -python-versions = ">=3.6,<4" +python-versions = "<4,>=3.6" files = [ - {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, - {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, + {file = "rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762"}, + {file = "rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75"}, ] [package.dependencies] @@ -1211,47 +1206,50 @@ pyasn1 = ">=0.1.3" [[package]] name = "s3transfer" -version = "0.1.13" +version = "0.13.1" description = "An Amazon S3 Transfer Manager" optional = false -python-versions = "*" +python-versions = ">=3.9" files = [ - {file = "s3transfer-0.1.13-py2.py3-none-any.whl", hash = "sha256:c7a9ec356982d5e9ab2d4b46391a7d6a950e2b04c472419f5fdec70cc0ada72f"}, - {file = "s3transfer-0.1.13.tar.gz", hash = "sha256:90dc18e028989c609146e241ea153250be451e05ecc0c2832565231dacdf59c1"}, + {file = "s3transfer-0.13.1-py3-none-any.whl", hash = "sha256:a981aa7429be23fe6dfc13e80e4020057cbab622b08c0315288758d67cabc724"}, + {file = "s3transfer-0.13.1.tar.gz", hash = "sha256:c3fdba22ba1bd367922f27ec8032d6a1cf5f10c934fb5d68cf60fd5a23d936cf"}, ] [package.dependencies] -botocore = ">=1.3.0,<2.0.0" +botocore = ">=1.37.4,<2.0a.0" + +[package.extras] +crt = ["botocore[crt] (>=1.37.4,<2.0a.0)"] [[package]] name = "setuptools" -version = "75.1.0" +version = "80.9.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "setuptools-75.1.0-py3-none-any.whl", hash = "sha256:35ab7fd3bcd95e6b7fd704e4a1539513edad446c097797f2985e0e4b960772f2"}, - {file = "setuptools-75.1.0.tar.gz", hash = "sha256:d59a21b17a275fb872a9c3dae73963160ae079f1049ed956880cd7c09b120538"}, + {file = "setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922"}, + {file = "setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c"}, ] [package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"] -core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.8.0)"] +core = ["importlib_metadata (>=6)", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.11.*)", "pytest-mypy"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.14.*)", "pytest-mypy"] [[package]] name = "six" -version = "1.16.0" +version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, + {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, + {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, ] [[package]] @@ -1267,50 +1265,51 @@ files = [ [[package]] name = "urllib3" -version = "1.25.11" +version = "2.5.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" +python-versions = ">=3.9" files = [ - {file = "urllib3-1.25.11-py2.py3-none-any.whl", hash = "sha256:f5321fbe4bf3fefa0efd0bfe7fb14e90909eb62a48ccda331726b4319897dd5e"}, - {file = "urllib3-1.25.11.tar.gz", hash = "sha256:8d7eaa5a82a1cac232164990f04874c594c9453ec55eef02eab885aa02fc17a2"}, + {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, + {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, ] [package.extras] -brotli = ["brotlipy (>=0.6.0)"] -secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] [[package]] name = "werkzeug" -version = "2.2.2" +version = "3.1.3" description = "The comprehensive WSGI web application library." optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ - {file = "Werkzeug-2.2.2-py3-none-any.whl", hash = "sha256:f979ab81f58d7318e064e99c4506445d60135ac5cd2e177a2de0089bfd4c9bd5"}, - {file = "Werkzeug-2.2.2.tar.gz", hash = "sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f"}, + {file = "werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e"}, + {file = "werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746"}, ] [package.dependencies] MarkupSafe = ">=2.1.1" [package.extras] -watchdog = ["watchdog"] +watchdog = ["watchdog (>=2.3)"] [[package]] name = "zope-event" -version = "5.0" +version = "5.1.1" description = "Very basic event publishing system" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ - {file = "zope.event-5.0-py3-none-any.whl", hash = "sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26"}, - {file = "zope.event-5.0.tar.gz", hash = "sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd"}, + {file = "zope_event-5.1.1-py3-none-any.whl", hash = "sha256:8d5ea7b992c42ce73a6fa9c2ba99a004c52cd9f05d87f3220768ef0329b92df7"}, + {file = "zope_event-5.1.1.tar.gz", hash = "sha256:c1ac931abf57efba71a2a313c5f4d57768a19b15c37e3f02f50eb1536be12d4e"}, ] [package.dependencies] -setuptools = "*" +setuptools = ">=75.8.2" [package.extras] docs = ["Sphinx"] @@ -1318,48 +1317,48 @@ test = ["zope.testrunner"] [[package]] name = "zope-interface" -version = "7.1.0" +version = "7.2" description = "Interfaces for Python" optional = false python-versions = ">=3.8" files = [ - {file = "zope.interface-7.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2bd9e9f366a5df08ebbdc159f8224904c1c5ce63893984abb76954e6fbe4381a"}, - {file = "zope.interface-7.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:661d5df403cd3c5b8699ac480fa7f58047a3253b029db690efa0c3cf209993ef"}, - {file = "zope.interface-7.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91b6c30689cfd87c8f264acb2fc16ad6b3c72caba2aec1bf189314cf1a84ca33"}, - {file = "zope.interface-7.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b6a4924f5bad9fe21d99f66a07da60d75696a136162427951ec3cb223a5570d"}, - {file = "zope.interface-7.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80a3c00b35f6170be5454b45abe2719ea65919a2f09e8a6e7b1362312a872cd3"}, - {file = "zope.interface-7.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:b936d61dbe29572fd2cfe13e30b925e5383bed1aba867692670f5a2a2eb7b4e9"}, - {file = "zope.interface-7.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0ac20581fc6cd7c754f6dff0ae06fedb060fa0e9ea6309d8be8b2701d9ea51c4"}, - {file = "zope.interface-7.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:848b6fa92d7c8143646e64124ed46818a0049a24ecc517958c520081fd147685"}, - {file = "zope.interface-7.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec1ef1fdb6f014d5886b97e52b16d0f852364f447d2ab0f0c6027765777b6667"}, - {file = "zope.interface-7.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bcff5c09d0215f42ba64b49205a278e44413d9bf9fa688fd9e42bfe472b5f4f"}, - {file = "zope.interface-7.1.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07add15de0cc7e69917f7d286b64d54125c950aeb43efed7a5ea7172f000fbc1"}, - {file = "zope.interface-7.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:9940d5bc441f887c5f375ec62bcf7e7e495a2d5b1da97de1184a88fb567f06af"}, - {file = "zope.interface-7.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f245d039f72e6f802902375755846f5de1ee1e14c3e8736c078565599bcab621"}, - {file = "zope.interface-7.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6159e767d224d8f18deff634a1d3722e68d27488c357f62ebeb5f3e2f5288b1f"}, - {file = "zope.interface-7.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e956b1fd7f3448dd5e00f273072e73e50dfafcb35e4227e6d5af208075593c9"}, - {file = "zope.interface-7.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff115ef91c0eeac69cd92daeba36a9d8e14daee445b504eeea2b1c0b55821984"}, - {file = "zope.interface-7.1.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bec001798ab62c3fc5447162bf48496ae9fba02edc295a9e10a0b0c639a6452e"}, - {file = "zope.interface-7.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:124149e2d42067b9c6597f4dafdc7a0983d0163868f897b7bb5dc850b14f9a87"}, - {file = "zope.interface-7.1.0-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:9733a9a0f94ef53d7aa64661811b20875b5bc6039034c6e42fb9732170130573"}, - {file = "zope.interface-7.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5fcf379b875c610b5a41bc8a891841533f98de0520287d7f85e25386cd10d3e9"}, - {file = "zope.interface-7.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0a45b5af9f72c805ee668d1479480ca85169312211bed6ed18c343e39307d5f"}, - {file = "zope.interface-7.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af4a12b459a273b0b34679a5c3dc5e34c1847c3dd14a628aa0668e19e638ea2"}, - {file = "zope.interface-7.1.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a735f82d2e3ed47ca01a20dfc4c779b966b16352650a8036ab3955aad151ed8a"}, - {file = "zope.interface-7.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:5501e772aff595e3c54266bc1bfc5858e8f38974ce413a8f1044aae0f32a83a3"}, - {file = "zope.interface-7.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ec59fe53db7d32abb96c6d4efeed84aab4a7c38c62d7a901a9b20c09dd936e7a"}, - {file = "zope.interface-7.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e53c291debef523b09e1fe3dffe5f35dde164f1c603d77f770b88a1da34b7ed6"}, - {file = "zope.interface-7.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:711eebc77f2092c6a8b304bad0b81a6ce3cf5490b25574e7309fbc07d881e3af"}, - {file = "zope.interface-7.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a00ead2e24c76436e1b457a5132d87f83858330f6c923640b7ef82d668525d1"}, - {file = "zope.interface-7.1.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e28ea0bc4b084fc93a483877653a033062435317082cdc6388dec3438309faf"}, - {file = "zope.interface-7.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:27cfb5205d68b12682b6e55ab8424662d96e8ead19550aad0796b08dd2c9a45e"}, - {file = "zope.interface-7.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9e3e48f3dea21c147e1b10c132016cb79af1159facca9736d231694ef5a740a8"}, - {file = "zope.interface-7.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a99240b1d02dc469f6afbe7da1bf617645e60290c272968f4e53feec18d7dce8"}, - {file = "zope.interface-7.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc8a318162123eddbdf22fcc7b751288ce52e4ad096d3766ff1799244352449d"}, - {file = "zope.interface-7.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7b25db127db3e6b597c5f74af60309c4ad65acd826f89609662f0dc33a54728"}, - {file = "zope.interface-7.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a29ac607e970b5576547f0e3589ec156e04de17af42839eedcf478450687317"}, - {file = "zope.interface-7.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:a14c9decf0eb61e0892631271d500c1e306c7b6901c998c7035e194d9150fdd1"}, - {file = "zope_interface-7.1.0.tar.gz", hash = "sha256:3f005869a1a05e368965adb2075f97f8ee9a26c61898a9e52a9764d93774f237"}, + {file = "zope.interface-7.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ce290e62229964715f1011c3dbeab7a4a1e4971fd6f31324c4519464473ef9f2"}, + {file = "zope.interface-7.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:05b910a5afe03256b58ab2ba6288960a2892dfeef01336dc4be6f1b9ed02ab0a"}, + {file = "zope.interface-7.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:550f1c6588ecc368c9ce13c44a49b8d6b6f3ca7588873c679bd8fd88a1b557b6"}, + {file = "zope.interface-7.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0ef9e2f865721553c6f22a9ff97da0f0216c074bd02b25cf0d3af60ea4d6931d"}, + {file = "zope.interface-7.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27f926f0dcb058211a3bb3e0e501c69759613b17a553788b2caeb991bed3b61d"}, + {file = "zope.interface-7.2-cp310-cp310-win_amd64.whl", hash = "sha256:144964649eba4c5e4410bb0ee290d338e78f179cdbfd15813de1a664e7649b3b"}, + {file = "zope.interface-7.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1909f52a00c8c3dcab6c4fad5d13de2285a4b3c7be063b239b8dc15ddfb73bd2"}, + {file = "zope.interface-7.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:80ecf2451596f19fd607bb09953f426588fc1e79e93f5968ecf3367550396b22"}, + {file = "zope.interface-7.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:033b3923b63474800b04cba480b70f6e6243a62208071fc148354f3f89cc01b7"}, + {file = "zope.interface-7.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a102424e28c6b47c67923a1f337ede4a4c2bba3965b01cf707978a801fc7442c"}, + {file = "zope.interface-7.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25e6a61dcb184453bb00eafa733169ab6d903e46f5c2ace4ad275386f9ab327a"}, + {file = "zope.interface-7.2-cp311-cp311-win_amd64.whl", hash = "sha256:3f6771d1647b1fc543d37640b45c06b34832a943c80d1db214a37c31161a93f1"}, + {file = "zope.interface-7.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:086ee2f51eaef1e4a52bd7d3111a0404081dadae87f84c0ad4ce2649d4f708b7"}, + {file = "zope.interface-7.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:21328fcc9d5b80768bf051faa35ab98fb979080c18e6f84ab3f27ce703bce465"}, + {file = "zope.interface-7.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6dd02ec01f4468da0f234da9d9c8545c5412fef80bc590cc51d8dd084138a89"}, + {file = "zope.interface-7.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e7da17f53e25d1a3bde5da4601e026adc9e8071f9f6f936d0fe3fe84ace6d54"}, + {file = "zope.interface-7.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cab15ff4832580aa440dc9790b8a6128abd0b88b7ee4dd56abacbc52f212209d"}, + {file = "zope.interface-7.2-cp312-cp312-win_amd64.whl", hash = "sha256:29caad142a2355ce7cfea48725aa8bcf0067e2b5cc63fcf5cd9f97ad12d6afb5"}, + {file = "zope.interface-7.2-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:3e0350b51e88658d5ad126c6a57502b19d5f559f6cb0a628e3dc90442b53dd98"}, + {file = "zope.interface-7.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:15398c000c094b8855d7d74f4fdc9e73aa02d4d0d5c775acdef98cdb1119768d"}, + {file = "zope.interface-7.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:802176a9f99bd8cc276dcd3b8512808716492f6f557c11196d42e26c01a69a4c"}, + {file = "zope.interface-7.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb23f58a446a7f09db85eda09521a498e109f137b85fb278edb2e34841055398"}, + {file = "zope.interface-7.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a71a5b541078d0ebe373a81a3b7e71432c61d12e660f1d67896ca62d9628045b"}, + {file = "zope.interface-7.2-cp313-cp313-win_amd64.whl", hash = "sha256:4893395d5dd2ba655c38ceb13014fd65667740f09fa5bb01caa1e6284e48c0cd"}, + {file = "zope.interface-7.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d3a8ffec2a50d8ec470143ea3d15c0c52d73df882eef92de7537e8ce13475e8a"}, + {file = "zope.interface-7.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:31d06db13a30303c08d61d5fb32154be51dfcbdb8438d2374ae27b4e069aac40"}, + {file = "zope.interface-7.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e204937f67b28d2dca73ca936d3039a144a081fc47a07598d44854ea2a106239"}, + {file = "zope.interface-7.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:224b7b0314f919e751f2bca17d15aad00ddbb1eadf1cb0190fa8175edb7ede62"}, + {file = "zope.interface-7.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baf95683cde5bc7d0e12d8e7588a3eb754d7c4fa714548adcd96bdf90169f021"}, + {file = "zope.interface-7.2-cp38-cp38-win_amd64.whl", hash = "sha256:7dc5016e0133c1a1ec212fc87a4f7e7e562054549a99c73c8896fa3a9e80cbc7"}, + {file = "zope.interface-7.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7bd449c306ba006c65799ea7912adbbfed071089461a19091a228998b82b1fdb"}, + {file = "zope.interface-7.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a19a6cc9c6ce4b1e7e3d319a473cf0ee989cbbe2b39201d7c19e214d2dfb80c7"}, + {file = "zope.interface-7.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72cd1790b48c16db85d51fbbd12d20949d7339ad84fd971427cf00d990c1f137"}, + {file = "zope.interface-7.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52e446f9955195440e787596dccd1411f543743c359eeb26e9b2c02b077b0519"}, + {file = "zope.interface-7.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ad9913fd858274db8dd867012ebe544ef18d218f6f7d1e3c3e6d98000f14b75"}, + {file = "zope.interface-7.2-cp39-cp39-win_amd64.whl", hash = "sha256:1090c60116b3da3bfdd0c03406e2f14a1ff53e5771aebe33fec1edc0a350175d"}, + {file = "zope.interface-7.2.tar.gz", hash = "sha256:8b49f1a3d1ee4cdaf5b32d2e738362c7f5e40ac8b46dd7d1a65e82a4872728fe"}, ] [package.dependencies] @@ -1373,4 +1372,4 @@ testing = ["coverage[toml]", "zope.event", "zope.testing"] [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "c94d1a39044fd275e14868fec19f3c8d101d1c04d21280b74c884bec30e70dd4" +content-hash = "ce6beffb766cd79fd4eef48eb49687a8b1f2c8719c8208580f2b1e7f76302c14" diff --git a/microservices/gatewayApi/pyproject.toml b/microservices/gatewayApi/pyproject.toml index 5d9ff736..04bd0705 100644 --- a/microservices/gatewayApi/pyproject.toml +++ b/microservices/gatewayApi/pyproject.toml @@ -7,24 +7,24 @@ authors = [] [tool.poetry.dependencies] python = "^3.11" -werkzeug = "2.2.2" +werkzeug = ">=2" ply = "3.10" cryptography = "38.0.4" authlib = "0.15.3" flask-swagger-ui = "3.36.0" -Jinja2 = "3.0.3" +Jinja2 = ">=3" PyYAML = "6.0.1" munch = "2.5.0" -boto3 = "1.9.12" -flask = "2.1.3" +boto3 = ">=1.9" +flask = "2.3.3" flask-compress = "1.4.0" flask-cors = "3.0.9" -gevent = "22.10.2" -# greenlet = "2.0.2" -gunicorn = "20.1.0" -python-keycloak = "0.22.0" +gevent = "^25.5.1" +gunicorn = ">=20.1.0" +python-keycloak = "^0" requests = "^2.32" flask-jwt-simple = "0.0.3" +urllib3 = "^2.5.0" [tool.poetry.dev-dependencies] autopep8 = "^1.5.7" diff --git a/microservices/gatewayApi/swagger.py b/microservices/gatewayApi/swagger.py index 3dcd94bc..ff730adb 100644 --- a/microservices/gatewayApi/swagger.py +++ b/microservices/gatewayApi/swagger.py @@ -59,7 +59,5 @@ def openapi_spec(ver:str): except: traceback.print_exc(file=sys.stdout) - log.error("Failed to do OIDC Discovery for %s, sleeping 5 seconds and trying again." % version) - time.sleep(5) - setup_swagger_docs(app, version) + log.error("Failed to do OIDC Discovery , sleeping 5 seconds and trying again.") diff --git a/microservices/gatewayApi/tests/models/test_config_pattern.py b/microservices/gatewayApi/tests/models/test_config_pattern.py new file mode 100644 index 00000000..4f460b41 --- /dev/null +++ b/microservices/gatewayApi/tests/models/test_config_pattern.py @@ -0,0 +1,60 @@ +import json +from os import environ +from patterns.eval import evaluate_pattern +import yaml +from v2.models.gateway_config_pattern import GatewayConfigPattern + +def test_model_r1(client): + context = { + 'ns_qualifier': 'all', + 'pattern': 'sdx-service-r1', + 'service_name': 'LAB-MIN-CITZ-MY-SERVICE', + 'gateway': 'gw-xxx', + 'upstream_uri': 'https://app-service-r1.example.com', + 'mtls_allow_list': 'ap-01.example.com', + 'route_host': 'ap-02.example.com', + 'route_path': '/LAB/MIN/CITZ/MY-SERVICE', + 'consumer_uri': '', + 'consumer_client_id': '', + 'openid_issuer': '', + 'openid_audience': '', + 'openid_scope': '', + } + + gw_pattern_context = GatewayConfigPattern (context) + # gw_pattern_context.set_gateway(namespace) + + response = gw_pattern_context.get_config_file() + + print(response) + yaml_documents_iter = yaml.load_all(response, Loader=yaml.FullLoader) + doc = next(yaml_documents_iter) + assert doc['services'][0]["name"] == context['service_name'] + +def test_model_keys_r1(client): + public_key_pem = """-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzvYpY1k1k0kP7vV1X1JHqz +bX5+5b0F6v9vQ1ZlZ5sX6Fz3Yx8u5m2K1v1ZlZ5sX6Fz3Yx8u5m2K1v1ZlZ5sX6Fz3Yx8u +-----END PUBLIC KEY-----""" + + context = { + 'ns_qualifier': 'all', + 'pattern': 'sdx-keys-r1', + 'gateway': 'gw-xxx', + 'key_name': 'key-123', + 'kid': 'key-123-kid', + 'public_key_pem': public_key_pem, + } + + gw_pattern_context = GatewayConfigPattern (context) + gw_pattern_context.set_gateway('gw-yyy') + + response = gw_pattern_context.get_config_file() + + print(response) + yaml_documents_iter = yaml.load_all(response, Loader=yaml.FullLoader) + doc = next(yaml_documents_iter) + print(yaml.dump(doc, width=9999, default_flow_style=False, default_style="double-quoted")) + assert doc['keys'][0]["name"] == context['key_name'] + assert doc['keys'][0]["tags"][0] == "ns.gw-yyy.all" + # assert doc['keys'][0]["pem"]["public_key"] == public_key_pem diff --git a/microservices/gatewayApi/tests/patterns/test_pattern.py b/microservices/gatewayApi/tests/patterns/test_pattern.py new file mode 100644 index 00000000..1f624e18 --- /dev/null +++ b/microservices/gatewayApi/tests/patterns/test_pattern.py @@ -0,0 +1,48 @@ +from os import environ +from patterns.eval import evaluate_pattern +import yaml + +def test_service_r1(client): + context = { + 'ns_qualifier': 'all', + 'service_name': 'LAB-MIN-CITZ-MY-SERVICE', + 'gateway': 'gw-xxx', + 'upstream_uri': 'https://app-service-r1.example.com', + 'mtls_allow_list': 'ap-01.example.com', + 'route_host': 'ap-02.example.com', + 'route_path': '/LAB/MIN/CITZ/MY-SERVICE', + 'consumer_uri': '', + 'consumer_client_id': '', + 'openid_issuer': '', + 'openid_audience': '', + 'openid_scope': '', + } + + response = evaluate_pattern('sdx-service-r1', context) + print(response) + yaml_documents_iter = yaml.load_all(response, Loader=yaml.FullLoader) + doc = next(yaml_documents_iter) + assert doc['services'][0]["name"] == context['service_name'] + +def test_application_r1(client): + context = { + 'ns_qualifier': 'all', + 'service_name': 'REQ-0001-LAB-MIN-CITZ-MY-UI', + 'gateway': 'gw-xxx', + 'upstream_uri': 'https://ap-02.example.com', + 'route_host': 'ap-01.example.com', + 'route_path': '/LAB/MIN/CITZ/OTHER-SERVICE', + 'consumer_uri': '', + 'consumer_client_id': '', + 'edge_kid': 'REQ-0001-LAB-MIN-CITZ-MY-UI.SIG.CURRENT', + 'openid_issuer': '', + 'openid_audience': '', + 'openid_scope': '', + } + + response = evaluate_pattern('sdx-p2p-consumer-r1', context) + print(response) + yaml_documents_iter = yaml.load_all(response, Loader=yaml.FullLoader) + doc = next(yaml_documents_iter) + assert doc['services'][0]["name"] == context['service_name'] + diff --git a/microservices/gatewayApi/utils/deck.py b/microservices/gatewayApi/utils/deck.py index 62ba7bbe..f5fbd4b1 100644 --- a/microservices/gatewayApi/utils/deck.py +++ b/microservices/gatewayApi/utils/deck.py @@ -1,6 +1,8 @@ def deck_cmd_sync_diff(deck_cli, cmd, select_tag, state): - if deck_cli == "deck" or deck_cli.startswith("deck_kong3_"): + if deck_cli == "deck" and cmd == "dump": + return [ deck_cli, "gateway", cmd, "--config", "/tmp/deck.yaml", "--skip-consumers", "--select-tag", select_tag] + elif deck_cli == "deck" or deck_cli.startswith("deck_kong3_"): return [ deck_cli, "gateway", cmd, "--config", "/tmp/deck.yaml", "--skip-consumers", "--select-tag", select_tag, state] else: return [ deck_cli, cmd, "--config", "/tmp/deck.yaml", "--skip-consumers", "--select-tag", select_tag, "--state", state] diff --git a/microservices/gatewayApi/v2/models/gateway_config_pattern.py b/microservices/gatewayApi/v2/models/gateway_config_pattern.py new file mode 100644 index 00000000..4cfc5767 --- /dev/null +++ b/microservices/gatewayApi/v2/models/gateway_config_pattern.py @@ -0,0 +1,30 @@ +from patterns.eval import evaluate_pattern +# Example: Initializing GatewayConfigPattern from a JSON object +# import json +# json_data = ''' +# { +# "pattern": "some-pattern", +# "gateway": "main-gateway", +# "service_name": "my-service", +# "upstream_uri": "http://upstream.example.com", +# "route_host": "api.example.com", +# "route_path": "/v1/resource", +# "mtls_allow_list": ["CN=abc"] +# } +# ''' +# data = json.loads(json_data) +# obj = GatewayConfigPattern(**data) +class GatewayConfigPattern: + def __init__(self, document: dict): + self.document = document + self.gateway = None + + def get_config_file(self): + context = self.document.copy() + pattern = context["pattern"] + if self.gateway: + context["gateway"] = self.gateway + return evaluate_pattern(pattern, context) + + def set_gateway(self, gateway: str): + self.gateway = gateway \ No newline at end of file diff --git a/microservices/gatewayApi/v2/routes/gateway.py b/microservices/gatewayApi/v2/routes/gateway.py index 05073b6a..d2fcb684 100644 --- a/microservices/gatewayApi/v2/routes/gateway.py +++ b/microservices/gatewayApi/v2/routes/gateway.py @@ -3,6 +3,7 @@ import sys import http import traceback +from urllib import response from urllib.parse import urlparse from subprocess import Popen, PIPE, STDOUT import uuid @@ -24,6 +25,7 @@ from utils.masking import mask from utils.deck import deck_cmd_sync_diff, deck_cmd_validate from clients.compatibility import check_kong3_compatibility +from v2.models.gateway_config_pattern import GatewayConfigPattern gw = Blueprint('gwa_v2', 'gateway') local_environment = os.environ.get("LOCAL_ENVIRONMENT", default=False) @@ -79,55 +81,57 @@ def delete_config(namespace: str, qualifier="") -> object: abort_early(event_id, 'delete', namespace, jsonify(error="Sync Failed.", results=mask(out.decode('utf-8')))) elif cmd == "sync" and not local_environment: - try: - session = requests.Session() - session.headers.update({"Content-Type": "application/json"}) - route_payload = { - "hosts": get_host_list(tempFolder), - "select_tag": selectTag, - "ns_attributes": ns_attributes.getAttrs() - } - dp = get_data_plane(ns_attributes) - rqst_url = app.config['data_planes'][dp]["kube-api"] - log.debug("[%s] - Initiating request to kube API" % (dp)) - res = session.put(rqst_url + "/namespaces/%s/routes" % namespace, json=route_payload, auth=( - app.config['kubeApiCreds']['kubeApiUser'], app.config['kubeApiCreds']['kubeApiPass'])) - log.debug("[%s] - The kube API responded with %s" % (dp, res.status_code)) - if res.status_code != 201: - log.debug("[%s] - The kube API could not process the request" % (dp)) - raise Exception("[%s] - Failed to apply routes: %s" % (dp, str(res.text))) - # route_count = prepare_apply_routes(namespace, selectTag, is_host_transform_enabled(), tempFolder) - # log.debug("%s - Prepared %d routes" % (namespace, route_count)) - # if route_count > 0: - # apply_routes(tempFolder) - # log.debug("%s - Applied %d routes" % (namespace, route_count)) - # route_count = prepare_delete_routes(namespace, selectTag, tempFolder) - # log.debug("%s - Prepared %d deletions" % (namespace, route_count)) - # if route_count > 0: - # delete_routes(tempFolder) - - # # create Network Security Policies (nsp) for any upstream that - # # has the format: ..svc - # log.debug("%s - Update NSPs" % (namespace)) - # ocp_ns_list = get_ocp_service_namespaces(tempFolder) - # for ocp_ns in ocp_ns_list: - # if check_nsp(namespace, ocp_ns) is False: - # apply_nsp(namespace, ocp_ns, tempFolder) - - # ok all looks good, so update a secret containing the original submitted request - # log.debug("%s - Update Original Config" % (namespace)) - # write_submitted_config("", tempFolder) - # prep_and_apply_secret(namespace, selectTag, tempFolder) - # log.debug("%s - Updated Original Config" % (namespace)) - session.close() - except HTTPException as ex: - traceback.print_exc() - log.error("Error updating custom routes. %s" % ex) - abort_early(event_id, 'delete', namespace, jsonify(error="Partially failed.")) - except: - traceback.print_exc() - log.error("Error updating custom routes. %s" % sys.exc_info()[0]) - abort_early(event_id, 'delete', namespace, jsonify(error="Partially failed.")) + dp = get_data_plane(ns_attributes) + if not dp.startswith("sdx-"): + + try: + session = requests.Session() + session.headers.update({"Content-Type": "application/json"}) + route_payload = { + "hosts": get_host_list(tempFolder), + "select_tag": selectTag, + "ns_attributes": ns_attributes.getAttrs() + } + rqst_url = app.config['data_planes'][dp]["kube-api"] + log.debug("[%s] - Initiating request to kube API" % (dp)) + res = session.put(rqst_url + "/namespaces/%s/routes" % namespace, json=route_payload, auth=( + app.config['kubeApiCreds']['kubeApiUser'], app.config['kubeApiCreds']['kubeApiPass'])) + log.debug("[%s] - The kube API responded with %s" % (dp, res.status_code)) + if res.status_code != 201: + log.debug("[%s] - The kube API could not process the request" % (dp)) + raise Exception("[%s] - Failed to apply routes: %s" % (dp, str(res.text))) + # route_count = prepare_apply_routes(namespace, selectTag, is_host_transform_enabled(), tempFolder) + # log.debug("%s - Prepared %d routes" % (namespace, route_count)) + # if route_count > 0: + # apply_routes(tempFolder) + # log.debug("%s - Applied %d routes" % (namespace, route_count)) + # route_count = prepare_delete_routes(namespace, selectTag, tempFolder) + # log.debug("%s - Prepared %d deletions" % (namespace, route_count)) + # if route_count > 0: + # delete_routes(tempFolder) + + # # create Network Security Policies (nsp) for any upstream that + # # has the format: ..svc + # log.debug("%s - Update NSPs" % (namespace)) + # ocp_ns_list = get_ocp_service_namespaces(tempFolder) + # for ocp_ns in ocp_ns_list: + # if check_nsp(namespace, ocp_ns) is False: + # apply_nsp(namespace, ocp_ns, tempFolder) + + # ok all looks good, so update a secret containing the original submitted request + # log.debug("%s - Update Original Config" % (namespace)) + # write_submitted_config("", tempFolder) + # prep_and_apply_secret(namespace, selectTag, tempFolder) + # log.debug("%s - Updated Original Config" % (namespace)) + session.close() + except HTTPException as ex: + traceback.print_exc() + log.error("Error updating custom routes. %s" % ex) + abort_early(event_id, 'delete', namespace, jsonify(error="Partially failed.")) + except: + traceback.print_exc() + log.error("Error updating custom routes. %s" % sys.exc_info()[0]) + abort_early(event_id, 'delete', namespace, jsonify(error="Partially failed.")) cleanup(tempFolder) @@ -163,7 +167,7 @@ def write_config(namespace: str) -> object: all_routes = get_routes() tag_match = "ns.%s" % namespace for route in all_routes: - if tag_match not in route['tags'] and 'hosts' in route: + if tag_match not in route['tags'] and 'hosts' in route and "sdx" not in route['tags']: for host in route['hosts']: reserved_hosts.append(host) reserved_hosts = list(set(reserved_hosts)) @@ -292,7 +296,10 @@ def write_config(namespace: str) -> object: try: protected_kube_namespaces = json.loads(app.config['protectedKubeNamespaces']) - do_validate_upstreams = app.config['data_planes'][dp].get("validate-upstreams", False) + if dp.startswith("sdx-"): + do_validate_upstreams = False + else: + do_validate_upstreams = app.config['data_planes'][dp].get("validate-upstreams", False) log.debug("Validate upstreams %s %s" % (dp, do_validate_upstreams)) validate_upstream(gw_config, ns_attributes, protected_kube_namespaces, do_validate_upstreams) @@ -351,7 +358,421 @@ def write_config(namespace: str) -> object: # skip creation of routes in local development environment elif cmd == "sync" and not local_environment: try: - if update_routes_flag: + if update_routes_flag and not dp.startswith("sdx-"): + host_list = get_host_list(tempFolder) + certs = [] + custom_domain_in_host_list = False + for host in host_list: + if is_host_custom_domain(host): + custom_domain_in_host_list = True + break + if custom_domain_in_host_list: + certs = get_public_certs_by_ns(namespace) + log.debug("[%s] Found %d certs in namespace" % (namespace, len(certs))) + session = requests.Session() + session.headers.update({"Content-Type": "application/json"}) + route_payload = { + "hosts": host_list, + "select_tag": selectTag, + "ns_attributes": ns_attributes.getAttrs(), + "overrides": { + "aps.route.session.cookie.enabled": get_route_overrides(tempFolder, "aps.route.session.cookie.enabled"), + "aps.route.dataclass.low": get_route_overrides(tempFolder, "aps.route.dataclass.low"), + "aps.route.dataclass.medium": get_route_overrides(tempFolder, "aps.route.dataclass.medium"), + "aps.route.dataclass.high": get_route_overrides(tempFolder, "aps.route.dataclass.high"), + "aps.route.dataclass.public": get_route_overrides(tempFolder, "aps.route.dataclass.public"), + }, + "certificates": certs + } + # Create a copy without certificates for logging + route_payload_log = { + "hosts": host_list, + "select_tag": selectTag, + "ns_attributes": ns_attributes.getAttrs(), + "overrides": route_payload["overrides"] + } + log.debug("[%s] - Initiating request to kube API %s" % (dp, route_payload_log)) + rqst_url = app.config['data_planes'][dp]["kube-api"] + res = session.put(rqst_url + "/namespaces/%s/routes" % namespace, json=route_payload, auth=( + app.config['kubeApiCreds']['kubeApiUser'], app.config['kubeApiCreds']['kubeApiPass'])) + log.debug("[%s] - The kube API responded with %s" % (dp, res.status_code)) + if res.status_code != 201: + log.debug("[%s] - The kube API could not process the request" % (dp)) + raise Exception("[%s] - Failed to apply routes: %s" % (dp, str(res.text))) + session.close() + + if has_namespace_local_host_permission(ns_attributes): + session = requests.Session() + session.headers.update({"Content-Type": "application/json"}) + rqst_url = app.config['data_planes'][dp]["kube-api"] + log.debug("[%s] - Initiating request to kube API for Certs" % (dp)) + res = session.get(rqst_url + "/namespaces/%s/local_tls" % namespace, auth=( + app.config['kubeApiCreds']['kubeApiUser'], app.config['kubeApiCreds']['kubeApiPass'])) + log.debug("[%s] - The kube API responded with %s" % (dp, res.status_code)) + if res.status_code != 200: + log.debug("[%s] - The kube API could not process the request" % (dp)) + raise Exception("[%s] - Failed to get certs: %s" % (dp, str(res.text))) + cert_data = res.json() + session.close() + + register_kong_certs(namespace, cert_data) + + except HTTPException as ex: + traceback.print_exc() + log.error("[%s] Error updating custom routes. %s" % (namespace, ex)) + abort_early(event_id, 'publish', namespace, jsonify(error="Partially failed.")) + except: + traceback.print_exc() + log.error("[%s] Error updating custom routes. %s" % (namespace, sys.exc_info()[0])) + abort_early(event_id, 'publish', namespace, jsonify(error="Partially failed.")) + + cleanup(tempFolder) + + log.debug("[%s] The exit code was: %d" % (namespace, deck_run.returncode)) + + message = "Sync successful." + if cmd == 'diff': + message = "Dry-run. No changes applied." + + if cmd == 'sync': + record_gateway_event(event_id, 'published', 'completed', namespace, blob=orig_config) + + results = mask(out.decode('utf-8')) + + # Add Kong 3 compatibility warning if needed + if has_incompatible_routes: + # Add unique failed routes + unique_failed_routes = sorted(set(all_failed_routes)) + route_list = "\n".join(f" - {route}" for route in unique_failed_routes) + warning_message = warning_message + "\n" + route_list + + results = results + "\n" + warning_message + + return make_response(jsonify(message=message, results=results)) + +@gw.route('/hello', + methods=['GET'], strict_slashes=False) +def hello(namespace: str) -> object: + return make_response(jsonify(message="hi", gateway=namespace), 200, {'Content-Type': 'application/json'}) + + +@gw.route('/pattern-output', + methods=['PUT'], strict_slashes=False) +def patterned_config(namespace: str) -> object: + """ + :return: String of the generated yaml configuration + """ + log = app.logger + + log.info("Received patterned config request for %s" % namespace) + + log.info("Request Data: %s" % request.data) + + config = request.get_json() + + log.info("Config: %s" % config) + + delete = config.get("delete", False) + delete_qualifier = config.get("deleteQualifier", None) + dry_run = config.get("dryRun", True) + dump_only = config.get("dump", False) + document = config.get("document", {}) + + + outFolder = namespace + + # ns_svc = NamespaceService() + # ns_attributes = ns_svc.get_namespace_attributes(namespace) + + dfile = None + select_tag_qualifier = None + + if delete: + select_tag_qualifier = delete_qualifier + + tempFolder = "%s/%s/%s" % ('/tmp', uuid.uuid4(), outFolder) + os.makedirs(tempFolder, exist_ok=False) + + gw_pattern_context = GatewayConfigPattern (document) + gw_pattern_context.set_gateway(namespace) + + dfile = gw_pattern_context.get_config_file() + + log.debug("Saved to %s" % tempFolder) + yaml_documents = load_yaml_files(dfile) + + if len(yaml_documents) == 0: + log.error("%s - %s" % (namespace, "Empty Configuration Passed")) + abort(make_response(jsonify(error="Empty Configuration Passed"), 400)) + + if delete: + # if deleting, then set the select tag qualifier and override the document + # to be a delete document + select_tag_qualifier = delete_qualifier + + delete_doc = { + "_format_version": "3.0", + "services": [] + } + + yaml_documents = [ yaml.load(yaml.dump(delete_doc), Loader=yaml.FullLoader) ] + + selectTag = "ns.%s" % namespace + ns_qualifier = None + if select_tag_qualifier is not None and select_tag_qualifier != "" and "." not in select_tag_qualifier: + ns_qualifier = "%s.%s" % (selectTag, select_tag_qualifier) + + return make_response(jsonify(ns_qualifier=ns_qualifier, documents=yaml_documents), 200, {'Content-Type': 'application/json'}) + + +@gw.route('/patterns', + methods=['PUT'], strict_slashes=False) +@admin_jwt(None) +@uma_enforce('namespace', 'GatewayConfig.Publish') +def patterned_write_config(namespace: str) -> object: + """ + (Over)write + :return: JSON of success message or error message + """ + + config = request.get_json() + + delete = config.get("delete", False) + delete_qualifier = config.get("deleteQualifier", None) + dry_run = config.get("dryRun", True) + dump_only = config.get("dump", False) + document = config.get("document", {}) + + event_id = str(uuid.uuid4()) + + log = app.logger + + outFolder = namespace + + ns_svc = NamespaceService() + ns_attributes = ns_svc.get_namespace_attributes(namespace) + + dp = get_data_plane(ns_attributes) + + # Build a list of existing hosts that are outside this namespace + # They become reserved and any conflict will return an error + reserved_hosts = [] + all_routes = get_routes() + tag_match = "ns.%s" % namespace + for route in all_routes: + if tag_match not in route['tags'] and 'hosts' in route and "sdx" not in route['tags']: + for host in route['hosts']: + reserved_hosts.append(host) + reserved_hosts = list(set(reserved_hosts)) + + dfile = None + select_tag_qualifier = None + + if delete: + select_tag_qualifier = delete_qualifier + + # if 'configFile' in request.files and not request.files['configFile'].filename == '': + # log.debug("[%s] %s", namespace, request.files['configFile']) + # dfile = request.files['configFile'] + # dry_run = request.values['dryRun'] + # if "qualifier" in request.values: + # select_tag_qualifier = request.values['qualifier'] + # elif request.content_type.startswith("application/json") \ + # and 'configFile' in request.json \ + # and not request.json['configFile'] in [None, '']: + # dfile = request.json['configFile'] + # dry_run = request.json['dryRun'] + # if "qualifier" in request.json: + # select_tag_qualifier = request.json['qualifier'] + # else: + # log.error("Missing input") + # log.error("%s", request.get_data()) + # log.error(request.form) + # log.error(request.content_type) + # log.error(request.headers) + # abort_early(event_id, 'publish', namespace, jsonify(error="Missing input")) + + cmd = "sync" + if dry_run == 'true' or dry_run is True: + cmd = "diff" + if dump_only == 'true' or dump_only is True: + cmd = "dump" + + if cmd == 'sync': + record_gateway_event(event_id, 'publish', 'received', namespace) + + tempFolder = "%s/%s/%s" % ('/tmp', uuid.uuid4(), outFolder) + os.makedirs(tempFolder, exist_ok=False) + + # dfile.save("%s/%s" % (tempFolder, 'config.yaml')) + + gw_pattern_context = GatewayConfigPattern (document) + gw_pattern_context.set_gateway(namespace) + + dfile = gw_pattern_context.get_config_file() + + # log.debug("Saved to %s" % tempFolder) + yaml_documents = load_yaml_files(dfile) + + if len(yaml_documents) == 0: + log.error("%s - %s" % (namespace, "Empty Configuration Passed")) + abort_early(event_id, 'publish', namespace, jsonify(error="Empty Configuration Passed")) + + if delete: + # if deleting, then set the select tag qualifier and override the document + # to be a delete document + select_tag_qualifier = delete_qualifier + + delete_doc = { + "_format_version": "3.0", + "services": [] + } + + yaml_documents = [ yaml.load(yaml.dump(delete_doc), Loader=yaml.FullLoader) ] + + selectTag = "ns.%s" % namespace + ns_qualifier = None + if select_tag_qualifier is not None and select_tag_qualifier != "" and "." not in select_tag_qualifier: + ns_qualifier = "%s.%s" % (selectTag, select_tag_qualifier) + + orig_config = prep_submitted_config(clone_yaml_files(yaml_documents)) + + update_routes_flag = False + + all_failed_routes = [] + has_incompatible_routes = False + + for index, gw_config in enumerate(yaml_documents): + log.info("[%s] Parsing file %s" % (namespace, index)) + + if gw_config is None: + continue + + ####################### + # Enrichments + ####################### + + # Add format version if its missing - needed in Kong v3+ + add_version_if_missing(gw_config) + + # Transformation route hosts if in non-prod environment (HOST_TRANSFORM_ENABLED) + host_transformation(namespace, dp, gw_config) + + # If there is a tag with a pipeline qualifier (i.e./ ns..dev) + # then add to tags automatically the tag: ns. + object_count = tags_transformation(namespace, gw_config) + + # Enrich the rate-limiting plugin with the appropriate Redis details + plugins_transformations(namespace, gw_config) + + # Disabled: + # + # Check Kong 3 compatibility + #is_compatible, compatibility_message, failed_routes, kong2_config = check_kong3_compatibility(namespace, gw_config) + #if not is_compatible: + # warning_message = compatibility_message + + # Track incompatible routes + #if not is_compatible: + # has_incompatible_routes = True + # all_failed_routes.extend(failed_routes) + + # Use kong2_config (which has compatibility tags) regardless of compatibility status + #if kong2_config: + # gw_config = kong2_config + + # After enrichments, dump config to file + with open("%s/%s" % (tempFolder, 'config-%02d.yaml' % index), 'w') as file: + yaml.dump(gw_config, file, width=9999, default_flow_style=False, default_style="double-quoted") + + ####################### + # Validations + ####################### + + # Validate that the every object is tagged with the namespace + try: + validate_base_entities(gw_config, ns_attributes) + validate_tags(gw_config, selectTag) + except Exception as ex: + traceback.print_exc() + log.error("%s - %s" % (namespace, " Tag Validation Errors: %s" % ex)) + abort_early(event_id, 'publish', namespace, jsonify(error="Validation Errors:\n%s" % ex)) + + # Validate that hosts are valid + try: + validate_hosts(gw_config, reserved_hosts, ns_attributes) + except Exception as ex: + traceback.print_exc() + log.error("%s - %s" % (namespace, " Host Validation Errors: %s" % ex)) + abort_early(event_id, 'publish', namespace, jsonify(error="Validation Errors:\n%s" % ex)) + + # Validate upstream URLs are valid + try: + protected_kube_namespaces = json.loads(app.config['protectedKubeNamespaces']) + + if dp.startswith("sdx-"): + do_validate_upstreams = False + else: + do_validate_upstreams = app.config['data_planes'][dp].get("validate-upstreams", False) + + log.debug("Validate upstreams %s %s" % (dp, do_validate_upstreams)) + validate_upstream(gw_config, ns_attributes, protected_kube_namespaces, do_validate_upstreams) + except Exception as ex: + traceback.print_exc() + log.error("%s - %s" % (namespace, " Upstream Validation Errors: %s" % ex)) + abort_early(event_id, 'publish', namespace, jsonify(error="Validation Errors:\n%s" % ex)) + + # Validation #3 + # Validate that certain plugins are configured (such as the gwa_gov_endpoint) at the right level + + # Validate based on DNS 952 + + nsq = traverse_get_ns_qualifier(gw_config, selectTag) + if nsq is not None: + if ns_qualifier is not None and nsq != ns_qualifier: + abort_early(event_id, 'publish', namespace, jsonify(error="Validation Errors:\n%s" % + ("Conflicting ns qualifiers (%s != %s)" % (ns_qualifier, nsq)))) + ns_qualifier = nsq + log.info("[%s] CHANGING ns_qualifier %s" % (namespace, ns_qualifier)) + elif ns_qualifier is not None and object_count > 0: + abort_early(event_id, 'publish', namespace, jsonify(error="Validation Errors:\n%s" % + ("Specified qualifier (%s) does not match tags in configuration (%s)" % (ns_qualifier, selectTag)))) + + if update_routes_check(gw_config): + update_routes_flag = True + + if ns_qualifier is not None: + selectTag = ns_qualifier + + # Call the 'deck' command + deck_cli = app.config['deckCLI'] + + log.info("[%s] (%s) %s action using %s" % (namespace, deck_cli, cmd, selectTag)) + + args = deck_cmd_validate(deck_cli, tempFolder) + + log.debug("[%s] Running %s" % (namespace, args)) + deck_validate = Popen(args, stdout=PIPE, stderr=STDOUT) + out, err = deck_validate.communicate() + + if deck_validate.returncode != 0: + log.warning("[%s] - %s" % (namespace, out.decode('utf-8'))) + abort_early(event_id, 'validate', namespace, jsonify( + error="Validation Failed.", results=mask(out.decode('utf-8')))) + + args = deck_cmd_sync_diff(deck_cli, cmd, selectTag, tempFolder) + + log.debug("[%s] Running %s" % (namespace, args)) + deck_run = Popen(args, stdout=PIPE, stderr=STDOUT) + out, err = deck_run.communicate() + if deck_run.returncode != 0: + # cleanup(tempFolder) + log.warn("[%s] - %s" % (namespace, out.decode('utf-8'))) + abort_early(event_id, 'publish', namespace, jsonify(error="Sync Failed.", results=mask(out.decode('utf-8')))) + # skip creation of routes in local development environment + elif cmd == "sync" and not local_environment: + try: + if update_routes_flag and not dp.startswith("sdx-"): host_list = get_host_list(tempFolder) certs = [] custom_domain_in_host_list = False @@ -453,7 +874,7 @@ def cleanup(dir_path): log.error("Error: %s : %s" % (dir_path, e.strerror)) def validate_base_entities(yaml, ns_attributes): - traversables = ['_format_version', '_plugin_configs', 'services', 'upstreams', 'certificates'] + traversables = ['_format_version', '_plugin_configs', 'services', 'upstreams', 'certificates', 'key_sets', 'keys'] allow_protected_ns = ns_attributes.get('perm-protected-ns', ['deny'])[0] == 'allow' if allow_protected_ns: @@ -483,7 +904,7 @@ def validate_tags(yaml, required_tag): def traverse(source, errors, yaml, required_tag, qualifiers): # If at root level, allow different resources than if its traversed down a level if source == "": - traversables = ['services', 'upstreams', 'consumers', 'certificates', 'ca_certificates'] + traversables = ['services', 'upstreams', 'consumers', 'certificates', 'ca_certificates', 'key_sets', 'keys'] else: traversables = ['routes', 'plugins'] @@ -635,7 +1056,7 @@ def tags_transformation(namespace, yaml): def traverse_tags_transform(yaml, namespace, required_tag): object_count = 0 log = app.logger - traversables = ['services', 'routes', 'plugins', 'upstreams', 'consumers', 'certificates', 'ca_certificates'] + traversables = ['services', 'routes', 'plugins', 'upstreams', 'consumers', 'certificates', 'ca_certificates', 'key_sets', 'keys'] for k in yaml: if k in traversables: for item in yaml[k]: @@ -655,7 +1076,7 @@ def traverse_tags_transform(yaml, namespace, required_tag): def traverse_has_ns_qualifier(yaml, required_tag): log = app.logger - traversables = ['services', 'routes', 'plugins', 'upstreams', 'consumers', 'certificates', 'ca_certificates'] + traversables = ['services', 'routes', 'plugins', 'upstreams', 'consumers', 'certificates', 'ca_certificates', 'key_sets', 'keys'] for k in yaml: if k in traversables: for item in yaml[k]: @@ -670,7 +1091,7 @@ def traverse_has_ns_qualifier(yaml, required_tag): def traverse_has_ns_tag_only(yaml, required_tag): log = app.logger - traversables = ['services', 'routes', 'plugins', 'upstreams', 'consumers', 'certificates', 'ca_certificates'] + traversables = ['services', 'routes', 'plugins', 'upstreams', 'consumers', 'certificates', 'ca_certificates', 'key_sets', 'keys'] for k in yaml: if k in traversables: for item in yaml[k]: @@ -691,7 +1112,7 @@ def has_ns_qualifier(tags, required_tag): def traverse_get_ns_qualifier(yaml, required_tag): log = app.logger - traversables = ['services', 'routes', 'plugins', 'upstreams', 'consumers', 'certificates', 'ca_certificates'] + traversables = ['services', 'routes', 'plugins', 'upstreams', 'consumers', 'certificates', 'ca_certificates', 'key_sets', 'keys'] for k in yaml: if k in traversables: for item in yaml[k]: @@ -731,4 +1152,9 @@ def clone_yaml_files (yaml_documents): for doc in yaml_documents: cloned_yaml.append(yaml.load(yaml.dump(doc), Loader=yaml.FullLoader)) return cloned_yaml - \ No newline at end of file + +def return_combined_yaml_files (yaml_documents): + combined_yaml = "" + for doc in yaml_documents: + combined_yaml = combined_yaml + "---\n" + yaml.safe_dump(doc, width=9999, sort_keys=False, default_flow_style=False, default_style="double-quoted") + return combined_yaml diff --git a/microservices/gatewayApi/v2/routes/namespaces.py b/microservices/gatewayApi/v2/routes/namespaces.py index 31684690..ad86329b 100644 --- a/microservices/gatewayApi/v2/routes/namespaces.py +++ b/microservices/gatewayApi/v2/routes/namespaces.py @@ -120,32 +120,33 @@ def delete_namespace(namespace: str) -> object: abort_early(event_id, 'delete', namespace, jsonify(error="Sync Failed.", results=mask(out.decode('utf-8')))) elif cmd == "sync" and not local_environment: - try: - session = requests.Session() - session.headers.update({"Content-Type": "application/json"}) - route_payload = { - "hosts": get_host_list(tempFolder), - "select_tag": selectTag, - "ns_attributes": ns_attributes.getAttrs() - } - dp = get_data_plane(ns_attributes) - rqst_url = app.config['data_planes'][dp]['kube-api'] - log.debug("[%s] - Initiating request to kube API" % (dp)) - res = session.put(rqst_url + "/namespaces/%s/routes" % namespace, json=route_payload, auth=( - app.config['kubeApiCreds']['kubeApiUser'], app.config['kubeApiCreds']['kubeApiPass'])) - log.debug("[%s] - The kube API responded with %s" % (dp, res.status_code)) - if res.status_code != 201: - log.debug("[%s] - The kube API could not process the request" % (dp)) - raise Exception("[%s] - Failed to apply routes: %s" % (dp, str(res.text))) - session.close() - except HTTPException as ex: - traceback.print_exc() - log.error("Error updating custom routes. %s" % ex) - abort_early(event_id, 'delete', namespace, jsonify(error="Partially failed.")) - except: - traceback.print_exc() - log.error("Error updating custom routes. %s" % sys.exc_info()[0]) - abort_early(event_id, 'delete', namespace, jsonify(error="Partially failed.")) + dp = get_data_plane(ns_attributes) + if not dp.startswith("sdx-"): + try: + session = requests.Session() + session.headers.update({"Content-Type": "application/json"}) + route_payload = { + "hosts": get_host_list(tempFolder), + "select_tag": selectTag, + "ns_attributes": ns_attributes.getAttrs() + } + rqst_url = app.config['data_planes'][dp]['kube-api'] + log.debug("[%s] - Initiating request to kube API" % (dp)) + res = session.put(rqst_url + "/namespaces/%s/routes" % namespace, json=route_payload, auth=( + app.config['kubeApiCreds']['kubeApiUser'], app.config['kubeApiCreds']['kubeApiPass'])) + log.debug("[%s] - The kube API responded with %s" % (dp, res.status_code)) + if res.status_code != 201: + log.debug("[%s] - The kube API could not process the request" % (dp)) + raise Exception("[%s] - Failed to apply routes: %s" % (dp, str(res.text))) + session.close() + except HTTPException as ex: + traceback.print_exc() + log.error("Error updating custom routes. %s" % ex) + abort_early(event_id, 'delete', namespace, jsonify(error="Partially failed.")) + except: + traceback.print_exc() + log.error("Error updating custom routes. %s" % sys.exc_info()[0]) + abort_early(event_id, 'delete', namespace, jsonify(error="Partially failed.")) cleanup(tempFolder) diff --git a/microservices/gatewayApi/v2/spec/spec.yaml b/microservices/gatewayApi/v2/spec/spec.yaml index c258aabb..f3f8fcea 100644 --- a/microservices/gatewayApi/v2/spec/spec.yaml +++ b/microservices/gatewayApi/v2/spec/spec.yaml @@ -1,6 +1,6 @@ openapi: 3.0.0 info: - version: 2.0.0 + version: 2.1.0 title: Gateway Administration (GWA) API license: name: Apache 2.0 @@ -38,6 +38,100 @@ paths: "500": $ref: "#/components/responses/500InternalServerError" + /namespaces/{namespace}/gateway/pattern-output: + put: + summary: Produces gateway config based on pre-defined patterns + description: Returns the config + tags: + - Gateway + parameters: + - name: namespace + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + type: object + responses: + "200": + description: Success + content: + application/json: + schema: + type: object + "401": + $ref: "#/components/responses/401Unauthorized" + "500": + $ref: "#/components/responses/500InternalServerError" + + /namespaces/{namespace}/gateway/patterns: + put: + summary: Updates the gateway config based on pre-defined patterns + description: Returns the changes that were performed + tags: + - Gateway + parameters: + - name: namespace + in: path + required: true + schema: + type: string + requestBody: + content: + application/json: + schema: + type: object + properties: + pattern: + type: string + description: The pattern to apply + enum: + - sdx-application-r1 + - sdx-service-r1 + ns_qualifier: + type: string + description: The namespace qualifier + service_name: + type: string + description: The name of the service + upstream_uri: + type: string + description: The upstream URI + route_host: + type: string + description: The API host + route_path: + type: string + description: The provider resource locator + mtls_allow_list: + type: array + items: + type: string + description: The allow list for the access point + openid_issuer: + type: string + description: The OpenID Connect issuer URL + openid_audience: + type: string + description: The OpenID Connect audience + openid_scope: + type: string + description: The OpenID Connect scope + responses: + "200": + description: Success + content: + application/json: + schema: + $ref: "#/components/schemas/status" + "401": + $ref: "#/components/responses/401Unauthorized" + "500": + $ref: "#/components/responses/500InternalServerError" + /namespaces/{namespace}/gateway: put: summary: Updates the gateway config diff --git a/microservices/kubeApi/clients/ocp_routes.py b/microservices/kubeApi/clients/ocp_routes.py index 7552623c..3c3bd1bd 100644 --- a/microservices/kubeApi/clients/ocp_routes.py +++ b/microservices/kubeApi/clients/ocp_routes.py @@ -7,6 +7,7 @@ from subprocess import Popen, PIPE, STDOUT from templates.v1.routes import ROUTE, ROUTE_HEAD from templates.v2.routes import V2_ROUTE +from templates.v3.routes import V3_ROUTE from config import settings from cryptography import x509 from cryptography.hazmat.backends import default_backend @@ -29,6 +30,9 @@ }, "v2": { "ROUTE": V2_ROUTE + }, + "v3": { + "ROUTE": V3_ROUTE } } diff --git a/microservices/kubeApi/templates/v3/routes.py b/microservices/kubeApi/templates/v3/routes.py new file mode 100644 index 00000000..af816d1e --- /dev/null +++ b/microservices/kubeApi/templates/v3/routes.py @@ -0,0 +1,42 @@ +from string import Template + +V3_ROUTE = Template(""" +apiVersion: route.openshift.io/v1 +kind: Route +metadata: + name: ${name} + resourceVersion: "${resource_version}" + annotations: + haproxy.router.openshift.io/balance: random + haproxy.router.openshift.io/disable_cookies: 'true' + haproxy.router.openshift.io/timeout: 30m +${data_class_annotation} + labels: + aps-generated-by: "gwa-cli" + aps-published-on: "${fmt_time}" + aps-namespace: "${ns}" + aps-select-tag: "${select_tag}" + aps-published-ts: "${timestamp}" + aps-ssl: "${ssl_ref}" + aps-data-plane: "${data_plane}" + aps-template-version: "${template_version}" +${custom_cert_label} +spec: + host: ${host} + port: + targetPort: kong-proxy-tls + tls: + termination: passthrough + insecureEdgeTerminationPolicy: None + to: + kind: Service + name: ${service_name} + weight: 100 + wildcardPolicy: None +status: + ingress: + - host: ${host} + routerName: router + wildcardPolicy: None +""") +