From f2202357615d8f27a34c5605e0a6bd27cd0908c9 Mon Sep 17 00:00:00 2001 From: Alan Crosswell Date: Fri, 6 Sep 2024 12:42:22 -0400 Subject: [PATCH 01/78] Fix test for changed error message from newer Django (djmain) (#1486) * fix djmain changes the error message text * remove unnecceesary verbose assert message and avoid E501 * conditionalize error message test based on Django version --- tests/test_commands.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/test_commands.py b/tests/test_commands.py index 5204ebf77..c4d359ce5 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -130,6 +130,8 @@ def test_application_created_with_algorithm(self): self.assertEqual(app.algorithm, "RS256") def test_validation_failed_message(self): + import django + output = StringIO() call_command( "createapplication", @@ -140,6 +142,10 @@ def test_validation_failed_message(self): stdout=output, ) - self.assertIn("user", output.getvalue()) - self.assertIn("783", output.getvalue()) - self.assertIn("does not exist", output.getvalue()) + output_str = output.getvalue() + self.assertIn("user", output_str) + self.assertIn("783", output_str) + if django.VERSION < (5, 2): + self.assertIn("does not exist", output_str) + else: + self.assertIn("is not a valid choice", output_str) From 1d19e54c926f475b4b090533cb23d184ae2f39e2 Mon Sep 17 00:00:00 2001 From: Alan Crosswell Date: Sat, 7 Sep 2024 08:42:59 -0400 Subject: [PATCH 02/78] 3.0.1: fix for migration error on upgrade to 3.0.0 (#1491) --- CHANGELOG.md | 4 ++++ oauth2_provider/__init__.py | 2 +- .../migrations/0012_add_token_checksum.py | 20 ++++++++++++++++--- oauth2_provider/models.py | 2 +- 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ba7e52f8..483336b04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Security --> +## [3.0.1] - 2024-09-07 +### Fixed +* #1491 Fix migration error when there are pre-existing Access Tokens. + ## [3.0.0] - 2024-09-05 ### WARNING - POTENTIAL BREAKING CHANGES diff --git a/oauth2_provider/__init__.py b/oauth2_provider/__init__.py index 528787cfc..055276878 100644 --- a/oauth2_provider/__init__.py +++ b/oauth2_provider/__init__.py @@ -1 +1 @@ -__version__ = "3.0.0" +__version__ = "3.0.1" diff --git a/oauth2_provider/migrations/0012_add_token_checksum.py b/oauth2_provider/migrations/0012_add_token_checksum.py index 7f62955e3..476c3b402 100644 --- a/oauth2_provider/migrations/0012_add_token_checksum.py +++ b/oauth2_provider/migrations/0012_add_token_checksum.py @@ -4,6 +4,16 @@ from django.db import migrations, models from oauth2_provider.settings import oauth2_settings +def forwards_func(apps, schema_editor): + """ + Forward migration touches every "old" accesstoken.token which will cause the checksum to be computed. + """ + AccessToken = apps.get_model(oauth2_settings.ACCESS_TOKEN_MODEL) + accesstokens = AccessToken._default_manager.all() + for accesstoken in accesstokens: + accesstoken.save(update_fields=['token_checksum']) + + class Migration(migrations.Migration): dependencies = [ ("oauth2_provider", "0011_refreshtoken_token_family"), @@ -14,13 +24,17 @@ class Migration(migrations.Migration): migrations.AddField( model_name="accesstoken", name="token_checksum", - field=oauth2_provider.models.TokenChecksumField( - blank=True, db_index=True, max_length=64, unique=True - ), + field=oauth2_provider.models.TokenChecksumField(blank=True, null=True, max_length=64), ), migrations.AlterField( model_name="accesstoken", name="token", field=models.TextField(), ), + migrations.RunPython(forwards_func, migrations.RunPython.noop), + migrations.AlterField( + model_name='accesstoken', + name='token_checksum', + field=oauth2_provider.models.TokenChecksumField(blank=False, max_length=64, db_index=True, unique=True), + ), ] diff --git a/oauth2_provider/models.py b/oauth2_provider/models.py index 831fc551f..a987b4435 100644 --- a/oauth2_provider/models.py +++ b/oauth2_provider/models.py @@ -392,7 +392,7 @@ class AbstractAccessToken(models.Model): token = models.TextField() token_checksum = TokenChecksumField( max_length=64, - blank=True, + blank=False, unique=True, db_index=True, ) From 90d7300c06be80e565c6557f0b8f260968748634 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2024 15:08:14 -0400 Subject: [PATCH 03/78] [pre-commit.ci] pre-commit autoupdate (#1492) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.3 → v0.6.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.3...v0.6.4) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b124c7342..371333ad6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.3 + rev: v0.6.4 hooks: - id: ruff args: [ --fix ] From a1538231ce6d076d42b63cb358dfec91d3c10740 Mon Sep 17 00:00:00 2001 From: Alan Crosswell Date: Fri, 20 Sep 2024 11:39:20 -0400 Subject: [PATCH 04/78] deal with 404 or 405 validator error (#1499) * deal with 404 or 405 validator error (apparently varies with version of django) * refactor: more precise test name * mock the post request instead of POSTing to example.com --------- Co-authored-by: Darrel O'Pry --- tests/test_oauth2_validators.py | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/tests/test_oauth2_validators.py b/tests/test_oauth2_validators.py index 31d97f64a..14c74506e 100644 --- a/tests/test_oauth2_validators.py +++ b/tests/test_oauth2_validators.py @@ -3,6 +3,7 @@ import json import pytest +import requests from django.contrib.auth import get_user_model from django.contrib.auth.hashers import make_password from django.utils import timezone @@ -501,18 +502,26 @@ def setUpTestData(cls): cls.introspection_token = "test_introspection_token" cls.validator = OAuth2Validator() - def test_response_when_auth_server_response_return_404(self): - with self.assertLogs(logger="oauth2_provider") as mock_log: - self.validator._get_token_from_authentication_server( - self.token, self.introspection_url, self.introspection_token, None - ) - self.assertIn( - "ERROR:oauth2_provider:Introspection: Failed to " - "get a valid response from authentication server. " - "Status code: 404, Reason: " - "Not Found.\nNoneType: None", - mock_log.output, - ) + def test_response_when_auth_server_response_not_200(self): + """ + Ensure we log the error when the authentication server returns a non-200 response. + """ + mock_response = requests.Response() + mock_response.status_code = 404 + mock_response.reason = "Not Found" + with mock.patch("requests.post") as mock_post: + mock_post.return_value = mock_response + with self.assertLogs(logger="oauth2_provider") as mock_log: + self.validator._get_token_from_authentication_server( + self.token, self.introspection_url, self.introspection_token, None + ) + self.assertIn( + "ERROR:oauth2_provider:Introspection: Failed to " + "get a valid response from authentication server. " + "Status code: 404, Reason: " + "Not Found.\nNoneType: None", + mock_log.output, + ) @pytest.mark.oauth2_settings(presets.OIDC_SETTINGS_RW) From 88f052634a3f958726880310e7a7f99f5a3f8a44 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 20 Sep 2024 11:55:25 -0400 Subject: [PATCH 05/78] [pre-commit.ci] pre-commit autoupdate (#1497) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.4 → v0.6.5](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.4...v0.6.5) - [github.com/sphinx-contrib/sphinx-lint: v0.9.1 → v1.0.0](https://github.com/sphinx-contrib/sphinx-lint/compare/v0.9.1...v1.0.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 371333ad6..a29f52aea 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.4 + rev: v0.6.5 hooks: - id: ruff args: [ --fix ] @@ -17,7 +17,7 @@ repos: - id: mixed-line-ending args: ['--fix=lf'] - repo: https://github.com/sphinx-contrib/sphinx-lint - rev: v0.9.1 + rev: v1.0.0 hooks: - id: sphinx-lint # Configuration for codespell is in pyproject.toml From 610177e8840e3eec6aeca011478de5d44f149aa7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 20 Sep 2024 12:05:24 -0400 Subject: [PATCH 06/78] Bump vite from 5.2.13 to 5.4.6 in /tests/app/rp (#1500) Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.2.13 to 5.4.6. - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/v5.4.6/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v5.4.6/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/app/rp/package-lock.json | 364 +++++++++++++++++---------------- tests/app/rp/package.json | 2 +- 2 files changed, 185 insertions(+), 181 deletions(-) diff --git a/tests/app/rp/package-lock.json b/tests/app/rp/package-lock.json index 627ce8af4..b1836da61 100644 --- a/tests/app/rp/package-lock.json +++ b/tests/app/rp/package-lock.json @@ -20,7 +20,7 @@ "svelte-check": "^3.8.0", "tslib": "^2.4.1", "typescript": "^5.0.0", - "vite": "^5.0.3" + "vite": "^5.4.6" } }, "node_modules/@ampproject/remapping": { @@ -45,9 +45,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", - "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", "cpu": [ "ppc64" ], @@ -61,9 +61,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", - "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", "cpu": [ "arm" ], @@ -77,9 +77,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", - "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", "cpu": [ "arm64" ], @@ -93,9 +93,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", - "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", "cpu": [ "x64" ], @@ -109,9 +109,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", - "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", "cpu": [ "arm64" ], @@ -125,9 +125,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", - "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", "cpu": [ "x64" ], @@ -141,9 +141,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", - "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", "cpu": [ "arm64" ], @@ -157,9 +157,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", - "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", "cpu": [ "x64" ], @@ -173,9 +173,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", - "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", "cpu": [ "arm" ], @@ -189,9 +189,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", - "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", "cpu": [ "arm64" ], @@ -205,9 +205,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", - "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", "cpu": [ "ia32" ], @@ -221,9 +221,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", - "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", "cpu": [ "loong64" ], @@ -237,9 +237,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", - "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", "cpu": [ "mips64el" ], @@ -253,9 +253,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", - "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", "cpu": [ "ppc64" ], @@ -269,9 +269,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", - "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", "cpu": [ "riscv64" ], @@ -285,9 +285,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", - "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", "cpu": [ "s390x" ], @@ -301,9 +301,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", - "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", "cpu": [ "x64" ], @@ -317,9 +317,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", - "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", "cpu": [ "x64" ], @@ -333,9 +333,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", - "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", "cpu": [ "x64" ], @@ -349,9 +349,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", - "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", "cpu": [ "x64" ], @@ -365,9 +365,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", - "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", "cpu": [ "arm64" ], @@ -381,9 +381,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", - "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", "cpu": [ "ia32" ], @@ -397,9 +397,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", - "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", "cpu": [ "x64" ], @@ -656,9 +656,9 @@ "dev": true }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", - "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.3.tgz", + "integrity": "sha512-MmKSfaB9GX+zXl6E8z4koOr/xU63AMVleLEa64v7R0QF/ZloMs5vcD1sHgM64GXXS1csaJutG+ddtzcueI/BLg==", "cpu": [ "arm" ], @@ -669,9 +669,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", - "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.3.tgz", + "integrity": "sha512-zrt8ecH07PE3sB4jPOggweBjJMzI1JG5xI2DIsUbkA+7K+Gkjys6eV7i9pOenNSDJH3eOr/jLb/PzqtmdwDq5g==", "cpu": [ "arm64" ], @@ -682,9 +682,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", - "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.3.tgz", + "integrity": "sha512-P0UxIOrKNBFTQaXTxOH4RxuEBVCgEA5UTNV6Yz7z9QHnUJ7eLX9reOd/NYMO3+XZO2cco19mXTxDMXxit4R/eQ==", "cpu": [ "arm64" ], @@ -695,9 +695,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz", - "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.3.tgz", + "integrity": "sha512-L1M0vKGO5ASKntqtsFEjTq/fD91vAqnzeaF6sfNAy55aD+Hi2pBI5DKwCO+UNDQHWsDViJLqshxOahXyLSh3EA==", "cpu": [ "x64" ], @@ -708,9 +708,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", - "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.3.tgz", + "integrity": "sha512-btVgIsCjuYFKUjopPoWiDqmoUXQDiW2A4C3Mtmp5vACm7/GnyuprqIDPNczeyR5W8rTXEbkmrJux7cJmD99D2g==", "cpu": [ "arm" ], @@ -721,9 +721,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", - "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.3.tgz", + "integrity": "sha512-zmjbSphplZlau6ZTkxd3+NMtE4UKVy7U4aVFMmHcgO5CUbw17ZP6QCgyxhzGaU/wFFdTfiojjbLG3/0p9HhAqA==", "cpu": [ "arm" ], @@ -734,9 +734,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", - "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.3.tgz", + "integrity": "sha512-nSZfcZtAnQPRZmUkUQwZq2OjQciR6tEoJaZVFvLHsj0MF6QhNMg0fQ6mUOsiCUpTqxTx0/O6gX0V/nYc7LrgPw==", "cpu": [ "arm64" ], @@ -747,9 +747,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", - "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.3.tgz", + "integrity": "sha512-MnvSPGO8KJXIMGlQDYfvYS3IosFN2rKsvxRpPO2l2cum+Z3exiExLwVU+GExL96pn8IP+GdH8Tz70EpBhO0sIQ==", "cpu": [ "arm64" ], @@ -760,9 +760,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", - "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.3.tgz", + "integrity": "sha512-+W+p/9QNDr2vE2AXU0qIy0qQE75E8RTwTwgqS2G5CRQ11vzq0tbnfBd6brWhS9bCRjAjepJe2fvvkvS3dno+iw==", "cpu": [ "ppc64" ], @@ -773,9 +773,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", - "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.3.tgz", + "integrity": "sha512-yXH6K6KfqGXaxHrtr+Uoy+JpNlUlI46BKVyonGiaD74ravdnF9BUNC+vV+SIuB96hUMGShhKV693rF9QDfO6nQ==", "cpu": [ "riscv64" ], @@ -786,9 +786,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", - "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.3.tgz", + "integrity": "sha512-R8cwY9wcnApN/KDYWTH4gV/ypvy9yZUHlbJvfaiXSB48JO3KpwSpjOGqO4jnGkLDSk1hgjYkTbTt6Q7uvPf8eg==", "cpu": [ "s390x" ], @@ -799,9 +799,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", - "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.3.tgz", + "integrity": "sha512-kZPbX/NOPh0vhS5sI+dR8L1bU2cSO9FgxwM8r7wHzGydzfSjLRCFAT87GR5U9scj2rhzN3JPYVC7NoBbl4FZ0g==", "cpu": [ "x64" ], @@ -812,9 +812,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", - "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.3.tgz", + "integrity": "sha512-S0Yq+xA1VEH66uiMNhijsWAafffydd2X5b77eLHfRmfLsRSpbiAWiRHV6DEpz6aOToPsgid7TI9rGd6zB1rhbg==", "cpu": [ "x64" ], @@ -825,9 +825,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", - "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.3.tgz", + "integrity": "sha512-9isNzeL34yquCPyerog+IMCNxKR8XYmGd0tHSV+OVx0TmE0aJOo9uw4fZfUuk2qxobP5sug6vNdZR6u7Mw7Q+Q==", "cpu": [ "arm64" ], @@ -838,9 +838,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", - "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.3.tgz", + "integrity": "sha512-nMIdKnfZfzn1Vsk+RuOvl43ONTZXoAPUUxgcU0tXooqg4YrAqzfKzVenqqk2g5efWh46/D28cKFrOzDSW28gTA==", "cpu": [ "ia32" ], @@ -851,9 +851,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", - "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.3.tgz", + "integrity": "sha512-fOvu7PCQjAj4eWDEuD8Xz5gpzFqXzGlxHZozHP4b9Jxv9APtdxL6STqztDzMLuRXEc4UpXGGhx029Xgm91QBeA==", "cpu": [ "x64" ], @@ -1274,9 +1274,9 @@ "dev": true }, "node_modules/esbuild": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", - "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "dev": true, "hasInstallScript": true, "bin": { @@ -1286,29 +1286,29 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.20.2", - "@esbuild/android-arm": "0.20.2", - "@esbuild/android-arm64": "0.20.2", - "@esbuild/android-x64": "0.20.2", - "@esbuild/darwin-arm64": "0.20.2", - "@esbuild/darwin-x64": "0.20.2", - "@esbuild/freebsd-arm64": "0.20.2", - "@esbuild/freebsd-x64": "0.20.2", - "@esbuild/linux-arm": "0.20.2", - "@esbuild/linux-arm64": "0.20.2", - "@esbuild/linux-ia32": "0.20.2", - "@esbuild/linux-loong64": "0.20.2", - "@esbuild/linux-mips64el": "0.20.2", - "@esbuild/linux-ppc64": "0.20.2", - "@esbuild/linux-riscv64": "0.20.2", - "@esbuild/linux-s390x": "0.20.2", - "@esbuild/linux-x64": "0.20.2", - "@esbuild/netbsd-x64": "0.20.2", - "@esbuild/openbsd-x64": "0.20.2", - "@esbuild/sunos-x64": "0.20.2", - "@esbuild/win32-arm64": "0.20.2", - "@esbuild/win32-ia32": "0.20.2", - "@esbuild/win32-x64": "0.20.2" + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, "node_modules/esm-env": { @@ -1791,9 +1791,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", "dev": true }, "node_modules/picomatch": { @@ -1809,9 +1809,9 @@ } }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "dev": true, "funding": [ { @@ -1829,8 +1829,8 @@ ], "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -1951,9 +1951,9 @@ } }, "node_modules/rollup": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", - "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.3.tgz", + "integrity": "sha512-7sqRtBNnEbcBtMeRVc6VRsJMmpI+JU1z9VTvW8D4gXIYQFz0aLcsE6rRkyghZkLfEgUZgVvOG7A5CVz/VW5GIA==", "dev": true, "dependencies": { "@types/estree": "1.0.5" @@ -1966,22 +1966,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.18.0", - "@rollup/rollup-android-arm64": "4.18.0", - "@rollup/rollup-darwin-arm64": "4.18.0", - "@rollup/rollup-darwin-x64": "4.18.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", - "@rollup/rollup-linux-arm-musleabihf": "4.18.0", - "@rollup/rollup-linux-arm64-gnu": "4.18.0", - "@rollup/rollup-linux-arm64-musl": "4.18.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", - "@rollup/rollup-linux-riscv64-gnu": "4.18.0", - "@rollup/rollup-linux-s390x-gnu": "4.18.0", - "@rollup/rollup-linux-x64-gnu": "4.18.0", - "@rollup/rollup-linux-x64-musl": "4.18.0", - "@rollup/rollup-win32-arm64-msvc": "4.18.0", - "@rollup/rollup-win32-ia32-msvc": "4.18.0", - "@rollup/rollup-win32-x64-msvc": "4.18.0", + "@rollup/rollup-android-arm-eabi": "4.21.3", + "@rollup/rollup-android-arm64": "4.21.3", + "@rollup/rollup-darwin-arm64": "4.21.3", + "@rollup/rollup-darwin-x64": "4.21.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.21.3", + "@rollup/rollup-linux-arm-musleabihf": "4.21.3", + "@rollup/rollup-linux-arm64-gnu": "4.21.3", + "@rollup/rollup-linux-arm64-musl": "4.21.3", + "@rollup/rollup-linux-powerpc64le-gnu": "4.21.3", + "@rollup/rollup-linux-riscv64-gnu": "4.21.3", + "@rollup/rollup-linux-s390x-gnu": "4.21.3", + "@rollup/rollup-linux-x64-gnu": "4.21.3", + "@rollup/rollup-linux-x64-musl": "4.21.3", + "@rollup/rollup-win32-arm64-msvc": "4.21.3", + "@rollup/rollup-win32-ia32-msvc": "4.21.3", + "@rollup/rollup-win32-x64-msvc": "4.21.3", "fsevents": "~2.3.2" } }, @@ -2095,9 +2095,9 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, "engines": { "node": ">=0.10.0" @@ -2312,14 +2312,14 @@ } }, "node_modules/vite": { - "version": "5.2.13", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.13.tgz", - "integrity": "sha512-SSq1noJfY9pR3I1TUENL3rQYDQCFqgD+lM6fTRAM8Nv6Lsg5hDLaXkjETVeBt+7vZBCMoibD+6IWnT2mJ+Zb/A==", + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.6.tgz", + "integrity": "sha512-IeL5f8OO5nylsgzd9tq4qD2QqI0k2CQLGrWD0rCN0EQJZpBK5vJAx0I+GDkMOXxQX/OfFHMuLIx6ddAxGX/k+Q==", "dev": true, "dependencies": { - "esbuild": "^0.20.1", - "postcss": "^8.4.38", - "rollup": "^4.13.0" + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" @@ -2338,6 +2338,7 @@ "less": "*", "lightningcss": "^1.21.0", "sass": "*", + "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" @@ -2355,6 +2356,9 @@ "sass": { "optional": true }, + "sass-embedded": { + "optional": true + }, "stylus": { "optional": true }, diff --git a/tests/app/rp/package.json b/tests/app/rp/package.json index 8caf72fe6..7f784006f 100644 --- a/tests/app/rp/package.json +++ b/tests/app/rp/package.json @@ -21,7 +21,7 @@ "svelte-check": "^3.8.0", "tslib": "^2.4.1", "typescript": "^5.0.0", - "vite": "^5.0.3" + "vite": "^5.4.6" }, "type": "module", "dependencies": { From e34819a51da022279c4b0b14dfdae8d01adf5d27 Mon Sep 17 00:00:00 2001 From: Darrel O'Pry Date: Fri, 20 Sep 2024 12:13:38 -0400 Subject: [PATCH 07/78] feat: VS Code Testing Activity Support (#1501) --- .env | 2 ++ .vscode/settings.json | 8 ++++++++ docs/contributing.rst | 21 +++++++++++++++++++++ pyproject.toml | 8 ++++++++ 4 files changed, 39 insertions(+) create mode 100644 .env create mode 100644 .vscode/settings.json diff --git a/.env b/.env new file mode 100644 index 000000000..dc223bf0b --- /dev/null +++ b/.env @@ -0,0 +1,2 @@ +# required for vscode testing activity to discover tests +DJANGO_SETTINGS_MODULE=tests.settings \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..fee847fe4 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "python.testing.pytestArgs": [ + "tests", + "--no-cov" + ], + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true +} \ No newline at end of file diff --git a/docs/contributing.rst b/docs/contributing.rst index 648993024..4f0b88b32 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -272,6 +272,27 @@ In test code, anywhere the database is referenced the Django router needs to be Without the 'using' option, this test fails in the multiple database scenario because 'default' will be used instead. +Debugging the Tests Interactively +--------------------------------- + +Interactive Debugging allows you to set breakpoints and inspect the state of the program at runtime. We strongly +recommend using an interactive debugger to streamline your development process. + +VS Code +^^^^^^^ + +VS Code is a popular IDE that supports debugging Python code. You can debug the tests interactively in VS Code by +following these steps: + +.. code-block:: bash + + pip install .[dev] + # open the project in VS Code + # click Testing (erlenmeyer flask) on the Activity Bar + # select the test you want to run or debug + + + Code conventions matter ----------------------- diff --git a/pyproject.toml b/pyproject.toml index ccd154d4d..401d33cab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,6 +40,14 @@ dependencies = [ "jwcrypto >= 1.5.0", ] +[project.optional-dependencies] +dev = [ + "pytest", + "pytest-cov", + "m2r", + "sphinx-rtd-theme", +] + [project.urls] Homepage = "https://django-oauth-toolkit.readthedocs.io/" Repository = "https://github.com/jazzband/django-oauth-toolkit" From 937ae211d7c239cde79359ca85881cad5177dbbb Mon Sep 17 00:00:00 2001 From: Matej Spiller Muys Date: Sun, 22 Sep 2024 19:56:25 +0200 Subject: [PATCH 08/78] Support for specifying client secret hasher (#1498) Co-authored-by: Matej Spiller Muys --- AUTHORS | 1 + CHANGELOG.md | 1 + docs/settings.rst | 4 ++++ oauth2_provider/models.py | 2 +- oauth2_provider/settings.py | 1 + tests/custom_hasher.py | 10 ++++++++++ tests/settings.py | 2 ++ tests/test_models.py | 16 ++++++++++++++++ 8 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 tests/custom_hasher.py diff --git a/AUTHORS b/AUTHORS index 431edeabd..d10ff1fb4 100644 --- a/AUTHORS +++ b/AUTHORS @@ -86,6 +86,7 @@ Ludwig Hähne Łukasz Skarżyński Madison Swain-Bowden Marcus Sonestedt +Matej Spiller Muys Matias Seniquiel Michael Howitz Owen Gong diff --git a/CHANGELOG.md b/CHANGELOG.md index 483336b04..39e11d4b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 +--> + ## [3.0.1] - 2024-09-07 ### Fixed diff --git a/docs/settings.rst b/docs/settings.rst index 0b76129f9..545736ccb 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -63,6 +63,37 @@ assigned ports. Note that you may override ``Application.get_allowed_schemes()`` to set this on a per-application basis. +ALLOW_URI_WILDCARDS +~~~~~~~~~~~~~~~~~~~ + +Default: ``False`` + +SECURITY WARNING: Enabling this setting can introduce security vulnerabilities. Only enable +this setting if you understand the risks. https://datatracker.ietf.org/doc/html/rfc6749#section-3.1.2 +states "The redirection endpoint URI MUST be an absolute URI as defined by [RFC3986] Section 4.3." The +intent of the URI restrictions is to prevent open redirects and phishing attacks. If you do enable this +ensure that the wildcards restrict URIs to resources under your control. You are strongly encouragd not +to use this feature in production. + +When set to ``True``, the server will allow wildcard characters in the domains for allowed_origins and +redirect_uris. + +``*`` is the only wildcard character allowed. + +``*`` can only be used as a prefix to a domain, must be the first character in +the domain, and cannot be in the top or second level domain. Matching is done using an +endsWith check. + +For example, +``https://*.example.com`` is allowed, +``https://*-myproject.example.com`` is allowed, +``https://*.sub.example.com`` is not allowed, +``https://*.com`` is not allowed, and +``https://example.*.com`` is not allowed. + +This feature is useful for working with CI service such as cloudflare, netlify, and vercel that offer branch +deployments for development previews and user acceptance testing. + ALLOWED_SCHEMES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/oauth2_provider/models.py b/oauth2_provider/models.py index 621ce5b34..0467ddfa1 100644 --- a/oauth2_provider/models.py +++ b/oauth2_provider/models.py @@ -213,7 +213,11 @@ def clean(self): if redirect_uris: validator = AllowedURIValidator( - allowed_schemes, name="redirect uri", allow_path=True, allow_query=True + allowed_schemes, + name="redirect uri", + allow_path=True, + allow_query=True, + allow_hostname_wildcard=oauth2_settings.ALLOW_URI_WILDCARDS, ) for uri in redirect_uris: validator(uri) @@ -227,7 +231,11 @@ def clean(self): allowed_origins = self.allowed_origins.strip().split() if allowed_origins: # oauthlib allows only https scheme for CORS - validator = AllowedURIValidator(oauth2_settings.ALLOWED_SCHEMES, "allowed origin") + validator = AllowedURIValidator( + oauth2_settings.ALLOWED_SCHEMES, + "allowed origin", + allow_hostname_wildcard=oauth2_settings.ALLOW_URI_WILDCARDS, + ) for uri in allowed_origins: validator(uri) @@ -777,12 +785,28 @@ def redirect_to_uri_allowed(uri, allowed_uris): :param allowed_uris: A list of URIs that are allowed """ + if not isinstance(allowed_uris, list): + raise ValueError("allowed_uris must be a list") + parsed_uri = urlparse(uri) uqs_set = set(parse_qsl(parsed_uri.query)) for allowed_uri in allowed_uris: parsed_allowed_uri = urlparse(allowed_uri) + if parsed_allowed_uri.scheme != parsed_uri.scheme: + # match failed, continue + continue + + """ check hostname """ + if oauth2_settings.ALLOW_URI_WILDCARDS and parsed_allowed_uri.hostname.startswith("*"): + """ wildcard hostname """ + if not parsed_uri.hostname.endswith(parsed_allowed_uri.hostname[1:]): + continue + elif parsed_allowed_uri.hostname != parsed_uri.hostname: + continue + # From RFC 8252 (Section 7.3) + # https://datatracker.ietf.org/doc/html/rfc8252#section-7.3 # # Loopback redirect URIs use the "http" scheme # [...] @@ -790,26 +814,26 @@ def redirect_to_uri_allowed(uri, allowed_uris): # time of the request for loopback IP redirect URIs, to accommodate # clients that obtain an available ephemeral port from the operating # system at the time of the request. + allowed_uri_is_loopback = parsed_allowed_uri.scheme == "http" and parsed_allowed_uri.hostname in [ + "127.0.0.1", + "::1", + ] + """ check port """ + if not allowed_uri_is_loopback and parsed_allowed_uri.port != parsed_uri.port: + continue + + """ check path """ + if parsed_allowed_uri.path != parsed_uri.path: + continue + + """ check querystring """ + aqs_set = set(parse_qsl(parsed_allowed_uri.query)) + if not aqs_set.issubset(uqs_set): + continue # circuit break - allowed_uri_is_loopback = ( - parsed_allowed_uri.scheme == "http" - and parsed_allowed_uri.hostname in ["127.0.0.1", "::1"] - and parsed_allowed_uri.port is None - ) - if ( - allowed_uri_is_loopback - and parsed_allowed_uri.scheme == parsed_uri.scheme - and parsed_allowed_uri.hostname == parsed_uri.hostname - and parsed_allowed_uri.path == parsed_uri.path - ) or ( - parsed_allowed_uri.scheme == parsed_uri.scheme - and parsed_allowed_uri.netloc == parsed_uri.netloc - and parsed_allowed_uri.path == parsed_uri.path - ): - aqs_set = set(parse_qsl(parsed_allowed_uri.query)) - if aqs_set.issubset(uqs_set): - return True + return True + # if uris matched then it's not allowed return False @@ -833,4 +857,5 @@ def is_origin_allowed(origin, allowed_origins): and parsed_allowed_origin.netloc == parsed_origin.netloc ): return True + return False diff --git a/oauth2_provider/settings.py b/oauth2_provider/settings.py index f5a6a25d6..9771aa4e7 100644 --- a/oauth2_provider/settings.py +++ b/oauth2_provider/settings.py @@ -71,6 +71,7 @@ "REQUEST_APPROVAL_PROMPT": "force", "ALLOWED_REDIRECT_URI_SCHEMES": ["http", "https"], "ALLOWED_SCHEMES": ["https"], + "ALLOW_URI_WILDCARDS": False, "OIDC_ENABLED": False, "OIDC_ISS_ENDPOINT": "", "OIDC_USERINFO_ENDPOINT": "", diff --git a/oauth2_provider/validators.py b/oauth2_provider/validators.py index b238b12d6..b2370cfd0 100644 --- a/oauth2_provider/validators.py +++ b/oauth2_provider/validators.py @@ -21,7 +21,15 @@ class URIValidator(URLValidator): class AllowedURIValidator(URIValidator): # TODO: find a way to get these associated with their form fields in place of passing name # TODO: submit PR to get `cause` included in the parent class ValidationError params` - def __init__(self, schemes, name, allow_path=False, allow_query=False, allow_fragments=False): + def __init__( + self, + schemes, + name, + allow_path=False, + allow_query=False, + allow_fragments=False, + allow_hostname_wildcard=False, + ): """ :param schemes: List of allowed schemes. E.g.: ["https"] :param name: Name of the validated URI. It is required for validation message. E.g.: "Origin" @@ -34,6 +42,7 @@ def __init__(self, schemes, name, allow_path=False, allow_query=False, allow_fra self.allow_path = allow_path self.allow_query = allow_query self.allow_fragments = allow_fragments + self.allow_hostname_wildcard = allow_hostname_wildcard def __call__(self, value): value = force_str(value) @@ -68,8 +77,57 @@ def __call__(self, value): params={"name": self.name, "value": value, "cause": "path not allowed"}, ) + if self.allow_hostname_wildcard and "*" in netloc: + domain_parts = netloc.split(".") + if netloc.count("*") > 1: + raise ValidationError( + "%(name)s URI validation error. %(cause)s: %(value)s", + params={ + "name": self.name, + "value": value, + "cause": "only one wildcard is allowed in the hostname", + }, + ) + if not netloc.startswith("*"): + raise ValidationError( + "%(name)s URI validation error. %(cause)s: %(value)s", + params={ + "name": self.name, + "value": value, + "cause": "wildcards must be at the beginning of the hostname", + }, + ) + if len(domain_parts) < 3: + raise ValidationError( + "%(name)s URI validation error. %(cause)s: %(value)s", + params={ + "name": self.name, + "value": value, + "cause": "wildcards cannot be in the top level or second level domain", + }, + ) + + # strip the wildcard from the netloc, we'll reassamble the value later to pass to URI Validator + if netloc.startswith("*."): + netloc = netloc[2:] + else: + netloc = netloc[1:] + + # domains cannot start with a hyphen, but can have them in the middle, so we strip hyphens + # after the wildcard so the final domain is valid and will succeed in URIVAlidator + if netloc.startswith("-"): + netloc = netloc[1:] + + # we stripped the wildcard from the netloc and path if they were allowed and present since they would + # fail validation we'll reassamble the URI to pass to the URIValidator + reassambled_uri = f"{scheme}://{netloc}{path}" + if query: + reassambled_uri += f"?{query}" + if fragment: + reassambled_uri += f"#{fragment}" + try: - super().__call__(value) + super().__call__(reassambled_uri) except ValidationError as e: raise ValidationError( "%(name)s URI validation error. %(cause)s: %(value)s", diff --git a/tests/test_application_views.py b/tests/test_application_views.py index 88617807d..d4c7e28a9 100644 --- a/tests/test_application_views.py +++ b/tests/test_application_views.py @@ -63,6 +63,156 @@ def test_application_registration_user(self): self.assertEqual(app.algorithm, form_data["algorithm"]) +@pytest.mark.usefixtures("oauth2_settings") +@pytest.mark.oauth2_settings({"ALLOW_URI_WILDCARDS": True}) +class TestApplicationRegistrationViewRedirectURIWithWildcard(BaseTest): + def _test_valid(self, redirect_uri): + self.client.login(username="foo_user", password="123456") + + form_data = { + "name": "Foo app", + "client_id": "client_id", + "client_secret": "client_secret", + "client_type": Application.CLIENT_CONFIDENTIAL, + "redirect_uris": redirect_uri, + "post_logout_redirect_uris": "http://example.com", + "authorization_grant_type": Application.GRANT_AUTHORIZATION_CODE, + "algorithm": "", + } + + response = self.client.post(reverse("oauth2_provider:register"), form_data) + self.assertEqual(response.status_code, 302) + + app = get_application_model().objects.get(name="Foo app") + self.assertEqual(app.user.username, "foo_user") + app = Application.objects.get() + self.assertEqual(app.name, form_data["name"]) + self.assertEqual(app.client_id, form_data["client_id"]) + self.assertEqual(app.redirect_uris, form_data["redirect_uris"]) + self.assertEqual(app.post_logout_redirect_uris, form_data["post_logout_redirect_uris"]) + self.assertEqual(app.client_type, form_data["client_type"]) + self.assertEqual(app.authorization_grant_type, form_data["authorization_grant_type"]) + self.assertEqual(app.algorithm, form_data["algorithm"]) + + def _test_invalid(self, uri, error_message): + self.client.login(username="foo_user", password="123456") + + form_data = { + "name": "Foo app", + "client_id": "client_id", + "client_secret": "client_secret", + "client_type": Application.CLIENT_CONFIDENTIAL, + "redirect_uris": uri, + "post_logout_redirect_uris": "http://example.com", + "authorization_grant_type": Application.GRANT_AUTHORIZATION_CODE, + "algorithm": "", + } + + response = self.client.post(reverse("oauth2_provider:register"), form_data) + self.assertEqual(response.status_code, 200) + self.assertContains(response, error_message) + + def test_application_registration_valid_3ld_wildcard(self): + self._test_valid("https://*.example.com") + + def test_application_registration_valid_3ld_partial_wildcard(self): + self._test_valid("https://*-partial.example.com") + + def test_application_registration_invalid_star(self): + self._test_invalid("*", "invalid_scheme: *") + + def test_application_registration_invalid_tld_wildcard(self): + self._test_invalid("https://*", "wildcards cannot be in the top level or second level domain") + + def test_application_registration_invalid_tld_partial_wildcard(self): + self._test_invalid("https://*-partial", "wildcards cannot be in the top level or second level domain") + + def test_application_registration_invalid_tld_not_startswith_wildcard_tld(self): + self._test_invalid("https://example.*", "wildcards must be at the beginning of the hostname") + + def test_application_registration_invalid_2ld_wildcard(self): + self._test_invalid("https://*.com", "wildcards cannot be in the top level or second level domain") + + def test_application_registration_invalid_2ld_partial_wildcard(self): + self._test_invalid( + "https://*-partial.com", "wildcards cannot be in the top level or second level domain" + ) + + def test_application_registration_invalid_2ld_not_startswith_wildcard_tld(self): + self._test_invalid("https://example.*.com", "wildcards must be at the beginning of the hostname") + + def test_application_registration_invalid_3ld_partial_not_startswith_wildcard_2ld(self): + self._test_invalid( + "https://invalid-*.example.com", "wildcards must be at the beginning of the hostname" + ) + + def test_application_registration_invalid_4ld_not_startswith_wildcard_3ld(self): + self._test_invalid( + "https://invalid.*.invalid.example.com", + "wildcards must be at the beginning of the hostname", + ) + + def test_application_registration_invalid_4ld_partial_not_startswith_wildcard_2ld(self): + self._test_invalid( + "https://invalid-*.invalid.example.com", + "wildcards must be at the beginning of the hostname", + ) + + +@pytest.mark.usefixtures("oauth2_settings") +@pytest.mark.oauth2_settings({"ALLOW_URI_WILDCARDS": True}) +class TestApplicationRegistrationViewAllowedOriginWithWildcard( + TestApplicationRegistrationViewRedirectURIWithWildcard +): + def _test_valid(self, uris): + self.client.login(username="foo_user", password="123456") + + form_data = { + "name": "Foo app", + "client_id": "client_id", + "client_secret": "client_secret", + "client_type": Application.CLIENT_CONFIDENTIAL, + "allowed_origins": uris, + "redirect_uris": "https://example.com", + "post_logout_redirect_uris": "http://example.com", + "authorization_grant_type": Application.GRANT_AUTHORIZATION_CODE, + "algorithm": "", + } + + response = self.client.post(reverse("oauth2_provider:register"), form_data) + self.assertEqual(response.status_code, 302) + + app = get_application_model().objects.get(name="Foo app") + self.assertEqual(app.user.username, "foo_user") + app = Application.objects.get() + self.assertEqual(app.name, form_data["name"]) + self.assertEqual(app.client_id, form_data["client_id"]) + self.assertEqual(app.redirect_uris, form_data["redirect_uris"]) + self.assertEqual(app.post_logout_redirect_uris, form_data["post_logout_redirect_uris"]) + self.assertEqual(app.client_type, form_data["client_type"]) + self.assertEqual(app.authorization_grant_type, form_data["authorization_grant_type"]) + self.assertEqual(app.algorithm, form_data["algorithm"]) + + def _test_invalid(self, uri, error_message): + self.client.login(username="foo_user", password="123456") + + form_data = { + "name": "Foo app", + "client_id": "client_id", + "client_secret": "client_secret", + "client_type": Application.CLIENT_CONFIDENTIAL, + "allowed_origins": uri, + "redirect_uris": "http://example.com", + "post_logout_redirect_uris": "http://example.com", + "authorization_grant_type": Application.GRANT_AUTHORIZATION_CODE, + "algorithm": "", + } + + response = self.client.post(reverse("oauth2_provider:register"), form_data) + self.assertEqual(response.status_code, 200) + self.assertContains(response, error_message) + + class TestApplicationViews(BaseTest): @classmethod def _create_application(cls, name, user): diff --git a/tests/test_models.py b/tests/test_models.py index 123c41b35..32ca07627 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -16,6 +16,7 @@ get_grant_model, get_id_token_model, get_refresh_token_model, + redirect_to_uri_allowed, ) from . import presets @@ -622,6 +623,79 @@ def test_application_clean(oauth2_settings, application): application.clean() +def _test_wildcard_redirect_uris_valid(oauth2_settings, application, uris): + oauth2_settings.ALLOW_URI_WILDCARDS = True + application.redirect_uris = uris + application.clean() + + +def _test_wildcard_redirect_uris_invalid(oauth2_settings, application, uris): + oauth2_settings.ALLOW_URI_WILDCARDS = True + application.redirect_uris = uris + with pytest.raises(ValidationError): + application.clean() + + +@pytest.mark.django_db(databases=retrieve_current_databases()) +@pytest.mark.oauth2_settings(presets.OIDC_SETTINGS_RW) +def test_application_clean_wildcard_redirect_uris_valid_3ld(oauth2_settings, application): + _test_wildcard_redirect_uris_valid(oauth2_settings, application, "https://*.example.com/path") + + +@pytest.mark.django_db(databases=retrieve_current_databases()) +@pytest.mark.oauth2_settings(presets.OIDC_SETTINGS_RW) +def test_application_clean_wildcard_redirect_uris_valid_partial_3ld(oauth2_settings, application): + _test_wildcard_redirect_uris_valid(oauth2_settings, application, "https://*-partial.example.com/path") + + +@pytest.mark.django_db(databases=retrieve_current_databases()) +@pytest.mark.oauth2_settings(presets.OIDC_SETTINGS_RW) +def test_application_clean_wildcard_redirect_uris_invalid_3ld_not_starting_with_wildcard( + oauth2_settings, application +): + _test_wildcard_redirect_uris_invalid(oauth2_settings, application, "https://invalid-*.example.com/path") + + +@pytest.mark.django_db(databases=retrieve_current_databases()) +@pytest.mark.oauth2_settings(presets.OIDC_SETTINGS_RW) +def test_application_clean_wildcard_redirect_uris_invalid_2ld(oauth2_settings, application): + _test_wildcard_redirect_uris_invalid(oauth2_settings, application, "https://*.com/path") + + +@pytest.mark.django_db(databases=retrieve_current_databases()) +@pytest.mark.oauth2_settings(presets.OIDC_SETTINGS_RW) +def test_application_clean_wildcard_redirect_uris_invalid_partial_2ld(oauth2_settings, application): + _test_wildcard_redirect_uris_invalid(oauth2_settings, application, "https://*-partial.com/path") + + +@pytest.mark.django_db(databases=retrieve_current_databases()) +@pytest.mark.oauth2_settings(presets.OIDC_SETTINGS_RW) +def test_application_clean_wildcard_redirect_uris_invalid_2ld_not_starting_with_wildcard( + oauth2_settings, application +): + _test_wildcard_redirect_uris_invalid(oauth2_settings, application, "https://invalid-*.com/path") + + +@pytest.mark.django_db(databases=retrieve_current_databases()) +@pytest.mark.oauth2_settings(presets.OIDC_SETTINGS_RW) +def test_application_clean_wildcard_redirect_uris_invalid_tld(oauth2_settings, application): + _test_wildcard_redirect_uris_invalid(oauth2_settings, application, "https://*/path") + + +@pytest.mark.django_db(databases=retrieve_current_databases()) +@pytest.mark.oauth2_settings(presets.OIDC_SETTINGS_RW) +def test_application_clean_wildcard_redirect_uris_invalid_tld_partial(oauth2_settings, application): + _test_wildcard_redirect_uris_invalid(oauth2_settings, application, "https://*-partial/path") + + +@pytest.mark.django_db(databases=retrieve_current_databases()) +@pytest.mark.oauth2_settings(presets.OIDC_SETTINGS_RW) +def test_application_clean_wildcard_redirect_uris_invalid_tld_not_starting_with_wildcard( + oauth2_settings, application +): + _test_wildcard_redirect_uris_invalid(oauth2_settings, application, "https://invalid-*/path") + + @pytest.mark.django_db(databases=retrieve_current_databases()) @pytest.mark.oauth2_settings(presets.ALLOWED_SCHEMES_DEFAULT) def test_application_origin_allowed_default_https(oauth2_settings, cors_application): @@ -636,3 +710,35 @@ def test_application_origin_allowed_http(oauth2_settings, cors_application): """Test that http schemes are allowed because http was added to ALLOWED_SCHEMES""" assert cors_application.origin_allowed("https://example.com") assert cors_application.origin_allowed("http://example.com") + + +def test_redirect_to_uri_allowed_expects_allowed_uri_list(): + with pytest.raises(ValueError): + redirect_to_uri_allowed("https://example.com", "https://example.com") + assert redirect_to_uri_allowed("https://example.com", ["https://example.com"]) + + +valid_wildcard_redirect_to_params = [ + ("https://valid.example.com", ["https://*.example.com"]), + ("https://valid.valid.example.com", ["https://*.example.com"]), + ("https://valid-partial.example.com", ["https://*-partial.example.com"]), + ("https://valid.valid-partial.example.com", ["https://*-partial.example.com"]), +] + + +@pytest.mark.parametrize("uri, allowed_uri", valid_wildcard_redirect_to_params) +def test_wildcard_redirect_to_uri_allowed_valid(uri, allowed_uri, oauth2_settings): + oauth2_settings.ALLOW_URI_WILDCARDS = True + assert redirect_to_uri_allowed(uri, allowed_uri) + + +invalid_wildcard_redirect_to_params = [ + ("https://invalid.com", ["https://*.example.com"]), + ("https://invalid.example.com", ["https://*-partial.example.com"]), +] + + +@pytest.mark.parametrize("uri, allowed_uri", invalid_wildcard_redirect_to_params) +def test_wildcard_redirect_to_uri_allowed_invalid(uri, allowed_uri, oauth2_settings): + oauth2_settings.ALLOW_URI_WILDCARDS = True + assert not redirect_to_uri_allowed(uri, allowed_uri) diff --git a/tests/test_validators.py b/tests/test_validators.py index eb382c154..a77a1e16a 100644 --- a/tests/test_validators.py +++ b/tests/test_validators.py @@ -171,3 +171,27 @@ def test_allow_fragment_invalid_urls(self): for uri in bad_uris: with self.assertRaises(ValidationError): validator(uri) + + def test_allow_hostname_wildcard(self): + validator = AllowedURIValidator(["https"], "test", allow_hostname_wildcard=True) + good_uris = [ + "https://*.example.com", + "https://*-partial.example.com", + "https://*.partial.example.com", + "https://*-partial.valid.example.com", + ] + for uri in good_uris: + # Check ValidationError not thrown + validator(uri) + + bad_uris = [ + "https://*/", + "https://*-partial", + "https://*.com", + "https://*-partial.com", + "https://*.*.example.com", + "https://invalid.*.example.com", + ] + for uri in bad_uris: + with self.assertRaises(ValidationError): + validator(uri) From ce34da45a5254c44726e33ff68e4185eb673ceba Mon Sep 17 00:00:00 2001 From: Jaap Roes Date: Mon, 7 Oct 2024 19:20:37 +0200 Subject: [PATCH 13/78] Add client_secret to sensitive_post_parameters (#1512) The client_secret is posted to the token endpoint when using the client_credentials grant. --- oauth2_provider/views/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/oauth2_provider/views/base.py b/oauth2_provider/views/base.py index 1e0d12dea..c5c904b14 100644 --- a/oauth2_provider/views/base.py +++ b/oauth2_provider/views/base.py @@ -292,7 +292,7 @@ class TokenView(OAuthLibMixin, View): * Client credentials """ - @method_decorator(sensitive_post_parameters("password")) + @method_decorator(sensitive_post_parameters("password", "client_secret")) def post(self, request, *args, **kwargs): url, headers, body, status = self.create_token_response(request) if status == 200: From eed322a33392521b3cb8e440426a1bc3d53de887 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 7 Oct 2024 15:59:13 -0400 Subject: [PATCH 14/78] [pre-commit.ci] pre-commit autoupdate (#1513) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.8 → v0.6.9](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.8...v0.6.9) - [github.com/pre-commit/pre-commit-hooks: v4.6.0 → v5.0.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.6.0...v5.0.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f2046a327..7a50f8d2b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,12 +1,12 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.8 + rev: v0.6.9 hooks: - id: ruff args: [ --fix ] - id: ruff-format - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v5.0.0 hooks: - id: check-ast - id: trailing-whitespace From f2f2e247bee66a8db692734fa2d431aeffbf3e60 Mon Sep 17 00:00:00 2001 From: Alan Crosswell Date: Mon, 7 Oct 2024 18:03:31 -0400 Subject: [PATCH 15/78] bump to actions/setup-node@v4 (#1514) --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f0bf9f155..d0521b5bc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -78,7 +78,7 @@ jobs: uses: actions/checkout@v4 - name: Set up NodeJS - uses: actions/setup-node@v2 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} From 28b512a677e8d916392865dd2802616285b864d5 Mon Sep 17 00:00:00 2001 From: Dulmandakh Date: Tue, 8 Oct 2024 06:14:53 +0800 Subject: [PATCH 16/78] Bump actions/cache to v4 (#1516) --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d0521b5bc..e3073dc7a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -40,7 +40,7 @@ jobs: echo "::set-output name=dir::$(pip cache dir)" - name: Cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ steps.pip-cache.outputs.dir }} key: From 59bd7af7d6c2b1126e28ea40c5a7618bbbe754ab Mon Sep 17 00:00:00 2001 From: Darrel O'Pry Date: Wed, 9 Oct 2024 11:11:17 -0400 Subject: [PATCH 17/78] fix: OP prompts for logout when no OP session (#1517) The OAuth provider is prompting users who no longer have an user session with the OAuth Provider to logout of the OP. This happens in scenarios given the user has logged out of the OP directly or via another client. In cases where the user does not have a session on the OP we should not prompt them to log out of the OP as there is no session, but we should still clear out their tokens to terminate the session for the Application. --- CHANGELOG.md | 6 +++- oauth2_provider/views/oidc.py | 67 ++++++++++++++++++++++++++++++----- tests/test_oidc_views.py | 35 ++++++++++++++---- 3 files changed, 91 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38b1d8b78..f16317265 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,12 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [unreleased] ### Added -* Support for Wildcard Origin and Redirect URIs, https://github.com/jazzband/django-oauth-toolkit/issues/1506 +* #1506 Support for Wildcard Origin and Redirect URIs ### Fixed +* #1517 OP prompts for logout when no OP session +* #1512 client_secret not marked sensitive + diff --git a/oauth2_provider/views/oidc.py b/oauth2_provider/views/oidc.py index c746c30ce..a252f1be4 100644 --- a/oauth2_provider/views/oidc.py +++ b/oauth2_provider/views/oidc.py @@ -367,17 +367,66 @@ def validate_logout_request(self, id_token_hint, client_id, post_logout_redirect return application, id_token.user if id_token else None def must_prompt(self, token_user): - """Indicate whether the logout has to be confirmed by the user. This happens if the - specifications force a confirmation, or it is enabled by `OIDC_RP_INITIATED_LOGOUT_ALWAYS_PROMPT`. + """ + per: https://openid.net/specs/openid-connect-rpinitiated-1_0.html + + > At the Logout Endpoint, the OP SHOULD ask the End-User whether to log + > out of the OP as well. Furthermore, the OP MUST ask the End-User this + > question if an id_token_hint was not provided or if the supplied ID + > Token does not belong to the current OP session with the RP and/or + > currently logged in End-User. - A logout without user interaction (i.e. no prompt) is only allowed - if an ID Token is provided that matches the current user. """ - return ( - oauth2_settings.OIDC_RP_INITIATED_LOGOUT_ALWAYS_PROMPT - or token_user is None - or token_user != self.request.user - ) + + if not self.request.user.is_authenticated: + """ + > the OP MUST ask ask the End-User whether to log out of the OP as + + If the user does not have an active session with the OP, they cannot + end their OP session, so there is nothing to prompt for. This occurs + in cases where the user has logged out of the OP via another channel + such as the OP's own logout page, session timeout or another RP's + logout page. + """ + return False + + if oauth2_settings.OIDC_RP_INITIATED_LOGOUT_ALWAYS_PROMPT: + """ + > At the Logout Endpoint, the OP SHOULD ask the End-User whether to + > log out of the OP as well + + The admin has configured the OP to always prompt the userfor logout + per the SHOULD recommendation. + """ + return True + + if token_user is None: + """ + > the OP MUST ask ask the End-User whether to log out of the OP as + > well if the supplied ID Token does not belong to the current OP + > session with the RP. + + token_user will only be populated if an ID token was found for the + RP (Application) that is requesting the logout. If token_user is not + then we must prompt the user. + """ + return True + + if token_user != self.request.user: + """ + > the OP MUST ask ask the End-User whether to log out of the OP as + > well if the supplied ID Token does not belong to the logged in + > End-User. + + is_authenticated indicates that there is a logged in user and was + tested in the first condition. + token_user != self.request.user indicates that the token does not + belong to the logged in user, Therefore we need to prompt the user. + """ + return True + + """ We didn't find a reason to prompt the user """ + return False def do_logout(self, application=None, post_logout_redirect_uri=None, state=None, token_user=None): user = token_user or self.request.user diff --git a/tests/test_oidc_views.py b/tests/test_oidc_views.py index 8bdf18360..65197cbd1 100644 --- a/tests/test_oidc_views.py +++ b/tests/test_oidc_views.py @@ -311,6 +311,10 @@ def test_must_prompt(oidc_tokens, other_user, rp_settings, ALWAYS_PROMPT): == ALWAYS_PROMPT ) assert RPInitiatedLogoutView(request=mock_request_for(other_user)).must_prompt(oidc_tokens.user) is True + assert ( + RPInitiatedLogoutView(request=mock_request_for(AnonymousUser())).must_prompt(oidc_tokens.user) + is False + ) def test__load_id_token(): @@ -577,13 +581,14 @@ def test_token_deletion_on_logout(oidc_tokens, logged_in_client, rp_settings): @pytest.mark.django_db(databases=retrieve_current_databases()) -def test_token_deletion_on_logout_expired_session(oidc_tokens, client, rp_settings): +def test_token_deletion_on_logout_without_op_session_get(oidc_tokens, client, rp_settings): AccessToken = get_access_token_model() IDToken = get_id_token_model() RefreshToken = get_refresh_token_model() assert AccessToken.objects.count() == 1 assert IDToken.objects.count() == 1 assert RefreshToken.objects.count() == 1 + rsp = client.get( reverse("oauth2_provider:rp-initiated-logout"), data={ @@ -591,15 +596,31 @@ def test_token_deletion_on_logout_expired_session(oidc_tokens, client, rp_settin "client_id": oidc_tokens.application.client_id, }, ) - assert rsp.status_code == 200 + assert rsp.status_code == 302 assert not is_logged_in(client) # Check that all tokens are active. - access_token = AccessToken.objects.get() - assert not access_token.is_expired() - id_token = IDToken.objects.get() - assert not id_token.is_expired() + assert AccessToken.objects.count() == 0 + assert IDToken.objects.count() == 0 + assert RefreshToken.objects.count() == 1 + + with pytest.raises(AccessToken.DoesNotExist): + AccessToken.objects.get() + + with pytest.raises(IDToken.DoesNotExist): + IDToken.objects.get() + refresh_token = RefreshToken.objects.get() - assert refresh_token.revoked is None + assert refresh_token.revoked is not None + + +@pytest.mark.django_db(databases=retrieve_current_databases()) +def test_token_deletion_on_logout_without_op_session_post(oidc_tokens, client, rp_settings): + AccessToken = get_access_token_model() + IDToken = get_id_token_model() + RefreshToken = get_refresh_token_model() + assert AccessToken.objects.count() == 1 + assert IDToken.objects.count() == 1 + assert RefreshToken.objects.count() == 1 rsp = client.post( reverse("oauth2_provider:rp-initiated-logout"), From 907d70f08c1bef94a485bde8fd3edb51952aec03 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 28 Oct 2024 15:29:47 -0400 Subject: [PATCH 18/78] [pre-commit.ci] pre-commit autoupdate (#1518) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.6.9 → v0.7.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.6.9...v0.7.1) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7a50f8d2b..42a6cb209 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.9 + rev: v0.7.1 hooks: - id: ruff args: [ --fix ] From 0d32dec28b2368e292c54c51665ec6e7837b3c3b Mon Sep 17 00:00:00 2001 From: Alex Kerkum Date: Mon, 4 Nov 2024 16:49:27 +0100 Subject: [PATCH 19/78] Use iterator in access token migration (#1522) --- AUTHORS | 1 + CHANGELOG.md | 1 + oauth2_provider/migrations/0012_add_token_checksum.py | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index d10ff1fb4..e2da60020 100644 --- a/AUTHORS +++ b/AUTHORS @@ -122,3 +122,4 @@ Wouter Klein Heerenbrink Yaroslav Halchenko Yuri Savin Miriam Forner +Alex Kerkum diff --git a/CHANGELOG.md b/CHANGELOG.md index f16317265..f86b8a8af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed * #1517 OP prompts for logout when no OP session * #1512 client_secret not marked sensitive +* #1521 Fix 0012 migration loading access token table into memory diff --git a/oauth2_provider/migrations/0012_add_token_checksum.py b/oauth2_provider/migrations/0012_add_token_checksum.py index 476c3b402..d27c65e54 100644 --- a/oauth2_provider/migrations/0012_add_token_checksum.py +++ b/oauth2_provider/migrations/0012_add_token_checksum.py @@ -9,7 +9,7 @@ def forwards_func(apps, schema_editor): Forward migration touches every "old" accesstoken.token which will cause the checksum to be computed. """ AccessToken = apps.get_model(oauth2_settings.ACCESS_TOKEN_MODEL) - accesstokens = AccessToken._default_manager.all() + accesstokens = AccessToken._default_manager.iterator() for accesstoken in accesstokens: accesstoken.save(update_fields=['token_checksum']) From 1c5e36d59cd8f740650a72ad80a455f8ed05ff3c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2024 13:43:40 -0500 Subject: [PATCH 20/78] [pre-commit.ci] pre-commit autoupdate (#1523) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.7.1 → v0.7.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.7.1...v0.7.2) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 42a6cb209..f59dff364 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.7.1 + rev: v0.7.2 hooks: - id: ruff args: [ --fix ] From ad77eac9b7cc6d984c35ab3e8cb35778719bc84c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 13:31:08 -0500 Subject: [PATCH 21/78] [pre-commit.ci] pre-commit autoupdate (#1524) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.7.2 → v0.7.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.7.2...v0.7.3) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f59dff364..086c7af38 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.7.2 + rev: v0.7.3 hooks: - id: ruff args: [ --fix ] From 1285ab0ec99d507c72f87242f545c50f9a5eef3d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 13:19:09 -0500 Subject: [PATCH 22/78] [pre-commit.ci] pre-commit autoupdate (#1528) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.7.3 → v0.7.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.7.3...v0.7.4) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 086c7af38..7d546c18f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.7.3 + rev: v0.7.4 hooks: - id: ruff args: [ --fix ] From e950255f3be99ae57153f4f94cbb1fd012281005 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 11:36:43 -0500 Subject: [PATCH 23/78] Bump @sveltejs/kit from 2.5.10 to 2.8.3 in /tests/app/rp (#1529) Bumps [@sveltejs/kit](https://github.com/sveltejs/kit/tree/HEAD/packages/kit) from 2.5.10 to 2.8.3. - [Release notes](https://github.com/sveltejs/kit/releases) - [Changelog](https://github.com/sveltejs/kit/blob/main/packages/kit/CHANGELOG.md) - [Commits](https://github.com/sveltejs/kit/commits/@sveltejs/kit@2.8.3/packages/kit) --- updated-dependencies: - dependency-name: "@sveltejs/kit" dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- tests/app/rp/package-lock.json | 34 +++++++++++++++++----------------- tests/app/rp/package.json | 2 +- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/app/rp/package-lock.json b/tests/app/rp/package-lock.json index 6ab5dc90e..1a22dffe4 100644 --- a/tests/app/rp/package-lock.json +++ b/tests/app/rp/package-lock.json @@ -13,7 +13,7 @@ "devDependencies": { "@sveltejs/adapter-auto": "^3.1.1", "@sveltejs/adapter-node": "^5.0.1", - "@sveltejs/kit": "^2.5.10", + "@sveltejs/kit": "^2.8.3", "prettier": "^3.3.2", "prettier-plugin-svelte": "^3.2.4", "svelte": "^4.2.19", @@ -496,9 +496,9 @@ } }, "node_modules/@polka/url": { - "version": "1.0.0-next.24", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.24.tgz", - "integrity": "sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ==", + "version": "1.0.0-next.28", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz", + "integrity": "sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw==", "dev": true }, "node_modules/@rollup/plugin-commonjs": { @@ -891,15 +891,15 @@ } }, "node_modules/@sveltejs/kit": { - "version": "2.5.10", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.5.10.tgz", - "integrity": "sha512-OqoyTmFG2cYmCFAdBfW+Qxbg8m23H4dv6KqwEt7ofr/ROcfcIl3Z/VT56L22H9f0uNZyr+9Bs1eh2gedOCK9kA==", + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.8.3.tgz", + "integrity": "sha512-DVBVwugfzzn0SxKA+eAmKqcZ7aHZROCHxH7/pyrOi+HLtQ721eEsctGb9MkhEuqj6q/9S/OFYdn37vdxzFPdvw==", "dev": true, "hasInstallScript": true, "dependencies": { "@types/cookie": "^0.6.0", "cookie": "^0.6.0", - "devalue": "^5.0.0", + "devalue": "^5.1.0", "esm-env": "^1.0.0", "import-meta-resolve": "^4.1.0", "kleur": "^4.1.5", @@ -907,7 +907,7 @@ "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", - "sirv": "^2.0.4", + "sirv": "^3.0.0", "tiny-glob": "^0.2.9" }, "bin": { @@ -917,7 +917,7 @@ "node": ">=18.13" }, "peerDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.0.0", + "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1", "svelte": "^4.0.0 || ^5.0.0-next.0", "vite": "^5.0.3" } @@ -1262,9 +1262,9 @@ } }, "node_modules/devalue": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.0.0.tgz", - "integrity": "sha512-gO+/OMXF7488D+u3ue+G7Y4AA3ZmUnB3eHJXmBTgNHvr4ZNzl36A0ZtG+XCRNYCkYx/bFmw4qtkoFLa+wSrwAA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.1.1.tgz", + "integrity": "sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==", "dev": true }, "node_modules/es6-promise": { @@ -2066,9 +2066,9 @@ "dev": true }, "node_modules/sirv": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", - "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.0.tgz", + "integrity": "sha512-BPwJGUeDaDCHihkORDchNyyTvWFhcusy1XMmhEVTQTwGeybFbp8YEmB+njbPnth1FibULBSBVwCQni25XlCUDg==", "dev": true, "dependencies": { "@polka/url": "^1.0.0-next.24", @@ -2076,7 +2076,7 @@ "totalist": "^3.0.0" }, "engines": { - "node": ">= 10" + "node": ">=18" } }, "node_modules/sorcery": { diff --git a/tests/app/rp/package.json b/tests/app/rp/package.json index 7f784006f..8bd3bb8fe 100644 --- a/tests/app/rp/package.json +++ b/tests/app/rp/package.json @@ -14,7 +14,7 @@ "devDependencies": { "@sveltejs/adapter-auto": "^3.1.1", "@sveltejs/adapter-node": "^5.0.1", - "@sveltejs/kit": "^2.5.10", + "@sveltejs/kit": "^2.8.3", "prettier": "^3.3.2", "prettier-plugin-svelte": "^3.2.4", "svelte": "^4.2.19", From d2a4c3fa45d72816e04249911de751e445db1da7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 13:23:10 -0500 Subject: [PATCH 24/78] [pre-commit.ci] pre-commit autoupdate (#1530) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7d546c18f..ca719c9a2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.7.4 + rev: v0.8.0 hooks: - id: ruff args: [ --fix ] From c403b2e5974a259b814330923da37012a875e729 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 2 Dec 2024 14:30:41 -0500 Subject: [PATCH 25/78] [pre-commit.ci] pre-commit autoupdate (#1531) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.8.0 → v0.8.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.8.0...v0.8.1) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ca719c9a2..40c6824a8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.8.0 + rev: v0.8.1 hooks: - id: ruff args: [ --fix ] From 7b244a34d640ca5ac0fd1c024624a7144eca021e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 13:16:06 -0500 Subject: [PATCH 26/78] [pre-commit.ci] pre-commit autoupdate (#1532) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.8.1 → v0.8.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.8.1...v0.8.2) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 40c6824a8..e134ab26f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.8.1 + rev: v0.8.2 hooks: - id: ruff args: [ --fix ] From ee65197cbb3833e28bd655b33b59c884745c5299 Mon Sep 17 00:00:00 2001 From: IT-Native Date: Thu, 12 Dec 2024 15:12:35 +0100 Subject: [PATCH 27/78] Fixes typo (#1533) Update getting_started.rst --- docs/getting_started.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/getting_started.rst b/docs/getting_started.rst index e95618723..d2ce14ca1 100644 --- a/docs/getting_started.rst +++ b/docs/getting_started.rst @@ -243,9 +243,9 @@ Start the development server:: python manage.py runserver -Point your browser to http://127.0.0.1:8000/o/applications/register/ lets create an application. +Point your browser to http://127.0.0.1:8000/o/applications/register/ and let's create an application. -Fill the form as show in the screenshot below and before save take note of ``Client id`` and ``Client secret``, we will use it in a minute. +Fill the form as shown in the screenshot below and before clicking on save take note of ``Client id`` and ``Client secret``, we will use it in a minute. If you want to use this application with OIDC and ``HS256`` (see :doc:`OpenID Connect `), uncheck ``Hash client secret`` to allow verifying tokens using JWT signatures. This means your client secret will be stored in cleartext but is the only way to successfully use signed JWT's with ``HS256``. From f6db0fa362e916762d4e7a5bcb84fa9c49a34610 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2024 13:55:09 -0500 Subject: [PATCH 28/78] [pre-commit.ci] pre-commit autoupdate (#1534) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e134ab26f..b72e72b11 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.8.2 + rev: v0.8.3 hooks: - id: ruff args: [ --fix ] From aa6d566881965c43cedae8e04a84beda9633be90 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 23 Dec 2024 14:52:24 -0500 Subject: [PATCH 29/78] [pre-commit.ci] pre-commit autoupdate (#1537) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b72e72b11..e5429e8a8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.8.3 + rev: v0.8.4 hooks: - id: ruff args: [ --fix ] From c61d852ef500ef8a925383b654d1273a89639bb4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 6 Jan 2025 15:16:36 -0500 Subject: [PATCH 30/78] [pre-commit.ci] pre-commit autoupdate (#1538) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.8.4 → v0.8.6](https://github.com/astral-sh/ruff-pre-commit/compare/v0.8.4...v0.8.6) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e5429e8a8..7c3e7e799 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.8.4 + rev: v0.8.6 hooks: - id: ruff args: [ --fix ] From 1b0520ac992e6c6179cdbcef68f383eb7a0c7980 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2025 14:21:47 -0500 Subject: [PATCH 31/78] [pre-commit.ci] pre-commit autoupdate (#1540) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.8.6 → v0.9.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.8.6...v0.9.1) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- oauth2_provider/models.py | 2 +- tests/test_models.py | 48 +++++++++++++++++++-------------------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7c3e7e799..d337a46d8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.8.6 + rev: v0.9.1 hooks: - id: ruff args: [ --fix ] diff --git a/oauth2_provider/models.py b/oauth2_provider/models.py index 0467ddfa1..a76db37c0 100644 --- a/oauth2_provider/models.py +++ b/oauth2_provider/models.py @@ -715,7 +715,7 @@ def batch_delete(queryset, query): flat_queryset = queryset.values_list("id", flat=True)[:CLEAR_EXPIRED_TOKENS_BATCH_SIZE] batch_length = flat_queryset.count() queryset.model.objects.filter(id__in=list(flat_queryset)).delete() - logger.debug(f"{batch_length} tokens deleted, {current_no-batch_length} left") + logger.debug(f"{batch_length} tokens deleted, {current_no - batch_length} left") queryset = queryset.model.objects.filter(query) time.sleep(CLEAR_EXPIRED_TOKENS_BATCH_INTERVAL) current_no = queryset.count() diff --git a/tests/test_models.py b/tests/test_models.py index 32ca07627..2c7ff8114 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -433,25 +433,25 @@ def test_clear_expired_tokens_with_tokens(self): initial_at_count = AccessToken.objects.count() assert initial_at_count == 2 * self.num_tokens, f"{2 * self.num_tokens} access tokens should exist." initial_expired_at_count = AccessToken.objects.filter(expires__lte=self.now).count() - assert ( - initial_expired_at_count == self.num_tokens - ), f"{self.num_tokens} expired access tokens should exist." + assert initial_expired_at_count == self.num_tokens, ( + f"{self.num_tokens} expired access tokens should exist." + ) initial_current_at_count = AccessToken.objects.filter(expires__gt=self.now).count() - assert ( - initial_current_at_count == self.num_tokens - ), f"{self.num_tokens} current access tokens should exist." + assert initial_current_at_count == self.num_tokens, ( + f"{self.num_tokens} current access tokens should exist." + ) initial_rt_count = RefreshToken.objects.count() - assert ( - initial_rt_count == self.num_tokens // 2 - ), f"{self.num_tokens // 2} refresh tokens should exist." + assert initial_rt_count == self.num_tokens // 2, ( + f"{self.num_tokens // 2} refresh tokens should exist." + ) initial_rt_expired_at_count = RefreshToken.objects.filter(access_token__expires__lte=self.now).count() - assert ( - initial_rt_expired_at_count == initial_rt_count / 2 - ), "half the refresh tokens should be for expired access tokens." + assert initial_rt_expired_at_count == initial_rt_count / 2, ( + "half the refresh tokens should be for expired access tokens." + ) initial_rt_current_at_count = RefreshToken.objects.filter(access_token__expires__gt=self.now).count() - assert ( - initial_rt_current_at_count == initial_rt_count / 2 - ), "half the refresh tokens should be for current access tokens." + assert initial_rt_current_at_count == initial_rt_count / 2, ( + "half the refresh tokens should be for current access tokens." + ) initial_gt_count = Grant.objects.count() assert initial_gt_count == self.num_tokens * 2, f"{self.num_tokens * 2} grants should exist." @@ -459,15 +459,15 @@ def test_clear_expired_tokens_with_tokens(self): # after clear_expired(): remaining_at_count = AccessToken.objects.count() - assert ( - remaining_at_count == initial_at_count // 2 - ), "half the initial access tokens should still exist." + assert remaining_at_count == initial_at_count // 2, ( + "half the initial access tokens should still exist." + ) remaining_expired_at_count = AccessToken.objects.filter(expires__lte=self.now).count() assert remaining_expired_at_count == 0, "no remaining expired access tokens should still exist." remaining_current_at_count = AccessToken.objects.filter(expires__gt=self.now).count() - assert ( - remaining_current_at_count == initial_current_at_count - ), "all current access tokens should still exist." + assert remaining_current_at_count == initial_current_at_count, ( + "all current access tokens should still exist." + ) remaining_rt_count = RefreshToken.objects.count() assert remaining_rt_count == initial_rt_count // 2, "half the refresh tokens should still exist." remaining_rt_expired_at_count = RefreshToken.objects.filter( @@ -477,9 +477,9 @@ def test_clear_expired_tokens_with_tokens(self): remaining_rt_current_at_count = RefreshToken.objects.filter( access_token__expires__gt=self.now ).count() - assert ( - remaining_rt_current_at_count == initial_rt_current_at_count - ), "all the refresh tokens for current access tokens should still exist." + assert remaining_rt_current_at_count == initial_rt_current_at_count, ( + "all the refresh tokens for current access tokens should still exist." + ) remaining_gt_count = Grant.objects.count() assert remaining_gt_count == initial_gt_count // 2, "half the remaining grants should still exist." From e11cfb2b63fd133a808142bd67488f4d7fe33d21 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 14:58:25 -0500 Subject: [PATCH 32/78] [pre-commit.ci] pre-commit autoupdate (#1541) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d337a46d8..3d89ddfa1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.9.1 + rev: v0.9.2 hooks: - id: ruff args: [ --fix ] From ffcf60987f4e6ade6632dccd97fd5d1824f45b9d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Jan 2025 07:13:57 -0500 Subject: [PATCH 33/78] Bump vite from 5.4.6 to 5.4.14 in /tests/app/rp (#1542) --- tests/app/rp/package-lock.json | 9 +++++---- tests/app/rp/package.json | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/app/rp/package-lock.json b/tests/app/rp/package-lock.json index 1a22dffe4..be5db7cb4 100644 --- a/tests/app/rp/package-lock.json +++ b/tests/app/rp/package-lock.json @@ -20,7 +20,7 @@ "svelte-check": "^3.8.0", "tslib": "^2.4.1", "typescript": "^5.0.0", - "vite": "^5.4.6" + "vite": "^5.4.14" } }, "node_modules/@ampproject/remapping": { @@ -2312,10 +2312,11 @@ } }, "node_modules/vite": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.6.tgz", - "integrity": "sha512-IeL5f8OO5nylsgzd9tq4qD2QqI0k2CQLGrWD0rCN0EQJZpBK5vJAx0I+GDkMOXxQX/OfFHMuLIx6ddAxGX/k+Q==", + "version": "5.4.14", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.14.tgz", + "integrity": "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==", "dev": true, + "license": "MIT", "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", diff --git a/tests/app/rp/package.json b/tests/app/rp/package.json index 8bd3bb8fe..3ec9569bd 100644 --- a/tests/app/rp/package.json +++ b/tests/app/rp/package.json @@ -21,7 +21,7 @@ "svelte-check": "^3.8.0", "tslib": "^2.4.1", "typescript": "^5.0.0", - "vite": "^5.4.6" + "vite": "^5.4.14" }, "type": "module", "dependencies": { From a24d0a8e83af98d827d313881b71fa049d193f35 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 27 Jan 2025 13:44:03 -0500 Subject: [PATCH 34/78] [pre-commit.ci] pre-commit autoupdate (#1544) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/astral-sh/ruff-pre-commit: v0.9.2 → v0.9.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.2...v0.9.3) - [github.com/codespell-project/codespell: v2.3.0 → v2.4.0](https://github.com/codespell-project/codespell/compare/v2.3.0...v2.4.0) * codespell --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Alan Crosswell --- .pre-commit-config.yaml | 4 ++-- CHANGELOG.md | 2 +- tests/test_authorization_code.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3d89ddfa1..facfd8f7d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.9.2 + rev: v0.9.3 hooks: - id: ruff args: [ --fix ] @@ -22,7 +22,7 @@ repos: - id: sphinx-lint # Configuration for codespell is in pyproject.toml - repo: https://github.com/codespell-project/codespell - rev: v2.3.0 + rev: v2.4.0 hooks: - id: codespell exclude: (package-lock.json|/locale/) diff --git a/CHANGELOG.md b/CHANGELOG.md index f86b8a8af..8dfe6c3e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -411,7 +411,7 @@ This is a major release with **BREAKING** changes. Please make sure to review th * **New feature**: The new setting `ERROR_RESPONSE_WITH_SCOPES` can now be set to True to include required scopes when DRF authorization fails due to improper scopes. * **New feature**: The new setting `REFRESH_TOKEN_GRACE_PERIOD_SECONDS` controls a grace period during which - refresh tokens may be re-used. + refresh tokens may be reused. * An `app_authorized` signal is fired when a token is generated. ## 1.0.0 [2017-06-07] diff --git a/tests/test_authorization_code.py b/tests/test_authorization_code.py index 122474950..f162e211a 100644 --- a/tests/test_authorization_code.py +++ b/tests/test_authorization_code.py @@ -989,7 +989,7 @@ def test_refresh_fail_repeating_requests(self): def test_refresh_repeating_requests_revokes_old_token(self): """ If a refresh token is reused, the server should invalidate *all* access tokens that have a relation - to the re-used token. This forces a malicious actor to be logged out. + to the reused token. This forces a malicious actor to be logged out. The server can't determine whether the first or the second client was legitimate, so it needs to revoke both. See https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics-29#name-recommendations From 48f4d545a6025ed88e3b42119be17d7316e4d229 Mon Sep 17 00:00:00 2001 From: Raphael Lullis Date: Wed, 29 Jan 2025 17:56:21 +0000 Subject: [PATCH 35/78] Fix range of allowed django versions (#1547) * Fix range of allowed django versions Running `docker-compose build` fails at the moment due to a package resolution conflict. Allowing to use django 4.2 solves the issue. * Update tests/app/idp/requirements.txt Co-authored-by: Alan Crosswell --------- Co-authored-by: Alan Crosswell --- tests/app/idp/requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/app/idp/requirements.txt b/tests/app/idp/requirements.txt index ba8e75052..ec77fcf9d 100644 --- a/tests/app/idp/requirements.txt +++ b/tests/app/idp/requirements.txt @@ -1,5 +1,5 @@ -Django>=3.2,<4.2 +Django>=4.2,<=5.1 django-cors-headers==3.14.0 django-environ==0.11.2 --e ../../../ \ No newline at end of file +-e ../../../ From 6d21bfb5ccea3564ab010b919cfc613f714bb51e Mon Sep 17 00:00:00 2001 From: Darrel O'Pry Date: Fri, 31 Jan 2025 18:28:11 -0500 Subject: [PATCH 36/78] fix: idp image can't find templates or statics (#1550) --- CHANGELOG.md | 1 + Dockerfile | 5 ++++- docs/contributing.rst | 22 ++++++++++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8dfe6c3e5..8c4770459 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * #1517 OP prompts for logout when no OP session * #1512 client_secret not marked sensitive * #1521 Fix 0012 migration loading access token table into memory +* #1584 Fix IDP container in docker compose environment could not find templates and static files. diff --git a/Dockerfile b/Dockerfile index e501e84d2..4565df5ff 100644 --- a/Dockerfile +++ b/Dockerfile @@ -57,7 +57,10 @@ ENV DATABASE_URL="sqlite:////data/db.sqlite3" COPY --from=builder /opt/venv /opt/venv ENV PATH="/opt/venv/bin:$PATH" COPY --from=builder /code /code -RUN mkdir -p /code/tests/app/idp/static /code/tests/app/idp/templates +RUN mkdir -p /data/static /data/templates +COPY --from=builder /code/tests/app/idp/static /data/static +COPY --from=builder /code/tests/app/idp/templates /data/templates + WORKDIR /code/tests/app/idp RUN apt-get update && apt-get install -y \ libpq5 \ diff --git a/docs/contributing.rst b/docs/contributing.rst index 4f0b88b32..569f5eab2 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -325,6 +325,28 @@ Reviewing and Merging PRs PRs that are incorrectly merged may (reluctantly) be reverted by the Project Leads. +End to End Testing +------------------ + +There is a demonstration Identity Provider (IDP) and Relying Party (RP) to allow for +end to end testing. They can be launched directly by following the instructions in +/test/apps/README.md or via docker compose. To launch via docker compose + +.. code-block:: bash + + # build the images with the current code + docker compose build + # wipe any existing services and volumes + docker compose rm -v + # start the services + docker compose up -d + +Please verify the RP behaves as expected by logging in, reloading, and logging out. + +open http://localhost:5173 in your browser and login with the following credentials: + +username: superuser +password: password Publishing a Release -------------------- From bc7edb05aab8d962704e4ec294d6dbd3953f45b2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 14:46:55 -0500 Subject: [PATCH 37/78] [pre-commit.ci] pre-commit autoupdate (#1551) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.9.3 → v0.9.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.3...v0.9.4) - [github.com/codespell-project/codespell: v2.4.0 → v2.4.1](https://github.com/codespell-project/codespell/compare/v2.4.0...v2.4.1) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index facfd8f7d..b67b72438 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.9.3 + rev: v0.9.4 hooks: - id: ruff args: [ --fix ] @@ -22,7 +22,7 @@ repos: - id: sphinx-lint # Configuration for codespell is in pyproject.toml - repo: https://github.com/codespell-project/codespell - rev: v2.4.0 + rev: v2.4.1 hooks: - id: codespell exclude: (package-lock.json|/locale/) From 87bcd28226996f11cd9daaaeaf542c71f800abcd Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 13:47:45 -0500 Subject: [PATCH 38/78] [pre-commit.ci] pre-commit autoupdate (#1553) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.9.4 → v0.9.6](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.4...v0.9.6) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b67b72438..50f835567 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.9.4 + rev: v0.9.6 hooks: - id: ruff args: [ --fix ] From c9218e4f2e7ec3a68391d25d724c751b8505f496 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 24 Feb 2025 13:00:02 -0500 Subject: [PATCH 39/78] [pre-commit.ci] pre-commit autoupdate (#1554) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.9.6 → v0.9.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.6...v0.9.7) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 50f835567..7fba749ce 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.9.6 + rev: v0.9.7 hooks: - id: ruff args: [ --fix ] From 9805863a3b718adbc2d0b8c95d9c9967d066e4f7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 3 Mar 2025 13:57:01 -0500 Subject: [PATCH 40/78] [pre-commit.ci] pre-commit autoupdate (#1555) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7fba749ce..3b24f4649 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.9.7 + rev: v0.9.9 hooks: - id: ruff args: [ --fix ] From be86257d327ccf5396f8986308027b0fdc216590 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 10 Mar 2025 13:59:33 -0400 Subject: [PATCH 41/78] [pre-commit.ci] pre-commit autoupdate (#1556) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.9.9 → v0.9.10](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.9...v0.9.10) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3b24f4649..f03a3a002 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.9.9 + rev: v0.9.10 hooks: - id: ruff args: [ --fix ] From 25ac4682f6eb981647286aab78cddec4f67083af Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 17 Mar 2025 15:12:05 -0400 Subject: [PATCH 42/78] [pre-commit.ci] pre-commit autoupdate (#1559) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.9.10 → v0.11.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.9.10...v0.11.0) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f03a3a002..a35ef3e49 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.9.10 + rev: v0.11.0 hooks: - id: ruff args: [ --fix ] From ec5984591b348b06ac60ef9d156be9a295d65110 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 24 Mar 2025 13:44:28 -0400 Subject: [PATCH 43/78] [pre-commit.ci] pre-commit autoupdate (#1560) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a35ef3e49..3671e158b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.0 + rev: v0.11.2 hooks: - id: ruff args: [ --fix ] From 172c636cf6c0db0740b02bac67e74b3702f6029d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 7 Apr 2025 14:15:52 -0400 Subject: [PATCH 44/78] [pre-commit.ci] pre-commit autoupdate (#1561) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.11.2 → v0.11.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.2...v0.11.4) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3671e158b..239764310 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.2 + rev: v0.11.4 hooks: - id: ruff args: [ --fix ] From 231669f06889562df1793e49702e2aafba3063fc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 14 Apr 2025 14:11:09 -0400 Subject: [PATCH 45/78] [pre-commit.ci] pre-commit autoupdate (#1563) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 239764310..e64c2528f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.4 + rev: v0.11.5 hooks: - id: ruff args: [ --fix ] From 839952f0e27a87583ff5506b750db18f576ff600 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Apr 2025 20:35:34 -0400 Subject: [PATCH 46/78] Bump @sveltejs/kit from 2.8.3 to 2.20.6 in /tests/app/rp (#1564) --- tests/app/rp/package-lock.json | 50 ++++++++++------------------------ tests/app/rp/package.json | 2 +- 2 files changed, 15 insertions(+), 37 deletions(-) diff --git a/tests/app/rp/package-lock.json b/tests/app/rp/package-lock.json index be5db7cb4..6f33dbbec 100644 --- a/tests/app/rp/package-lock.json +++ b/tests/app/rp/package-lock.json @@ -13,7 +13,7 @@ "devDependencies": { "@sveltejs/adapter-auto": "^3.1.1", "@sveltejs/adapter-node": "^5.0.1", - "@sveltejs/kit": "^2.8.3", + "@sveltejs/kit": "^2.20.6", "prettier": "^3.3.2", "prettier-plugin-svelte": "^3.2.4", "svelte": "^4.2.19", @@ -891,24 +891,23 @@ } }, "node_modules/@sveltejs/kit": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.8.3.tgz", - "integrity": "sha512-DVBVwugfzzn0SxKA+eAmKqcZ7aHZROCHxH7/pyrOi+HLtQ721eEsctGb9MkhEuqj6q/9S/OFYdn37vdxzFPdvw==", + "version": "2.20.6", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.20.6.tgz", + "integrity": "sha512-ImUkSQ//Xf4N9r0HHAe5vRA7RyQ7U1Ue1YUT235Ig+IiIqbsixEulHTHrP5LtBiC8xOkJoPZQ1VZ/nWHNOaGGw==", "dev": true, - "hasInstallScript": true, + "license": "MIT", "dependencies": { "@types/cookie": "^0.6.0", "cookie": "^0.6.0", "devalue": "^5.1.0", - "esm-env": "^1.0.0", + "esm-env": "^1.2.2", "import-meta-resolve": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", - "sirv": "^3.0.0", - "tiny-glob": "^0.2.9" + "sirv": "^3.0.0" }, "bin": { "svelte-kit": "svelte-kit.js" @@ -917,9 +916,9 @@ "node": ">=18.13" }, "peerDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1", + "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0", "svelte": "^4.0.0 || ^5.0.0-next.0", - "vite": "^5.0.3" + "vite": "^5.0.3 || ^6.0.0" } }, "node_modules/@sveltejs/vite-plugin-svelte": { @@ -1312,10 +1311,11 @@ } }, "node_modules/esm-env": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.0.0.tgz", - "integrity": "sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA==", - "dev": true + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", + "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", + "dev": true, + "license": "MIT" }, "node_modules/estree-walker": { "version": "3.0.3", @@ -1425,18 +1425,6 @@ "node": ">= 6" } }, - "node_modules/globalyzer": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz", - "integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==", - "dev": true - }, - "node_modules/globrex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", - "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", - "dev": true - }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -2261,16 +2249,6 @@ "node": ">=0.4.0" } }, - "node_modules/tiny-glob": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", - "integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==", - "dev": true, - "dependencies": { - "globalyzer": "0.1.0", - "globrex": "^0.1.2" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", diff --git a/tests/app/rp/package.json b/tests/app/rp/package.json index 3ec9569bd..2e1a3afb6 100644 --- a/tests/app/rp/package.json +++ b/tests/app/rp/package.json @@ -14,7 +14,7 @@ "devDependencies": { "@sveltejs/adapter-auto": "^3.1.1", "@sveltejs/adapter-node": "^5.0.1", - "@sveltejs/kit": "^2.8.3", + "@sveltejs/kit": "^2.20.6", "prettier": "^3.3.2", "prettier-plugin-svelte": "^3.2.4", "svelte": "^4.2.19", From b63c5766b5f8c37fa42f8a0134261198574df8bc Mon Sep 17 00:00:00 2001 From: Brian Helba Date: Fri, 18 Apr 2025 13:31:16 -0400 Subject: [PATCH 47/78] Improve settings documentation (#1567) Previously, it was unclear that the swappable model settings should always be set without a namespace, as the sub-section titles didn't include the required `OAUTH2_PROVIDER_` prefix. The warning at the top may not be noticed by people looking for a specific settings, and it was still unclear given the sub-section titles. Additionally, this documents the `OAUTH2_PROVIDER_ID_TOKEN_MODEL` and `OAUTH2_PROVIDER_GRANT_MODEL` settings, which were previously undocumented. Also, this corrects a mistake where `ACCESS_TOKEN_GENERATOR` was mentioned as the setting which enables the use of `SettingsScopes`; the actual setting is `SCOPES_BACKEND_CLASS`. --- docs/settings.rst | 80 ++++++++++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 35 deletions(-) diff --git a/docs/settings.rst b/docs/settings.rst index 545736ccb..985ca5d2c 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -1,10 +1,8 @@ Settings ======== -Our configurations are all namespaced under the ``OAUTH2_PROVIDER`` settings with the exception of -``OAUTH2_PROVIDER_APPLICATION_MODEL``, ``OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL``, ``OAUTH2_PROVIDER_GRANT_MODEL``, -``OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL``: this is because of the way Django currently implements -swappable models. See `issue #90 `_ for details. +Our configurations are all namespaced under the ``OAUTH2_PROVIDER`` settings, with the exception +of the `List of non-namespaced settings`_. For example: @@ -24,24 +22,17 @@ For example: A big *thank you* to the guys from Django REST Framework for inspiring this. -List of available settings --------------------------- +List of available settings within OAUTH2_PROVIDER +------------------------------------------------- ACCESS_TOKEN_EXPIRE_SECONDS ~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Default: ``36000`` The number of seconds an access token remains valid. Requesting a protected resource after this duration will fail. Keep this value high enough so clients can cache the token for a reasonable amount of time. -ACCESS_TOKEN_MODEL -~~~~~~~~~~~~~~~~~~ -The import string of the class (model) representing your access tokens. Overwrite -this value if you wrote your own implementation (subclass of -``oauth2_provider.models.AccessToken``). - ACCESS_TOKEN_GENERATOR ~~~~~~~~~~~~~~~~~~~~~~ Import path of a callable used to generate access tokens. @@ -49,7 +40,6 @@ Import path of a callable used to generate access tokens. ALLOWED_REDIRECT_URI_SCHEMES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Default: ``["http", "https"]`` A list of schemes that the ``redirect_uri`` field will be validated against. @@ -65,7 +55,6 @@ a per-application basis. ALLOW_URI_WILDCARDS ~~~~~~~~~~~~~~~~~~~ - Default: ``False`` SECURITY WARNING: Enabling this setting can introduce security vulnerabilities. Only enable @@ -96,7 +85,6 @@ deployments for development previews and user acceptance testing. ALLOWED_SCHEMES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Default: ``["https"]`` A list of schemes that the ``allowed_origins`` field will be validated against. @@ -105,13 +93,6 @@ Adding ``"http"`` to the list is considered to be safe only for local developmen Note that `OAUTHLIB_INSECURE_TRANSPORT `_ environment variable should be also set to allow HTTP origins. - -APPLICATION_MODEL -~~~~~~~~~~~~~~~~~ -The import string of the class (model) representing your applications. Overwrite -this value if you wrote your own implementation (subclass of -``oauth2_provider.models.Application``). - AUTHORIZATION_CODE_EXPIRE_SECONDS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Default: ``60`` @@ -214,12 +195,6 @@ period the application, the app then has only a consumed refresh token and the only recourse is to have the user re-authenticate. A suggested value, if this is enabled, is 2 minutes. -REFRESH_TOKEN_MODEL -~~~~~~~~~~~~~~~~~~~ -The import string of the class (model) representing your refresh tokens. Overwrite -this value if you wrote your own implementation (subclass of -``oauth2_provider.models.RefreshToken``). - REFRESH_TOKEN_REUSE_PROTECTION ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When this is set to ``True`` (default ``False``), and ``ROTATE_REFRESH_TOKEN`` is used, the server will check @@ -257,7 +232,7 @@ Defaults to ``oauth2_provider.scopes.SettingsScopes``, which reads scopes throug SCOPES ~~~~~~ -.. note:: (0.12.0+) Only used if ``ACCESS_TOKEN_GENERATOR`` is set to the SettingsScopes default. +.. note:: (0.12.0+) Only used if ``SCOPES_BACKEND_CLASS`` is set to the SettingsScopes default. A dictionary mapping each scope name to its human description. @@ -265,7 +240,7 @@ A dictionary mapping each scope name to its human description. DEFAULT_SCOPES ~~~~~~~~~~~~~~ -.. note:: (0.12.0+) Only used if ``ACCESS_TOKEN_GENERATOR`` is set to the SettingsScopes default. +.. note:: (0.12.0+) Only used if ``SCOPES_BACKEND_CLASS`` is set to the SettingsScopes default. A list of scopes that should be returned by default. This is a subset of the keys of the ``SCOPES`` setting. @@ -277,13 +252,13 @@ By default this is set to ``'__all__'`` meaning that the whole set of ``SCOPES`` READ_SCOPE ~~~~~~~~~~ -.. note:: (0.12.0+) Only used if ``ACCESS_TOKEN_GENERATOR`` is set to the SettingsScopes default. +.. note:: (0.12.0+) Only used if ``SCOPES_BACKEND_CLASS`` is set to the SettingsScopes default. The name of the *read* scope. WRITE_SCOPE ~~~~~~~~~~~ -.. note:: (0.12.0+) Only used if ``ACCESS_TOKEN_GENERATOR`` is set to the SettingsScopes default. +.. note:: (0.12.0+) Only used if ``SCOPES_BACKEND_CLASS`` is set to the SettingsScopes default. The name of the *write* scope. @@ -340,7 +315,6 @@ Default: ``False`` Whether or not :doc:`oidc` support is enabled. - OIDC_RSA_PRIVATE_KEY ~~~~~~~~~~~~~~~~~~~~ Default: ``""`` @@ -470,11 +444,47 @@ Time of sleep in seconds used by ``cleartokens`` management command between batc Set this to a non-zero value (e.g. ``0.1``) to add a pause between batch sizes to reduce system load when clearing large batches of expired tokens. +List of non-namespaced settings +------------------------------- +.. note:: + These settings must be set as top-level Django settings (outside of ``OAUTH2_PROVIDER``), + because of the way Django currently implements swappable models. + See `issue #90 `_ for details. + + +OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The import string of the class (model) representing your access tokens. +Overwrite this value if you wrote your own implementation (subclass of +``oauth2_provider.models.AccessToken``). + +OAUTH2_PROVIDER_APPLICATION_MODEL +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The import string of the class (model) representing your applications. +Overwrite this value if you wrote your own implementation (subclass of +``oauth2_provider.models.Application``). + +OAUTH2_PROVIDER_ID_TOKEN_MODEL +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The import string of the class (model) representing your OpenID Connect ID Token. +Overwrite this value if you wrote your own implementation (subclass of +``oauth2_provider.models.IDToken``). + +OAUTH2_PROVIDER_GRANT_MODEL +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The import string of the class (model) representing your OAuth2 grants. +Overwrite this value if you wrote your own implementation (subclass of +``oauth2_provider.models.Grant``). + +OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The import string of the class (model) representing your refresh tokens. +Overwrite this value if you wrote your own implementation (subclass of +``oauth2_provider.models.RefreshToken``). Settings imported from Django project ------------------------------------- USE_TZ ~~~~~~ - Used to determine whether or not to make token expire dates timezone aware. From 6507e66c2d76a6acef860a4bd40390e7d054c243 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 21 Apr 2025 16:26:45 -0400 Subject: [PATCH 48/78] [pre-commit.ci] pre-commit autoupdate (#1569) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.11.5 → v0.11.6](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.5...v0.11.6) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e64c2528f..485a3091a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.5 + rev: v0.11.6 hooks: - id: ruff args: [ --fix ] From db4c6c75c19596e9c70be9f6b4c8f68ef21c9a43 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 28 Apr 2025 13:43:15 -0400 Subject: [PATCH 49/78] [pre-commit.ci] pre-commit autoupdate (#1570) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.11.6 → v0.11.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.6...v0.11.7) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 485a3091a..7cb239c7e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.6 + rev: v0.11.7 hooks: - id: ruff args: [ --fix ] From 64b1681ad4c6e05a41139b274e625fff38afab20 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 14:29:38 -0400 Subject: [PATCH 50/78] [pre-commit.ci] pre-commit autoupdate (#1572) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.11.7 → v0.11.8](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.7...v0.11.8) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7cb239c7e..c24b5a443 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.7 + rev: v0.11.8 hooks: - id: ruff args: [ --fix ] From 877e62542e44c1d0a2ddad8a09bb7a8d7b41c44c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 12 May 2025 13:48:50 -0400 Subject: [PATCH 51/78] [pre-commit.ci] pre-commit autoupdate (#1574) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.11.8 → v0.11.9](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.8...v0.11.9) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c24b5a443..11f11a935 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.8 + rev: v0.11.9 hooks: - id: ruff args: [ --fix ] From 1d5bfe776fd3e18a94715aaa6fd2940ae041797b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 19 May 2025 14:08:12 -0400 Subject: [PATCH 52/78] [pre-commit.ci] pre-commit autoupdate (#1575) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 11f11a935..9aa44ada9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.9 + rev: v0.11.10 hooks: - id: ruff args: [ --fix ] From 6eb18c3cd31a14aed290e276f71c84a44f085d9c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 26 May 2025 14:39:41 -0400 Subject: [PATCH 53/78] [pre-commit.ci] pre-commit autoupdate (#1577) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9aa44ada9..d36da279e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.10 + rev: v0.11.11 hooks: - id: ruff args: [ --fix ] From 8d3e7a907c5c82c66953b74a015bbc98aa4e636c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 2 Jun 2025 13:56:42 -0400 Subject: [PATCH 54/78] [pre-commit.ci] pre-commit autoupdate (#1578) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.11.11 → v0.11.12](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.11...v0.11.12) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d36da279e..8c244f88b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.11 + rev: v0.11.12 hooks: - id: ruff args: [ --fix ] From 2f2b7f0b5172a0144e390a5555bf7b2fe23a384e Mon Sep 17 00:00:00 2001 From: q0w <43147888+q0w@users.noreply.github.com> Date: Thu, 5 Jun 2025 23:10:34 +0500 Subject: [PATCH 55/78] Remove unnecessary select for removing an auth code grant (#1568) --- AUTHORS | 1 + oauth2_provider/oauth2_validators.py | 6 ++---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index e2da60020..e5357ae7c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -123,3 +123,4 @@ Yaroslav Halchenko Yuri Savin Miriam Forner Alex Kerkum +q0w diff --git a/oauth2_provider/oauth2_validators.py b/oauth2_provider/oauth2_validators.py index b20d0dd6c..db459a446 100644 --- a/oauth2_provider/oauth2_validators.py +++ b/oauth2_provider/oauth2_validators.py @@ -322,10 +322,8 @@ def invalidate_authorization_code(self, client_id, code, request, *args, **kwarg :raises: InvalidGrantError if the grant does not exist. """ - try: - grant = Grant.objects.get(code=code, application=request.client) - grant.delete() - except Grant.DoesNotExist: + deleted_grant_count, _ = Grant.objects.filter(code=code, application=request.client).delete() + if not deleted_grant_count: raise errors.InvalidGrantError(request=request) def validate_client_id(self, client_id, request, *args, **kwargs): From 362fecb87f439069cc967f47ebbd3ebde614abbe Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 9 Jun 2025 14:33:13 -0400 Subject: [PATCH 56/78] [pre-commit.ci] pre-commit autoupdate (#1580) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.11.12 → v0.11.13](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.12...v0.11.13) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8c244f88b..34a93c193 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.12 + rev: v0.11.13 hooks: - id: ruff args: [ --fix ] From d862f531a2ab96fb3cf05f6ffbf9baf8192c1459 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 30 Jun 2025 13:43:56 -0400 Subject: [PATCH 57/78] [pre-commit.ci] pre-commit autoupdate (#1581) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.11.13 → v0.12.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.11.13...v0.12.1) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 34a93c193..e117b66b4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.13 + rev: v0.12.1 hooks: - id: ruff args: [ --fix ] From bd6336993c2339876907d54fb6556cc79bfbe377 Mon Sep 17 00:00:00 2001 From: Darrel O'Pry Date: Sun, 20 Jul 2025 13:52:50 -0400 Subject: [PATCH 58/78] fix: codecov uploads failing (#1585) --- .github/workflows/test.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e3073dc7a..0b453d269 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,6 +6,8 @@ jobs: test-package: name: Test Package (Python ${{ matrix.python-version }}, Django ${{ matrix.django-version }}) runs-on: ubuntu-latest + permissions: + id-token: write # Required for Codecov OIDC token strategy: fail-fast: false matrix: @@ -60,9 +62,10 @@ jobs: DJANGO: ${{ matrix.django-version }} - name: Upload coverage - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v5 with: name: Python ${{ matrix.python-version }} + use_oidc: true test-demo-rp: name: Test Demo Relying Party From 3fad7654182d7eddf076d7070e2af4bfe95341e1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 20 Jul 2025 14:11:06 -0400 Subject: [PATCH 59/78] Bump vite from 5.4.14 to 5.4.19 in /tests/app/rp (#1587) --- tests/app/rp/package-lock.json | 8 ++++---- tests/app/rp/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/app/rp/package-lock.json b/tests/app/rp/package-lock.json index 6f33dbbec..c8186b56d 100644 --- a/tests/app/rp/package-lock.json +++ b/tests/app/rp/package-lock.json @@ -20,7 +20,7 @@ "svelte-check": "^3.8.0", "tslib": "^2.4.1", "typescript": "^5.0.0", - "vite": "^5.4.14" + "vite": "^5.4.19" } }, "node_modules/@ampproject/remapping": { @@ -2290,9 +2290,9 @@ } }, "node_modules/vite": { - "version": "5.4.14", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.14.tgz", - "integrity": "sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA==", + "version": "5.4.19", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz", + "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", "dev": true, "license": "MIT", "dependencies": { diff --git a/tests/app/rp/package.json b/tests/app/rp/package.json index 2e1a3afb6..603114a1a 100644 --- a/tests/app/rp/package.json +++ b/tests/app/rp/package.json @@ -21,7 +21,7 @@ "svelte-check": "^3.8.0", "tslib": "^2.4.1", "typescript": "^5.0.0", - "vite": "^5.4.14" + "vite": "^5.4.19" }, "type": "module", "dependencies": { From 121abd4fb8398401907860005a8be8bdec0149f3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 4 Aug 2025 14:35:33 -0400 Subject: [PATCH 60/78] [pre-commit.ci] pre-commit autoupdate (#1584) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.12.1 → v0.12.7](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.1...v0.12.7) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e117b66b4..f4eb471cf 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.12.1 + rev: v0.12.7 hooks: - id: ruff args: [ --fix ] From 1c2d99673169d0962c215a835fed392c3e2fdbf0 Mon Sep 17 00:00:00 2001 From: Dawid Wolski <130552247+dawidwolski-identt@users.noreply.github.com> Date: Tue, 12 Aug 2025 18:08:38 +0200 Subject: [PATCH 61/78] Fix: AttributeError in IntrospectTokenView when token not provided (#1562) When token is not provided explicitly respond with a 400 status and properly structured JSON error. Before this a 500 was being returned for an unhandled exception. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Darrel O'Pry --- CHANGELOG.md | 1 + oauth2_provider/views/introspect.py | 5 +++++ tests/test_introspection_view.py | 14 ++++++++++++++ tox.ini | 11 ++++++----- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c4770459..514c45ec6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * #1512 client_secret not marked sensitive * #1521 Fix 0012 migration loading access token table into memory * #1584 Fix IDP container in docker compose environment could not find templates and static files. +* #1562 Fix: Handle AttributeError in IntrospectTokenView diff --git a/oauth2_provider/views/introspect.py b/oauth2_provider/views/introspect.py index 5474c3a7e..5b9810c82 100644 --- a/oauth2_provider/views/introspect.py +++ b/oauth2_provider/views/introspect.py @@ -26,6 +26,11 @@ class IntrospectTokenView(ClientProtectedScopedResourceView): @staticmethod def get_token_response(token_value=None): + if token_value is None: + return JsonResponse( + {"error": "invalid_request", "error_description": "Token parameter is missing."}, + status=400, + ) try: token_checksum = hashlib.sha256(token_value.encode("utf-8")).hexdigest() token = ( diff --git a/tests/test_introspection_view.py b/tests/test_introspection_view.py index 3db23bbcd..ad7d8983d 100644 --- a/tests/test_introspection_view.py +++ b/tests/test_introspection_view.py @@ -279,6 +279,20 @@ def test_view_post_notexisting_token(self): }, ) + def test_view_post_no_token(self): + """ + Test that when you pass no token HTTP 400 is returned + """ + auth_headers = { + "HTTP_AUTHORIZATION": "Bearer " + self.resource_server_token.token, + } + response = self.client.post(reverse("oauth2_provider:introspect"), **auth_headers) + + self.assertEqual(response.status_code, 400) + content = response.json() + self.assertIsInstance(content, dict) + self.assertEqual(content["error"], "invalid_request") + def test_view_post_valid_client_creds_basic_auth(self): """Test HTTP basic auth working""" auth_headers = get_basic_auth_header(self.application.client_id, CLEARTEXT_SECRET) diff --git a/tox.ini b/tox.ini index 303b0d51d..d5cf8d2dc 100644 --- a/tox.ini +++ b/tox.ini @@ -5,10 +5,10 @@ envlist = docs, lint, sphinxlint, - py{38,39,310,311,312}-dj42, - py{310,311,312}-dj50, - py{310,311,312}-dj51, - py{310,311,312}-djmain, + py{38,39,310,311,312,313}-dj42, + py{310,311,312,313}-dj50, + py{310,311,312,313}-dj51, + py{310,311,312,313}-djmain, py39-multi-db-dj-42 [gh-actions] @@ -18,6 +18,7 @@ python = 3.10: py310 3.11: py311 3.12: py312 + 3.13: py313 [gh-actions:env] DJANGO = @@ -54,7 +55,7 @@ deps = passenv = PYTEST_ADDOPTS -[testenv:py{310,311,312}-djmain] +[testenv:py{310,311,312,313}-djmain] ignore_errors = true ignore_outcome = true From 8f132e7a26f8fb47ea0e3f8bc86af3fde2440a52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cihad=20G=C3=9CNDO=C4=9EDU?= Date: Tue, 12 Aug 2025 20:00:11 +0300 Subject: [PATCH 62/78] feat: add Turkish translations for OAuth2 provider messages (#1586) * Add Turkish translations for OAuth2 provider messages --- AUTHORS | 1 + CHANGELOG.md | 1 + .../locale/tr/LC_MESSAGES/django.po | 215 ++++++++++++++++++ 3 files changed, 217 insertions(+) create mode 100644 oauth2_provider/locale/tr/LC_MESSAGES/django.po diff --git a/AUTHORS b/AUTHORS index e5357ae7c..7d24d3afd 100644 --- a/AUTHORS +++ b/AUTHORS @@ -35,6 +35,7 @@ Bart Merenda Bas van Oostveen Brian Helba Carl Schwan +Cihad GUNDOGDU Daniel Golding Daniel 'Vector' Kerr Darrel O'Pry diff --git a/CHANGELOG.md b/CHANGELOG.md index 514c45ec6..a8bc0fa32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [unreleased] ### Added * #1506 Support for Wildcard Origin and Redirect URIs +* #1586 Turkish language support added diff --git a/oauth2_provider/locale/pt_BR/LC_MESSAGES/django.po b/oauth2_provider/locale/pt_BR/LC_MESSAGES/django.po index 48d673e33..e852622e3 100644 --- a/oauth2_provider/locale/pt_BR/LC_MESSAGES/django.po +++ b/oauth2_provider/locale/pt_BR/LC_MESSAGES/django.po @@ -9,10 +9,10 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-12-30 09:50-0300\n" -"PO-Revision-Date: 2021-12-30 09:50-0300\n" -"Last-Translator: Eduardo Oliveira \n" +"PO-Revision-Date: 2025-07-01 12:35-0300\n" +"Last-Translator: Thales Barbosa Bento \n" "Language-Team: LANGUAGE \n" -"Language: \n" +"Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" @@ -101,7 +101,7 @@ msgstr "Tem certeza que deseja remover a aplicação?" msgid "Cancel" msgstr "Cancelar" -#: templates/oauth2_provider/application_confirm_delete.html:13 +#: templates/oauth2_provider/application_confirm_delete.html:13F #: templates/oauth2_provider/application_detail.html:38 #: templates/oauth2_provider/authorized-token-delete.html:7 msgid "Delete" @@ -113,7 +113,7 @@ msgstr "ID do Cliente" #: templates/oauth2_provider/application_detail.html:15 msgid "Client secret" -msgstr "Palavra-Chave Secreta do Cliente" +msgstr "Chave Secreta do Cliente" #: templates/oauth2_provider/application_detail.html:20 msgid "Client type" @@ -200,3 +200,258 @@ msgstr "Revogar" #: templates/oauth2_provider/authorized-tokens.html:19 msgid "There are no authorized tokens yet." msgstr "Não existem tokens autorizados ainda." + +msgid "Allowed origins list to enable CORS, space separated" +msgstr "Lista de origens permitidas para habilitar CORS, separadas por espaços" + +msgid "Access tokens" +msgstr "Tokens de acesso" + +msgid "Applications" +msgstr "Aplicações" + +msgid "Grants" +msgstr "Concessões" + +msgid "Id tokens" +msgstr "Tokens de ID" + +msgid "Refresh tokens" +msgstr "Tokens de atualização" + +msgid "Access token" +msgstr "Token de acesso" + +msgid "Application" +msgstr "Aplicação" + +msgid "Grant" +msgstr "Concessão" + +msgid "Id token" +msgstr "Token de ID" + +msgid "Refresh token" +msgstr "Token de atualização" + +msgid "Skip authorization" +msgstr "Pular autorização" + +msgid "Algorithm" +msgstr "Algoritmo" + +msgid "Created" +msgstr "Criado" + +msgid "Updated" +msgstr "Atualizado" + +msgid "Scope" +msgstr "Escopo" + +msgid "Scopes" +msgstr "Escopos" + +msgid "Expires" +msgstr "Expira" + +msgid "Token" +msgstr "Token" + +msgid "User" +msgstr "Usuário" + +msgid "Code" +msgstr "Código" + +msgid "Redirect URI" +msgstr "URI de Redirecionamento" + +msgid "State" +msgstr "Estado" + +msgid "Nonce" +msgstr "Nonce (Number Used Once)" + +msgid "Code Challenge" +msgstr "Desafio do Código" + +msgid "Code Challenge Method" +msgstr "Método do Desafio do Código" + +msgid "PKCE required" +msgstr "PKCE obrigatório" + +msgid "Post logout redirect URI" +msgstr "URI de redirecionamento pós-logout" + +msgid "Hash client secret" +msgstr "Hash da chave secreta do cliente" + +msgid "The client secret will be hashed" +msgstr "A chave secreta do cliente será transformada em hash" + +msgid "Skip authorization completely" +msgstr "Pular autorização completamente" + +msgid "Post logout redirect URIs" +msgstr "URIs de redirecionamento pós-logout" + +msgid "Allowed Post Logout Redirect URIs list, space separated" +msgstr "Lista de URIs de redirecionamento pós-logout permitidas, separadas por espaços" + +msgid "Authorization Grant" +msgstr "Concessão de Autorização" + +msgid "Authorization Grants" +msgstr "Concessões de Autorização" + +msgid "Invalid client or client credentials" +msgstr "Cliente inválido ou credenciais do cliente inválidas" + +msgid "Invalid authorization code" +msgstr "Código de autorização inválido" + +msgid "Invalid redirect URI" +msgstr "URI de redirecionamento inválida" + +msgid "Invalid grant type" +msgstr "Tipo de concessão inválido" + +msgid "Invalid scope" +msgstr "Escopo inválido" + +msgid "Invalid request" +msgstr "Solicitação inválida" + +msgid "Unsupported grant type" +msgstr "Tipo de concessão não suportado" + +msgid "Unsupported response type" +msgstr "Tipo de resposta não suportado" + +msgid "Authorization server error" +msgstr "Erro do servidor de autorização" + +msgid "Temporarily unavailable" +msgstr "Temporariamente indisponível" + +msgid "Access denied" +msgstr "Acesso negado" + +msgid "Invalid client" +msgstr "Cliente inválido" + +msgid "The client identifier provided is invalid" +msgstr "O identificador do cliente fornecido é inválido" + +msgid "The client authentication failed" +msgstr "A autenticação do cliente falhou" + +msgid "The authorization grant is invalid, expired, or revoked" +msgstr "A concessão de autorização é inválida, expirada ou revogada" + +msgid "The authenticated client is not authorized to use this authorization grant type" +msgstr "O cliente autenticado não está autorizado a usar este tipo de concessão de autorização" + +msgid "The request is missing a required parameter" +msgstr "A solicitação está perdendo um parâmetro obrigatório" + +msgid "The authorization server encountered an unexpected condition" +msgstr "O servidor de autorização encontrou uma condição inesperada" + +msgid "The authorization server is currently unable to handle the request" +msgstr "O servidor de autorização atualmente não consegue processar a solicitação" + +msgid "The resource owner or authorization server denied the request" +msgstr "O proprietário do recurso ou servidor de autorização negou a solicitação" + +msgid "The authorization server does not support obtaining authorization codes" +msgstr "O servidor de autorização não suporta obter códigos de autorização" + +msgid "The authorization server does not support the revocation of access tokens" +msgstr "O servidor de autorização não suporta a revogação de tokens de acesso" + +msgid "The authorization server does not support the revocation of refresh tokens" +msgstr "O servidor de autorização não suporta a revogação de tokens de atualização" + +msgid "The authorization server does not support the use of the transformation parameter" +msgstr "O servidor de autorização não suporta o uso do parâmetro de transformação" + +msgid "The target resource is invalid, missing, malformed, or not supported" +msgstr "O recurso de destino é inválido, ausente, malformado ou não suportado" + +msgid "Rotate refresh token" +msgstr "Rotacionar token de atualização" + +msgid "Reuse refresh token" +msgstr "Reutilizar token de atualização" + +msgid "Application name" +msgstr "Nome da aplicação" + +msgid "Application description" +msgstr "Descrição da aplicação" + +msgid "Application logo" +msgstr "Logo da aplicação" + +msgid "Application website" +msgstr "Website da aplicação" + +msgid "Terms of service" +msgstr "Termos de serviço" + +msgid "Privacy policy" +msgstr "Política de privacidade" + +msgid "Support email" +msgstr "Email de suporte" + +msgid "Support URL" +msgstr "URL de suporte" + +msgid "Redirect URIs" +msgstr "URIs de Redirecionamento" + +msgid "Source refresh token" +msgstr "Token de atualização de origem" + +msgid "Token checksum" +msgstr "Checksum do token" + +msgid "Token family" +msgstr "Família do token" + +msgid "Code challenge" +msgstr "Desafio do código" + +msgid "Code challenge method" +msgstr "Método do desafio do código" + +msgid "Claims" +msgstr "Reivindicações" + +msgid "JWT Token ID" +msgstr "ID do Token JWT" + +msgid "Allowed origins" +msgstr "Origens permitidas" + +msgid "Client ID" +msgstr "ID do Cliente" + +msgid "Revoked" +msgstr "Revogado" + +msgid "Authorization grant type" +msgstr "Tipo de concessão de autorização" + +msgid "Hashed on Save. Copy it now if this is a new secret." +msgstr "Hashed em Salvar. Copie agora se este for um novo segredo." + +msgid "allowed origin URI Validation error" +msgstr "erro de validação de URI de origem permitido. invalid_scheme" + +msgid "Date" +msgstr "Data" From 52b0b2f103c647280ed7711fda0ba674913addc0 Mon Sep 17 00:00:00 2001 From: David Reguera <33068707+nablabits@users.noreply.github.com> Date: Tue, 12 Aug 2025 19:47:06 +0200 Subject: [PATCH 64/78] chore(docs): Move around label targets. (#1576) The ...createapplication anchor seems to have ended up in the wrong spot. This moves it to just before the heading. --- docs/management_commands.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/management_commands.rst b/docs/management_commands.rst index 83770041e..0a3f1bda0 100644 --- a/docs/management_commands.rst +++ b/docs/management_commands.rst @@ -5,8 +5,6 @@ Django OAuth Toolkit exposes some useful management commands that can be run via or :doc:`Celery `. .. _cleartokens: -.. _createapplication: - cleartokens ~~~~~~~~~~~ @@ -27,7 +25,7 @@ The ``cleartokens`` management command will also delete expired access and ID to Note: Refresh tokens need to expire before AccessTokens can be removed from the database. Using ``cleartokens`` without ``REFRESH_TOKEN_EXPIRE_SECONDS`` has limited effect. - +.. _createapplication: createapplication ~~~~~~~~~~~~~~~~~ From 0a5ffdbca596d1b21e7f98ea56ead2cec76192c7 Mon Sep 17 00:00:00 2001 From: Subham Date: Thu, 14 Aug 2025 04:55:52 +0530 Subject: [PATCH 65/78] chore: update deprecated jwcrypto key_type to kty in tests (#1590) key_type was deprecated in latchset/jwcrypto@0edf66d, https://github.com/latchset/jwcrypto/releases/tag/v0.9.0 --- tests/test_authorization_code.py | 2 +- tests/test_models.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_authorization_code.py b/tests/test_authorization_code.py index f162e211a..660e5e5d4 100644 --- a/tests/test_authorization_code.py +++ b/tests/test_authorization_code.py @@ -1867,7 +1867,7 @@ def test_id_token(self): # Check decoding JWT using HS256 key = self.application.jwk_key - assert key.key_type == "oct" + assert key.kty == "oct" jwt_token = jwt.JWT(key=key, jwt=content["id_token"]) claims = json.loads(jwt_token.claims) assert claims["sub"] == "1" diff --git a/tests/test_models.py b/tests/test_models.py index 2c7ff8114..eb01aac8f 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -563,7 +563,7 @@ def test_clear_expired_id_tokens(oauth2_settings, oidc_tokens, rf): def test_application_key(oauth2_settings, application): # RS256 key key = application.jwk_key - assert key.key_type == "RSA" + assert key.kty == "RSA" # RS256 key, but not configured oauth2_settings.OIDC_RSA_PRIVATE_KEY = None @@ -574,7 +574,7 @@ def test_application_key(oauth2_settings, application): # HS256 key application.algorithm = Application.HS256_ALGORITHM key = application.jwk_key - assert key.key_type == "oct" + assert key.kty == "oct" # No algorithm application.algorithm = Application.NO_ALGORITHM From 762cf8d1d35f72071626de788aa8948111f6cdcc Mon Sep 17 00:00:00 2001 From: Darrel O'Pry Date: Fri, 3 Oct 2025 11:21:06 -0400 Subject: [PATCH 66/78] chore: update from jazzband to django-oauth (#1592) --- .github/workflows/release.yml | 11 ++- CHANGELOG.md | 22 ++--- CODE_OF_CONDUCT.md | 150 ++++++++++++++++++++++--------- CONTRIBUTING.md | 8 +- Dockerfile | 4 +- README.rst | 47 ++++------ docs/advanced_topics.rst | 2 +- docs/contributing.rst | 24 +++-- docs/index.rst | 2 +- docs/settings.rst | 2 +- pyproject.toml | 2 +- tests/test_authorization_code.py | 2 +- tests/test_models.py | 2 +- 13 files changed, 163 insertions(+), 115 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 64302e819..0c4329265 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,7 +7,7 @@ on: jobs: build: - if: github.repository == 'jazzband/django-oauth-toolkit' + if: github.repository == 'django-oauth/django-oauth-toolkit' runs-on: ubuntu-latest steps: @@ -29,10 +29,9 @@ jobs: python -m build twine check dist/* - - name: Upload packages to Jazzband + - name: Upload packages to PyPI if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') - uses: pypa/gh-action-pypi-publish@master + uses: pypa/gh-action-pypi-publish@release/v1 with: - user: jazzband - password: ${{ secrets.JAZZBAND_RELEASE_KEY }} - repository_url: https://jazzband.co/projects/django-oauth-toolkit/upload + user: __token__ + password: ${{ secrets.PYPI_PUBLISH_TOKEN }} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 32dd1734c..59eb50c61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,8 +10,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * #1506 Support for Wildcard Origin and Redirect URIs * #1586 Turkish language support added - @@ -241,7 +243,7 @@ This is a major release with **BREAKING** changes. Please make sure to review th ## [1.6.1] 2021-12-23 ### Changed -* Note: Only Django 4.0.1+ is supported due to a regression in Django 4.0.0. [Explanation](https://github.com/jazzband/django-oauth-toolkit/pull/1046#issuecomment-998015272) +* Note: Only Django 4.0.1+ is supported due to a regression in Django 4.0.0. [Explanation](https://github.com/django-oauth/django-oauth-toolkit/pull/1046#issuecomment-998015272) ### Fixed * Miscellaneous 1.6.0 packaging issues. @@ -332,7 +334,7 @@ This is a major release with **BREAKING** changes. Please make sure to review th ### Fixed * #812: Reverts #643 pass wrong request object to authenticate function. -* Fix concurrency issue with refresh token requests (#[810](https://github.com/jazzband/django-oauth-toolkit/pull/810)) +* Fix concurrency issue with refresh token requests (#[810](https://github.com/django-oauth/django-oauth-toolkit/pull/810)) * #817: Reverts #734 tutorial documentation error. @@ -371,16 +373,16 @@ This is a major release with **BREAKING** changes. Please make sure to review th ### Fixed * Fix a race condition in creation of AccessToken with external oauth2 server. -* Fix several concurrency issues. (#[638](https://github.com/jazzband/django-oauth-toolkit/issues/638)) -* Fix to pass `request` to `django.contrib.auth.authenticate()` (#[636](https://github.com/jazzband/django-oauth-toolkit/issues/636)) +* Fix several concurrency issues. (#[638](https://github.com/django-oauth/django-oauth-toolkit/issues/638)) +* Fix to pass `request` to `django.contrib.auth.authenticate()` (#[636](https://github.com/django-oauth/django-oauth-toolkit/issues/636)) * Fix missing `oauth2_error` property exception oauthlib_core.verify_request method raises exceptions in authenticate. - (#[633](https://github.com/jazzband/django-oauth-toolkit/issues/633)) + (#[633](https://github.com/django-oauth/django-oauth-toolkit/issues/633)) * Fix "django.db.utils.NotSupportedError: FOR UPDATE cannot be applied to the nullable side of an outer join" for postgresql. - (#[714](https://github.com/jazzband/django-oauth-toolkit/issues/714)) + (#[714](https://github.com/django-oauth/django-oauth-toolkit/issues/714)) * Fix to return a new refresh token during grace period rather than the recently-revoked one. - (#[702](https://github.com/jazzband/django-oauth-toolkit/issues/702)) + (#[702](https://github.com/django-oauth/django-oauth-toolkit/issues/702)) * Fix a bug in refresh token revocation. - (#[625](https://github.com/jazzband/django-oauth-toolkit/issues/625)) + (#[625](https://github.com/django-oauth/django-oauth-toolkit/issues/625)) ## 1.2.0 [2018-06-03] @@ -402,7 +404,7 @@ This is a major release with **BREAKING** changes. Please make sure to review th * **Critical**: Django OAuth Toolkit 1.1.0 contained a migration that would revoke all existing RefreshTokens (`0006_auto_20171214_2232`). This release corrects the migration. If you have already ran it in production, please see the following issue for more details: - https://github.com/jazzband/django-oauth-toolkit/issues/589 + https://github.com/django-oauth/django-oauth-toolkit/issues/589 ## 1.1.0 [2018-04-13] diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index e0d5efab5..e5ee8d275 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,46 +1,108 @@ # Code of Conduct -As contributors and maintainers of the Jazzband projects, and in the interest of -fostering an open and welcoming community, we pledge to respect all people who -contribute through reporting issues, posting feature requests, updating documentation, -submitting pull requests or patches, and other activities. - -We are committed to making participation in the Jazzband a harassment-free experience -for everyone, regardless of the level of experience, gender, gender identity and -expression, sexual orientation, disability, personal appearance, body size, race, -ethnicity, age, religion, or nationality. - -Examples of unacceptable behavior by participants include: - -- The use of sexualized language or imagery -- Personal attacks -- Trolling or insulting/derogatory comments -- Public or private harassment -- Publishing other's private information, such as physical or electronic addresses, - without explicit permission -- Other unethical or unprofessional conduct - -The Jazzband roadies have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are not -aligned to this Code of Conduct, or to ban temporarily or permanently any contributor -for other behaviors that they deem inappropriate, threatening, offensive, or harmful. - -By adopting this Code of Conduct, the roadies commit themselves to fairly and -consistently applying these principles to every aspect of managing the jazzband -projects. Roadies who do not follow or enforce the Code of Conduct may be permanently -removed from the Jazzband roadies. - -This code of conduct applies both within project spaces and in public spaces when an -individual is representing the project or its community. - -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by -contacting the roadies at `roadies@jazzband.co`. All complaints will be reviewed and -investigated and will result in a response that is deemed necessary and appropriate to -the circumstances. Roadies are obligated to maintain confidentiality with regard to the -reporter of an incident. - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version -1.3.0, available at [https://contributor-covenant.org/version/1/3/0/][version] - -[homepage]: https://contributor-covenant.org -[version]: https://contributor-covenant.org/version/1/3/0/ +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Maintainers are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Maintainers have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official email address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[django-oauth-coc@googlegroups.com](mailto:django-oauth-coc@googlegroups.com). +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Warning + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 2. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 3. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Django Commons Code of Conduct](https://github.com/django-commons/membership/blob/main/CODE_OF_CONDUCT.md) \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 49518f460..b89d471e6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,10 +1,10 @@ -[![Jazzband](https://jazzband.co/static/img/jazzband.svg)](https://jazzband.co/) +# Contribute to Django OAuth Toolkit -This is a [Jazzband](https://jazzband.co/) project. By contributing you agree to abide by the [Contributor Code of Conduct](https://jazzband.co/about/conduct) and follow the [guidelines](https://jazzband.co/about/guidelines). +Thanks for your interest, we love contributions! There are many ways to participate. We are always in need of help with code review, bug fixes, feature development, documentation, and community development. -# Contribute to Django OAuth Toolkit +By contributing you agree to abide by the [Code of Conduct](./CODE_OF_CONDUCT.md) -Thanks for your interest, we love contributions! +We are striving to make a free and open source standards compliant OAuth2/OIDC Identity Provider that adheres to best practices out of the box. Let that goal be your guide as you make decisions related to the project. Please [follow these guidelines](https://django-oauth-toolkit.readthedocs.io/en/latest/contributing.html) when submitting pull requests. diff --git a/Dockerfile b/Dockerfile index 4565df5ff..8828cead5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -37,8 +37,8 @@ FROM python:3.11.6-slim # allow embed sha1 at build time as release. ARG GIT_SHA1 -LABEL org.opencontainers.image.authors="https://jazzband.co/projects/django-oauth-toolkit" -LABEL org.opencontainers.image.source="https://github.com/jazzband/django-oauth-toolkit" +LABEL org.opencontainers.image.authors="https://github.com/django-oauth/django-oauth-toolkit/blob/master/AUTHORS" +LABEL org.opencontainers.image.source="https://github.com/django-oauth/django-oauth-toolkit" LABEL org.opencontainers.image.revision=${GIT_SHA1} diff --git a/README.rst b/README.rst index e8b49d2a6..e58098806 100644 --- a/README.rst +++ b/README.rst @@ -1,21 +1,17 @@ Django OAuth Toolkit ==================== -.. image:: https://jazzband.co/static/img/badge.svg - :target: https://jazzband.co/ - :alt: Jazzband - *OAuth2 goodies for the Djangonauts!* .. image:: https://badge.fury.io/py/django-oauth-toolkit.svg :target: http://badge.fury.io/py/django-oauth-toolkit -.. image:: https://github.com/jazzband/django-oauth-toolkit/workflows/Test/badge.svg - :target: https://github.com/jazzband/django-oauth-toolkit/actions +.. image:: https://github.com/django-oauth/django-oauth-toolkit/workflows/Test/badge.svg + :target: https://github.com/django-oauth/django-oauth-toolkit/actions :alt: GitHub Actions -.. image:: https://codecov.io/gh/jazzband/django-oauth-toolkit/branch/master/graph/badge.svg - :target: https://codecov.io/gh/jazzband/django-oauth-toolkit +.. image:: https://codecov.io/gh/django-oauth/django-oauth-toolkit/branch/master/graph/badge.svg + :target: https://codecov.io/gh/django-oauth/django-oauth-toolkit :alt: Coverage .. image:: https://img.shields.io/pypi/pyversions/django-oauth-toolkit.svg @@ -38,7 +34,7 @@ capabilities to your Django projects. Django OAuth Toolkit makes extensive use o Reporting security issues ------------------------- -Please report any security issues to the JazzBand security team at . Do not file an issue on the tracker. +Please report any security issues to the Django OAuth security team at . Do not file an issue on the tracker. Requirements ------------ @@ -78,7 +74,7 @@ If you need an OAuth2 provider you'll want to add the following to your ``urls.p Changelog --------- -See `CHANGELOG.md `_. +See `CHANGELOG.md `_. Documentation @@ -99,9 +95,8 @@ We need help maintaining and enhancing django-oauth-toolkit (DOT). Join the team ~~~~~~~~~~~~~ -Please consider joining `Jazzband `__ (If not -already a member) and the `DOT project -team `__. +There are no barriers to participation. Anyone can open an issue, pr, or review a pull request. Please +dive in! How you can help ~~~~~~~~~~~~~~~~ @@ -109,15 +104,15 @@ How you can help See our `contributing `__ info and the open -`issues `__ and -`PRs `__, +`issues `__ and +`PRs `__, especially those labeled -`help-wanted `__. +`help-wanted `__. Discussions ~~~~~~~~~~~ Have questions or want to discuss the project? -See `the discussions `__. +See `the discussions `__. Submit PRs and Perform Reviews @@ -127,18 +122,12 @@ PR submissions and reviews are always appreciated! Since we require an independent review of any PR before it can be merged, having your second set of eyes looking at PRs is extremely valuable. -Please don’t merge PRs -~~~~~~~~~~~~~~~~~~~~~~ - -Please be aware that we don’t want *every* Jazzband member to merge PRs -but just a handful of project team members so that we can maintain a -modicum of control over what goes into a release of this security oriented code base. Only `project -leads `__ are able to -publish releases to Pypi and it becomes difficult when creating a new -release for the leads to deal with “unexpected” merged PRs. -Become a Project Lead +Become a Maintainer ~~~~~~~~~~~~~~~~~~~~~ -If you are interested in stepping up to be a Project Lead, please take a look at -the `discussion about this `__. +If you are interested in stepping up to be a Maintainer, please open an issue. For maintainers we're +looking for a positive attitude, attentiveness to the specifications, strong coding and +communication skills, and a willingness to work with others. Maintainers are responsible for +merging pull requests, managing issues, creating releases, and ensuring the overall health of the +project. diff --git a/docs/advanced_topics.rst b/docs/advanced_topics.rst index 204e3f860..a99559e93 100644 --- a/docs/advanced_topics.rst +++ b/docs/advanced_topics.rst @@ -63,7 +63,7 @@ That's all, now Django OAuth Toolkit will use your model wherever an Application .. note:: ``OAUTH2_PROVIDER_APPLICATION_MODEL`` is the only setting variable that is not namespaced, this is because of the way Django currently implements swappable models. - See `issue #90 `_ for details. + See `issue #90 `_ for details. Configuring multiple databases ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/contributing.rst b/docs/contributing.rst index 569f5eab2..1b491d70b 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -2,17 +2,13 @@ Contributing ============ -.. image:: https://jazzband.co/static/img/jazzband.svg - :target: https://jazzband.co/ - :alt: Jazzband - -This is a `Jazzband `_ project. By contributing you agree to abide by the `Contributor Code of Conduct `_ and follow the `guidelines `_. +By contributing you agree to abide by the `Code of Conduct `_ and follow the `guidelines `_. Setup ===== -Fork ``django-oauth-toolkit`` repository on `GitHub `_ and follow these steps: +Fork ``django-oauth-toolkit`` repository on `GitHub `_ and follow these steps: * Create a virtualenv and activate it * Clone your repository locally @@ -21,7 +17,7 @@ Issues ====== You can find the list of bugs, enhancements and feature requests on the -`issue tracker `_. If you want to fix an issue, pick up one and +`issue tracker `_. If you want to fix an issue, pick up one and add a comment stating you're working on it. Code Style @@ -161,7 +157,7 @@ When you begin your PR, you'll be asked to provide the following: * ``Fixed`` for any bug fixes. * ``Security`` in case of vulnerabilities. (Please report any security issues to the - JazzBand security team ````. Do not file an issue on the tracker + security team ````. Do not file an issue on the tracker or submit a PR until directed to do so.) * Make sure your name is in :file:`AUTHORS`. We want to give credit to all contributors! @@ -169,7 +165,7 @@ When you begin your PR, you'll be asked to provide the following: If your PR is not yet ready to be merged mark it as a Work-in-Progress By prepending ``WIP:`` to the PR title so that it doesn't get inadvertently approved and merged. -Make sure to request a review by assigning Reviewer ``jazzband/django-oauth-toolkit``. +Make sure to request a review by assigning Reviewer ``django-oauth/django-oauth-toolkit``. This will assign the review to the project team and a member will review it. In the meantime you can continue to add commits to your topic branch (and push them up to GitHub) either if you see something that needs changing, or in response to a reviewer's comments. If a reviewer asks for changes, you do not need to close the pull and reissue it @@ -184,7 +180,7 @@ outdated code and your changes diverge too far from master, the pull request has To pull in upstream changes:: - git remote add upstream https://github.com/jazzband/django-oauth-toolkit.git + git remote add upstream https://github.com/django-oauth/django-oauth-toolkit.git git fetch upstream Then merge the changes that you fetched:: @@ -316,7 +312,7 @@ Reviewing and Merging PRs ------------------------- - Make sure the PR description includes the `pull request template - `_ + `_ - Confirm that all required checklist items from the PR template are both indicated as done in the PR description and are actually done. - Perform a careful review and ask for any needed changes. @@ -351,11 +347,11 @@ password: password Publishing a Release -------------------- -Only Project Leads can `publish a release `_ to pypi.org +Only maintainers can publish a release to pypi.org and rtfd.io. This checklist is a reminder of the required steps. - When planning a new release, create a `milestone - `_ + `_ and assign issues, PRs, etc. to that milestone. - Review all commits since the last release and confirm that they are properly documented in the CHANGELOG. Reword entries as appropriate with links to docs @@ -366,7 +362,7 @@ and rtfd.io. This checklist is a reminder of the required steps. - :file:`oauth2_provider/__init__.py` to set ``__version__ = "..."`` - Once the final PR is merged, create and push a tag for the release. You'll shortly - get a notification from Jazzband of the availability of two pypi packages (source tgz + get a notification of the availability of two pypi packages (source tgz and wheel). Download these locally before releasing them. - Do a ``tox -e build`` and extract the downloaded and built wheel zip and tgz files into temp directories and do a ``diff -r`` to make sure they have the same content. diff --git a/docs/index.rst b/docs/index.rst index 07ed24314..8519827ed 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -16,7 +16,7 @@ See our :doc:`Changelog ` for information on updates. Support ------- -If you need help please submit a `question `_. +If you need help please submit a `question `_. Requirements ------------ diff --git a/docs/settings.rst b/docs/settings.rst index 985ca5d2c..9c71bb2a8 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -449,7 +449,7 @@ List of non-namespaced settings .. note:: These settings must be set as top-level Django settings (outside of ``OAUTH2_PROVIDER``), because of the way Django currently implements swappable models. - See `issue #90 `_ for details. + See `issue #90 `_ for details. OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL diff --git a/pyproject.toml b/pyproject.toml index 401d33cab..2bb4a83b2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,7 +50,7 @@ dev = [ [project.urls] Homepage = "https://django-oauth-toolkit.readthedocs.io/" -Repository = "https://github.com/jazzband/django-oauth-toolkit" +Repository = "https://github.com/django-oauth/django-oauth-toolkit" [tool.setuptools.dynamic] version = {attr = "oauth2_provider.__version__"} diff --git a/tests/test_authorization_code.py b/tests/test_authorization_code.py index 660e5e5d4..360fac957 100644 --- a/tests/test_authorization_code.py +++ b/tests/test_authorization_code.py @@ -505,7 +505,7 @@ def test_code_post_auth_failing_redirection_uri_with_querystring(self): """ Test that in case of error the querystring of the redirection uri is preserved - See https://github.com/jazzband/django-oauth-toolkit/issues/238 + See https://github.com/django-oauth/django-oauth-toolkit/issues/238 """ self.client.login(username="test_user", password="123456") diff --git a/tests/test_models.py b/tests/test_models.py index eb01aac8f..8c0048066 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -168,7 +168,7 @@ def test_custom_application_model(self): If a custom application model is installed, it should be present in the related objects and not the swapped out one. - See issue #90 (https://github.com/jazzband/django-oauth-toolkit/issues/90) + See issue #90 (https://github.com/django-oauth/django-oauth-toolkit/issues/90) """ related_object_names = [ f.name From 842a4d5b0f2ab7019b42a0cd3ae913008e7fce6a Mon Sep 17 00:00:00 2001 From: Darrel O'Pry Date: Fri, 3 Oct 2025 12:11:08 -0400 Subject: [PATCH 67/78] Release 3.1 (#1600) * chore: prepare 3.1 release * chore: full release --- CHANGELOG.md | 18 ++++++++++++++++-- oauth2_provider/__init__.py | 2 +- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59eb50c61..1f17685ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,24 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - ## [unreleased] + + +## [3.1.0] + + +**NOTE**: This is the first release under the new [django-oauth](https://github.com/django-oauth) organization. The project moved in order to be more independent and to bypass quota limits on parallel CI jobs we were encountering in Jazzband. The project will emulateDjango Commons going forward in it's operation. We're always on the look for willing maintainers and contributors. Feel free to start participating any time. PR's are always welcome. + ### Added -* #1506 Support for Wildcard Origin and Redirect URIs +* #1506 Support for Wildcard Origin and Redirect URIs - Adds a new setting [ALLOW_URL_WILDCARDS](https://django-oauth-toolkit.readthedocs.io/en/latest/settings.html#allow-uri-wildcards). This feature is useful for working with CI service such as cloudflare, netlify, and vercel that offer branch +deployments for development previews and user acceptance testing. * #1586 Turkish language support added ### Changed diff --git a/oauth2_provider/__init__.py b/oauth2_provider/__init__.py index 055276878..f5f41e567 100644 --- a/oauth2_provider/__init__.py +++ b/oauth2_provider/__init__.py @@ -1 +1 @@ -__version__ = "3.0.1" +__version__ = "3.1.0" From 0164aec7fb7ee7f8f755d4f992252133c4d72ab7 Mon Sep 17 00:00:00 2001 From: Darrel O'Pry Date: Sun, 2 Nov 2025 01:09:20 -0400 Subject: [PATCH 68/78] chore: update rp and idp styling (#1607) - rp improve styling for readability - rp updated to latest @dopry/svelte-oidc to get EventLog - idp add home page with links to admin and rp - add tasks to pr template asking contributors to update the idp and rp with new features --- .github/pull_request_template.md | 2 + .github/workflows/test.yml | 8 +- tests/app/idp/idp/urls.py | 2 + tests/app/idp/templates/home/index.html | 19 + tests/app/rp/package-lock.json | 1924 ++---- tests/app/rp/package.json | 24 +- tests/app/rp/src/app.html | 73 +- tests/app/rp/src/routes/+page.svelte | 67 +- tests/app/rp/static/materialize.min.css | 7732 +++++++++++++++++++++++ 9 files changed, 8556 insertions(+), 1295 deletions(-) create mode 100644 tests/app/idp/templates/home/index.html create mode 100644 tests/app/rp/static/materialize.min.css diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 9e41b33cf..cf8622d1a 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -14,3 +14,5 @@ Fixes # - [ ] documentation updated - [ ] `CHANGELOG.md` updated (only for user relevant changes) - [ ] author name in `AUTHORS` +- [ ] tests/app/idp updated to demonstrate new features +- [ ] tests/app/rp updated to demonstrate new features diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0b453d269..b4cdaa8fa 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,6 +2,10 @@ name: Test on: [push, pull_request] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: test-package: name: Test Package (Python ${{ matrix.python-version }}, Django ${{ matrix.django-version }}) @@ -74,8 +78,8 @@ jobs: fail-fast: false matrix: node-version: - - "18.x" - - "20.x" + - "22.x" + - "24.x" steps: - name: Checkout uses: actions/checkout@v4 diff --git a/tests/app/idp/idp/urls.py b/tests/app/idp/idp/urls.py index 90e8abd48..8be65c35b 100644 --- a/tests/app/idp/idp/urls.py +++ b/tests/app/idp/idp/urls.py @@ -17,9 +17,11 @@ from django.contrib import admin from django.urls import include, path +from django.views.generic import TemplateView urlpatterns = [ + path('', TemplateView.as_view(template_name='home/index.html'), name='home'), # Maps the root URL to your home_view path("admin/", admin.site.urls), path("o/", include("oauth2_provider.urls", namespace="oauth2_provider")), path("accounts/", include("django.contrib.auth.urls")), diff --git a/tests/app/idp/templates/home/index.html b/tests/app/idp/templates/home/index.html new file mode 100644 index 000000000..5176db721 --- /dev/null +++ b/tests/app/idp/templates/home/index.html @@ -0,0 +1,19 @@ + + + + + + + Identity Provider Home + + + +

Welcome to the Identity Provider (IdP)

+

This is the home page of the Identity Provider used for testing OAuth2 flows.

+

Please ensure that the test relying party is running to proceed with authentication tests.

+ + + \ No newline at end of file diff --git a/tests/app/rp/package-lock.json b/tests/app/rp/package-lock.json index c8186b56d..2e63efd62 100644 --- a/tests/app/rp/package-lock.json +++ b/tests/app/rp/package-lock.json @@ -8,46 +8,35 @@ "name": "rp", "version": "0.0.1", "dependencies": { - "@dopry/svelte-oidc": "^1.1.0" + "@dopry/svelte-oidc": "^1.2.0", + "jose": "^6.1.0" }, "devDependencies": { - "@sveltejs/adapter-auto": "^3.1.1", - "@sveltejs/adapter-node": "^5.0.1", - "@sveltejs/kit": "^2.20.6", - "prettier": "^3.3.2", - "prettier-plugin-svelte": "^3.2.4", - "svelte": "^4.2.19", - "svelte-check": "^3.8.0", - "tslib": "^2.4.1", - "typescript": "^5.0.0", - "vite": "^5.4.19" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" + "@sveltejs/adapter-auto": "^7.0.0", + "@sveltejs/adapter-node": "^5.4.0", + "@sveltejs/kit": "^2.48.4", + "@sveltejs/vite-plugin-svelte": "^6.2.1", + "prettier": "^3.6.2", + "prettier-plugin-svelte": "^3.4.0", + "svelte": "^5.43.2", + "svelte-check": "^4.3.3", + "tslib": "^2.8.1", + "typescript": "^5.9.3", + "vite": "^7.1.12" } }, "node_modules/@dopry/svelte-oidc": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@dopry/svelte-oidc/-/svelte-oidc-1.1.0.tgz", - "integrity": "sha512-FfXm/f2vRNxFsYxKs8hal1Huf94dqKrRIppDzjDIH9cNy683b9sN9NUY0mZtrHc1yJL+jyfNNsB+bY9/9fCErA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@dopry/svelte-oidc/-/svelte-oidc-1.2.0.tgz", + "integrity": "sha512-iQKkgxjua264dgkm9u2vxMRwN4CKQywOaAQlszFVxcricAjWb5hCmcxD6qeMvprhmE5gB+Bk7JXpuUAq21O72A==", "dependencies": { - "oidc-client": "1.11.5" + "oidc-client-ts": "^3.3.0" } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", "cpu": [ "ppc64" ], @@ -57,13 +46,13 @@ "aix" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", "cpu": [ "arm" ], @@ -73,13 +62,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", "cpu": [ "arm64" ], @@ -89,13 +78,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", "cpu": [ "x64" ], @@ -105,13 +94,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", "cpu": [ "arm64" ], @@ -121,13 +110,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", "cpu": [ "x64" ], @@ -137,13 +126,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", "cpu": [ "arm64" ], @@ -153,13 +142,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", "cpu": [ "x64" ], @@ -169,13 +158,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", "cpu": [ "arm" ], @@ -185,13 +174,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", "cpu": [ "arm64" ], @@ -201,13 +190,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", "cpu": [ "ia32" ], @@ -217,13 +206,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", "cpu": [ "loong64" ], @@ -233,13 +222,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", "cpu": [ "mips64el" ], @@ -249,13 +238,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", "cpu": [ "ppc64" ], @@ -265,13 +254,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", "cpu": [ "riscv64" ], @@ -281,13 +270,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", "cpu": [ "s390x" ], @@ -297,13 +286,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", "cpu": [ "x64" ], @@ -313,13 +302,29 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", "cpu": [ "x64" ], @@ -329,13 +334,29 @@ "netbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", "cpu": [ "x64" ], @@ -345,13 +366,29 @@ "openbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", "cpu": [ "x64" ], @@ -361,13 +398,13 @@ "sunos" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", "cpu": [ "arm64" ], @@ -377,13 +414,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", "cpu": [ "ia32" ], @@ -393,13 +430,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", "cpu": [ "x64" ], @@ -409,21 +446,27 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { @@ -435,19 +478,10 @@ "node": ">=6.0.0" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { @@ -460,41 +494,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/@polka/url": { "version": "1.0.0-next.28", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.28.tgz", @@ -502,20 +501,21 @@ "dev": true }, "node_modules/@rollup/plugin-commonjs": { - "version": "25.0.8", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.8.tgz", - "integrity": "sha512-ZEZWTK5n6Qde0to4vS9Mr5x/0UZoqCxPVR9KRUjU4kA2sO7GEUn1fop0DAwpO6z0Nw/kJON9bDmSxdWxO/TT1A==", + "version": "28.0.9", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-28.0.9.tgz", + "integrity": "sha512-PIR4/OHZ79romx0BVVll/PkwWpJ7e5lsqFa3gFfcrFPWwLXLV39JVUzQV9RKjWerE7B845Hqjj9VYlQeieZ2dA==", "dev": true, "dependencies": { "@rollup/pluginutils": "^5.0.1", "commondir": "^1.0.1", "estree-walker": "^2.0.2", - "glob": "^8.0.3", + "fdir": "^6.2.0", "is-reference": "1.2.1", - "magic-string": "^0.30.3" + "magic-string": "^0.30.3", + "picomatch": "^4.0.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=16.0.0 || 14 >= 14.17" }, "peerDependencies": { "rollup": "^2.68.0||^3.0.0||^4.0.0" @@ -526,62 +526,6 @@ } } }, - "node_modules/@rollup/plugin-commonjs/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@rollup/plugin-commonjs/node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true - }, - "node_modules/@rollup/plugin-commonjs/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@rollup/plugin-commonjs/node_modules/is-reference": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", - "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", - "dev": true, - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/@rollup/plugin-commonjs/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@rollup/plugin-json": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-json/-/plugin-json-6.1.0.tgz", @@ -603,15 +547,14 @@ } }, "node_modules/@rollup/plugin-node-resolve": { - "version": "15.2.3", - "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.3.tgz", - "integrity": "sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==", + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.3.tgz", + "integrity": "sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg==", "dev": true, "dependencies": { "@rollup/pluginutils": "^5.0.1", "@types/resolve": "1.20.2", "deepmerge": "^4.2.2", - "is-builtin-module": "^3.2.1", "is-module": "^1.0.0", "resolve": "^1.22.1" }, @@ -649,16 +592,22 @@ } } }, - "node_modules/@rollup/pluginutils/node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true + "node_modules/@rollup/pluginutils/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz", - "integrity": "sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.5.tgz", + "integrity": "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==", "cpu": [ "arm" ], @@ -669,9 +618,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz", - "integrity": "sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.5.tgz", + "integrity": "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==", "cpu": [ "arm64" ], @@ -682,9 +631,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz", - "integrity": "sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.5.tgz", + "integrity": "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==", "cpu": [ "arm64" ], @@ -695,9 +644,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz", - "integrity": "sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.5.tgz", + "integrity": "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==", "cpu": [ "x64" ], @@ -707,10 +656,36 @@ "darwin" ] }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.5.tgz", + "integrity": "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.5.tgz", + "integrity": "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz", - "integrity": "sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.5.tgz", + "integrity": "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==", "cpu": [ "arm" ], @@ -721,9 +696,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz", - "integrity": "sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.5.tgz", + "integrity": "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==", "cpu": [ "arm" ], @@ -734,9 +709,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz", - "integrity": "sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.5.tgz", + "integrity": "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==", "cpu": [ "arm64" ], @@ -747,9 +722,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz", - "integrity": "sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.5.tgz", + "integrity": "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==", "cpu": [ "arm64" ], @@ -759,10 +734,23 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz", - "integrity": "sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==", + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.5.tgz", + "integrity": "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.5.tgz", + "integrity": "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==", "cpu": [ "ppc64" ], @@ -773,9 +761,22 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz", - "integrity": "sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.5.tgz", + "integrity": "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.5.tgz", + "integrity": "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==", "cpu": [ "riscv64" ], @@ -786,9 +787,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz", - "integrity": "sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.5.tgz", + "integrity": "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==", "cpu": [ "s390x" ], @@ -799,9 +800,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz", - "integrity": "sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.5.tgz", + "integrity": "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==", "cpu": [ "x64" ], @@ -812,9 +813,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz", - "integrity": "sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.5.tgz", + "integrity": "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==", "cpu": [ "x64" ], @@ -824,10 +825,23 @@ "linux" ] }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.5.tgz", + "integrity": "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openharmony" + ] + }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz", - "integrity": "sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.5.tgz", + "integrity": "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==", "cpu": [ "arm64" ], @@ -838,9 +852,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz", - "integrity": "sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.5.tgz", + "integrity": "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==", "cpu": [ "ia32" ], @@ -850,10 +864,23 @@ "win32" ] }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.5.tgz", + "integrity": "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz", - "integrity": "sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.5.tgz", + "integrity": "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==", "cpu": [ "x64" ], @@ -863,27 +890,39 @@ "win32" ] }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "dev": true + }, + "node_modules/@sveltejs/acorn-typescript": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.6.tgz", + "integrity": "sha512-4awhxtMh4cx9blePWl10HRHj8Iivtqj+2QdDCSMDzxG+XKa9+VCNupQuCuvzEhYPzZSrX+0gC+0lHA/0fFKKQQ==", + "dev": true, + "peerDependencies": { + "acorn": "^8.9.0" + } + }, "node_modules/@sveltejs/adapter-auto": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-3.1.1.tgz", - "integrity": "sha512-6LeZft2Fo/4HfmLBi5CucMYmgRxgcETweQl/yQoZo/895K3S9YWYN4Sfm/IhwlIpbJp3QNvhKmwCHbsqQNYQpw==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-7.0.0.tgz", + "integrity": "sha512-ImDWaErTOCkRS4Gt+5gZuymKFBobnhChXUZ9lhUZLahUgvA4OOvRzi3sahzYgbxGj5nkA6OV0GAW378+dl/gyw==", "dev": true, - "dependencies": { - "import-meta-resolve": "^4.0.0" - }, "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "node_modules/@sveltejs/adapter-node": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@sveltejs/adapter-node/-/adapter-node-5.0.1.tgz", - "integrity": "sha512-eYdmxdUWMW+dad1JfMsWBPY2vjXz9eE+52A2AQnXPScPJlIxIVk5mmbaEEzrZivLfO2wEcLTZ5vdC03W69x+iA==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-node/-/adapter-node-5.4.0.tgz", + "integrity": "sha512-NMsrwGVPEn+J73zH83Uhss/hYYZN6zT3u31R3IHAn3MiKC3h8fjmIAhLfTSOeNHr5wPYfjjMg8E+1gyFgyrEcQ==", "dev": true, "dependencies": { - "@rollup/plugin-commonjs": "^25.0.7", + "@rollup/plugin-commonjs": "^28.0.1", "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^15.2.3", + "@rollup/plugin-node-resolve": "^16.0.0", "rollup": "^4.9.5" }, "peerDependencies": { @@ -891,17 +930,18 @@ } }, "node_modules/@sveltejs/kit": { - "version": "2.20.6", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.20.6.tgz", - "integrity": "sha512-ImUkSQ//Xf4N9r0HHAe5vRA7RyQ7U1Ue1YUT235Ig+IiIqbsixEulHTHrP5LtBiC8xOkJoPZQ1VZ/nWHNOaGGw==", + "version": "2.48.4", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.48.4.tgz", + "integrity": "sha512-TGFX1pZUt9qqY20Cv5NyYvy0iLWHf2jXi8s+eCGsig7jQMdwZWKUFMR6TbvFNhfDSUpc1sH/Y5EHv20g3HHA3g==", "dev": true, - "license": "MIT", "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", + "acorn": "^8.14.1", "cookie": "^0.6.0", - "devalue": "^5.1.0", + "devalue": "^5.3.2", "esm-env": "^1.2.2", - "import-meta-resolve": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", @@ -916,50 +956,52 @@ "node": ">=18.13" }, "peerDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0", + "@opentelemetry/api": "^1.0.0", + "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0", "svelte": "^4.0.0 || ^5.0.0-next.0", - "vite": "^5.0.3 || ^6.0.0" + "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + } } }, "node_modules/@sveltejs/vite-plugin-svelte": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.0.2.tgz", - "integrity": "sha512-MpmF/cju2HqUls50WyTHQBZUV3ovV/Uk8k66AN2gwHogNAG8wnW8xtZDhzNBsFJJuvmq1qnzA5kE7YfMJNFv2Q==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-6.2.1.tgz", + "integrity": "sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ==", "dev": true, - "peer": true, "dependencies": { - "@sveltejs/vite-plugin-svelte-inspector": "^2.0.0", - "debug": "^4.3.4", + "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", + "debug": "^4.4.1", "deepmerge": "^4.3.1", - "kleur": "^4.1.5", - "magic-string": "^0.30.5", - "svelte-hmr": "^0.15.3", - "vitefu": "^0.2.5" + "magic-string": "^0.30.17", + "vitefu": "^1.1.1" }, "engines": { - "node": "^18.0.0 || >=20" + "node": "^20.19 || ^22.12 || >=24" }, "peerDependencies": { - "svelte": "^4.0.0 || ^5.0.0-next.0", - "vite": "^5.0.0" + "svelte": "^5.0.0", + "vite": "^6.3.0 || ^7.0.0" } }, - "node_modules/@sveltejs/vite-plugin-svelte/node_modules/@sveltejs/vite-plugin-svelte-inspector": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-2.0.0.tgz", - "integrity": "sha512-gjr9ZFg1BSlIpfZ4PRewigrvYmHWbDrq2uvvPB1AmTWKuM+dI1JXQSUu2pIrYLb/QncyiIGkFDFKTwJ0XqQZZg==", + "node_modules/@sveltejs/vite-plugin-svelte-inspector": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-5.0.1.tgz", + "integrity": "sha512-ubWshlMk4bc8mkwWbg6vNvCeT7lGQojE3ijDh3QTR6Zr/R+GXxsGbyH4PExEPpiFmqPhYiVSVmHBjUcVc1JIrA==", "dev": true, - "peer": true, "dependencies": { - "debug": "^4.3.4" + "debug": "^4.4.1" }, "engines": { - "node": "^18.0.0 || >=20" + "node": "^20.19 || ^22.12 || >=24" }, "peerDependencies": { - "@sveltejs/vite-plugin-svelte": "^3.0.0", - "svelte": "^4.0.0 || ^5.0.0-next.0", - "vite": "^5.0.0" + "@sveltejs/vite-plugin-svelte": "^6.0.0-next.0", + "svelte": "^5.0.0", + "vite": "^6.3.0 || ^7.0.0" } }, "node_modules/@types/cookie": { @@ -969,15 +1011,9 @@ "dev": true }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true - }, - "node_modules/@types/pug": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@types/pug/-/pug-2.0.10.tgz", - "integrity": "sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true }, "node_modules/@types/resolve": { @@ -987,9 +1023,10 @@ "dev": true }, "node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, "bin": { "acorn": "bin/acorn" }, @@ -997,187 +1034,54 @@ "node": ">=0.4.0" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, "engines": { - "node": ">= 8" + "node": ">= 0.4" } }, - "node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", "dev": true, - "dependencies": { - "dequal": "^2.0.3" + "engines": { + "node": ">= 0.4" } }, - "node_modules/axobject-query": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.0.0.tgz", - "integrity": "sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==", - "dev": true, - "dependencies": { - "dequal": "^2.0.3" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, "dependencies": { - "fill-range": "^7.1.1" + "readdirp": "^4.0.1" }, "engines": { - "node": ">=8" - } - }, - "node_modules/buffer-crc32": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", - "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/builtin-modules": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", - "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", - "dev": true, - "engines": { - "node": ">=6" + "node": ">= 14.16.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://paulmillr.com/funding/" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", "dev": true, "engines": { "node": ">=6" } }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/code-red": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz", - "integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==", - "dev": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15", - "@types/estree": "^1.0.1", - "acorn": "^8.10.0", - "estree-walker": "^3.0.3", - "periscopic": "^3.1.0" - } - }, - "node_modules/code-red/node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", "dev": true }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, "node_modules/cookie": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", @@ -1187,42 +1091,13 @@ "node": ">= 0.6" } }, - "node_modules/core-js": { - "version": "3.30.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.30.1.tgz", - "integrity": "sha512-ZNS5nbiSwDTq4hFosEDqm65izl2CWmLz0hARJMyNQBgkUZMIF51cQiMvIQKA6hvuaeWxQDP3hEedM1JZIgTldQ==", - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/crypto-js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", - "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" - }, - "node_modules/css-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", - "dev": true, - "dependencies": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, - "peer": true, "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -1242,72 +1117,51 @@ "node": ">=0.10.0" } }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/detect-indent": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", - "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/devalue": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.1.1.tgz", - "integrity": "sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==", - "dev": true - }, - "node_modules/es6-promise": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", - "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.4.2.tgz", + "integrity": "sha512-MwPZTKEPK2k8Qgfmqrd48ZKVvzSQjgW0lXLxiIBA8dQjtf/6mw6pggHNLcyDKyf+fI6eXxlQwPsfaCMTU5U+Bw==", "dev": true }, "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", "dev": true, "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" }, "engines": { - "node": ">=12" + "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" } }, "node_modules/esm-env": { @@ -1317,58 +1171,38 @@ "dev": true, "license": "MIT" }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "node_modules/esrap": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.1.2.tgz", + "integrity": "sha512-DgvlIQeowRNyvLPWW4PT7Gu13WznY288Du086E751mwwbsgr29ytBiYeLzAGIo0qk3Ujob0SDk8TiSaM5WQzNg==", "dev": true, "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" + "@jridgewell/sourcemap-codec": "^1.4.15" } }, - "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, "engines": { - "node": ">=8" + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -1392,45 +1226,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -1443,133 +1238,52 @@ "node": ">= 0.4" } }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-meta-resolve": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", - "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-builtin-module": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", - "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, "dependencies": { - "builtin-modules": "^3.3.0" + "hasown": "^2.0.2" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dev": true, - "dependencies": { - "hasown": "^2.0.0" + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-module": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", "dev": true }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, "node_modules/is-reference": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", - "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", "dev": true, "dependencies": { "@types/estree": "*" } }, + "node_modules/jose": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.0.tgz", + "integrity": "sha512-TTQJyoEoKcC1lscpVDCSsVgYzUDg/0Bt3WE//WiTPK6uOCQC2KZS4MpugbMWt/zyjkopgZoXhZuCi00gLudfUA==", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/jwt-decode": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", + "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", + "engines": { + "node": ">=18" + } + }, "node_modules/kleur": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", @@ -1586,85 +1300,12 @@ "dev": true }, "node_modules/magic-string": { - "version": "0.30.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.7.tgz", - "integrity": "sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==", - "dev": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/mri": { @@ -1686,16 +1327,15 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true, - "peer": true + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true }, "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "dev": true, "funding": [ { @@ -1710,55 +1350,15 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/oidc-client": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/oidc-client/-/oidc-client-1.11.5.tgz", - "integrity": "sha512-LcKrKC8Av0m/KD/4EFmo9Sg8fSQ+WFJWBrmtWd+tZkNn3WT/sQG3REmPANE9tzzhbjW6VkTNy4xhAXCfPApAOg==", - "dependencies": { - "acorn": "^7.4.1", - "base64-js": "^1.5.1", - "core-js": "^3.8.3", - "crypto-js": "^4.0.0", - "serialize-javascript": "^4.0.0" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, + "node_modules/oidc-client-ts": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/oidc-client-ts/-/oidc-client-ts-3.3.0.tgz", + "integrity": "sha512-t13S540ZwFOEZKLYHJwSfITugupW4uYLwuQSSXyKH/wHwZ+7FvgHE7gnNJh1YQIZ1Yd1hKSRjqeXGSUtS0r9JA==", "dependencies": { - "callsites": "^3.0.0" + "jwt-decode": "^4.0.0" }, "engines": { - "node": ">=6" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "node": ">=18" } }, "node_modules/path-parse": { @@ -1767,39 +1367,28 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, - "node_modules/periscopic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", - "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", - "dev": true, - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^3.0.0", - "is-reference": "^3.0.0" - } - }, "node_modules/picocolors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", - "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/postcss": { - "version": "8.4.47", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", - "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "dev": true, "funding": [ { @@ -1816,8 +1405,8 @@ } ], "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.1.0", + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, "engines": { @@ -1825,9 +1414,9 @@ } }, "node_modules/prettier": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz", - "integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -1840,111 +1429,55 @@ } }, "node_modules/prettier-plugin-svelte": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.2.4.tgz", - "integrity": "sha512-tZv+ADfeOWFNQkXkRh6zUXE16w3Vla8x2Ug0B/EnSmjR4EnwdwZbGgL/liSwR1kcEALU5mAAyua98HBxheCxgg==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.4.0.tgz", + "integrity": "sha512-pn1ra/0mPObzqoIQn/vUTR3ZZI6UuZ0sHqMK5x2jMLGrs53h0sXhkVuDcrlssHwIMk7FYrMjHBPoUSyyEEDlBQ==", "dev": true, "peerDependencies": { "prettier": "^3.0.0", "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" } }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", "dev": true, - "dependencies": { - "picomatch": "^2.2.1" - }, "engines": { - "node": ">=8.10.0" + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", "dev": true, "dependencies": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, "engines": { - "node": ">=4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "dependencies": { - "glob": "^7.1.3" + "node": ">= 0.4" }, - "bin": { - "rimraf": "bin.js" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/rollup": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz", - "integrity": "sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==", + "version": "4.52.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.5.tgz", + "integrity": "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==", "dev": true, "dependencies": { - "@types/estree": "1.0.5" + "@types/estree": "1.0.8" }, "bin": { "rollup": "dist/bin/rollup" @@ -1954,48 +1487,31 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.22.4", - "@rollup/rollup-android-arm64": "4.22.4", - "@rollup/rollup-darwin-arm64": "4.22.4", - "@rollup/rollup-darwin-x64": "4.22.4", - "@rollup/rollup-linux-arm-gnueabihf": "4.22.4", - "@rollup/rollup-linux-arm-musleabihf": "4.22.4", - "@rollup/rollup-linux-arm64-gnu": "4.22.4", - "@rollup/rollup-linux-arm64-musl": "4.22.4", - "@rollup/rollup-linux-powerpc64le-gnu": "4.22.4", - "@rollup/rollup-linux-riscv64-gnu": "4.22.4", - "@rollup/rollup-linux-s390x-gnu": "4.22.4", - "@rollup/rollup-linux-x64-gnu": "4.22.4", - "@rollup/rollup-linux-x64-musl": "4.22.4", - "@rollup/rollup-win32-arm64-msvc": "4.22.4", - "@rollup/rollup-win32-ia32-msvc": "4.22.4", - "@rollup/rollup-win32-x64-msvc": "4.22.4", + "@rollup/rollup-android-arm-eabi": "4.52.5", + "@rollup/rollup-android-arm64": "4.52.5", + "@rollup/rollup-darwin-arm64": "4.52.5", + "@rollup/rollup-darwin-x64": "4.52.5", + "@rollup/rollup-freebsd-arm64": "4.52.5", + "@rollup/rollup-freebsd-x64": "4.52.5", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.5", + "@rollup/rollup-linux-arm-musleabihf": "4.52.5", + "@rollup/rollup-linux-arm64-gnu": "4.52.5", + "@rollup/rollup-linux-arm64-musl": "4.52.5", + "@rollup/rollup-linux-loong64-gnu": "4.52.5", + "@rollup/rollup-linux-ppc64-gnu": "4.52.5", + "@rollup/rollup-linux-riscv64-gnu": "4.52.5", + "@rollup/rollup-linux-riscv64-musl": "4.52.5", + "@rollup/rollup-linux-s390x-gnu": "4.52.5", + "@rollup/rollup-linux-x64-gnu": "4.52.5", + "@rollup/rollup-linux-x64-musl": "4.52.5", + "@rollup/rollup-openharmony-arm64": "4.52.5", + "@rollup/rollup-win32-arm64-msvc": "4.52.5", + "@rollup/rollup-win32-ia32-msvc": "4.52.5", + "@rollup/rollup-win32-x64-gnu": "4.52.5", + "@rollup/rollup-win32-x64-msvc": "4.52.5", "fsevents": "~2.3.2" } }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, "node_modules/sade": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", @@ -2008,45 +1524,6 @@ "node": ">=6" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/sander": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz", - "integrity": "sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==", - "dev": true, - "dependencies": { - "es6-promise": "^3.1.2", - "graceful-fs": "^4.1.3", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.2" - } - }, - "node_modules/serialize-javascript": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", - "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", - "dependencies": { - "randombytes": "^2.1.0" - } - }, "node_modules/set-cookie-parser": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz", @@ -2067,21 +1544,6 @@ "node": ">=18" } }, - "node_modules/sorcery": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/sorcery/-/sorcery-0.11.1.tgz", - "integrity": "sha512-o7npfeJE6wi6J9l0/5LKshFzZ2rMatRiCDwYeDQaOzqdzRJwALhX7mk/A/ecg6wjMu7wdZbmXfD2S/vpOg0bdQ==", - "dev": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.14", - "buffer-crc32": "^1.0.0", - "minimist": "^1.2.0", - "sander": "^0.5.0" - }, - "bin": { - "sorcery": "bin/sorcery" - } - }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -2091,18 +1553,6 @@ "node": ">=0.10.0" } }, - "node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -2116,149 +1566,76 @@ } }, "node_modules/svelte": { - "version": "4.2.19", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.19.tgz", - "integrity": "sha512-IY1rnGr6izd10B0A8LqsBfmlT5OILVuZ7XsI0vdGPEvuonFV7NYEUK4dAkm9Zg2q0Um92kYjTpS1CAP3Nh/KWw==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.1", - "@jridgewell/sourcemap-codec": "^1.4.15", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/estree": "^1.0.1", - "acorn": "^8.9.0", - "aria-query": "^5.3.0", - "axobject-query": "^4.0.0", - "code-red": "^1.0.3", - "css-tree": "^2.3.1", - "estree-walker": "^3.0.3", - "is-reference": "^3.0.1", + "version": "5.43.2", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.43.2.tgz", + "integrity": "sha512-ro1umEzX8rT5JpCmlf0PPv7ncD8MdVob9e18bhwqTKNoLjS8kDvhVpaoYVPc+qMwDAOfcwJtyY7ZFSDbOaNPgA==", + "dev": true, + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "@jridgewell/sourcemap-codec": "^1.5.0", + "@sveltejs/acorn-typescript": "^1.0.5", + "@types/estree": "^1.0.5", + "acorn": "^8.12.1", + "aria-query": "^5.3.1", + "axobject-query": "^4.1.0", + "clsx": "^2.1.1", + "esm-env": "^1.2.1", + "esrap": "^2.1.0", + "is-reference": "^3.0.3", "locate-character": "^3.0.0", - "magic-string": "^0.30.4", - "periscopic": "^3.1.0" + "magic-string": "^0.30.11", + "zimmerframe": "^1.1.2" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/svelte-check": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.8.0.tgz", - "integrity": "sha512-7Nxn+3X97oIvMzYJ7t27w00qUf1Y52irE2RU2dQAd5PyvfGp4E7NLhFKVhb6PV2fx7dCRMpNKDIuazmGthjpSQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.3.3.tgz", + "integrity": "sha512-RYP0bEwenDXzfv0P1sKAwjZSlaRyqBn0Fz1TVni58lqyEiqgwztTpmodJrGzP6ZT2aHl4MbTvWP6gbmQ3FOnBg==", "dev": true, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.17", - "chokidar": "^3.4.1", - "fast-glob": "^3.2.7", - "import-fresh": "^3.2.1", + "@jridgewell/trace-mapping": "^0.3.25", + "chokidar": "^4.0.1", + "fdir": "^6.2.0", "picocolors": "^1.0.0", - "sade": "^1.7.4", - "svelte-preprocess": "^5.1.3", - "typescript": "^5.0.3" + "sade": "^1.7.4" }, "bin": { "svelte-check": "bin/svelte-check" }, - "peerDependencies": { - "svelte": "^3.55.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0" - } - }, - "node_modules/svelte-hmr": { - "version": "0.15.3", - "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.15.3.tgz", - "integrity": "sha512-41snaPswvSf8TJUhlkoJBekRrABDXDMdpNpT2tfHIv4JuhgvHqLMhEPGtaQn0BmbNSTkuz2Ed20DF2eHw0SmBQ==", - "dev": true, - "peer": true, "engines": { - "node": "^12.20 || ^14.13.1 || >= 16" + "node": ">= 18.0.0" }, "peerDependencies": { - "svelte": "^3.19.0 || ^4.0.0" + "svelte": "^4.0.0 || ^5.0.0-next.0", + "typescript": ">=5.0.0" } }, - "node_modules/svelte-preprocess": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/svelte-preprocess/-/svelte-preprocess-5.1.4.tgz", - "integrity": "sha512-IvnbQ6D6Ao3Gg6ftiM5tdbR6aAETwjhHV+UKGf5bHGYR69RQvF1ho0JKPcbUON4vy4R7zom13jPjgdOWCQ5hDA==", + "node_modules/svelte/node_modules/is-reference": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", + "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", "dev": true, - "hasInstallScript": true, "dependencies": { - "@types/pug": "^2.0.6", - "detect-indent": "^6.1.0", - "magic-string": "^0.30.5", - "sorcery": "^0.11.0", - "strip-indent": "^3.0.0" - }, - "engines": { - "node": ">= 16.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.10.2", - "coffeescript": "^2.5.1", - "less": "^3.11.3 || ^4.0.0", - "postcss": "^7 || ^8", - "postcss-load-config": "^2.1.0 || ^3.0.0 || ^4.0.0 || ^5.0.0", - "pug": "^3.0.0", - "sass": "^1.26.8", - "stylus": "^0.55.0", - "sugarss": "^2.0.0 || ^3.0.0 || ^4.0.0", - "svelte": "^3.23.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0", - "typescript": ">=3.9.5 || ^4.0.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "coffeescript": { - "optional": true - }, - "less": { - "optional": true - }, - "postcss": { - "optional": true - }, - "postcss-load-config": { - "optional": true - }, - "pug": { - "optional": true - }, - "sass": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "typescript": { - "optional": true - } + "@types/estree": "^1.0.6" } }, - "node_modules/svelte/node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "dependencies": { - "is-number": "^7.0.0" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { - "node": ">=8.0" + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" } }, "node_modules/totalist": { @@ -2271,40 +1648,42 @@ } }, "node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true }, "node_modules/typescript": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", - "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=12.20" + "node": ">=14.17" } }, "node_modules/vite": { - "version": "5.4.19", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz", - "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", + "version": "7.1.12", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.12.tgz", + "integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==", "dev": true, - "license": "MIT", "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" + "esbuild": "^0.25.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^20.19.0 || >=22.12.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -2313,19 +1692,25 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" }, "peerDependenciesMeta": { "@types/node": { "optional": true }, + "jiti": { + "optional": true + }, "less": { "optional": true }, @@ -2346,17 +1731,22 @@ }, "terser": { "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true } } }, "node_modules/vitefu": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz", - "integrity": "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.1.tgz", + "integrity": "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==", "dev": true, - "peer": true, "peerDependencies": { - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "peerDependenciesMeta": { "vite": { @@ -2364,10 +1754,10 @@ } } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "node_modules/zimmerframe": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz", + "integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==", "dev": true } } diff --git a/tests/app/rp/package.json b/tests/app/rp/package.json index 603114a1a..8662ba38a 100644 --- a/tests/app/rp/package.json +++ b/tests/app/rp/package.json @@ -12,19 +12,21 @@ "format": "prettier --plugin-search-dir . --write ." }, "devDependencies": { - "@sveltejs/adapter-auto": "^3.1.1", - "@sveltejs/adapter-node": "^5.0.1", - "@sveltejs/kit": "^2.20.6", - "prettier": "^3.3.2", - "prettier-plugin-svelte": "^3.2.4", - "svelte": "^4.2.19", - "svelte-check": "^3.8.0", - "tslib": "^2.4.1", - "typescript": "^5.0.0", - "vite": "^5.4.19" + "@sveltejs/adapter-auto": "^7.0.0", + "@sveltejs/adapter-node": "^5.4.0", + "@sveltejs/kit": "^2.48.4", + "@sveltejs/vite-plugin-svelte": "^6.2.1", + "prettier": "^3.6.2", + "prettier-plugin-svelte": "^3.4.0", + "svelte": "^5.43.2", + "svelte-check": "^4.3.3", + "tslib": "^2.8.1", + "typescript": "^5.9.3", + "vite": "^7.1.12" }, "type": "module", "dependencies": { - "@dopry/svelte-oidc": "^1.1.0" + "@dopry/svelte-oidc": "^1.2.0", + "jose": "^6.1.0" } } diff --git a/tests/app/rp/src/app.html b/tests/app/rp/src/app.html index 77ec85d79..7a8d95035 100644 --- a/tests/app/rp/src/app.html +++ b/tests/app/rp/src/app.html @@ -3,10 +3,79 @@ - + + + Django OAuth Toolkit RP Demo + %sveltekit.head% + -
%sveltekit.body%
+
+

Django OAuth Toolkit Test RP

+ +
%sveltekit.body%
+
diff --git a/tests/app/rp/src/routes/+page.svelte b/tests/app/rp/src/routes/+page.svelte index 1df1a226b..9641425b7 100644 --- a/tests/app/rp/src/routes/+page.svelte +++ b/tests/app/rp/src/routes/+page.svelte @@ -1,17 +1,16 @@ + + + + + +
+ +
+ + diff --git a/tests/app/rp/src/routes/device/+page.svelte b/tests/app/rp/src/routes/device/+page.svelte new file mode 100644 index 000000000..cfa9555e7 --- /dev/null +++ b/tests/app/rp/src/routes/device/+page.svelte @@ -0,0 +1,490 @@ + + + + Device Authorization Flow Test + + +
+

Test the OAuth 2.0 Device Authorization Grant

+

+ This page demonstrates the Device Authorization Flow (RFC 8628), which is used by devices + with limited input capabilities (like smart TVs, IoT devices, etc.) to obtain OAuth tokens. + Do not use device-authorization in a browser, this is just an illustrative example to + streamline manual testing for maintainers. It shows how you'd need to implement the flow on + your device. Have a look at this full user journey test for an implementation in Python. +

+
+ +{#if status === 'idle'} +
+

Step 1: Initiate Authorization

+

Click the button below to start the device authorization flow.

+ +
+{/if} + +{#if status === 'authorizing'} +
+

Initiating...

+

Contacting the authorization server...

+
+
+{/if} + +{#if status === 'polling'} +
+

Step 2: Authorize the Device

+

+ Open the verification URL below in a new tab, enter the user code, and approve the + authorization. +

+ +
+
+ User Code: + {userCode} +
+
+ Verification URL: + + {verificationUri} + +
+
+ Expires in: + {expiresIn} seconds +
+
+ + + +
+
+

Polling for authorization... (checking every {interval} seconds)

+
+ + +
+{/if} + +{#if status === 'complete'} +
+

✓ Authorization Complete!

+

Successfully obtained an access token.

+ +
+
+ Token Type: + {tokenType} +
+
+ Expires In: + {expiresInToken} seconds +
+ {#if scope} +
+ Scope: + {scope} +
+ {/if} +
+ Access Token: + +
+ {#if refreshToken} +
+ Refresh Token: + +
+ {/if} +
+ + +
+{/if} + +{#if status === 'error'} +
+

Error

+

{errorMessage}

+ +
+{/if} + +
+

How it works

+
    +
  1. + Device requests authorization: The device sends a request to the authorization + server with its client ID. +
  2. +
  3. + Server returns codes: The server responds with a device code, user code, + and verification URI. +
  4. +
  5. + User authorizes: The user visits the verification URI on another device + (like a phone or computer), enters the user code, and approves the authorization. +
  6. +
  7. + Device polls for token: Meanwhile, the device polls the token endpoint using + the device code until the user completes authorization. +
  8. +
  9. + Token granted: Once the user approves, the polling request returns the access + token. +
  10. +
+
+ + diff --git a/tests/test_device.py b/tests/test_device.py new file mode 100644 index 000000000..727c81002 --- /dev/null +++ b/tests/test_device.py @@ -0,0 +1,769 @@ +from datetime import datetime, timedelta, timezone +from unittest import mock +from urllib.parse import urlencode + +import django.http.response +import pytest +from django import http +from django.conf import settings +from django.contrib.auth import get_user_model +from django.test import RequestFactory +from django.urls import reverse + +import oauth2_provider.models +from oauth2_provider.models import ( + get_access_token_model, + get_application_model, + get_device_grant_model, + get_refresh_token_model, +) +from oauth2_provider.utils import set_oauthlib_user_to_device_request_user + +from . import presets +from .common_testing import OAuth2ProviderTestCase as TestCase + + +Application = get_application_model() +AccessToken = get_access_token_model() +RefreshToken = get_refresh_token_model() +UserModel = get_user_model() +DeviceModel: oauth2_provider.models.DeviceGrant = get_device_grant_model() + + +@pytest.mark.usefixtures("oauth2_settings") +@pytest.mark.oauth2_settings(presets.DEFAULT_SCOPES_RW) +class DeviceFlowBaseTestCase(TestCase): + factory = RequestFactory() + + @classmethod + def setUpTestData(cls): + cls.test_user = UserModel.objects.create_user("test_user", "test@example.com", "123456") + cls.dev_user = UserModel.objects.create_user("dev_user", "dev@example.com", "123456") + + cls.application = Application.objects.create( + name="test_client_credentials_app", + user=cls.dev_user, + client_type=Application.CLIENT_PUBLIC, + authorization_grant_type=Application.GRANT_DEVICE_CODE, + client_secret="abcdefghijklmnopqrstuvwxyz1234567890", + ) + + def tearDown(self): + DeviceModel.objects.all().delete() + return super().tearDown() + + +class TestDeviceFlow(DeviceFlowBaseTestCase): + """ + The first 2 tests test the device flow in order + how the device flow works + """ + + @mock.patch( + "oauthlib.oauth2.rfc8628.endpoints.device_authorization.generate_token", + lambda: "abc", + ) + def test_device_flow_authorization_initiation(self): + """ + Tests the initial stage of the flow when the device sends its device authorization + request to the authorization server. + + Device Authorization Request(https://datatracker.ietf.org/doc/html/rfc8628#section-3.1) + + This request shape: + POST /device_authorization HTTP/1.1 + Host: server.example.com + Content-Type: application/x-www-form-urlencoded + + client_id=1406020730&scope=example_scope + + Should respond with this response shape: + Device Authorization Response (https://datatracker.ietf.org/doc/html/rfc8628#section-3.2) + { + "device_code": "GmRhmhcxhwAzkoEqiMEg_DnyEysNkuNhszIySk9eS", + "user_code": "WDJB-MJHT", + "verification_uri": "https://example.com/device", + "expires_in": 1800, + "interval": 5 + } + """ + + self.oauth2_settings.OAUTH_DEVICE_VERIFICATION_URI = "example.com/device" + self.oauth2_settings.OAUTH_DEVICE_USER_CODE_GENERATOR = lambda: "xyz" + + request_data: dict[str, str] = { + "client_id": self.application.client_id, + } + request_as_x_www_form_urlencoded: str = urlencode(request_data) + + response: django.http.response.JsonResponse = self.client.post( + reverse("oauth2_provider:device-authorization"), + data=request_as_x_www_form_urlencoded, + content_type="application/x-www-form-urlencoded", + ) + + assert response.status_code == 200 + + # let's make sure the device was created in the db + assert DeviceModel.objects.get(device_code="abc").status == DeviceModel.AUTHORIZATION_PENDING + + assert response.json() == { + "verification_uri": "example.com/device", + "expires_in": 1800, + "user_code": "xyz", + "device_code": "abc", + "interval": 5, + } + + @mock.patch( + "oauthlib.oauth2.rfc8628.endpoints.device_authorization.generate_token", + lambda: "abc", + ) + def test_device_flow_authorization_user_code_confirm_and_access_token(self): + """ + This is a full user journey test. + + The device initiates the flow by calling the /device-authorization endpoint and starts + polling the /authorize endpoint getting back error until the user approves in the + browser. + + In the meantime, the user visits the /device endpoint in their browsers to submit the + user code and approve, after which the /authorize returns the tokens to the device. + """ + + # ----------------------- + # 0: Setup device flow, where the device sends an authorization request and + # starts polling. The polling will fail because the user has not approved yet + # ----------------------- + self.oauth2_settings.OAUTH_DEVICE_VERIFICATION_URI = "example.com/device" + self.oauth2_settings.OAUTH_DEVICE_USER_CODE_GENERATOR = lambda: "xyz" + self.oauth2_settings.OAUTH_PRE_TOKEN_VALIDATION = [set_oauthlib_user_to_device_request_user] + + request_data: dict[str, str] = { + "client_id": self.application.client_id, + } + request_as_x_www_form_urlencoded: str = urlencode(request_data) + + device_authorization_response: http.response.JsonResponse = self.client.post( + reverse("oauth2_provider:device-authorization"), + data=request_as_x_www_form_urlencoded, + content_type="application/x-www-form-urlencoded", + ) + + assert device_authorization_response.__getitem__("content-type") == "application/json" + device = DeviceModel.objects.get(device_code="abc") + self.assertJSONEqual( + raw=device_authorization_response.content, + expected_data={ + "verification_uri": "example.com/device", + "expires_in": 1800, + "user_code": device.user_code, + "device_code": device.device_code, + "interval": 5, + }, + ) + + # Device polls /token and gets back error because the user hasn't approved yet + token_payload = { + "device_code": device.device_code, + "client_id": self.application.client_id, + "grant_type": "urn:ietf:params:oauth:grant-type:device_code", + } + + token_response: http.response.JsonResponse = self.client.post( + "/o/token/", + data=urlencode(token_payload), + content_type="application/x-www-form-urlencoded", + ) + # TokenView should always respond with application/json as it's meant to be + # consumed by devices. + assert token_response.__getitem__("content-type") == "application/json" + assert token_response.status_code == 400 + self.assertJSONEqual(raw=token_response.content, expected_data={"error": "authorization_pending"}) + + # /device and /device_confirm require a user to be logged in + # to access it + UserModel.objects.create_user( + username="test_user_device_flow", + email="test_device@example.com", + password="password123", + ) + self.client.login(username="test_user_device_flow", password="password123") + + # -------------------------------------------------------------------------------- + # 1. User visits the /device endpoint in their browsers and submits the user code + # submits wrong code then right code + # -------------------------------------------------------------------------------- + + # 1. User visits the /device endpoint in their browsers and submits the user code + # (GET Request to load it) + get_response = self.client.get(reverse("oauth2_provider:device")) + assert get_response.status_code == 200 + assert "form" in get_response.context # Ensure the form is rendered in the context + + # 1.1.0 User visits the /device endpoint in their browsers and submits wrong user code + self.assertContains( + self.client.post(reverse("oauth2_provider:device"), data={"user_code": "invalid_code"}), + status_code=200, + text="Incorrect user code", + count=1, + ) + + # Note: the device not being in the expected test covered in the other tests + + # 1.1.1: user submits valid user code + device_confirm_url = reverse( + "oauth2_provider:device-confirm", + kwargs={"user_code": "xyz", "client_id": self.application.client_id}, + ) + + self.assertRedirects( + response=self.client.post( + reverse("oauth2_provider:device"), + data={"user_code": "xyz"}, + ), + expected_url=device_confirm_url, + ) + + # -------------------------------------------------------------------------------- + # 2: We redirect to the accept/deny form (the user is still in their browser) + # and approves + # -------------------------------------------------------------------------------- + device_grant_status_url = reverse( + "oauth2_provider:device-grant-status", + kwargs={"user_code": "xyz", "client_id": self.application.client_id}, + ) + + self.assertRedirects( + response=self.client.post(device_confirm_url, data={"action": "accept"}), + expected_url=device_grant_status_url, + ) + + # -------------------------------------------------------------------------------- + # 3: We redirect to the device grant status page (the user is still in their browser) + # -------------------------------------------------------------------------------- + self.assertContains( + response=self.client.get(device_grant_status_url), + text="Device Authorized", + count=1, + ) + + device = DeviceModel.objects.get(device_code="abc") + assert device.status == device.AUTHORIZED + + # ------------------------- + # 4: Device polls /token successfully + # ------------------------- + token_payload = { + "device_code": device.device_code, + "client_id": self.application.client_id, + "grant_type": "urn:ietf:params:oauth:grant-type:device_code", + } + + token_response = self.client.post( + "/o/token/", + data=urlencode(token_payload), + content_type="application/x-www-form-urlencoded", + ) + # TokenView should always respond with application/json as it's meant to be + # consumed by devices. + assert token_response.__getitem__("content-type") == "application/json" + assert token_response.status_code == 200 + + token_data = token_response.json() + assert token_data == { + "access_token": mock.ANY, + "expires_in": 36000, + "token_type": "Bearer", + "scope": "read write", + "refresh_token": mock.ANY, + } + + # ensure the access token and refresh token have the same user as the device that just authenticated + access_token: oauth2_provider.models.AccessToken = AccessToken.objects.get( + token=token_data["access_token"] + ) + assert access_token.user == device.user + + refresh_token: oauth2_provider.models.RefreshToken = RefreshToken.objects.get( + token=token_data["refresh_token"] + ) + assert refresh_token.user == device.user + + def test_user_denies_access(self): + """ + This test asserts the when the user denies access, the state of the grant is saved + and the user is redirected to the page where they can see the "denied" state. + + The /token View returning the appropriate message for the "denied" state is covered + in test_token_view_returns_error_if_device_in_invalid_state. + """ + UserModel.objects.create_user( + username="test_user_device_flow", + email="test_device@example.com", + password="password123", + ) + self.client.login(username="test_user_device_flow", password="password123") + + device = DeviceModel( + client_id="client_id", + device_code="device_code", + user_code="user_code", + scope="scope", + expires=datetime.now() + timedelta(days=1), + status=DeviceModel.AUTHORIZATION_PENDING, + ) + device.save() + + device_confirm_url = reverse( + "oauth2_provider:device-confirm", + kwargs={"user_code": "user_code", "client_id": "client_id"}, + ) + + device_grant_status_url = reverse( + "oauth2_provider:device-grant-status", + kwargs={"user_code": "user_code", "client_id": "client_id"}, + ) + + self.assertRedirects( + response=self.client.post(device_confirm_url, data={"action": "deny"}), + expected_url=device_grant_status_url, + ) + + device.refresh_from_db() + assert device.status == device.DENIED + + def test_device_confirm_view_returns_400_on_incorrect_action(self): + """ + This test asserts that the confirm view returns 400 if action is not + "accept" or "deny". + """ + UserModel.objects.create_user( + username="test_user_device_flow", + email="test_device@example.com", + password="password123", + ) + self.client.login(username="test_user_device_flow", password="password123") + + device = DeviceModel( + client_id="client_id", + device_code="device_code", + user_code="user_code", + scope="scope", + expires=datetime.now() + timedelta(days=1), + status=DeviceModel.AUTHORIZATION_PENDING, + ) + device.save() + + device_confirm_url = reverse( + "oauth2_provider:device-confirm", + kwargs={"user_code": "user_code", "client_id": "client_id"}, + ) + response = self.client.post(device_confirm_url, data={"action": "inccorect_action"}) + + assert response.status_code == 400 + + def test_device_flow_authorization_device_invalid_state_returns_form_error(self): + """ + This test asserts that only devices in the expected state (authorization-pending) + can be approved/denied by the user. + """ + + UserModel.objects.create_user( + username="test_user_device_flow", + email="test_device@example.com", + password="password123", + ) + self.client.login(username="test_user_device_flow", password="password123") + + device = DeviceModel( + client_id="client_id", + device_code="device_code", + user_code="user_code", + scope="scope", + expires=datetime.now() + timedelta(days=1), + ) + device.save() + + # This simulates pytest.mark.parameterize, which unfortunately does not work with unittest + # and consequently with Django TestCase. + for invalid_state in ["authorized", "denied", "LOL_status"]: + # Set the device into an incorrect state. + device.status = invalid_state + device.save(update_fields=["status"]) + + self.assertContains( + response=self.client.post( + reverse("oauth2_provider:device"), + data={"user_code": "user_code"}, + ), + status_code=200, + text="User code has already been used", + count=1, + ) + + def test_device_flow_authorization_device_expired_returns_form_error(self): + """ + This test asserts that only devices in the expected state (authorization-pending) + can be approved/denied by the user. + """ + + UserModel.objects.create_user( + username="test_user_device_flow", + email="test_device@example.com", + password="password123", + ) + self.client.login(username="test_user_device_flow", password="password123") + + device = DeviceModel( + client_id="client_id", + device_code="device_code", + user_code="user_code", + scope="scope", + expires=datetime.now() + timedelta(seconds=-1), # <- essentially expired + ) + device.save() + + self.assertContains( + response=self.client.post( + reverse("oauth2_provider:device"), + data={"user_code": "user_code"}, + ), + status_code=200, + text="Expired user code", + count=1, + ) + + def test_token_view_returns_error_if_device_in_invalid_state(self): + """ + This test asserts that the token view returns the appropriate errors as specified + in https://datatracker.ietf.org/doc/html/rfc8628#section-3.5, in case the device + has not yet been approved by the user. + """ + + device = DeviceModel( + client_id="client_id", + device_code="device_code", + user_code="user_code", + scope="scope", + expires=datetime.now() + timedelta(seconds=60), + ) + device.save() + + token_payload = { + "device_code": "device_code", + "client_id": "client_id", + "grant_type": "urn:ietf:params:oauth:grant-type:device_code", + } + + testcases = [ + ("authorization-pending", '{"error": "authorization_pending"}', 400), + ("expired", '{"error": "expired_token"}', 400), + ("denied", '{"error": "access_denied"}', 400), + ("LOL_status", '{"error": "internal_error"}', 500), + ] + for invalid_state, expected_error_message, expected_error_code in testcases: + device.status = invalid_state + device.save(update_fields=["status"]) + + response = self.client.post( + "/o/token/", + data=urlencode(token_payload), + content_type="application/x-www-form-urlencoded", + ) + self.assertContains( + response=response, + status_code=expected_error_code, + text=expected_error_message, + count=1, + ) + # TokenView should always respond with application/json as it's meant to be + # consumed by devices. + self.assertEqual(response.__getitem__("content-type"), "application/json") + + def test_token_view_returns_404_error_if_device_not_found(self): + device = DeviceModel( + client_id="client_id", + device_code="device_code", + user_code="user_code", + scope="scope", + expires=datetime.now() + timedelta(seconds=60), + ) + device.save() + + token_payload = { + "device_code": "another_device_code", + "client_id": "client_id", + "grant_type": "urn:ietf:params:oauth:grant-type:device_code", + } + + response = self.client.post( + "/o/token/", + data=urlencode(token_payload), + content_type="application/x-www-form-urlencoded", + ) + self.assertContains( + response=response, + status_code=404, + text="device_not_found", + count=1, + ) + # TokenView should always respond with application/json as it's meant to be + # consumed by devices. + self.assertEqual(response.__getitem__("content-type"), "application/json") + + def test_token_view_status_equals_what_oauthlib_token_response_method_returns(self): + """ + Tests the use case where oauthlib create_token_response returns a status different + than 200. + """ + + class MockOauthlibCoreClass: + def create_token_response(self, _): + return "url", {"headers_are_ignored": True}, '{"Key": "Value"}', 299 + + device = DeviceModel( + client_id="client_id", + device_code="device_code", + user_code="user_code", + scope="scope", + expires=datetime.now() + timedelta(seconds=60), + status="authorized", + ) + device.save() + + token_payload = { + "device_code": "device_code", + "client_id": "client_id", + "grant_type": "urn:ietf:params:oauth:grant-type:device_code", + } + + with mock.patch( + "oauth2_provider.views.mixins.OAuthLibMixin.get_oauthlib_core", MockOauthlibCoreClass + ): + response = self.client.post( + "/o/token/", + data=urlencode(token_payload), + content_type="application/x-www-form-urlencoded", + ) + + self.assertEqual(response["content-type"], "application/json") + self.assertContains( + response=response, + status_code=299, + text='{"Key": "Value"}', + count=1, + ) + assert not response.has_header("headers_are_ignored") + + @mock.patch( + "oauthlib.oauth2.rfc8628.endpoints.device_authorization.generate_token", + lambda: "abc", + ) + def test_device_polling_interval_can_be_changed(self): + """ + Tests the device polling rate(interval) can be changed to something other than the default + of 5 seconds. + """ + + self.oauth2_settings.OAUTH_DEVICE_VERIFICATION_URI = "example.com/device" + self.oauth2_settings.OAUTH_DEVICE_USER_CODE_GENERATOR = lambda: "xyz" + + self.oauth2_settings.DEVICE_FLOW_INTERVAL = 10 + + request_data: dict[str, str] = { + "client_id": self.application.client_id, + } + request_as_x_www_form_urlencoded: str = urlencode(request_data) + + response: django.http.response.JsonResponse = self.client.post( + reverse("oauth2_provider:device-authorization"), + data=request_as_x_www_form_urlencoded, + content_type="application/x-www-form-urlencoded", + ) + + assert response.status_code == 200 + + assert response.json() == { + "verification_uri": "example.com/device", + "expires_in": 1800, + "user_code": "xyz", + "device_code": "abc", + "interval": 10, + } + + def test_incorrect_client_id_sent(self): + """ + Ensure the correct error is returned when an invalid client is sent + """ + request_data: dict[str, str] = { + "client_id": "client_id_that_does_not_exist", + } + request_as_x_www_form_urlencoded: str = urlencode(request_data) + + response: django.http.response.JsonResponse = self.client.post( + reverse("oauth2_provider:device-authorization"), + data=request_as_x_www_form_urlencoded, + content_type="application/x-www-form-urlencoded", + ) + + assert response.status_code == 400 + + assert response.json() == { + "error": "invalid_request", + "error_description": "Invalid client_id parameter value.", + } + + def test_missing_client_id(self): + """ + Ensure the correct error is returned when the client id is missing. + """ + request_data: dict[str, str] = { + "not_client_id": "client_id_that_does_not_exist", + } + request_as_x_www_form_urlencoded: str = urlencode(request_data) + + response: django.http.response.JsonResponse = self.client.post( + reverse("oauth2_provider:device-authorization"), + data=request_as_x_www_form_urlencoded, + content_type="application/x-www-form-urlencoded", + ) + + assert response.status_code == 400 + + assert response.json() == { + "error": "invalid_request", + "error_description": "Missing client_id parameter.", + } + + def test_device_confirm_and_user_code_views_require_login(self): + URLs = [ + reverse("oauth2_provider:device-confirm", kwargs={"user_code": None, "client_id": "abc"}), + reverse("oauth2_provider:device-confirm", kwargs={"user_code": "abc", "client_id": "abc"}), + reverse("oauth2_provider:device"), + ] + + for url in URLs: + r = self.client.get(url) + assert r.status_code == 302 + assert r["Location"] == f"{settings.LOGIN_URL}?next={url}" + + r = self.client.post(url) + assert r.status_code == 302 + assert r["Location"] == f"{settings.LOGIN_URL}?next={url}" + + def test_device_confirm_view_GET_returns_404_when_device_does_not_exist(self): + UserModel.objects.create_user( + username="test_user_device_flow", + email="test_device@example.com", + password="password123", + ) + self.client.login(username="test_user_device_flow", password="password123") + + device = DeviceModel( + client_id="client_id", + device_code="device_code", + user_code="user_code", + scope="scope", + expires=datetime.now(), + ) + device.save() + + self.assertContains( + response=self.client.get( + reverse( + "oauth2_provider:device-confirm", + kwargs={"user_code": "not_user_code", "client_id": "not_client_id"}, + ) + ), + status_code=404, + text="The requested resource was not found on this server.", + ) + + # Asserts for valid user_code and client_id but invalid states + for invalid_state in ["authorized", "denied", "expired"]: + device.status = invalid_state + device.save(update_fields=["status"]) + + self.assertContains( + response=self.client.get( + reverse( + "oauth2_provider:device-confirm", + kwargs={"user_code": "not_user_code", "client_id": "client_id"}, + ) + ), + status_code=404, + text="The requested resource was not found on this server.", + ) + + def test_device_confirm_view_POST_returns_404_when_device_does_not_exist(self): + UserModel.objects.create_user( + username="test_user_device_flow", + email="test_device@example.com", + password="password123", + ) + self.client.login(username="test_user_device_flow", password="password123") + + device = DeviceModel( + client_id="client_id", + device_code="device_code", + user_code="user_code", + scope="scope", + expires=datetime.now(), + ) + device.save() + + self.assertContains( + response=self.client.post( + reverse( + "oauth2_provider:device-confirm", + kwargs={"user_code": "not_user_code", "client_id": "client_id"}, + ), + data={"action": "accept"}, + ), + status_code=404, + text="The requested resource was not found on this server.", + count=1, + ) + + # Asserts for valid user_code and client_id but invalid states + for invalid_state in ["authorized", "denied", "expired"]: + device.status = invalid_state + device.save(update_fields=["status"]) + + self.assertContains( + response=self.client.post( + reverse( + "oauth2_provider:device-confirm", + kwargs={"user_code": "user_code", "client_id": "client_id"}, + ), + data={"action": "accept"}, + ), + status_code=404, + text="The requested resource was not found on this server.", + count=1, + ) + + def test_device_is_expired_method_sets_status_to_expired_if_deadline_passed(self): + device = DeviceModel( + client_id="client_id", + device_code="device_code", + user_code="user_code", + scope="scope", + expires=datetime.now(tz=timezone.utc) + timedelta(seconds=-1), # <- essentially expired + ) + device.save() + + assert device.status == device.AUTHORIZATION_PENDING # default value + + # call is_expired() which should update the state + is_expired = device.is_expired() + + assert is_expired + assert device.status == device.EXPIRED + + # calling again is_expired() should return true and not change the state + is_expired = device.is_expired() + + assert is_expired + assert device.status == device.EXPIRED diff --git a/tests/test_oauth2_validators.py b/tests/test_oauth2_validators.py index 14c74506e..7e7e46de7 100644 --- a/tests/test_oauth2_validators.py +++ b/tests/test_oauth2_validators.py @@ -180,6 +180,12 @@ def test_authenticate_basic_auth_not_utf8(self): self.request.headers = {"HTTP_AUTHORIZATION": "Basic test"} self.assertFalse(self.validator._authenticate_basic_auth(self.request)) + def test_authenticate_basic_auth_public_app_with_device_code(self): + self.request.grant_type = "urn:ietf:params:oauth:grant-type:device_code" + self.request.headers = get_basic_auth_header("client_id", CLEARTEXT_SECRET) + self.application.client_type = Application.CLIENT_PUBLIC + self.assertTrue(self.validator._authenticate_basic_auth(self.request)) + def test_authenticate_check_secret(self): hashed = make_password(CLEARTEXT_SECRET) self.assertTrue(self.validator._check_secret(CLEARTEXT_SECRET, CLEARTEXT_SECRET)) diff --git a/tests/test_utils.py b/tests/test_utils.py index 2c319b6ea..eef4b985c 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,3 +1,5 @@ +import pytest + from oauth2_provider import utils @@ -25,3 +27,24 @@ def test_jwk_from_pem_caches_jwk(): jwk3 = utils.jwk_from_pem(a_different_tiny_rsa_key) assert jwk3 is not jwk1 + + +def test_user_code_generator(): + # Default argument, 8 characters + user_code = utils.user_code_generator() + assert isinstance(user_code, str) + assert len(user_code) == 8 + + for character in user_code: + assert character >= "0" + assert character <= "V" + + another_user_code = utils.user_code_generator() + assert another_user_code != user_code + + shorter_user_code = utils.user_code_generator(user_code_length=1) + assert len(shorter_user_code) == 1 + + with pytest.raises(ValueError): + utils.user_code_generator(user_code_length=0) + utils.user_code_generator(user_code_length=-1) diff --git a/tox.ini b/tox.ini index 0a85f5fb8..29e93a2ae 100644 --- a/tox.ini +++ b/tox.ini @@ -46,7 +46,7 @@ deps = dj52: Django>=5.2,<6.0 djmain: https://github.com/django/django/archive/main.tar.gz djangorestframework - oauthlib>=3.2.2 + oauthlib>=3.3.0 jwcrypto coverage pytest @@ -79,7 +79,7 @@ commands = deps = Jinja2<3.1 sphinx<3 - oauthlib>=3.2.2 + oauthlib>=3.3.0 m2r>=0.2.1 mistune<2 sphinx-rtd-theme diff --git a/uv.lock b/uv.lock index 43764ff93..d5f28ba2c 100644 --- a/uv.lock +++ b/uv.lock @@ -1,6 +1,6 @@ version = 1 revision = 3 -requires-python = ">=3.8, <3.14" +requires-python = ">=3.8, <=3.14" resolution-markers = [ "python_full_version >= '3.11'", "python_full_version == '3.10.*'", @@ -260,6 +260,28 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/eb/6d/bf9bda840d5f1dfdbf0feca87fbdb64a918a69bca42cfa0ba7b137c48cb8/cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27", size = 172909, upload-time = "2025-09-08T23:23:14.32Z" }, { url = "https://files.pythonhosted.org/packages/37/18/6519e1ee6f5a1e579e04b9ddb6f1676c17368a7aba48299c3759bbc3c8b3/cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75", size = 183402, upload-time = "2025-09-08T23:23:15.535Z" }, { url = "https://files.pythonhosted.org/packages/cb/0e/02ceeec9a7d6ee63bb596121c2c8e9b3a9e150936f4fbef6ca1943e6137c/cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91", size = 177780, upload-time = "2025-09-08T23:23:16.761Z" }, + { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, + { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, + { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, + { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, + { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, + { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, + { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, + { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, + { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, + { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, { url = "https://files.pythonhosted.org/packages/c0/cc/08ed5a43f2996a16b462f64a7055c6e962803534924b9b2f1371d8c00b7b/cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf", size = 184288, upload-time = "2025-09-08T23:23:48.404Z" }, { url = "https://files.pythonhosted.org/packages/3d/de/38d9726324e127f727b4ecc376bc85e505bfe61ef130eaf3f290c6847dd4/cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7", size = 180509, upload-time = "2025-09-08T23:23:49.73Z" }, { url = "https://files.pythonhosted.org/packages/9b/13/c92e36358fbcc39cf0962e83223c9522154ee8630e1df7c0b3a39a8124e2/cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c", size = 208813, upload-time = "2025-09-08T23:23:51.263Z" }, @@ -344,6 +366,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" }, { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" }, { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" }, + { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" }, + { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" }, + { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" }, + { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" }, + { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" }, + { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" }, + { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" }, + { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" }, + { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" }, + { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" }, + { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" }, + { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" }, + { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" }, + { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" }, + { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" }, { url = "https://files.pythonhosted.org/packages/0a/4e/3926a1c11f0433791985727965263f788af00db3482d89a7545ca5ecc921/charset_normalizer-3.4.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ce8a0633f41a967713a59c4139d29110c07e826d131a316b50ce11b1d79b4f84", size = 198599, upload-time = "2025-10-14T04:41:53.213Z" }, { url = "https://files.pythonhosted.org/packages/ec/7c/b92d1d1dcffc34592e71ea19c882b6709e43d20fa498042dea8b815638d7/charset_normalizer-3.4.4-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaabd426fe94daf8fd157c32e571c85cb12e66692f15516a83a03264b08d06c3", size = 143090, upload-time = "2025-10-14T04:41:54.385Z" }, { url = "https://files.pythonhosted.org/packages/84/ce/61a28d3bb77281eb24107b937a497f3c43089326d27832a63dcedaab0478/charset_normalizer-3.4.4-cp38-cp38-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c4ef880e27901b6cc782f1b95f82da9313c0eb95c3af699103088fa0ac3ce9ac", size = 139490, upload-time = "2025-10-14T04:41:55.551Z" }, @@ -547,6 +585,32 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ee/51/a03bec00d37faaa891b3ff7387192cef20f01604e5283a5fabc95346befa/coverage-7.10.7-cp313-cp313t-win32.whl", hash = "sha256:2a78cd46550081a7909b3329e2266204d584866e8d97b898cd7fb5ac8d888b1a", size = 221403, upload-time = "2025-09-21T20:02:37.034Z" }, { url = "https://files.pythonhosted.org/packages/53/22/3cf25d614e64bf6d8e59c7c669b20d6d940bb337bdee5900b9ca41c820bb/coverage-7.10.7-cp313-cp313t-win_amd64.whl", hash = "sha256:33a5e6396ab684cb43dc7befa386258acb2d7fae7f67330ebb85ba4ea27938eb", size = 222469, upload-time = "2025-09-21T20:02:39.011Z" }, { url = "https://files.pythonhosted.org/packages/49/a1/00164f6d30d8a01c3c9c48418a7a5be394de5349b421b9ee019f380df2a0/coverage-7.10.7-cp313-cp313t-win_arm64.whl", hash = "sha256:86b0e7308289ddde73d863b7683f596d8d21c7d8664ce1dee061d0bcf3fbb4bb", size = 220731, upload-time = "2025-09-21T20:02:40.939Z" }, + { url = "https://files.pythonhosted.org/packages/23/9c/5844ab4ca6a4dd97a1850e030a15ec7d292b5c5cb93082979225126e35dd/coverage-7.10.7-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b06f260b16ead11643a5a9f955bd4b5fd76c1a4c6796aeade8520095b75de520", size = 218302, upload-time = "2025-09-21T20:02:42.527Z" }, + { url = "https://files.pythonhosted.org/packages/f0/89/673f6514b0961d1f0e20ddc242e9342f6da21eaba3489901b565c0689f34/coverage-7.10.7-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:212f8f2e0612778f09c55dd4872cb1f64a1f2b074393d139278ce902064d5b32", size = 218578, upload-time = "2025-09-21T20:02:44.468Z" }, + { url = "https://files.pythonhosted.org/packages/05/e8/261cae479e85232828fb17ad536765c88dd818c8470aca690b0ac6feeaa3/coverage-7.10.7-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3445258bcded7d4aa630ab8296dea4d3f15a255588dd535f980c193ab6b95f3f", size = 249629, upload-time = "2025-09-21T20:02:46.503Z" }, + { url = "https://files.pythonhosted.org/packages/82/62/14ed6546d0207e6eda876434e3e8475a3e9adbe32110ce896c9e0c06bb9a/coverage-7.10.7-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bb45474711ba385c46a0bfe696c695a929ae69ac636cda8f532be9e8c93d720a", size = 252162, upload-time = "2025-09-21T20:02:48.689Z" }, + { url = "https://files.pythonhosted.org/packages/ff/49/07f00db9ac6478e4358165a08fb41b469a1b053212e8a00cb02f0d27a05f/coverage-7.10.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:813922f35bd800dca9994c5971883cbc0d291128a5de6b167c7aa697fcf59360", size = 253517, upload-time = "2025-09-21T20:02:50.31Z" }, + { url = "https://files.pythonhosted.org/packages/a2/59/c5201c62dbf165dfbc91460f6dbbaa85a8b82cfa6131ac45d6c1bfb52deb/coverage-7.10.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:93c1b03552081b2a4423091d6fb3787265b8f86af404cff98d1b5342713bdd69", size = 249632, upload-time = "2025-09-21T20:02:51.971Z" }, + { url = "https://files.pythonhosted.org/packages/07/ae/5920097195291a51fb00b3a70b9bbd2edbfe3c84876a1762bd1ef1565ebc/coverage-7.10.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cc87dd1b6eaf0b848eebb1c86469b9f72a1891cb42ac7adcfbce75eadb13dd14", size = 251520, upload-time = "2025-09-21T20:02:53.858Z" }, + { url = "https://files.pythonhosted.org/packages/b9/3c/a815dde77a2981f5743a60b63df31cb322c944843e57dbd579326625a413/coverage-7.10.7-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:39508ffda4f343c35f3236fe8d1a6634a51f4581226a1262769d7f970e73bffe", size = 249455, upload-time = "2025-09-21T20:02:55.807Z" }, + { url = "https://files.pythonhosted.org/packages/aa/99/f5cdd8421ea656abefb6c0ce92556709db2265c41e8f9fc6c8ae0f7824c9/coverage-7.10.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:925a1edf3d810537c5a3abe78ec5530160c5f9a26b1f4270b40e62cc79304a1e", size = 249287, upload-time = "2025-09-21T20:02:57.784Z" }, + { url = "https://files.pythonhosted.org/packages/c3/7a/e9a2da6a1fc5d007dd51fca083a663ab930a8c4d149c087732a5dbaa0029/coverage-7.10.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2c8b9a0636f94c43cd3576811e05b89aa9bc2d0a85137affc544ae5cb0e4bfbd", size = 250946, upload-time = "2025-09-21T20:02:59.431Z" }, + { url = "https://files.pythonhosted.org/packages/ef/5b/0b5799aa30380a949005a353715095d6d1da81927d6dbed5def2200a4e25/coverage-7.10.7-cp314-cp314-win32.whl", hash = "sha256:b7b8288eb7cdd268b0304632da8cb0bb93fadcfec2fe5712f7b9cc8f4d487be2", size = 221009, upload-time = "2025-09-21T20:03:01.324Z" }, + { url = "https://files.pythonhosted.org/packages/da/b0/e802fbb6eb746de006490abc9bb554b708918b6774b722bb3a0e6aa1b7de/coverage-7.10.7-cp314-cp314-win_amd64.whl", hash = "sha256:1ca6db7c8807fb9e755d0379ccc39017ce0a84dcd26d14b5a03b78563776f681", size = 221804, upload-time = "2025-09-21T20:03:03.4Z" }, + { url = "https://files.pythonhosted.org/packages/9e/e8/71d0c8e374e31f39e3389bb0bd19e527d46f00ea8571ec7ec8fd261d8b44/coverage-7.10.7-cp314-cp314-win_arm64.whl", hash = "sha256:097c1591f5af4496226d5783d036bf6fd6cd0cbc132e071b33861de756efb880", size = 220384, upload-time = "2025-09-21T20:03:05.111Z" }, + { url = "https://files.pythonhosted.org/packages/62/09/9a5608d319fa3eba7a2019addeacb8c746fb50872b57a724c9f79f146969/coverage-7.10.7-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:a62c6ef0d50e6de320c270ff91d9dd0a05e7250cac2a800b7784bae474506e63", size = 219047, upload-time = "2025-09-21T20:03:06.795Z" }, + { url = "https://files.pythonhosted.org/packages/f5/6f/f58d46f33db9f2e3647b2d0764704548c184e6f5e014bef528b7f979ef84/coverage-7.10.7-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9fa6e4dd51fe15d8738708a973470f67a855ca50002294852e9571cdbd9433f2", size = 219266, upload-time = "2025-09-21T20:03:08.495Z" }, + { url = "https://files.pythonhosted.org/packages/74/5c/183ffc817ba68e0b443b8c934c8795553eb0c14573813415bd59941ee165/coverage-7.10.7-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8fb190658865565c549b6b4706856d6a7b09302c797eb2cf8e7fe9dabb043f0d", size = 260767, upload-time = "2025-09-21T20:03:10.172Z" }, + { url = "https://files.pythonhosted.org/packages/0f/48/71a8abe9c1ad7e97548835e3cc1adbf361e743e9d60310c5f75c9e7bf847/coverage-7.10.7-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:affef7c76a9ef259187ef31599a9260330e0335a3011732c4b9effa01e1cd6e0", size = 262931, upload-time = "2025-09-21T20:03:11.861Z" }, + { url = "https://files.pythonhosted.org/packages/84/fd/193a8fb132acfc0a901f72020e54be5e48021e1575bb327d8ee1097a28fd/coverage-7.10.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6e16e07d85ca0cf8bafe5f5d23a0b850064e8e945d5677492b06bbe6f09cc699", size = 265186, upload-time = "2025-09-21T20:03:13.539Z" }, + { url = "https://files.pythonhosted.org/packages/b1/8f/74ecc30607dd95ad50e3034221113ccb1c6d4e8085cc761134782995daae/coverage-7.10.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:03ffc58aacdf65d2a82bbeb1ffe4d01ead4017a21bfd0454983b88ca73af94b9", size = 259470, upload-time = "2025-09-21T20:03:15.584Z" }, + { url = "https://files.pythonhosted.org/packages/0f/55/79ff53a769f20d71b07023ea115c9167c0bb56f281320520cf64c5298a96/coverage-7.10.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1b4fd784344d4e52647fd7857b2af5b3fbe6c239b0b5fa63e94eb67320770e0f", size = 262626, upload-time = "2025-09-21T20:03:17.673Z" }, + { url = "https://files.pythonhosted.org/packages/88/e2/dac66c140009b61ac3fc13af673a574b00c16efdf04f9b5c740703e953c0/coverage-7.10.7-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:0ebbaddb2c19b71912c6f2518e791aa8b9f054985a0769bdb3a53ebbc765c6a1", size = 260386, upload-time = "2025-09-21T20:03:19.36Z" }, + { url = "https://files.pythonhosted.org/packages/a2/f1/f48f645e3f33bb9ca8a496bc4a9671b52f2f353146233ebd7c1df6160440/coverage-7.10.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:a2d9a3b260cc1d1dbdb1c582e63ddcf5363426a1a68faa0f5da28d8ee3c722a0", size = 258852, upload-time = "2025-09-21T20:03:21.007Z" }, + { url = "https://files.pythonhosted.org/packages/bb/3b/8442618972c51a7affeead957995cfa8323c0c9bcf8fa5a027421f720ff4/coverage-7.10.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a3cc8638b2480865eaa3926d192e64ce6c51e3d29c849e09d5b4ad95efae5399", size = 261534, upload-time = "2025-09-21T20:03:23.12Z" }, + { url = "https://files.pythonhosted.org/packages/b2/dc/101f3fa3a45146db0cb03f5b4376e24c0aac818309da23e2de0c75295a91/coverage-7.10.7-cp314-cp314t-win32.whl", hash = "sha256:67f8c5cbcd3deb7a60b3345dffc89a961a484ed0af1f6f73de91705cc6e31235", size = 221784, upload-time = "2025-09-21T20:03:24.769Z" }, + { url = "https://files.pythonhosted.org/packages/4c/a1/74c51803fc70a8a40d7346660379e144be772bab4ac7bb6e6b905152345c/coverage-7.10.7-cp314-cp314t-win_amd64.whl", hash = "sha256:e1ed71194ef6dea7ed2d5cb5f7243d4bcd334bfb63e59878519be558078f848d", size = 222905, upload-time = "2025-09-21T20:03:26.93Z" }, + { url = "https://files.pythonhosted.org/packages/12/65/f116a6d2127df30bcafbceef0302d8a64ba87488bf6f73a6d8eebf060873/coverage-7.10.7-cp314-cp314t-win_arm64.whl", hash = "sha256:7fe650342addd8524ca63d77b2362b02345e5f1a093266787d210c70a50b471a", size = 220922, upload-time = "2025-09-21T20:03:28.672Z" }, { url = "https://files.pythonhosted.org/packages/a3/ad/d1c25053764b4c42eb294aae92ab617d2e4f803397f9c7c8295caa77a260/coverage-7.10.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fff7b9c3f19957020cac546c70025331113d2e61537f6e2441bc7657913de7d3", size = 217978, upload-time = "2025-09-21T20:03:30.362Z" }, { url = "https://files.pythonhosted.org/packages/52/2f/b9f9daa39b80ece0b9548bbb723381e29bc664822d9a12c2135f8922c22b/coverage-7.10.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bc91b314cef27742da486d6839b677b3f2793dfe52b51bbbb7cf736d5c29281c", size = 218370, upload-time = "2025-09-21T20:03:32.147Z" }, { url = "https://files.pythonhosted.org/packages/dd/6e/30d006c3b469e58449650642383dddf1c8fb63d44fdf92994bfd46570695/coverage-7.10.7-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:567f5c155eda8df1d3d439d40a45a6a5f029b429b06648235f1e7e51b522b396", size = 244802, upload-time = "2025-09-21T20:03:33.919Z" }, @@ -641,6 +705,32 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/9a/b8/49cf253e1e7a3bedb85199b201862dd7ca4859f75b6cf25ffa7298aa0760/coverage-7.11.0-cp313-cp313t-win32.whl", hash = "sha256:cee6291bb4fed184f1c2b663606a115c743df98a537c969c3c64b49989da96c2", size = 219180, upload-time = "2025-10-15T15:14:09.786Z" }, { url = "https://files.pythonhosted.org/packages/ac/e1/1a541703826be7ae2125a0fb7f821af5729d56bb71e946e7b933cc7a89a4/coverage-7.11.0-cp313-cp313t-win_amd64.whl", hash = "sha256:a386c1061bf98e7ea4758e4313c0ab5ecf57af341ef0f43a0bf26c2477b5c268", size = 220241, upload-time = "2025-10-15T15:14:11.471Z" }, { url = "https://files.pythonhosted.org/packages/d5/d1/5ee0e0a08621140fd418ec4020f595b4d52d7eb429ae6a0c6542b4ba6f14/coverage-7.11.0-cp313-cp313t-win_arm64.whl", hash = "sha256:f9ea02ef40bb83823b2b04964459d281688fe173e20643870bb5d2edf68bc836", size = 218510, upload-time = "2025-10-15T15:14:13.46Z" }, + { url = "https://files.pythonhosted.org/packages/f4/06/e923830c1985ce808e40a3fa3eb46c13350b3224b7da59757d37b6ce12b8/coverage-7.11.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c770885b28fb399aaf2a65bbd1c12bf6f307ffd112d6a76c5231a94276f0c497", size = 216110, upload-time = "2025-10-15T15:14:15.157Z" }, + { url = "https://files.pythonhosted.org/packages/42/82/cdeed03bfead45203fb651ed756dfb5266028f5f939e7f06efac4041dad5/coverage-7.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a3d0e2087dba64c86a6b254f43e12d264b636a39e88c5cc0a01a7c71bcfdab7e", size = 216395, upload-time = "2025-10-15T15:14:16.863Z" }, + { url = "https://files.pythonhosted.org/packages/fc/ba/e1c80caffc3199aa699813f73ff097bc2df7b31642bdbc7493600a8f1de5/coverage-7.11.0-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:73feb83bb41c32811973b8565f3705caf01d928d972b72042b44e97c71fd70d1", size = 247433, upload-time = "2025-10-15T15:14:18.589Z" }, + { url = "https://files.pythonhosted.org/packages/80/c0/5b259b029694ce0a5bbc1548834c7ba3db41d3efd3474489d7efce4ceb18/coverage-7.11.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c6f31f281012235ad08f9a560976cc2fc9c95c17604ff3ab20120fe480169bca", size = 249970, upload-time = "2025-10-15T15:14:20.307Z" }, + { url = "https://files.pythonhosted.org/packages/8c/86/171b2b5e1aac7e2fd9b43f7158b987dbeb95f06d1fbecad54ad8163ae3e8/coverage-7.11.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9570ad567f880ef675673992222746a124b9595506826b210fbe0ce3f0499cd", size = 251324, upload-time = "2025-10-15T15:14:22.419Z" }, + { url = "https://files.pythonhosted.org/packages/1a/7e/7e10414d343385b92024af3932a27a1caf75c6e27ee88ba211221ff1a145/coverage-7.11.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8badf70446042553a773547a61fecaa734b55dc738cacf20c56ab04b77425e43", size = 247445, upload-time = "2025-10-15T15:14:24.205Z" }, + { url = "https://files.pythonhosted.org/packages/c4/3b/e4f966b21f5be8c4bf86ad75ae94efa0de4c99c7bbb8114476323102e345/coverage-7.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a09c1211959903a479e389685b7feb8a17f59ec5a4ef9afde7650bd5eabc2777", size = 249324, upload-time = "2025-10-15T15:14:26.234Z" }, + { url = "https://files.pythonhosted.org/packages/00/a2/8479325576dfcd909244d0df215f077f47437ab852ab778cfa2f8bf4d954/coverage-7.11.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:5ef83b107f50db3f9ae40f69e34b3bd9337456c5a7fe3461c7abf8b75dd666a2", size = 247261, upload-time = "2025-10-15T15:14:28.42Z" }, + { url = "https://files.pythonhosted.org/packages/7b/d8/3a9e2db19d94d65771d0f2e21a9ea587d11b831332a73622f901157cc24b/coverage-7.11.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:f91f927a3215b8907e214af77200250bb6aae36eca3f760f89780d13e495388d", size = 247092, upload-time = "2025-10-15T15:14:30.784Z" }, + { url = "https://files.pythonhosted.org/packages/b3/b1/bbca3c472544f9e2ad2d5116b2379732957048be4b93a9c543fcd0207e5f/coverage-7.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cdbcd376716d6b7fbfeedd687a6c4be019c5a5671b35f804ba76a4c0a778cba4", size = 248755, upload-time = "2025-10-15T15:14:32.585Z" }, + { url = "https://files.pythonhosted.org/packages/89/49/638d5a45a6a0f00af53d6b637c87007eb2297042186334e9923a61aa8854/coverage-7.11.0-cp314-cp314-win32.whl", hash = "sha256:bab7ec4bb501743edc63609320aaec8cd9188b396354f482f4de4d40a9d10721", size = 218793, upload-time = "2025-10-15T15:14:34.972Z" }, + { url = "https://files.pythonhosted.org/packages/30/cc/b675a51f2d068adb3cdf3799212c662239b0ca27f4691d1fff81b92ea850/coverage-7.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:3d4ba9a449e9364a936a27322b20d32d8b166553bfe63059bd21527e681e2fad", size = 219587, upload-time = "2025-10-15T15:14:37.047Z" }, + { url = "https://files.pythonhosted.org/packages/93/98/5ac886876026de04f00820e5094fe22166b98dcb8b426bf6827aaf67048c/coverage-7.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:ce37f215223af94ef0f75ac68ea096f9f8e8c8ec7d6e8c346ee45c0d363f0479", size = 218168, upload-time = "2025-10-15T15:14:38.861Z" }, + { url = "https://files.pythonhosted.org/packages/14/d1/b4145d35b3e3ecf4d917e97fc8895bcf027d854879ba401d9ff0f533f997/coverage-7.11.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:f413ce6e07e0d0dc9c433228727b619871532674b45165abafe201f200cc215f", size = 216850, upload-time = "2025-10-15T15:14:40.651Z" }, + { url = "https://files.pythonhosted.org/packages/ca/d1/7f645fc2eccd318369a8a9948acc447bb7c1ade2911e31d3c5620544c22b/coverage-7.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:05791e528a18f7072bf5998ba772fe29db4da1234c45c2087866b5ba4dea710e", size = 217071, upload-time = "2025-10-15T15:14:42.755Z" }, + { url = "https://files.pythonhosted.org/packages/54/7d/64d124649db2737ceced1dfcbdcb79898d5868d311730f622f8ecae84250/coverage-7.11.0-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cacb29f420cfeb9283b803263c3b9a068924474ff19ca126ba9103e1278dfa44", size = 258570, upload-time = "2025-10-15T15:14:44.542Z" }, + { url = "https://files.pythonhosted.org/packages/6c/3f/6f5922f80dc6f2d8b2c6f974835c43f53eb4257a7797727e6ca5b7b2ec1f/coverage-7.11.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:314c24e700d7027ae3ab0d95fbf8d53544fca1f20345fd30cd219b737c6e58d3", size = 260738, upload-time = "2025-10-15T15:14:46.436Z" }, + { url = "https://files.pythonhosted.org/packages/0e/5f/9e883523c4647c860b3812b417a2017e361eca5b635ee658387dc11b13c1/coverage-7.11.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:630d0bd7a293ad2fc8b4b94e5758c8b2536fdf36c05f1681270203e463cbfa9b", size = 262994, upload-time = "2025-10-15T15:14:48.3Z" }, + { url = "https://files.pythonhosted.org/packages/07/bb/43b5a8e94c09c8bf51743ffc65c4c841a4ca5d3ed191d0a6919c379a1b83/coverage-7.11.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e89641f5175d65e2dbb44db15fe4ea48fade5d5bbb9868fdc2b4fce22f4a469d", size = 257282, upload-time = "2025-10-15T15:14:50.236Z" }, + { url = "https://files.pythonhosted.org/packages/aa/e5/0ead8af411411330b928733e1d201384b39251a5f043c1612970310e8283/coverage-7.11.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c9f08ea03114a637dab06cedb2e914da9dc67fa52c6015c018ff43fdde25b9c2", size = 260430, upload-time = "2025-10-15T15:14:52.413Z" }, + { url = "https://files.pythonhosted.org/packages/ae/66/03dd8bb0ba5b971620dcaac145461950f6d8204953e535d2b20c6b65d729/coverage-7.11.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce9f3bde4e9b031eaf1eb61df95c1401427029ea1bfddb8621c1161dcb0fa02e", size = 258190, upload-time = "2025-10-15T15:14:54.268Z" }, + { url = "https://files.pythonhosted.org/packages/45/ae/28a9cce40bf3174426cb2f7e71ee172d98e7f6446dff936a7ccecee34b14/coverage-7.11.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:e4dc07e95495923d6fd4d6c27bf70769425b71c89053083843fd78f378558996", size = 256658, upload-time = "2025-10-15T15:14:56.436Z" }, + { url = "https://files.pythonhosted.org/packages/5c/7c/3a44234a8599513684bfc8684878fd7b126c2760f79712bb78c56f19efc4/coverage-7.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:424538266794db2861db4922b05d729ade0940ee69dcf0591ce8f69784db0e11", size = 259342, upload-time = "2025-10-15T15:14:58.538Z" }, + { url = "https://files.pythonhosted.org/packages/e1/e6/0108519cba871af0351725ebdb8660fd7a0fe2ba3850d56d32490c7d9b4b/coverage-7.11.0-cp314-cp314t-win32.whl", hash = "sha256:4c1eeb3fb8eb9e0190bebafd0462936f75717687117339f708f395fe455acc73", size = 219568, upload-time = "2025-10-15T15:15:00.382Z" }, + { url = "https://files.pythonhosted.org/packages/c9/76/44ba876e0942b4e62fdde23ccb029ddb16d19ba1bef081edd00857ba0b16/coverage-7.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b56efee146c98dbf2cf5cffc61b9829d1e94442df4d7398b26892a53992d3547", size = 220687, upload-time = "2025-10-15T15:15:02.322Z" }, + { url = "https://files.pythonhosted.org/packages/b9/0c/0df55ecb20d0d0ed5c322e10a441775e1a3a5d78c60f0c4e1abfe6fcf949/coverage-7.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:b5c2705afa83f49bd91962a4094b6b082f94aef7626365ab3f8f4bd159c5acf3", size = 218711, upload-time = "2025-10-15T15:15:04.575Z" }, { url = "https://files.pythonhosted.org/packages/5f/04/642c1d8a448ae5ea1369eac8495740a79eb4e581a9fb0cbdce56bbf56da1/coverage-7.11.0-py3-none-any.whl", hash = "sha256:4b7589765348d78fb4e5fb6ea35d07564e387da2fc5efff62e0222971f155f68", size = 207761, upload-time = "2025-10-15T15:15:06.439Z" }, ] @@ -676,6 +766,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/96/92/8a6a9525893325fc057a01f654d7efc2c64b9de90413adcf605a85744ff4/cryptography-46.0.3-cp311-abi3-win32.whl", hash = "sha256:f260d0d41e9b4da1ed1e0f1ce571f97fe370b152ab18778e9e8f67d6af432018", size = 3055988, upload-time = "2025-10-15T23:17:14.65Z" }, { url = "https://files.pythonhosted.org/packages/7e/bf/80fbf45253ea585a1e492a6a17efcb93467701fa79e71550a430c5e60df0/cryptography-46.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:a9a3008438615669153eb86b26b61e09993921ebdd75385ddd748702c5adfddb", size = 3514451, upload-time = "2025-10-15T23:17:16.142Z" }, { url = "https://files.pythonhosted.org/packages/2e/af/9b302da4c87b0beb9db4e756386a7c6c5b8003cd0e742277888d352ae91d/cryptography-46.0.3-cp311-abi3-win_arm64.whl", hash = "sha256:5d7f93296ee28f68447397bf5198428c9aeeab45705a55d53a6343455dcb2c3c", size = 2928007, upload-time = "2025-10-15T23:17:18.04Z" }, + { url = "https://files.pythonhosted.org/packages/f5/e2/a510aa736755bffa9d2f75029c229111a1d02f8ecd5de03078f4c18d91a3/cryptography-46.0.3-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:00a5e7e87938e5ff9ff5447ab086a5706a957137e6e433841e9d24f38a065217", size = 7158012, upload-time = "2025-10-15T23:17:19.982Z" }, + { url = "https://files.pythonhosted.org/packages/73/dc/9aa866fbdbb95b02e7f9d086f1fccfeebf8953509b87e3f28fff927ff8a0/cryptography-46.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c8daeb2d2174beb4575b77482320303f3d39b8e81153da4f0fb08eb5fe86a6c5", size = 4288728, upload-time = "2025-10-15T23:17:21.527Z" }, + { url = "https://files.pythonhosted.org/packages/c5/fd/bc1daf8230eaa075184cbbf5f8cd00ba9db4fd32d63fb83da4671b72ed8a/cryptography-46.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:39b6755623145ad5eff1dab323f4eae2a32a77a7abef2c5089a04a3d04366715", size = 4435078, upload-time = "2025-10-15T23:17:23.042Z" }, + { url = "https://files.pythonhosted.org/packages/82/98/d3bd5407ce4c60017f8ff9e63ffee4200ab3e23fe05b765cab805a7db008/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:db391fa7c66df6762ee3f00c95a89e6d428f4d60e7abc8328f4fe155b5ac6e54", size = 4293460, upload-time = "2025-10-15T23:17:24.885Z" }, + { url = "https://files.pythonhosted.org/packages/26/e9/e23e7900983c2b8af7a08098db406cf989d7f09caea7897e347598d4cd5b/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:78a97cf6a8839a48c49271cdcbd5cf37ca2c1d6b7fdd86cc864f302b5e9bf459", size = 3995237, upload-time = "2025-10-15T23:17:26.449Z" }, + { url = "https://files.pythonhosted.org/packages/91/15/af68c509d4a138cfe299d0d7ddb14afba15233223ebd933b4bbdbc7155d3/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:dfb781ff7eaa91a6f7fd41776ec37c5853c795d3b358d4896fdbb5df168af422", size = 4967344, upload-time = "2025-10-15T23:17:28.06Z" }, + { url = "https://files.pythonhosted.org/packages/ca/e3/8643d077c53868b681af077edf6b3cb58288b5423610f21c62aadcbe99f4/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6f61efb26e76c45c4a227835ddeae96d83624fb0d29eb5df5b96e14ed1a0afb7", size = 4466564, upload-time = "2025-10-15T23:17:29.665Z" }, + { url = "https://files.pythonhosted.org/packages/0e/43/c1e8726fa59c236ff477ff2b5dc071e54b21e5a1e51aa2cee1676f1c986f/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:23b1a8f26e43f47ceb6d6a43115f33a5a37d57df4ea0ca295b780ae8546e8044", size = 4292415, upload-time = "2025-10-15T23:17:31.686Z" }, + { url = "https://files.pythonhosted.org/packages/42/f9/2f8fefdb1aee8a8e3256a0568cffc4e6d517b256a2fe97a029b3f1b9fe7e/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:b419ae593c86b87014b9be7396b385491ad7f320bde96826d0dd174459e54665", size = 4931457, upload-time = "2025-10-15T23:17:33.478Z" }, + { url = "https://files.pythonhosted.org/packages/79/30/9b54127a9a778ccd6d27c3da7563e9f2d341826075ceab89ae3b41bf5be2/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:50fc3343ac490c6b08c0cf0d704e881d0d660be923fd3076db3e932007e726e3", size = 4466074, upload-time = "2025-10-15T23:17:35.158Z" }, + { url = "https://files.pythonhosted.org/packages/ac/68/b4f4a10928e26c941b1b6a179143af9f4d27d88fe84a6a3c53592d2e76bf/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22d7e97932f511d6b0b04f2bfd818d73dcd5928db509460aaf48384778eb6d20", size = 4420569, upload-time = "2025-10-15T23:17:37.188Z" }, + { url = "https://files.pythonhosted.org/packages/a3/49/3746dab4c0d1979888f125226357d3262a6dd40e114ac29e3d2abdf1ec55/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d55f3dffadd674514ad19451161118fd010988540cee43d8bc20675e775925de", size = 4681941, upload-time = "2025-10-15T23:17:39.236Z" }, + { url = "https://files.pythonhosted.org/packages/fd/30/27654c1dbaf7e4a3531fa1fc77986d04aefa4d6d78259a62c9dc13d7ad36/cryptography-46.0.3-cp314-cp314t-win32.whl", hash = "sha256:8a6e050cb6164d3f830453754094c086ff2d0b2f3a897a1d9820f6139a1f0914", size = 3022339, upload-time = "2025-10-15T23:17:40.888Z" }, + { url = "https://files.pythonhosted.org/packages/f6/30/640f34ccd4d2a1bc88367b54b926b781b5a018d65f404d409aba76a84b1c/cryptography-46.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:760f83faa07f8b64e9c33fc963d790a2edb24efb479e3520c14a45741cd9b2db", size = 3494315, upload-time = "2025-10-15T23:17:42.769Z" }, + { url = "https://files.pythonhosted.org/packages/ba/8b/88cc7e3bd0a8e7b861f26981f7b820e1f46aa9d26cc482d0feba0ecb4919/cryptography-46.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:516ea134e703e9fe26bcd1277a4b59ad30586ea90c365a87781d7887a646fe21", size = 2919331, upload-time = "2025-10-15T23:17:44.468Z" }, { url = "https://files.pythonhosted.org/packages/fd/23/45fe7f376a7df8daf6da3556603b36f53475a99ce4faacb6ba2cf3d82021/cryptography-46.0.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:cb3d760a6117f621261d662bccc8ef5bc32ca673e037c83fbe565324f5c46936", size = 7218248, upload-time = "2025-10-15T23:17:46.294Z" }, { url = "https://files.pythonhosted.org/packages/27/32/b68d27471372737054cbd34c84981f9edbc24fe67ca225d389799614e27f/cryptography-46.0.3-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4b7387121ac7d15e550f5cb4a43aef2559ed759c35df7336c402bb8275ac9683", size = 4294089, upload-time = "2025-10-15T23:17:48.269Z" }, { url = "https://files.pythonhosted.org/packages/26/42/fa8389d4478368743e24e61eea78846a0006caffaf72ea24a15159215a14/cryptography-46.0.3-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15ab9b093e8f09daab0f2159bb7e47532596075139dd74365da52ecc9cb46c5d", size = 4440029, upload-time = "2025-10-15T23:17:49.837Z" }, @@ -764,7 +869,7 @@ requires-dist = [ { name = "django", specifier = ">=4.2" }, { name = "jwcrypto", specifier = ">=1.5.0" }, { name = "m2r", marker = "extra == 'dev'" }, - { name = "oauthlib", specifier = ">=3.2.2" }, + { name = "oauthlib", specifier = ">=3.3.0" }, { name = "pytest", marker = "extra == 'dev'" }, { name = "pytest-cov", marker = "extra == 'dev'" }, { name = "requests", specifier = ">=2.13.0" }, @@ -833,7 +938,7 @@ dependencies = [ [package.metadata] requires-dist = [ - { name = "django", specifier = ">=4.2,<=5.1" }, + { name = "django", specifier = ">=4.2,<=5.2" }, { name = "django-cors-headers", specifier = "==3.14.0" }, { name = "django-environ", specifier = "==0.11.2" }, { name = "django-oauth-toolkit", editable = "." }, @@ -1069,6 +1174,28 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/80/d6/2d1b89f6ca4bff1036499b1e29a1d02d282259f3681540e16563f27ebc23/markupsafe-3.0.3-cp313-cp313t-win32.whl", hash = "sha256:69c0b73548bc525c8cb9a251cddf1931d1db4d2258e9599c28c07ef3580ef354", size = 14612, upload-time = "2025-09-27T18:37:02.639Z" }, { url = "https://files.pythonhosted.org/packages/2b/98/e48a4bfba0a0ffcf9925fe2d69240bfaa19c6f7507b8cd09c70684a53c1e/markupsafe-3.0.3-cp313-cp313t-win_amd64.whl", hash = "sha256:1b4b79e8ebf6b55351f0d91fe80f893b4743f104bff22e90697db1590e47a218", size = 15200, upload-time = "2025-09-27T18:37:03.582Z" }, { url = "https://files.pythonhosted.org/packages/0e/72/e3cc540f351f316e9ed0f092757459afbc595824ca724cbc5a5d4263713f/markupsafe-3.0.3-cp313-cp313t-win_arm64.whl", hash = "sha256:ad2cf8aa28b8c020ab2fc8287b0f823d0a7d8630784c31e9ee5edea20f406287", size = 13973, upload-time = "2025-09-27T18:37:04.929Z" }, + { url = "https://files.pythonhosted.org/packages/33/8a/8e42d4838cd89b7dde187011e97fe6c3af66d8c044997d2183fbd6d31352/markupsafe-3.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:eaa9599de571d72e2daf60164784109f19978b327a3910d3e9de8c97b5b70cfe", size = 11619, upload-time = "2025-09-27T18:37:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/b5/64/7660f8a4a8e53c924d0fa05dc3a55c9cee10bbd82b11c5afb27d44b096ce/markupsafe-3.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c47a551199eb8eb2121d4f0f15ae0f923d31350ab9280078d1e5f12b249e0026", size = 12029, upload-time = "2025-09-27T18:37:07.213Z" }, + { url = "https://files.pythonhosted.org/packages/da/ef/e648bfd021127bef5fa12e1720ffed0c6cbb8310c8d9bea7266337ff06de/markupsafe-3.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f34c41761022dd093b4b6896d4810782ffbabe30f2d443ff5f083e0cbbb8c737", size = 24408, upload-time = "2025-09-27T18:37:09.572Z" }, + { url = "https://files.pythonhosted.org/packages/41/3c/a36c2450754618e62008bf7435ccb0f88053e07592e6028a34776213d877/markupsafe-3.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:457a69a9577064c05a97c41f4e65148652db078a3a509039e64d3467b9e7ef97", size = 23005, upload-time = "2025-09-27T18:37:10.58Z" }, + { url = "https://files.pythonhosted.org/packages/bc/20/b7fdf89a8456b099837cd1dc21974632a02a999ec9bf7ca3e490aacd98e7/markupsafe-3.0.3-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e8afc3f2ccfa24215f8cb28dcf43f0113ac3c37c2f0f0806d8c70e4228c5cf4d", size = 22048, upload-time = "2025-09-27T18:37:11.547Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a7/591f592afdc734f47db08a75793a55d7fbcc6902a723ae4cfbab61010cc5/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ec15a59cf5af7be74194f7ab02d0f59a62bdcf1a537677ce67a2537c9b87fcda", size = 23821, upload-time = "2025-09-27T18:37:12.48Z" }, + { url = "https://files.pythonhosted.org/packages/7d/33/45b24e4f44195b26521bc6f1a82197118f74df348556594bd2262bda1038/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:0eb9ff8191e8498cca014656ae6b8d61f39da5f95b488805da4bb029cccbfbaf", size = 21606, upload-time = "2025-09-27T18:37:13.485Z" }, + { url = "https://files.pythonhosted.org/packages/ff/0e/53dfaca23a69fbfbbf17a4b64072090e70717344c52eaaaa9c5ddff1e5f0/markupsafe-3.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:2713baf880df847f2bece4230d4d094280f4e67b1e813eec43b4c0e144a34ffe", size = 23043, upload-time = "2025-09-27T18:37:14.408Z" }, + { url = "https://files.pythonhosted.org/packages/46/11/f333a06fc16236d5238bfe74daccbca41459dcd8d1fa952e8fbd5dccfb70/markupsafe-3.0.3-cp314-cp314-win32.whl", hash = "sha256:729586769a26dbceff69f7a7dbbf59ab6572b99d94576a5592625d5b411576b9", size = 14747, upload-time = "2025-09-27T18:37:15.36Z" }, + { url = "https://files.pythonhosted.org/packages/28/52/182836104b33b444e400b14f797212f720cbc9ed6ba34c800639d154e821/markupsafe-3.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:bdc919ead48f234740ad807933cdf545180bfbe9342c2bb451556db2ed958581", size = 15341, upload-time = "2025-09-27T18:37:16.496Z" }, + { url = "https://files.pythonhosted.org/packages/6f/18/acf23e91bd94fd7b3031558b1f013adfa21a8e407a3fdb32745538730382/markupsafe-3.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:5a7d5dc5140555cf21a6fefbdbf8723f06fcd2f63ef108f2854de715e4422cb4", size = 14073, upload-time = "2025-09-27T18:37:17.476Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f0/57689aa4076e1b43b15fdfa646b04653969d50cf30c32a102762be2485da/markupsafe-3.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:1353ef0c1b138e1907ae78e2f6c63ff67501122006b0f9abad68fda5f4ffc6ab", size = 11661, upload-time = "2025-09-27T18:37:18.453Z" }, + { url = "https://files.pythonhosted.org/packages/89/c3/2e67a7ca217c6912985ec766c6393b636fb0c2344443ff9d91404dc4c79f/markupsafe-3.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1085e7fbddd3be5f89cc898938f42c0b3c711fdcb37d75221de2666af647c175", size = 12069, upload-time = "2025-09-27T18:37:19.332Z" }, + { url = "https://files.pythonhosted.org/packages/f0/00/be561dce4e6ca66b15276e184ce4b8aec61fe83662cce2f7d72bd3249d28/markupsafe-3.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1b52b4fb9df4eb9ae465f8d0c228a00624de2334f216f178a995ccdcf82c4634", size = 25670, upload-time = "2025-09-27T18:37:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/50/09/c419f6f5a92e5fadde27efd190eca90f05e1261b10dbd8cbcb39cd8ea1dc/markupsafe-3.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed51ac40f757d41b7c48425901843666a6677e3e8eb0abcff09e4ba6e664f50", size = 23598, upload-time = "2025-09-27T18:37:21.177Z" }, + { url = "https://files.pythonhosted.org/packages/22/44/a0681611106e0b2921b3033fc19bc53323e0b50bc70cffdd19f7d679bb66/markupsafe-3.0.3-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f190daf01f13c72eac4efd5c430a8de82489d9cff23c364c3ea822545032993e", size = 23261, upload-time = "2025-09-27T18:37:22.167Z" }, + { url = "https://files.pythonhosted.org/packages/5f/57/1b0b3f100259dc9fffe780cfb60d4be71375510e435efec3d116b6436d43/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e56b7d45a839a697b5eb268c82a71bd8c7f6c94d6fd50c3d577fa39a9f1409f5", size = 24835, upload-time = "2025-09-27T18:37:23.296Z" }, + { url = "https://files.pythonhosted.org/packages/26/6a/4bf6d0c97c4920f1597cc14dd720705eca0bf7c787aebc6bb4d1bead5388/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:f3e98bb3798ead92273dc0e5fd0f31ade220f59a266ffd8a4f6065e0a3ce0523", size = 22733, upload-time = "2025-09-27T18:37:24.237Z" }, + { url = "https://files.pythonhosted.org/packages/14/c7/ca723101509b518797fedc2fdf79ba57f886b4aca8a7d31857ba3ee8281f/markupsafe-3.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5678211cb9333a6468fb8d8be0305520aa073f50d17f089b5b4b477ea6e67fdc", size = 23672, upload-time = "2025-09-27T18:37:25.271Z" }, + { url = "https://files.pythonhosted.org/packages/fb/df/5bd7a48c256faecd1d36edc13133e51397e41b73bb77e1a69deab746ebac/markupsafe-3.0.3-cp314-cp314t-win32.whl", hash = "sha256:915c04ba3851909ce68ccc2b8e2cd691618c4dc4c4232fb7982bca3f41fd8c3d", size = 14819, upload-time = "2025-09-27T18:37:26.285Z" }, + { url = "https://files.pythonhosted.org/packages/1a/8a/0402ba61a2f16038b48b39bccca271134be00c5c9f0f623208399333c448/markupsafe-3.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4faffd047e07c38848ce017e8725090413cd80cbc23d86e55c587bf979e579c9", size = 15426, upload-time = "2025-09-27T18:37:27.316Z" }, + { url = "https://files.pythonhosted.org/packages/70/bc/6f1c2f612465f5fa89b95bead1f44dcb607670fd42891d8fdcd5d039f4f4/markupsafe-3.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:32001d6a8fc98c8cb5c947787c5d08b0a50663d139f1305bac5885d98d9b40fa", size = 14146, upload-time = "2025-09-27T18:37:28.327Z" }, { url = "https://files.pythonhosted.org/packages/56/23/0d8c13a44bde9154821586520840643467aee574d8ce79a17da539ee7fed/markupsafe-3.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15d939a21d546304880945ca1ecb8a039db6b4dc49b2c5a400387cdae6a62e26", size = 11623, upload-time = "2025-09-27T18:37:29.296Z" }, { url = "https://files.pythonhosted.org/packages/fd/23/07a2cb9a8045d5f3f0890a8c3bc0859d7a47bfd9a560b563899bec7b72ed/markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc", size = 12049, upload-time = "2025-09-27T18:37:30.234Z" }, { url = "https://files.pythonhosted.org/packages/bc/e4/6be85eb81503f8e11b61c0b6369b6e077dcf0a74adbd9ebf6b349937b4e9/markupsafe-3.0.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f4b68347f8c5eab4a13419215bdfd7f8c9b19f2b25520968adfad23eb0ce60c", size = 21923, upload-time = "2025-09-27T18:37:31.177Z" }, @@ -1634,6 +1761,22 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/55/92/afed3d497f7c186dc71e6ee6d4fcb0acfa5f7d0a1a2878f8beae379ae0cc/tomli-2.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad805ea85eda330dbad64c7ea7a4556259665bdf9d2672f5dccc740eb9d3ca05", size = 248909, upload-time = "2025-10-08T22:01:23.859Z" }, { url = "https://files.pythonhosted.org/packages/f8/84/ef50c51b5a9472e7265ce1ffc7f24cd4023d289e109f669bdb1553f6a7c2/tomli-2.3.0-cp313-cp313-win32.whl", hash = "sha256:97d5eec30149fd3294270e889b4234023f2c69747e555a27bd708828353ab606", size = 96946, upload-time = "2025-10-08T22:01:24.893Z" }, { url = "https://files.pythonhosted.org/packages/b2/b7/718cd1da0884f281f95ccfa3a6cc572d30053cba64603f79d431d3c9b61b/tomli-2.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0c95ca56fbe89e065c6ead5b593ee64b84a26fca063b5d71a1122bf26e533999", size = 107705, upload-time = "2025-10-08T22:01:26.153Z" }, + { url = "https://files.pythonhosted.org/packages/19/94/aeafa14a52e16163008060506fcb6aa1949d13548d13752171a755c65611/tomli-2.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:cebc6fe843e0733ee827a282aca4999b596241195f43b4cc371d64fc6639da9e", size = 154244, upload-time = "2025-10-08T22:01:27.06Z" }, + { url = "https://files.pythonhosted.org/packages/db/e4/1e58409aa78eefa47ccd19779fc6f36787edbe7d4cd330eeeedb33a4515b/tomli-2.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4c2ef0244c75aba9355561272009d934953817c49f47d768070c3c94355c2aa3", size = 148637, upload-time = "2025-10-08T22:01:28.059Z" }, + { url = "https://files.pythonhosted.org/packages/26/b6/d1eccb62f665e44359226811064596dd6a366ea1f985839c566cd61525ae/tomli-2.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c22a8bf253bacc0cf11f35ad9808b6cb75ada2631c2d97c971122583b129afbc", size = 241925, upload-time = "2025-10-08T22:01:29.066Z" }, + { url = "https://files.pythonhosted.org/packages/70/91/7cdab9a03e6d3d2bb11beae108da5bdc1c34bdeb06e21163482544ddcc90/tomli-2.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0eea8cc5c5e9f89c9b90c4896a8deefc74f518db5927d0e0e8d4a80953d774d0", size = 249045, upload-time = "2025-10-08T22:01:31.98Z" }, + { url = "https://files.pythonhosted.org/packages/15/1b/8c26874ed1f6e4f1fcfeb868db8a794cbe9f227299402db58cfcc858766c/tomli-2.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b74a0e59ec5d15127acdabd75ea17726ac4c5178ae51b85bfe39c4f8a278e879", size = 245835, upload-time = "2025-10-08T22:01:32.989Z" }, + { url = "https://files.pythonhosted.org/packages/fd/42/8e3c6a9a4b1a1360c1a2a39f0b972cef2cc9ebd56025168c4137192a9321/tomli-2.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5870b50c9db823c595983571d1296a6ff3e1b88f734a4c8f6fc6188397de005", size = 253109, upload-time = "2025-10-08T22:01:34.052Z" }, + { url = "https://files.pythonhosted.org/packages/22/0c/b4da635000a71b5f80130937eeac12e686eefb376b8dee113b4a582bba42/tomli-2.3.0-cp314-cp314-win32.whl", hash = "sha256:feb0dacc61170ed7ab602d3d972a58f14ee3ee60494292d384649a3dc38ef463", size = 97930, upload-time = "2025-10-08T22:01:35.082Z" }, + { url = "https://files.pythonhosted.org/packages/b9/74/cb1abc870a418ae99cd5c9547d6bce30701a954e0e721821df483ef7223c/tomli-2.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:b273fcbd7fc64dc3600c098e39136522650c49bca95df2d11cf3b626422392c8", size = 107964, upload-time = "2025-10-08T22:01:36.057Z" }, + { url = "https://files.pythonhosted.org/packages/54/78/5c46fff6432a712af9f792944f4fcd7067d8823157949f4e40c56b8b3c83/tomli-2.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:940d56ee0410fa17ee1f12b817b37a4d4e4dc4d27340863cc67236c74f582e77", size = 163065, upload-time = "2025-10-08T22:01:37.27Z" }, + { url = "https://files.pythonhosted.org/packages/39/67/f85d9bd23182f45eca8939cd2bc7050e1f90c41f4a2ecbbd5963a1d1c486/tomli-2.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f85209946d1fe94416debbb88d00eb92ce9cd5266775424ff81bc959e001acaf", size = 159088, upload-time = "2025-10-08T22:01:38.235Z" }, + { url = "https://files.pythonhosted.org/packages/26/5a/4b546a0405b9cc0659b399f12b6adb750757baf04250b148d3c5059fc4eb/tomli-2.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a56212bdcce682e56b0aaf79e869ba5d15a6163f88d5451cbde388d48b13f530", size = 268193, upload-time = "2025-10-08T22:01:39.712Z" }, + { url = "https://files.pythonhosted.org/packages/42/4f/2c12a72ae22cf7b59a7fe75b3465b7aba40ea9145d026ba41cb382075b0e/tomli-2.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c5f3ffd1e098dfc032d4d3af5c0ac64f6d286d98bc148698356847b80fa4de1b", size = 275488, upload-time = "2025-10-08T22:01:40.773Z" }, + { url = "https://files.pythonhosted.org/packages/92/04/a038d65dbe160c3aa5a624e93ad98111090f6804027d474ba9c37c8ae186/tomli-2.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5e01decd096b1530d97d5d85cb4dff4af2d8347bd35686654a004f8dea20fc67", size = 272669, upload-time = "2025-10-08T22:01:41.824Z" }, + { url = "https://files.pythonhosted.org/packages/be/2f/8b7c60a9d1612a7cbc39ffcca4f21a73bf368a80fc25bccf8253e2563267/tomli-2.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8a35dd0e643bb2610f156cca8db95d213a90015c11fee76c946aa62b7ae7e02f", size = 279709, upload-time = "2025-10-08T22:01:43.177Z" }, + { url = "https://files.pythonhosted.org/packages/7e/46/cc36c679f09f27ded940281c38607716c86cf8ba4a518d524e349c8b4874/tomli-2.3.0-cp314-cp314t-win32.whl", hash = "sha256:a1f7f282fe248311650081faafa5f4732bdbfef5d45fe3f2e702fbc6f2d496e0", size = 107563, upload-time = "2025-10-08T22:01:44.233Z" }, + { url = "https://files.pythonhosted.org/packages/84/ff/426ca8683cf7b753614480484f6437f568fd2fda2edbdf57a2d3d8b27a0b/tomli-2.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:70a251f8d4ba2d9ac2542eecf008b3c8a9fc5c3f9f02c56a9d7952612be2fdba", size = 119756, upload-time = "2025-10-08T22:01:45.234Z" }, { url = "https://files.pythonhosted.org/packages/77/b8/0135fadc89e73be292b473cb820b4f5a08197779206b33191e801feeae40/tomli-2.3.0-py3-none-any.whl", hash = "sha256:e95b1af3c5b07d9e643909b5abbec77cd9f1217e6d0bca72b0234736b9fb1f1b", size = 14408, upload-time = "2025-10-08T22:01:46.04Z" }, ] From 2cc2b60be3bbd6b687531b788bd2f7edd39e6001 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=9Fuayip=20=C3=BCz=C3=BClmez?= <17948971+realsuayip@users.noreply.github.com> Date: Fri, 14 Nov 2025 01:37:34 +0000 Subject: [PATCH 77/78] fix: token request throws an error when client is provided in body (#1252) It ends up overwriting request.client which should be the application. This fix ensures request client is set to the client_id --- AUTHORS | 1 + CHANGELOG.md | 6 +++- oauth2_provider/oauth2_validators.py | 31 ++++++++++++------ tests/test_authorization_code.py | 21 ++++++++++++ tests/test_oauth2_validators.py | 48 ++++++++++++++++++++++++++-- 5 files changed, 95 insertions(+), 12 deletions(-) diff --git a/AUTHORS b/AUTHORS index 2d8d5465b..e35ed4cb9 100644 --- a/AUTHORS +++ b/AUTHORS @@ -102,6 +102,7 @@ Peter Karman Peter McDonald Petr Dlouhý pySilver +@realsuayip Rodney Richardson Rustem Saiargaliev Rustem Saiargaliev diff --git a/CHANGELOG.md b/CHANGELOG.md index 1821a8ae1..eadc051d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,12 +9,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added * Support for Django 5.2 * Support for Python 3.14 (Django >= 5.2.8) +* #1539 Add device authorization grant support + ### Fixed +* #1252 Fix crash when 'client' is in token request body + @@ -27,7 +32,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * #1506 Support for Wildcard Origin and Redirect URIs - Adds a new setting [ALLOW_URL_WILDCARDS](https://django-oauth-toolkit.readthedocs.io/en/latest/settings.html#allow-uri-wildcards). This feature is useful for working with CI service such as cloudflare, netlify, and vercel that offer branch deployments for development previews and user acceptance testing. * #1586 Turkish language support added -* #1539 Add device authorization grant support ### Changed The project is now hosted in the django-oauth organization. diff --git a/oauth2_provider/oauth2_validators.py b/oauth2_provider/oauth2_validators.py index ec974b0c6..a202a6a82 100644 --- a/oauth2_provider/oauth2_validators.py +++ b/oauth2_provider/oauth2_validators.py @@ -214,19 +214,31 @@ def _load_application(self, client_id, request): If request.client was not set, load application instance for given client_id and store it in request.client """ - - # we want to be sure that request has the client attribute! - assert hasattr(request, "client"), '"request" instance has no "client" attribute' - + if request.client: + # check for cached client, to save the db hit if this has already been loaded + if not isinstance(request.client, Application): + # resetting request.client (client_id=%r): not an Application, something else set request.client erroneously + request.client = None + elif request.client.client_id != client_id: + # resetting request.client (client_id=%r): request.client.client_id does not match the given client_id + request.client = None + elif not request.client.is_usable(request): + # resetting request.client (client_id=%r): request.client is a valid Application, but is not usable + request.client = None + else: + # request.client is a valid Application, reusing it + return request.client try: - request.client = request.client or Application.objects.get(client_id=client_id) - # Check that the application can be used (defaults to always True) - if not request.client.is_usable(request): - log.debug("Failed body authentication: Application %r is disabled" % (client_id)) + # cache not hit, loading application from database for client_id %r + client = Application.objects.get(client_id=client_id) + if not client.is_usable(request): + # Failed to load application: Application %r is not usable return None + request.client = client + # Loaded application with client_id %r from database return request.client except Application.DoesNotExist: - log.debug("Failed body authentication: Application %r does not exist" % (client_id)) + # Failed to load application: Application with client_id %r does not exist return None def _set_oauth2_error_on_request(self, request, access_token, scopes): @@ -289,6 +301,7 @@ def client_authentication_required(self, request, *args, **kwargs): pass self._load_application(request.client_id, request) + log.debug("Determining if client authentication is required for client %r", request.client) if request.client: return request.client.client_type == AbstractApplication.CLIENT_CONFIDENTIAL diff --git a/tests/test_authorization_code.py b/tests/test_authorization_code.py index 360fac957..369b1939f 100644 --- a/tests/test_authorization_code.py +++ b/tests/test_authorization_code.py @@ -1308,6 +1308,27 @@ def test_request_body_params(self): self.assertEqual(content["scope"], "read write") self.assertEqual(content["expires_in"], self.oauth2_settings.ACCESS_TOKEN_EXPIRE_SECONDS) + def test_request_body_params_client_typo(self): + """ + Verify that using incorrect parameter name (client instead of client_id) returns invalid_client error + """ + self.client.login(username="test_user", password="123456") + authorization_code = self.get_auth() + + token_request_data = { + "grant_type": "authorization_code", + "code": authorization_code, + "redirect_uri": "http://example.org", + "client": self.application.client_id, + "client_secret": CLEARTEXT_SECRET, + } + + response = self.client.post(reverse("oauth2_provider:token"), data=token_request_data) + self.assertEqual(response.status_code, 401) + + content = json.loads(response.content.decode("utf-8")) + self.assertEqual(content["error"], "invalid_client") + def test_public(self): """ Request an access token using client_type: public diff --git a/tests/test_oauth2_validators.py b/tests/test_oauth2_validators.py index 7e7e46de7..3fb292060 100644 --- a/tests/test_oauth2_validators.py +++ b/tests/test_oauth2_validators.py @@ -216,8 +216,52 @@ def test_client_authentication_required(self): self.request.client = "" self.assertTrue(self.validator.client_authentication_required(self.request)) - def test_load_application_fails_when_request_has_no_client(self): - self.assertRaises(AssertionError, self.validator.authenticate_client_id, "client_id", {}) + def test_load_application_loads_client_id_when_request_has_no_client(self): + self.request.client = None + application = self.validator._load_application("client_id", self.request) + self.assertEqual(application, self.application) + + def test_load_application_uses_cached_when_request_has_valid_client_matching_client_id(self): + self.request.client = self.application + application = self.validator._load_application("client_id", self.request) + self.assertIs(application, self.application) + self.assertIs(self.request.client, self.application) + + def test_load_application_succeeds_when_request_has_invalid_client_valid_client_id(self): + self.request.client = 'invalid_client' + application = self.validator._load_application("client_id", self.request) + self.assertEqual(application, self.application) + self.assertEqual(self.request.client, self.application) + + def test_load_application_overwrites_client_on_client_id_mismatch(self): + another_application = Application.objects.create( + client_id="another_client_id", + client_secret=CLEARTEXT_SECRET, + user=self.user, + client_type=Application.CLIENT_PUBLIC, + authorization_grant_type=Application.GRANT_PASSWORD, + ) + self.request.client = another_application + application = self.validator._load_application("client_id", self.request) + self.assertEqual(application, self.application) + self.assertEqual(self.request.client, self.application) + another_application.delete() + + @mock.patch.object(Application, "is_usable") + def test_load_application_returns_none_when_client_not_usable_cached(self, mock_is_usable): + mock_is_usable.return_value = False + self.request.client = self.application + application = self.validator._load_application("client_id", self.request) + self.assertIsNone(application) + self.assertIsNone(self.request.client) + + @mock.patch.object(Application, "is_usable") + def test_load_application_returns_none_when_client_not_usable_db_lookup(self, mock_is_usable): + mock_is_usable.return_value = False + self.request.client = None + application = self.validator._load_application("client_id", self.request) + self.assertIsNone(application) + self.assertIsNone(self.request.client) def test_rotate_refresh_token__is_true(self): self.assertTrue(self.validator.rotate_refresh_token(mock.MagicMock())) From bade920ee5951c22f9aa20ac39f234a7e8498968 Mon Sep 17 00:00:00 2001 From: Tuhin Mitra <48290754+Tuhin-thinks@users.noreply.github.com> Date: Fri, 14 Nov 2025 07:36:02 +0530 Subject: [PATCH 78/78] Fixed Handled error in OAuth2ExtraTokenMiddleware when authheader has `Bearer` with no token-string following up (#1502) * fix: error when authheader is `Bearer ` with no token-string it throws an error. --- AUTHORS | 3 +- CHANGELOG.md | 1 + oauth2_provider/middleware.py | 5 +- tests/test_oauth2_provider_middleware.py | 98 ++++++++++++++++++++++++ 4 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 tests/test_oauth2_provider_middleware.py diff --git a/AUTHORS b/AUTHORS index e35ed4cb9..4ebe787cd 100644 --- a/AUTHORS +++ b/AUTHORS @@ -128,4 +128,5 @@ Yaroslav Halchenko Yuri Savin Miriam Forner Alex Kerkum -q0w +Tuhin Mitra +q0w \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index eadc051d3..a29772c13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 --> ### Fixed * #1252 Fix crash when 'client' is in token request body +* #1496 Fix error when Bearer token string is empty but preceded by `Bearer` keyword. diff --git a/oauth2_provider/middleware.py b/oauth2_provider/middleware.py index 65c9cf03c..5a8a86d87 100644 --- a/oauth2_provider/middleware.py +++ b/oauth2_provider/middleware.py @@ -52,8 +52,9 @@ def __init__(self, get_response): def __call__(self, request): authheader = request.META.get("HTTP_AUTHORIZATION", "") - if authheader.startswith("Bearer"): - tokenstring = authheader.split()[1] + splits = authheader.split(maxsplit=1) + if authheader.startswith("Bearer") and len(splits) == 2: + tokenstring = splits[1] AccessToken = get_access_token_model() try: token_checksum = hashlib.sha256(tokenstring.encode("utf-8")).hexdigest() diff --git a/tests/test_oauth2_provider_middleware.py b/tests/test_oauth2_provider_middleware.py new file mode 100644 index 000000000..90610f78b --- /dev/null +++ b/tests/test_oauth2_provider_middleware.py @@ -0,0 +1,98 @@ +import datetime +import hashlib + +from django.contrib.auth import get_user_model +from django.test import RequestFactory, TestCase + +from oauth2_provider.middleware import OAuth2ExtraTokenMiddleware +from oauth2_provider.models import get_access_token_model, get_application_model + + +Application = get_application_model() +AccessToken = get_access_token_model() +User = get_user_model() + + +class TestOAuth2ExtraTokenMiddleware(TestCase): + def setUp(self): + self.factory = RequestFactory() + self.middleware = OAuth2ExtraTokenMiddleware(lambda r: None) + + # Create test user and application for valid token tests + self.user = User.objects.create_user("test_user", "test@example.com", "123456") + self.application = Application.objects.create( + name="Test Application", + user=self.user, + client_type=Application.CLIENT_CONFIDENTIAL, + authorization_grant_type=Application.GRANT_AUTHORIZATION_CODE, + ) + + def test_malformed_bearer_header_no_token(self): + """Test that 'Authorization: Bearer' without token doesn't crash""" + request = self.factory.get("/", HTTP_AUTHORIZATION="Bearer") + + # This should not raise an IndexError + _ = self.middleware(request) + + # Should not have access_token attribute + self.assertFalse(hasattr(request, "access_token")) + + def test_malformed_bearer_header_empty_token(self): + """Test that 'Authorization: Bearer ' with empty token doesn't crash""" + request = self.factory.get("/", HTTP_AUTHORIZATION="Bearer ") + + # This should not raise an IndexError + _ = self.middleware(request) + + # Should not have access_token attribute + self.assertFalse(hasattr(request, "access_token")) + + def test_valid_bearer_token(self): + """Test that valid bearer token works correctly""" + # Create a valid access token + token_string = "test-token-12345" + token_checksum = hashlib.sha256(token_string.encode("utf-8")).hexdigest() + access_token = AccessToken.objects.create( + user=self.user, + scope="read", + expires=datetime.datetime.now() + datetime.timedelta(days=1), + token=token_string, + token_checksum=token_checksum, + application=self.application, + ) + + request = self.factory.get("/", HTTP_AUTHORIZATION=f"Bearer {token_string}") + + _ = self.middleware(request) + + # Should have access_token attribute set + self.assertTrue(hasattr(request, "access_token")) + self.assertEqual(request.access_token, access_token) + + def test_invalid_bearer_token(self): + """Test that invalid bearer token doesn't crash but doesn't set access_token""" + request = self.factory.get("/", HTTP_AUTHORIZATION="Bearer invalid-token-xyz") + + # This should not raise an exception + _ = self.middleware(request) + + # Should not have access_token attribute + self.assertFalse(hasattr(request, "access_token")) + + def test_no_authorization_header(self): + """Test that request without Authorization header works normally""" + request = self.factory.get("/") + + _ = self.middleware(request) + + # Should not have access_token attribute + self.assertFalse(hasattr(request, "access_token")) + + def test_non_bearer_authorization_header(self): + """Test that non-Bearer authorization headers are ignored""" + request = self.factory.get("/", HTTP_AUTHORIZATION="Basic dXNlcjpwYXNz") + + _ = self.middleware(request) + + # Should not have access_token attribute + self.assertFalse(hasattr(request, "access_token"))